Example #1
0
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
Example #2
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()
Example #3
0
class HUD(pygame.sprite.Sprite):
    """Handles drawing any displayed info, namely:
		* text for the problem, solution, and any other messages
		* timer bar for problem
		* the lines separating the screens
	"""
    def __init__(self, rect, group=None):
        pygame.sprite.Sprite.__init__(self, group)
        self.evManager = EventManager()
        self.evManager.RegisterListener(self)

        self.time = 0

        #self.image = pygame.Surface(rect.size, SRCALPHA)
        self.image = pygame.Surface(rect.size)
        self.image.set_colorkey((0, 0, 0))
        self.image.fill((0, 0, 0, 0))
        self.color = (255, 255, 255)
        self.rect = self.image.get_rect()

        self.hero = None

        #set hud areas
        w = rect.width
        h = rect.height
        self.heroBox = self.image.subsurface(
            (0, 0, w / 2, h * 2 / 3))  #top left box
        #pygame.draw.rect(self.heroBox, self.color, self.heroBox.get_rect(), 4)
        self.DrawBorder(self.heroBox)

        self.enemyBox = self.image.subsurface(
            (w / 2, 0, w / 2, h * 2 / 3))  #top right box
        #pygame.draw.rect(self.enemyBox, self.color, self.enemyBox.get_rect(), 4)
        self.DrawBorder(self.enemyBox)

        self.dataBox = self.image.subsurface((0, h * 2 / 3, w, h / 3))
        #pygame.draw.rect(self.dataBox, self.color, self.dataBox.get_rect(), 4)
        self.DrawBorder(self.dataBox)

        self.solutionBox = self.dataBox.subsurface((50, 50, 100, 50))
        self.timerBox = self.dataBox.subsurface((30, 100, 300, 20))
        self.timer = (0, 0)  #beginning and end of timer

        self.instrBox = self.dataBox.subsurface((200, 20, w / 2, 100))

        #set hud font
        self.font = pygame.font.SysFont("Arial", 18)
        self.font_height = 20

        #initialize instruction text:
        self.instr_wait = "Press SPACE to attack"
        self.instr_prob = "%s \nType solution and press ENTER to solve"
        self.instr_win = "Victory! \nPress SPACE for next battle"
        self.instr_loose = "Game Over! \nPress ESC to quit"
        self.instr = self.instr_wait
        self.sol = ""

    def DrawBorder(self, surf):
        surf.fill(self.color)
        rect = surf.get_rect().inflate(-4, -4)
        surf.fill((200, 200, 200, 127), rect)
        rect = rect.inflate(-4, -4)
        surf.fill((0, 0, 0), rect)
        return rect

    def Victory(self):
        self.instr = self.instr_win

    def Defeat(self):
        self.instr = self.instr_loose

    def DrawInstructions(self, color=False):
        if not color: color = self.color
        vpos = 0
        self.instrBox.fill((0, 0, 0))
        for text in self.instr.split('\n'):
            pImg = self.font.render(text, False, color)
            self.instrBox.blit(pImg, (0, vpos))
            vpos += self.font_height

    def DrawSolution(self, color=False):
        if not color: color = self.color
        sImg = self.font.render(self.sol, False, color)
        self.solutionBox.fill((0, 0, 0))
        self.solutionBox.blit(sImg, (0, 0))

    def UpdateTimer(self):
        #rect = self.DrawBorder(self.timerBox)
        rect = self.timerBox.get_rect()
        w = rect.width
        p = 1. * (self.timer[1] - self.time) / (self.timer[1] - self.timer[0])
        w = w * p
        rect = (0, 0, w, rect.height)
        self.timerBox.fill((0, 0, 0))
        self.timerBox.fill(self.color, rect)

    def Notify(self, event):
        """events handled:
		TickEvent:
			shrink the timer (if we're counting)
		RequestSolutionEvent:
			display the problem & timer
		SolutionUpdateEvent:
			update the solution to the current state
		SolveEvent:
			Hide problem & timer
		"""
        if isinstance(event, TickEvent):
            self.time = event.time
            self.DrawInstructions()
            self.DrawSolution()
            if self.time < self.timer[1]:  #timer on
                self.UpdateTimer()
        elif isinstance(event, SpawnHeroEvent):
            self.hero = event.evID
        elif isinstance(event, RequestSolutionEvent):
            self.instr = self.instr_prob % event.problem
            self.timer = (self.time, event.endTime)
        elif isinstance(event, SolutionUpdateEvent):
            self.sol = event.solution
        elif isinstance(event, SolveEvent):
            self.sol = ""
            self.instr = self.instr_wait
            self.timer = (0, 0)
            self.timerBox.fill((0, 0, 0))
        elif isinstance(event, VictoryEvent):
            self.instr = self.instr_win
        elif isinstance(event, NextBattleEvent):
            self.instr = self.instr_wait
