# Snowboard!.py
# A Simple Snowboard game using pygame.


import os
import sys
import random
import pygame
from pygame.locals import *


# Create sprite class for moving pieces
class SimpleSprite:
  	
   def __init__( self, image ):
 	#Load image, set square     
      self.image = image
      self.rectangle = image.get_rect()

   def place( self, screen ):
      #Place on the screen 
      return screen.blit( self.image, self.rectangle )

   def remove( self, screen, background ):
      #Put under bakcgroudn to remove   
      return screen.blit( background, self.rectangle,
         self.rectangle )


# Player sprite needs normal, lef, right, and crash graphics
class Player( SimpleSprite ):
   #Need four states 
   def __init__( self, images, crashImage,
      centerX = 0, centerY = 0 ):

      #Initial image and plarer state
      self.movingImages = images
      self.crashImage = crashImage
      self.centerX = centerX
      self.centerY = centerY
      self.playerPosition = 1   # start player facing down
      self.speed = 0
      self.Load_Image()

   def Load_Image( self ):
      #Load image
      if self.playerPosition == -1:   # player has crashed
         image = self.crashImage        
      else:
         image = self.movingImages[ self.playerPosition ]

      SimpleSprite.__init__( self, image )
      self.rectangle.centerx = self.centerX
      self.rectangle.centery = self.centerY

   def Move_Left( self ):
      #Change to move left graphic
      if self.playerPosition == -1:   # player crashed
         self.speed = 1
         self.playerPosition = 0      # move left of obstacle
      elif self.playerPosition > 0:
         self.playerPosition -= 1

      self.Load_Image()

   def Move_Right( self ):
      #Change to move right graphic
      if self.playerPosition == -1:   # player crashed
         self.speed = 1
         self.playerPosition = 2      # move right of obstacle
      elif self.playerPosition < ( len( self.movingImages ) - 1 ):
         self.playerPosition += 1

      self.Load_Image()
	#Speedign up and slowing down
   def Decrease_Speed( self ):

      if self.speed > 0:
         self.speed -= 1

   def Increase_Speed( self ):

      if self.speed < 10:      
         self.speed += 1

      # player crashed, start player facing down
      if self.playerPosition == -1:
         self.playerPosition = 1
         self.Load_Image()

   def Collision( self ):
      #Change graphic to player crashed

      self.speed = 0   
      self.playerPosition = -1
      self.Load_Image()

   def Collision_Watch( self ):
      #Slightly smaller box for collisions and overlap
      return self.rectangle.inflate( -20, -20 )

   def Are_We_Moving( self ):
      #Player is not moving - speed=0

      if self.speed == 0:
         return 0
      else:
         return 1
      
   def Distance_Moved( self ):
      #Go faster when facing straight down

      xIncrement, yIncrement = 0, 0      

      if self.Are_We_Moving():

         if self.playerPosition == 1:
            xIncrement = 0
            yIncrement = 2 * self.speed
         else:
            xIncrement = ( self.playerPosition - 1 ) * self.speed
            yIncrement = self.speed

      return xIncrement, yIncrement


# Load up the trees to avoid
class Obstacle( SimpleSprite ):

   def __init__( self, image, centerX = 0, centerY = 0 ):
      #Load tree image and place rectangle
      SimpleSprite.__init__( self, image )
      # move tree to specified location
      self.positiveRectangle = self.rectangle
      self.positiveRectangle.centerx = centerX
      self.positiveRectangle.centery = centerY

      # display tree in moved position to buffer visible area
      self.rectangle = self.positiveRectangle.move( -60, -60 )

   def move( self, xIncrement, yIncrement ):
      #Move trees up as player moves down the slope
      
      self.positiveRectangle.centerx -= xIncrement
      self.positiveRectangle.centery -= yIncrement

      # change position for next pass
      if self.positiveRectangle.centery < 25:
         self.positiveRectangle[ 0 ] += \
            random.randrange( -640, 640 )

      # keep rectangle values from overflowing
      self.positiveRectangle[ 0 ] %= 760
      self.positiveRectangle[ 1 ] %= 600

      # display obstacle in moved position to buffer visible area
      self.rectangle = self.positiveRectangle.move( -60, -60 )

   def Collision_Watch( self ):
      #Make collision box smaller than graphic
      return self.rectangle.inflate( -20, -20 )

