class CPUSpinnerController(object): """Controls the game clock -- generating an Event for each game tick, and throttling the game to limit CPU usage""" def __init__(self, maxfps=40): self.evManager = EventManager() self.evManager.RegisterListener(self) self.keepGoing = 1 self.clock = pygame.time.Clock() self.maxfps = maxfps def Run(self): """Start the game by telling the game clock to go!""" while self.keepGoing: event = TickEvent(pygame.time.get_ticks(), self.clock.tick(self.maxfps)) self.evManager.Notify(event) def Notify(self, event): """handled events: Quit Event: Stop the clock from ticking (the app will then close) """ if isinstance(event, QuitEvent): #this will stop the while loop from running self.keepGoing = 0
class Game(object): """Responsible for setting the stage: Namely, making sure that the appropriate Actors exist and know the appropriate things """ __metaclass__ = SingletonType STATE_PREPARING = 0 STATE_RUNNING = 1 STATE_PAUSED = 2 STATE_GAMEOVER = 3 def __init__(self): self.evManager = EventManager() self.evManager.RegisterListener(self) self.state = Game.STATE_PREPARING self.heroes = {} self.enemies = {} self.enemyCount = 0 self.problem = (0, 0) self.time = 0 self.solutionWait = 5000 #milliseconds self.solEndTime = 0 def Start(self): """starts the game action -- spawning any Actors needed, setting required variables and generating the GameStartedEvent""" self.SpawnHero() self.SpawnEnemy() self.state = Game.STATE_RUNNING self.evManager.Notify(GameStartedEvent(self)) def SpawnHero(self): self.heroes["hero"] = HeroModel("hero", self.enemies) self.evManager.Notify(SpawnHeroEvent(self.heroes["hero"].evID)) def SpawnEnemy(self): self.enemyCount += 1 enemyID = "enemy%s" % self.enemyCount self.enemies[enemyID] = EnemyModel(enemyID, self.heroes, self.time) self.evManager.Notify(SpawnEnemyEvent(enemyID)) def myOpponents(self, actor): if isinstance(actor, Hero): return self.enemies elif isinstance(actor, Enemy): return self.heroes def Notify(self, event): """Handled events: TickEvent: progress the game action by one game tick (currently just starts game if it's not already set up) DieEvent: Actor has died. If hero, game is over, if (last) enemy, victory is achieved NextBattleEvent: Player has requested next battle, so spawn an enemy """ if self.state == Game.STATE_GAMEOVER: return #don't do anything if the game is over elif self.state == Game.STATE_PREPARING: if isinstance(event, TickEvent): self.Start() if isinstance(event, TickEvent): self.time = event.time if self.state == Game.STATE_RUNNING: if isinstance(event, DieEvent): if event.subject in self.heroes.keys(): self.state = Game.STATE_GAMEOVER self.evManager.Notify(GameOverEvent()) #del self.heroes[event.subject] elif event.subject in self.enemies.keys(): self.evManager.Notify(VictoryEvent()) del self.enemies[event.subject] elif isinstance(event, NextBattleEvent): self.SpawnEnemy()
class KeyboardController(object): """Takes input from the keboard and translates that into game Events to trigger action in the rest of the system""" STATE_ACTION = 0 STATE_SOLVE = 1 STATE_VICTORY = 2 def __init__(self): self.evManager = EventManager() self.evManager.RegisterListener(self) self.state = KeyboardController.STATE_ACTION self.solution = "" self.solveTime = 0 def Notify(self, event): """Takes mostly keyboard inputs and generates events based on state. STATE_ACTION: Generic state -- basically just waiting for player to attack RequestSolutionEvent: Change state to STATE_SOLVE TickEvent: Check to see if game is quit or attack is requested based on key presses STATE_VICTORY: Player has defeated all enemies (waiting for next battle) STATE_SOLVE: Player has attacked, and is in the process of entering the solution SolveEvent: Attack has completed, change state back to STATE_ACTION and reset solution TickEvent: Listen for any key presses (quit, numbers, delete, submit) and update appropriately """ events = [] #events to be raised at the end if necessary #always quit (regardless of state) if isinstance(event, TickEvent): pgEventList = pygame.event.get() for pgEvent in pgEventList: if pgEvent.type == QUIT: events.append(QuitEvent()) elif pgEvent.type == KEYDOWN: if pgEvent.key == K_ESCAPE: events.append(QuitEvent()) #always switch to victory state (regardless of current state) elif isinstance(event, VictoryEvent): print "controller victory" self.state = KeyboardController.STATE_VICTORY if self.state == KeyboardController.STATE_ACTION: if isinstance(event, RequestSolutionEvent): self.state = KeyboardController.STATE_SOLVE elif isinstance(event, TickEvent): #Handle Input Events for pgEvent in pgEventList: if pgEvent.type == KEYDOWN: if pgEvent.key == K_SPACE: events.append(RequestAttackEvent()) elif self.state == KeyboardController.STATE_SOLVE: if isinstance(event, SolveEvent): self.state = KeyboardController.STATE_ACTION self.solution = "" elif isinstance(event, TickEvent): for pgEvent in pgEventList: if pgEvent.type == KEYDOWN: if pgEvent.unicode in u'0123456789': #typed a digit self.solution += pgEvent.unicode events.append(SolutionUpdateEvent(self.solution)) elif pgEvent.key == K_BACKSPACE: self.solution = self.solution[:-1] events.append(SolutionUpdateEvent(self.solution)) elif pgEvent.key in (K_RETURN, K_KP_ENTER): events.append(SolveEvent(self.solution)) elif self.state == KeyboardController.STATE_VICTORY: if isinstance(event, TickEvent): for pgEvent in pgEventList: if pgEvent.type == KEYDOWN: if pgEvent.key == K_SPACE: events.append(NextBattleEvent()) self.state = KeyboardController.STATE_ACTION for ev in events: self.evManager.Notify(ev)
class ActorModel(object): """Generic class for anything 'alive' -- namely anything that can be involved in combat Tracks the following data: * Current state of Actor (Waiting, Attacking, Defending, Hurting or Dead) * The amount of time that should be spent in the various states * The current and maximum amount of health the Actor has """ STATE_WAITING = 0 STATE_ATTACKING = 1 STATE_DEFENDING = 2 STATE_HURTING = 3 STATE_DEAD = 4 def __init__(self, evID, opponents): self.evManager = EventManager() self.evManager.RegisterListener(self) self.evID = evID self.opponents = opponents self.victim = None self.state = ActorModel.STATE_WAITING self.AttackWait = 300 #milliseconds self.HurtWait = 200 #milliseconds self.time = 0 self.AttackEndTime = self.time self.HurtEndTime = self.time self.maxHealth = 100 self.health = self.maxHealth self.attackPower = 0 # to be defined in subclasses def Wait(self): """Sets Actor's current state to waiting (neutral)""" self.state = ActorModel.STATE_WAITING event = WaitEvent(self.evID) self.evManager.Notify(event) def nextVictim(self): """Determines next victim from list of known opponents""" victims = self.opponents.keys() Debug("available victims: %s" % victims, 5) if len(victims) == 0 or self.victim not in victims: self.victim = None for a in ( 1, 2 ): #search list twice to wrap, since current victim might be last opponent in list for v in victims: if self.victim == None: self.victim = v return if v == self.victim: self.victim = None #will set victim and return on next go-round def Attack(self, damage): """Sets Actor's current state to attacking -- and hurts victim based on given damage value""" if self.state == ActorModel.STATE_WAITING: self.state = ActorModel.STATE_ATTACKING self.AttackEndTime = self.time + self.AttackWait self.nextVictim( ) #currently there is no targeting system -- so just pick the next opponent in line Debug("victim %s has been chosen" % self.victim, 5) event = AttackEvent(self.evID, self.victim, damage) self.evManager.Notify(event) def Defend(self): """Sets Actor's current state do defending (not yet used)""" if self.state == ActorModel.STATE_WAITING: self.state = ActorModel.STATE_DEFENDING event = DefendEvent(self.evID) self.evManager.Notify(event) def Hurt(self, damage): """Sets Actors current state to hurting and deducts damage from health""" self.state = ActorModel.STATE_HURTING self.HurtEndTime = self.time + self.HurtWait if self.health >= 0: self.health -= damage if self.health < 0: self.health = 0 event = HurtEvent(self.evID, 1.0 * self.health / self.maxHealth) self.evManager.Notify(event) if self.health == 0: self.Die() def Die(self): """Sets Actor's current state to dead""" self.state = ActorModel.STATE_DEAD event = DieEvent(self.evID) self.evManager.Notify(event) self.evManager.UnregisterListener(self) def Notify(self, event): """Handles the following events: TickEvent: verify whether actor is currently still attacking or hurting based on duration values for these states HurtEvent: if this actor is being attacked, call hurt """ if isinstance(event, TickEvent): self.time = event.time #ready to stop attacking if self.state == ActorModel.STATE_ATTACKING and self.AttackEndTime <= self.time: self.Wait() if self.state == ActorModel.STATE_HURTING and self.HurtEndTime <= self.time: self.Wait() elif isinstance(event, AttackEvent) and event.object == self.evID: self.Hurt(event.damage)