Example #4
0
class PygameView:
    """Creates the game window, and handles drawing everything inside it"""
    def __init__(self):
        self.evManager = EventManager()
        self.evManager.RegisterListener(self)

        #set up pygame requriements
        pygame.init()
        self.window = pygame.display.set_mode((640, 480))
        pygame.display.set_caption('Mathemagics')
        self.background = pygame.Surface(self.window.get_size())
        self.background.fill((0, 0, 0))

        self.backSprites = pygame.sprite.RenderUpdates()
        self.menuSprites = pygame.sprite.RenderUpdates()
        self.actorSprites = pygame.sprite.RenderUpdates()

        self.mapspr = MapSprite(self.window.get_rect(), self.backSprites)
        self.hud = HUD(self.window.get_rect(), self.menuSprites)

    def SpawnHero(self, evID):
        x = self.window.get_width() / 4
        y = self.window.get_height() / 3
        hero = HeroSprite(x, y, evID, self.actorSprites)

    def SpawnEnemy(self, evID):
        x = self.window.get_width() * 3 / 4
        y = self.window.get_height() / 3
        enemy = EnemySprite(x, y, evID, self.actorSprites)

    def GetActor(self, evID):
        for sprite in self.actorSprites.sprites():
            if sprite.evID == evID: return sprite

    def Notify(self, event):
        """Handled events:
		TickEvent:
			redraw back & front sprites using double buffering
		DieEvent:
		SpawnEvent:
			Create the spawned actor
		"""
        if isinstance(event, TickEvent):
            #Draw Everything
            self.backSprites.clear(self.window, self.background)
            self.menuSprites.clear(self.window, self.background)
            self.actorSprites.clear(self.window, self.background)

            self.backSprites.update()
            self.menuSprites.update()
            self.actorSprites.update()

            dirtyRects1 = self.backSprites.draw(self.window)
            dirtyRects2 = self.menuSprites.draw(self.window)
            dirtyRects3 = self.actorSprites.draw(self.window)

            pygame.display.update(dirtyRects1 + dirtyRects2 + dirtyRects3)

        #..should go to HUD?
        elif isinstance(event, DieEvent):
            subject = self.GetActor(event.subject)
            if subject.evID == 'hero': self.hud.Defeat()  #placeholder
            else: self.hud.Victory()

        elif isinstance(event, SpawnEvent):
            if isinstance(event, SpawnHeroEvent): self.SpawnHero(event.evID)
            if isinstance(event, SpawnEnemyEvent): self.SpawnEnemy(event.evID)
Example #5
0
class ActorSprite(pygame.sprite.Sprite):
    """ Virtual sprite for a generic game actor
		subclasses must define the following:
			self.waitImage
			self.attackImage
			self.defendImage
			self.hurtImage
	"""
    def __init__(self, x, y, evID, group=None):
        self.evManager = EventManager()
        self.evManager.RegisterListener(self)

        pygame.sprite.Sprite.__init__(self, group)

        self.evID = evID

        self.pos = (x, y)
        self.InitImages()

        self.image = self.waitImage  #start out waiting

        self.rect = self.image.get_rect()
        self.rect.center = self.pos

        self.healthBox = pygame.Surface((self.rect.width, 15))
        self.healthBox.fill((0, 255, 0))
        #self.healthBox.set_colorkey((0,0,0))

    def InitImages(self):
        # predefine images
        self.deadImage = pygame.Surface((0, 0))
        self.waitImage = pygame.Surface((64, 64))  #Idle image
        self.attackImage = pygame.Surface((64, 64))  #Attack image
        self.defendImage = pygame.Surface((64, 64))  #Defend image
        self.hurtImage = pygame.Surface((64, 64))  #Hurt image

    def update(self):
        pygame.sprite.Sprite.update(self)
        self.image.blit(self.healthBox, (0, 0))

    def UpdateHealth(self, newHealth):
        y = self.healthBox.get_rect().height
        x = self.healthBox.get_rect().width
        dx = x * newHealth
        grey = 255 * newHealth
        r = min((255 - grey) * 2, 255)
        g = min(grey * 2, 255)
        b = 0

        self.healthBox.fill((0, 0, 0))
        self.healthBox.fill((r, g, b), (0, 0, dx, y))

    def Wait(self):
        #if self.image == self.attackImage:
        self.image = self.waitImage
        self.rect = self.image.get_rect()
        self.rect.center = self.pos

    def Attack(self):
        if self.image == self.waitImage:
            self.image = self.attackImage
            self.rect = self.image.get_rect()
            self.rect.center = self.pos

    def Defend(self):
        if self.image == self.waitImage:
            self.image = self.defendImage
            self.rect = self.image.get_rect()
            self.rect.center = self.pos

    def Hurt(self, newHealth):
        self.UpdateHealth(newHealth)
        self.image = self.hurtImage
        self.rect = self.image.get_rect()
        self.rect.center = self.pos

    def Die(self):
        self.image = self.deadImage

    #todo: actor objects should only get notifications for themselves (and maybe clock tick events)
    def Notify(self, event):
        """handled events:
		WaitEvent:
			Display Wait
		AttackEvent:
			Display Attack
		Defend:
			Display Defend
		HurtEvent:
			Dislpay Hurt
		DieEvent:
			Display Die
		"""
        if isinstance(event,
                      ActorStateChangeEvent) and self.evID == event.subject:
            if isinstance(event, WaitEvent): self.Wait()
            elif isinstance(event, AttackEvent): self.Attack()
            elif isinstance(event, DefendEvent): self.Defend()
            elif isinstance(event, HurtEvent): self.Hurt(event.newHealth)
            elif isinstance(event, DieEvent):
                self.Die()
Example #6
0
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)
Example #7
0
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)