class FinishLine( SimpleSprite ):
   #Movable finish line is also a SimpleSprite

   def __init__( self, image, centerX = 0, centerY = 0 ):
      #Load image and initialize

      SimpleSprite.__init__( self, image )

      # move FinishLine to specified location
      self.rectangle.centerx = centerX
      self.rectangle.centery = centerY

   def move( self, xIncrement, yIncrement ):
      #Finish can go up and down for game difficulty
      
      self.rectangle.centerx -= xIncrement
      self.rectangle.centery -= yIncrement

# Generic function to place a message on screen
def Display_Message( message, screen, background ):
   font = pygame.font.Font( None, 48 )
   text = font.render( message, 1, ( 1, 1, 1 ) )
   textPosition = text.get_rect()
   textPosition.centerx = background.get_rect().centerx
   textPosition.centery = background.get_rect().centery
   return screen.blit( text, textPosition )


def main():

   # define constants
   WAIT_TIME = 20            # time to wait between frames
   COURSE_DEPTH = 25 * 480   # 50 screens long
   NUMBER_TREES = 5     # controls number of trees

   # variables
   distanceTraveled = 0      # vertical distance
   nextTime = 0              # time to generate next frame
   courseOver = 0            # the course has not been completed
   allTrees = []         # randomly generated obstacles
   dirtyRectangles = []      # screen positions that have changed
   timePack = None         # current time clock pack on screen
   timeLeft = 60             # time left to finish course


   # find path to sounds
   collisionFile = os.path.join( "data", "THUMP.wav" )
   chimeFile = os.path.join( "data", "MMMMM1.wav" )
   startFile = os.path.join( "data", "THX.wav" )
   applauseFile = os.path.join( "data", "WOW2.wav" )
   gameOverFile = os.path.join( "data", "BUZZER.wav" )

   # find path to images
   girlFiles = []
   girlFiles.append( os.path.join( "data", "surferLeft.gif" ) )
   girlFiles.append( os.path.join( "data", "surfer.gif" ) )
   girlFiles.append( os.path.join( "data", "surferRight.gif" ) )
   girlCrashFile = os.path.join( "data", "surferCrashed.gif" )
   treeFile = os.path.join( "data", "tree.gif" )
   timePackFile = os.path.join( "data", "time.gif" )
   game_background = os.path.join("data", "background2.png")

   
   # initialize pygame
   pygame.init()
   screen = pygame.display.set_mode( ( 640, 480 ) )
   
   pygame.display.set_caption( "Snowboard!" )
   pygame.mouse.set_visible( 0 )   # make mouse invisible

   # grab background image
   background = pygame.image.load( game_background ).convert()	
   # blit background onto screen and update entire display
   screen.blit( background, ( 0, 0 ) )
   pygame.display.update()

   collisionSound = pygame.mixer.Sound( collisionFile )
   chimeSound = pygame.mixer.Sound( chimeFile )
   startSound = pygame.mixer.Sound( startFile )
   applauseSound = pygame.mixer.Sound( applauseFile )
   gameOverSound = pygame.mixer.Sound( gameOverFile )

   # load images, convert pixel format and make white transparent
   loadedImages = []
   
   for file in girlFiles:
      surface = pygame.image.load( file ).convert()
      surface.set_colorkey( surface.get_at( ( 0, 0 ) ) )
      loadedImages.append( surface )

   # load crash image
   girlCrashImage = pygame.image.load( girlCrashFile ).convert()
   girlCrashImage.set_colorkey( girlCrashImage.get_at( ( 0, 0 ) ) )
 
   # load tree image
   treeImage = pygame.image.load( treeFile ).convert()
   treeImage.set_colorkey( treeImage.get_at( ( 0, 0 ) ) )

   # load timePack image
   timePackImage = pygame.image.load( timePackFile ).convert()
   timePackImage.set_colorkey( surface.get_at( ( 0, 0 ) ) )

   # initialize the girl-snowboarder
   centerX = screen.get_width() / 2
   theGirl = Player( loadedImages, girlCrashImage, centerX, 25 )
   
   # place tree in randomly generated spot
   for i in range( NUMBER_TREES ):
      allTrees.append( Obstacle( treeImage,
         random.randrange( 0, 760 ), random.randrange( 0, 600 ) ) )

   

   startSound.play()
   pygame.time.set_timer( USEREVENT, 1000 )

   while not courseOver:
   
      # wait if moving too fast for selected frame rate
      currentTime = pygame.time.get_ticks()
      
      if currentTime < nextTime:
         pygame.time.delay( nextTime - currentTime )
   
      nextTime = currentTime + WAIT_TIME
   
      # remove objects from screen
      dirtyRectangles.append( theGirl.remove( screen,
         background ) )
      
      for tree in allTrees:    
         dirtyRectangles.append( tree.remove( screen,
            background ) )
   
      if timePack is not None:
         dirtyRectangles.append( timePack.remove( screen,
            background ) )

      # get next event from event queue
      event = pygame.event.poll()

      # if player quits program or presses escape key
      if event.type == QUIT or \
         ( event.type == KEYDOWN and event.key == K_ESCAPE ):
         sys.exit()

      # if up arrow key was pressed, slow player
      elif event.type == KEYDOWN and event.key == K_UP:
         theGirl.Decrease_Speed()

      # if down arrow key was pressed, speed up player
      elif event.type == KEYDOWN and event.key == K_DOWN:
         theGirl.Increase_Speed()      

      # if right arrow key was pressed, move player right
      elif event.type == KEYDOWN and event.key == K_RIGHT:
         theGirl.Move_Right()

      # if left arrow key was pressed, move player left 
      elif event.type == KEYDOWN and event.key == K_LEFT:
         theGirl.Move_Left()

      # one second has passed
      elif event.type == USEREVENT:
         timeLeft -= 1
      
      # 1 in 100 odds of creating new timePack
      if timePack is None and not random.randrange( 100 ):
         timePack = FinishLine( timePackImage,
            random.randrange( 0, 640 ), 480 )

      # update obstacle and timePack positions if player moving
      if theGirl.Are_We_Moving():
         xIncrement, yIncrement = theGirl.Distance_Moved()

         for tree in allTrees:
            tree.move( xIncrement, yIncrement )

         if timePack is not None:
            timePack.move( xIncrement, yIncrement )

            if timePack.rectangle.bottom < 0:
               timePack = None         

         distanceTraveled += yIncrement
   
      # check for collisions with smaller bounding boxes
      # for better playability
      treeBoxes = []

      for tree in allTrees:
         treeBoxes.append( tree.Collision_Watch() )

      # retrieve list of obstacles colliding with player
      Collision = theGirl.Collision_Watch().collidelist( treeBoxes )

      # move tree one screen down
      if Collision != -1:
         collisionSound.play()
         allTrees[ Collision ].move( 0, -540 )
         theGirl.Collision()
         timeLeft -= 5

      # determine whether player has gotten timePack
      if timePack is not None:

         if theGirl.Collision_Watch().colliderect(
            timePack.rectangle ):
            chimeSound.play()
            timePack = None
            timeLeft += 5

      # place objects on screen
      dirtyRectangles.append( theGirl.place( screen ) )

      for tree in allTrees:   
         dirtyRectangles.append( tree.place( screen ) )

      if timePack is not None:
         dirtyRectangles.append( timePack.place( screen ) )

     
      # update changed areas of display
      pygame.display.update( dirtyRectangles )
      dirtyRectangles = []

      # check for course end
      if distanceTraveled > COURSE_DEPTH:
         courseOver = 1

      # check for game over
      elif timeLeft <= 0:
         break

   if courseOver:
      applauseSound.play()
      message = "You Win!"
   else:
      gameOverSound.play()
      message = "Game Over!"

   pygame.display.update( Display_Message( message, screen,
      background ) )

   # wait until player wants to close program
   while 1:
      event = pygame.event.poll()

      if event.type == QUIT or \
         ( event.type == KEYDOWN and event.key == K_ESCAPE ):
         break

if __name__ == "__main__":
   main()

