"Game Engine handler base class- used in GameMenu and GamePlay." import pygame from pygame.locals import * import random, math import game, gfx, input, snd from sprites import objship, objtext, objblast, objbomb, objflame from sprites import objtextmessage, objsstext from sprites.objgroundexplosion import GroundExplosion from sprites.objairexplosion import AirExplosion import scoredisplay, exhaust, levels from basegamehandler import BaseGameHandler from text import * def load_game_resources(): pass class GameEngine(BaseGameHandler): def __init__(self, prevhandler): BaseGameHandler.__init__(self) self.staticobjs = [] self.textobjs = [] self.airobjs = [] self.rocketobjs = [] self.groundobjs = [] self.blastobjs = [] self.bombobjs = [] self.explosionobjs = [] self.exhaustobjs = [] self.objlists = [self.textobjs, self.groundobjs, self.airobjs, self.bombobjs,self.blastobjs, self.explosionobjs,self.exhaustobjs,self.staticobjs] self.prevhandler = prevhandler self.state = '' self.statetick = self.dummyfunc self.lasttick = pygame.time.get_ticks() self.speedadjust = 1.0 self.timer = 0 # state transition timer self.objectivetimer = 0 # objective met transition timer self.objectivemet = 0 self.start_timer = game.start_timer self.start_pos = game.start_pos self.player = objship.Ship() game.player = self.player self.exhaust = exhaust.Exhaust() self.levels = levels.Levels(self.groundobjs,self.airobjs,self.exhaust) self.bgfill = gfx.surface.fill # override handler translate method self.translate = input.translate # provide a mapping table self.input_mapping = { input.END: (self.userquit, []), input.UP: (self.player.cmd_up, []), input.DOWN: (self.player.cmd_down, []), input.LEFT: (self.player.cmd_left, []), input.RIGHT: (self.player.cmd_right, []), input.PRESS: (self.player.cmd_turbo, [1]), input.RELEASE:(self.player.cmd_turbo, [0]), input.FIRE: (self.player.cmd_fire,[]), input.PAUSE: (self.pause,[]), input.DEBUG: (self.toggle_debug,[]), input.SELECT: (self.do_nothing,[]), input.CHEAT: (self.do_nothing,[]) } def input(self, i): func, args = self.input_mapping[i] func(*args) def event(self, e): pass def get_events(self): return pygame.event.get(input.joystick_events) + input.get_key_events() # this uses key presses def run(self): """calculate speed adjustment & do state_tick""" now = pygame.time.get_ticks() # leaving single frame mode if self.lasttick == 0: self.lasttick = now self.speedadjust = 1.0 self.statetick() return # speed adjust code not used right now #diff = now - self.lasttick #ratio = float(diff) / game.clock.fps_ticks #self.lasttick = now #if game.single_frame: # self.speedadjust = 1.0 #else: # self.speedadjust = max(ratio, 1.0) #print self.speedadjust # #self.statetick() def dummyfunc(self): pass def changestate(self, state): # call end function for old state getattr(self, self.state+'_end', self.dummyfunc)() # set new state and call start function self.state = state getattr(self, state+'_start', self.dummyfunc)() # set tick function self.statetick = getattr(self, state+'_tick') def runobjects(self, objects): G, B, S = gfx, self.background, self.speedadjust gfx.updatestars(B, G) self.exhaust.erase_tick_draw(B,G,S) for l, o in [(l, o) for l in objects for o in l]: o.erase(B) o.tick(S) if o.dead: o.erase(B) o.die() l.remove(o) for l in objects: for o in l: o.draw(G) def background(self, area): #return self.bgfill(0, area) return gfx.surface.blit(gfx.background,area,area) def tickleveltime(self, speedadjust=1): if game.timeleft: game.timeleft = game.timeleft - game.timetick * speedadjust if game.timeleft < 0: game.timeleft = 0.0 # normal play def normal_start(self): self.clocks = 0 def normal_tick(self): self.tickleveltime(self.speedadjust) self.clocks += 1 self.runobjects(self.objlists) self.check_nonplayer_collisions() self.check_player_collisions() self.addactors() self.scoredisplay.update(game.score,game.fuel,game.bombs,game.lives) if self.player.dead == 1 and self.player.exploding == 1: self.timer -= 1 if self.timer < 0: self.changestate('playerdie') elif self.objectivemet == 1: self.objectivetimer -=1 if self.objectivetimer < 0: self.changestate('levelend') def normal_end(self): pass # collision detection routines - these need refactoring def add_score(self,pointvalue): game.score += pointvalue if game.score < 0: game.score = 0 def objective_done(self): self.objectivemet = 1 self.objectivetimer = game.state_transition_time self.levels.mission_complete() # note: should be just one routine that can add both explosions? def add_groundexplosion(self,object,explosionlist=None): if explosionlist == None: explosionlist = self.explosionobjs # check to see if there's another explosion close by - if there is, don't add another. x = object.rect.centerx for exp in self.explosionobjs: if abs(x - exp.rect.centerx) < 20: return explosionlist.append(GroundExplosion(object.rect.centerx,object.dx)) def add_airexplosion(self,object,explosionlist=None): if explosionlist == None: explosionlist = self.explosionobjs explosionlist.append(AirExplosion(object.rect.centerx,object.rect.centery,object.dx,object.dy)) def check_nonplayer_collisions(self): new_explosionobjs = [] for e in self.explosionobjs: for g in self.groundobjs: if e.collide(g.rect): g.markdead() self.add_score(g.pointvalue) self.add_groundexplosion(g,new_explosionobjs) # is this object the mission objective? if g.objective == 1: self.objective_done() self.explosionobjs += new_explosionobjs for b in self.bombobjs: # has it hit the ground? if b.exploding == 1: b.dead = 1 self.add_groundexplosion(b) else: for g in self.groundobjs: if b.collide(g.rect): if g.bombable == 1: # bomb dies b.markdead() g.markdead() self.add_score(g.pointvalue) self.add_groundexplosion(g) # is this the mission objective? if g.objective == 1: self.objective_done() else: # it's like a tree or cactus (hack warning!!) b.dy = -b.gravity b.dx = (b.dx + game.groundspeed) * 0.5 - game.groundspeed # next ground object continue for b in self.blastobjs: for a in self.airobjs: if b.collide(a.rect): # blast dies b.markdead() self.add_airexplosion(a) # if the blast killed the air object... if a.markdead(): self.add_score(a.pointvalue) # is this the mission objective? if a.objective == 1: self.objective_done() # go on to next blast break for a in self.airobjs: if a.exploding == 1: a.dead = 1 if a.y > game.groundarena.bottom - game.ground_explosion_height: self.add_groundexplosion(a) else: self.add_airexplosion(a) # check player collisions def do_player_die(self): # check for cheat mode if game.shields == 0: self.player.die() self.player.active = 0 self.timer = game.state_transition_time def player_collides_with(self,objectlist,add_explosion): """check for collisions with each object in objectlist; if there is a collison, do the things necessary to kill player, and use the add_explosion method to add an explosion.""" crect = self.player.rect.inflate(-2,-2) for obj in objectlist: if obj.collide(crect): obj.dead = 1 self.add_score(obj.pointvalue) self.do_player_die() add_explosion(self.player) def check_player_collisions(self): """has player hit something- ground, ground objects, air objects?""" # has player hit ground? if self.player.dead == 0: if self.player.rect.bottom == game.groundarena.bottom: self.player.dead = 1 self.player.exploding = 1 self.timer = game.state_transition_time self.add_groundexplosion(self.player) # if player is below the bombing height, they can collide with ground objects if self.player.dead == 0 and self.player.shipstate == objship.ship_canbomb: self.player_collides_with(self.groundobjs,self.add_groundexplosion) # player can always collide with air objects if self.player.dead == 0: self.player_collides_with(self.airobjs,self.add_airexplosion) #player start def playerstart_start(self): game.fuel = game.start_fuel game.bombs = game.start_bombs def playerstart_tick(self): self.changestate('levelstart') def playerstart_end(self): pass #player die def playerdie_start(self): # check for cheat mode if game.infinite_lives == 0: game.lives -= 1 if game.lives <= 0: game.lives = 0 self.timer = game.state_transition_time self.scoredisplay.update(game.score,game.fuel,game.bombs,game.lives) def playerdie_tick(self): if game.lives <= 0: self.changestate('gameover') self.timer -= 1 if self.timer < 0: self.changestate('playerstart') self.runobjects(self.objlists) def playerdie_end(self): self.player.dead = 1 for o in [(o) for l in self.objlists for o in l]: o.dead = 1 self.runobjects(self.objlists) # level start def levelstart_start(self): # global variable warning! # the game menu messes this up game.player = self.player gfx.clearscreen() gfx.surface.set_clip() ground = pygame.Surface((game.ground_rect.width,game.ground_rect.height)).convert() ground.fill(game.ground_color) gfx.surface.blit(ground,game.ground_rect) gfx.dirty(game.ground_rect) gfx.surface.set_clip(game.groundarena) # init score display self.scoredisplay = scoredisplay.Scoredisplay(gfx.surface) self.scoredisplay.drawlegend() self.scoredisplay.update(game.score,game.fuel,game.bombs,game.lives) self.player.start(self.start_pos) self.staticobjs.append(self.player) self.player.dx = 1 self.objectivemet = 0 self.timer = game.state_transition_time self.player.flame = objflame.Flame(self.player) self.staticobjs.append(self.player.flame) # no player input allowed yet self.player.active = 0 # put up mission name size = 40 pos = pygame.Rect([game.arena.left,530,game.arena.width,size]) text = "Mission %d: %s" % (self.levels.get_mission_number(),self.levels.get_mission_descr()) self.mission_descr = objsstext.SubsurfaceText(gfx.surface,gfx.background, text,pos,BABELFISH_FONTPATH,size,BLUE_GREY,center=1) self.mission_descr.draw() self.levels.start_mission_over() def levelstart_tick(self): self.timer -= 1 if self.timer < 0: self.changestate('normal') self.player.x += self.player.dx self.player.rect.topleft = [self.player.x,self.player.y] self.runobjects(self.objlists) def levelstart_end(self): # now accept player input self.player.move = [0,0] self.player.blasting = 0 self.player.active = 1 # level end def levelend_start(self): self.timer = game.state_transition_time # accept no input now self.player.active = 0 self.levelend_message = objtextmessage.TextMessage(gfx.surface,gfx.background,'Mission Completed!') # don't draw it now- GamePlay will subclass this method and draw it there... #self.levelend_message.draw() def levelend_tick(self): self.timer -= 1 if self.timer < 0: # go to next mission if self.levels.next_mission(): self.changestate('levelstart') else: # no more missions, game is over! self.changestate('gamewon') # move player offscreen self.player.dx += 0.04 if self.player.dx > 2.6: self.player.dx = 2.6 self.player.dy += -0.02 if self.player.dy < -1.2: self.player.dy = -1.2 self.player.x += self.player.dx self.player.y += self.player.dy self.player.rect.topleft = [self.player.x,self.player.y] self.runobjects(self.objlists) self.check_nonplayer_collisions() self.player.change_state(objship.ship_canshoot) def levelend_end(self): for o in [(o) for l in self.objlists for o in l]: o.dead = 1 self.runobjects(self.objlists) self.check_nonplayer_collisions() self.levelend_message.erase() self.mission_descr.erase() # game start def gamestart_start(self): game.score = 0 game.lives = game.start_lives game.fuel = game.start_fuel game.bombs = game.start_bombs self.levels.start_game() def gamestart_tick(self): self.changestate('playerstart') def gamestart_end(self): pass # game over def gameover_start(self): self.timer = game.state_transition_time self.scoredisplay.update(game.score,game.fuel,game.bombs,game.lives) gameover_message = objtextmessage.TextMessage(gfx.surface,gfx.background,'Game Over') gameover_message.draw() pass def gameover_tick(self): self.timer -= 1 if self.timer < 0: self.final_game_end() self.runobjects(self.objlists) self.check_nonplayer_collisions() # game won def gamewon_start(self): self.timer = game.state_transition_time self.scoredisplay.update(game.score,game.fuel,game.bombs,game.lives) gamewon_message = objtextmessage.TextMessage(gfx.surface,gfx.background,'All missions completed!') gamewon_message.draw() pass def gamewon_tick(self): self.timer -= 1 if self.timer < 0: self.final_game_end() self.runobjects(self.objlists) self.check_nonplayer_collisions() # end the game... def final_game_end(self): # toss all the events in the event queue pygame.event.get() nexthandler = self.prevhandler game.handler = nexthandler # add new objects # this needs to go in a separate Levels class def addactors(self): if self.player.dead == 0: # laser blasts if self.player.blasting == 1: self.blastobjs.append(objblast.Blast(self.player)) self.player.blasting = 0 # bombs if self.player.bombing == 1: self.bombobjs.append(objbomb.Bomb(self.player)) self.player.bombing = 0 # add ground and air objects self.levels.addactors() # game pause def pause(self): game.single_frame = not game.single_frame self.lasttick = 0 # quit game def userquit(self): self.playerdie_end() # toss all the events in the event queue pygame.event.get() pygame.time.delay(200) game.handler = self.prevhandler # toggle debug info def toggle_debug(self): game.debug_mode = (game.debug_mode + 1) % 2 # do nothing command def do_nothing(self): pass