示例#1
0
    def __init__(self, form_position):
        super().__init__()
        ### Local variables ####################################################
        the_color = choice(color.LIST)
        the_id = id(the_color)
        ########################################################################

        ### Object Attributes ##################################################
        self.amount_lowered = 0
        self._anim = 0.0
        self.color = the_color
        self.column = None
        self._form_position = form_position
        self.current_frame_list = ENEMY_FRAMES_COLOR_BLIND if settings.SETTINGS[
            'color_blind'] else ENEMY_FRAMES
        self.image = self.current_frame_list[the_id][0]
        self.position = list(START_POS)
        self.rect = Rect(START_POS, self.image.get_size())
        self.state = Enemy.STATES.IDLE
        self.emitter = ParticleEmitter(color.color_particles[the_id],
                                       self.rect, 1)
        ########################################################################

        ### Preparation ########################################################
        del self.acceleration, self.velocity
示例#2
0
 def appear(self):
     self.position = self.__get_snap()
     self.rect.topleft = self.position
     self.image = self.current_frame_list[id(self.color)][0]
     self.gridcell = [
         self.rect.centerx // self.rect.width,
         self.rect.centery // self.rect.height,
     ]  #(x, y)
     self.emitter = ParticleEmitter(color.color_particles[id(self.color)],
                                    self.rect, 5, Block.particle_group)
     self.change_state(Block.STATES.START_FALLING)
     self.add(Block.GROUP)
示例#3
0
    def __init__(self):
        super().__init__()
        self._anim = 0.0
        self.column = None
        self.current_frame_list = UFO_FRAMES
        self.image = config.get_sprite(FRAMES[0])
        self.odds = expovariate(AVG_WAIT)
        self.position = list(START_POS)
        self.rect = Rect(START_POS, self.image.get_size())
        self.state = UFO.STATES.IDLE
        self.emitter = ParticleEmitter(color.random_color_particles, self.rect)

        del self.acceleration
示例#4
0
 def appear(self):
     self.position     = self.__get_snap()
     self.rect.topleft = self.position
     self.image        = self.current_frame_list[id(self.color)][0]
     self.gridcell     = [
                          self.rect.centerx // self.rect.width,
                          self.rect.centery // self.rect.height,
                         ] #(x, y)
     self.emitter      = ParticleEmitter(color.color_particles[id(self.color)], self.rect, 5, Block.particle_group)
     self.change_state(Block.STATES.START_FALLING)
     self.add(Block.GROUP)
示例#5
0
    def __init__(self):
        '''
        @ivar anim: A counter for ship animation
        @ivar image: The graphic
        @ivar invincible: How many frames of invincibility the player has if any
        @ivar my_bullet: The single bullet this ship may fire
        '''
        super().__init__()

        self.anim           = 0.0
        self.appear_emitter = ParticleEmitter(APPEAR_POOL, START_POS.copy(), 2)
        self.emitter        = ParticleEmitter(DEATH_POOL, START_POS.copy(), 2)
        self.flames         = FlameTrail()
        self.image          = FRAMES[0]
        self.invincible     = 0
        self.light_column   = LightColumn()
        self.my_bullet      = ShipBullet()
        self.position       = list(START_POS.topleft)
        self.rect           = START_POS.copy()
        self.respawn_time   = 3 * 60  # In frames
        self.change_state(Ship.STATES.RESPAWN)
示例#6
0
    def __init__(self):
        '''
        @ivar anim: A counter for ship animation
        @ivar image: The graphic
        @ivar invincible: How many frames of invincibility the player has if any
        @ivar my_bullet: The single bullet this ship may fire
        '''
        super().__init__()

        self.anim = 0.0
        self.appear_emitter = ParticleEmitter(APPEAR_POOL, START_POS.copy(), 2)
        self.emitter = ParticleEmitter(DEATH_POOL, START_POS.copy(), 2)
        self.flames = FlameTrail()
        self.image = FRAMES[0]
        self.invincible = 0
        self.light_column = LightColumn()
        self.my_bullet = ShipBullet()
        self.position = list(START_POS.topleft)
        self.rect = START_POS.copy()
        self.respawn_time = 3 * 60  # In frames
        self.change_state(Ship.STATES.RESPAWN)
示例#7
0
    def __init__(self):
        super().__init__()
        self._anim    = 0.0
        self.column   = None
        self.current_frame_list = UFO_FRAMES
        self.image    = config.get_sprite(FRAMES[0])
        self.odds     = expovariate(AVG_WAIT)
        self.position = list(START_POS)
        self.rect     = Rect(START_POS, self.image.get_size())
        self.state    = UFO.STATES.IDLE
        self.emitter  = ParticleEmitter(color.random_color_particles, self.rect)

        del self.acceleration
示例#8
0
 def __init__(self, form_position):
     super().__init__()
     ### Local variables ####################################################
     the_color = choice(color.LIST)
     the_id    = id(the_color)
     ########################################################################
     
     ### Object Attributes ##################################################
     self.amount_lowered     = 0
     self._anim              = 0.0
     self.color              = the_color
     self.column             = None
     self._form_position     = form_position
     self.current_frame_list = ENEMY_FRAMES_COLOR_BLIND if settings.SETTINGS['color_blind'] else ENEMY_FRAMES
     self.image              = self.current_frame_list[the_id][0]
     self.position           = list(START_POS)
     self.rect               = Rect(START_POS, self.image.get_size())
     self.state              = Enemy.STATES.IDLE
     self.emitter            = ParticleEmitter(color.color_particles[the_id], self.rect, 1)
     ########################################################################
     
     ### Preparation ########################################################
     del self.acceleration, self.velocity
示例#9
0
class Block(GameObject):
    '''
    Blocks are left by enemies when they're killed.  Match three of the same
    color, and they'll disappear.
    '''
    STATES         = config.Enum(*BLOCK_STATES)
    block_full     = False
    GROUP          = None
    particle_group = None

    def __init__(self, position, newcolor=choice(color.LIST), special=False):
        GameObject.__init__(self)
        self._anim      = 0
        self.color      = newcolor
        self.temp_color = self.color
        self.current_frame_list = _block_frames_color_blind if settings.SETTINGS['color_blind'] else _block_frames
        self.image              = self.current_frame_list[id(self.color)][0]
        self.position = position
        self.rect     = pygame.Rect(position, self.image.get_size()) #(x, y)
        self._special = special
        self.state    = Block.STATES.IDLE

    def __str__(self):
        return ("<Block - color: %s, cell: %s, position: %s, rect: %s, state: %i>" %
               (self.color, self.gridcell, self.position, self.rect, self.state))
        
    def __repr__(self):
        return self.__str__()

    def appear(self):
        self.position     = self.__get_snap()
        self.rect.topleft = self.position
        self.image        = self.current_frame_list[id(self.color)][0]
        self.gridcell     = [
                             self.rect.centerx // self.rect.width,
                             self.rect.centery // self.rect.height,
                            ] #(x, y)
        self.emitter      = ParticleEmitter(color.color_particles[id(self.color)], self.rect, 5, Block.particle_group)
        self.change_state(Block.STATES.START_FALLING)
        self.add(Block.GROUP)

    def start_falling(self):
        '''
        Starts the Block falling down.  Only called once before this Block's
        state switches to STATES.FALLING.  Blocks that are falling must not be
        part of matches.
        '''
        self.acceleration[1] = GRAVITY
        blockgrid.check_block(self, False)

        if self.gridcell[1]:
        #If we're not at the top of the grid...
            block_above = blockgrid.blocks[self.gridcell[0]][self.gridcell[1] - 1]
            if block_above:
            #If there's at least one block above us...
                assert isinstance(block_above, Block), \
                "%s expected a Block, got a %s" % (self, block_above)

                for i in blockgrid.blocks[self.gridcell[0]]:
                #For all grid cells above us...
                    if i and not i.velocity[1]:
                    #If this is a block that's not moving...
                        i.change_state(Block.STATES.START_FALLING)
                    else:
                        break

        self.change_state(Block.STATES.FALLING)

    def fall(self):
        '''
        Falls down.  For the sake of efficiency, blocks work independently of
        the collision detection system, since they're only going to move
        vertically, and only depend on other blocks for collisions.
        '''
        gridcell = self.gridcell
        position = self.position
        rect     = self.rect
        
        self.velocity[1] = min(MAX_SPEED, self.velocity[1] + self.acceleration[1])
        position[1]     += self.velocity[1]
        rect.top         = position[1] + 0.5 #Round to the nearest integer
        gridcell[1]      = self.rect.centery // self.rect.height
        self.emitter.rect.topleft = rect.topleft

        self.__animate()
        
        if rect.bottom >= blockgrid.RECT.bottom:
        #If we've hit the bottom of the grid...
            rect.bottom     = blockgrid.RECT.bottom
            position[1]     = rect.top
            self.change_state(Block.STATES.IMPACT)
        elif self.gridcell[1] + 1 < blockgrid.SIZE[1]:
        #Else if it was another block...
            below = blockgrid.blocks[gridcell[0]][gridcell[1] + 1]
            if below and rect.bottom >= below.rect.top:
            #If we've gone past the block below...
                rect.bottom     = below.rect.top
                position[1]     = rect.top
                self.change_state(Block.STATES.IMPACT)
            assert isinstance(below, Block) or below is None, \
            "A %s is trying to collide with a stray %s!" % (self, below)
        
        assert self.state == Block.STATES.FALLING \
        and rect.colliderect(blockgrid.RECT) \
        and blockgrid.RECT.collidepoint(position), \
        "An active %s has somehow left the field!" % self

    def wait(self):
        '''
        Constantly checks to see if this block can fall.
        '''
        gridcell = self.gridcell
        if self.rect.bottom < blockgrid.RECT.bottom:
        #If we're not at the bottom of the grid...
            block_below = blockgrid.blocks[gridcell[0]][gridcell[1] + 1]
            if not block_below or block_below.velocity[1]:
            #If there's no block directly below...
                blockgrid.check_block(self, False)
                self.acceleration[1] = GRAVITY
                self.change_state(Block.STATES.START_FALLING)
                
        if __debug__ and self.rect.collidepoint(pygame.mouse.get_pos()):
            print(self)

    def stop(self):
        '''
        Handles the Block when it hits the bottom of the grid or another block.
        Changes velocity, plays sounds, etc.
        '''
        self.acceleration[1] = 0.0
        self.velocity[1]     = 0.0
        self.position        = self.__get_snap()
        self.rect.topleft    = self.position
        self.gridcell[1]     = self.rect.centery // self.rect.height #(row, col)
        self.state           = Block.STATES.ACTIVE
        blockgrid.blocks[self.gridcell[0]][self.gridcell[1]] = self
            
        blockgrid.check_block(self, True)
        _bump.play()
        #blockgrid.update()
        
        if self._special:
        #If this is a special block...
            UFO_BLOCK.play()
            self.emitter.pool = color.random_color_particles
            if self.gridcell[1] < blockgrid.SIZE[1] - 1:
            #If we're not at the bottom of the grid...
                self.color = blockgrid.blocks[self.gridcell[0]][self.gridcell[1]+ 1].color
                blockgrid.clear_color(self.color)
            else:
                blockgrid.clear_row(self.gridcell[1])

        elif not self.gridcell[1]:
        #If we go past the the playing field...
            self._anim = len(FRAMES) - 2  #Bring us to the second-to-last frame
            self.__animate()              #And let the animation system finish
            gamedata.lives = 0

    def vanish(self):
        blockgrid.check_block(self, False)
        self.emitter.burst(20)
        self.kill()
        
        blockgrid.blocks[self.gridcell[0]][self.gridcell[1]] = None
        self._anim                                 = 0
        self.position                              = [-300.0, -300.0]
        self.rect.topleft                          = self.position
        self.gridcell                              = None
        self.change_state(Block.STATES.IDLE)
        self.__replace()
        
    def __replace(self):
        '''
        Puts this Block back in the set of spares.
        
        @postcondition: This Block is ready to be recycled.
        '''
        _blocks_set.add(self)
        
    def __animate(self):
        if self._anim < len(FRAMES) - 1:
        #If we haven't hit the last frame of animation...
            self._anim += 1
            self.image  = self.current_frame_list[id(self.color)][self._anim]

        if self._special:
        #If this is a _special block...
            self.image = self.current_frame_list[id(choice(color.LIST))][self._anim]
    
    def __get_snap(self):
        '''
        Returns a position list that snaps this block to the grid.
        '''
        size = self.image.get_size()
        return [
                round(self.position[0] // size[0]) * size[0],
                round(self.position[1] // size[1]) * size[1],
               ]
        
    actions = {
                STATES.IDLE         : None           ,
                STATES.APPEARING    : 'appear'       ,
                STATES.ACTIVE       : 'wait'         ,
                STATES.FALLING      : 'fall'         ,
                STATES.START_FALLING: 'start_falling',
                STATES.IMPACT       : 'stop'         ,
                STATES.DYING        : 'vanish'       ,
              }
示例#10
0
class Enemy(GameObject):
    STATES = config.Enum(*ENEMY_STATES)
    anim = 0.0
    base_speed = 0.5
    GROUP = None
    shoot_odds = 0.002
    should_flip = False
    start_time = None
    velocity = [0.5, 0.0]

    def __init__(self, form_position):
        super().__init__()
        ### Local variables ####################################################
        the_color = choice(color.LIST)
        the_id = id(the_color)
        ########################################################################

        ### Object Attributes ##################################################
        self.amount_lowered = 0
        self._anim = 0.0
        self.color = the_color
        self.column = None
        self._form_position = form_position
        self.current_frame_list = ENEMY_FRAMES_COLOR_BLIND if settings.SETTINGS[
            'color_blind'] else ENEMY_FRAMES
        self.image = self.current_frame_list[the_id][0]
        self.position = list(START_POS)
        self.rect = Rect(START_POS, self.image.get_size())
        self.state = Enemy.STATES.IDLE
        self.emitter = ParticleEmitter(color.color_particles[the_id],
                                       self.rect, 1)
        ########################################################################

        ### Preparation ########################################################
        del self.acceleration, self.velocity
        ########################################################################

    def appear(self):
        self.add(Enemy.GROUP)
        self.position = [
            START_POS[0] * (self._form_position[0] + 1) * 1.5,
            START_POS[1] * (self._form_position[1] + 1) * 1.5,
        ]
        self.rect.topleft = (self.position[0] + .5, self.position[1] + .5)
        self.color = choice(color.LIST)
        self.__animate()
        self.emitter.pool = color.color_particles[id(self.color)]
        self.change_state(Enemy.STATES.ACTIVE)

    def move(self):
        self.__animate()
        self.position[0] += Enemy.velocity[0]
        self.rect.topleft = (self.position[0] + .5, self.position[1] + .5)

        if uniform(0,
                   1) < Enemy.shoot_odds and not enemybullet.EnemyBullet.halt:
            #With Enemy.shoot_odds% of firing...
            #TODO: Use another probability distribution
            b = enemybullet.get_enemy_bullet()
            b.rect.midtop = self.rect.midbottom
            b.position = list(b.rect.topleft)
            b.add(enemybullet.EnemyBullet.GROUP)

        if not Enemy.should_flip:
            #If the squadron of enemies is not marked to reverse direction...
            if self.rect.left < 0 or self.rect.right > config.SCREEN_WIDTH:
                #If this enemy touches either end of the screen...
                Enemy.should_flip = True

    def die(self):
        balloflight.get_ball(self.rect.topleft, self.column,
                             self.color).add(Enemy.GROUP)
        _hurt.play()
        self.emitter.burst(20)
        self.kill()

        self.position = [-300.0, -300.0]
        self.rect.topleft = self.position
        self.change_state(Enemy.STATES.IDLE)
        Enemy.velocity[0] += copysign(0.1, Enemy.velocity[0])
        #^ Increase the enemy squadron's speed (copysign() considers direction)

    def lower(self):
        self.__animate()
        self.amount_lowered += 1
        self.position[1] += 1
        self.rect.top = self.position[1]
        if self.amount_lowered == LOWER_INCREMENT:
            self.amount_lowered = 0
            self.change_state(Enemy.STATES.ACTIVE)

    def cheer(self):
        self.__animate()
        self.position[1] -= 2 * sin((Enemy.anim / 2) -
                                    (pi / 4) * self._form_position[0])
        self.rect.top = self.position[1] + .5

    def __animate(self):
        self._anim = int(3 - abs(Enemy.anim - 3)) % 4
        self.image = self.current_frame_list[id(self.color)][self._anim]

    actions = {
        STATES.APPEARING: 'appear',
        STATES.LOWERING: 'lower',
        STATES.ACTIVE: 'move',
        STATES.DYING: 'die',
        STATES.IDLE: None,
        STATES.CHEERING: 'cheer',
    }
示例#11
0

### Functions ##################################################################
def _star_appear(self):
    '''
    Stars appear on the left edge of the screen and travel right at one of five
    possible speeds.
    '''
    self.velocity[0] = randint(1, 5)


def _star_move(self):
    '''
    And this is the part where they actually move.
    '''
    self.position[0] += self.velocity[0]
    self.rect.left = self.position[0]


################################################################################

STARS_GROUP = pygame.sprite.RenderUpdates()
_STAR_IMAGE = config.get_sprite(Rect(4, 170, 2, 2))

EARTH = HudObject(config.EARTH, (0, 0))
GRID = HudObject(config.GRID_BG, (0, 0))
STARS = ParticleEmitter(ParticlePool(_STAR_IMAGE, _star_move, _star_appear),
                        Rect(0, 0, 0, config.SCREEN_HEIGHT), 8, STARS_GROUP)

EARTH.rect.midbottom = config.SCREEN_RECT.midbottom
示例#12
0
class Enemy(GameObject):
    STATES       = config.Enum(*ENEMY_STATES)
    anim         = 0.0
    base_speed   = 0.5
    GROUP        = None
    shoot_odds   = 0.002
    should_flip  = False
    start_time   = None
    velocity     = [0.5, 0.0]

    def __init__(self, form_position):
        super().__init__()
        ### Local variables ####################################################
        the_color = choice(color.LIST)
        the_id    = id(the_color)
        ########################################################################
        
        ### Object Attributes ##################################################
        self.amount_lowered     = 0
        self._anim              = 0.0
        self.color              = the_color
        self.column             = None
        self._form_position     = form_position
        self.current_frame_list = ENEMY_FRAMES_COLOR_BLIND if settings.SETTINGS['color_blind'] else ENEMY_FRAMES
        self.image              = self.current_frame_list[the_id][0]
        self.position           = list(START_POS)
        self.rect               = Rect(START_POS, self.image.get_size())
        self.state              = Enemy.STATES.IDLE
        self.emitter            = ParticleEmitter(color.color_particles[the_id], self.rect, 1)
        ########################################################################
        
        ### Preparation ########################################################
        del self.acceleration, self.velocity
        ########################################################################

    def appear(self):
        self.add(Enemy.GROUP)
        self.position     = [
                             START_POS[0] * (self._form_position[0] + 1) * 1.5,
                             START_POS[1] * (self._form_position[1] + 1) * 1.5,
                            ]
        self.rect.topleft = (self.position[0] + .5, self.position[1] + .5)
        self.color        = choice(color.LIST)
        self.__animate()
        self.emitter.pool = color.color_particles[id(self.color)]
        self.change_state(Enemy.STATES.ACTIVE)

    def move(self):
        self.__animate()
        self.position[0] += Enemy.velocity[0]
        self.rect.topleft = (self.position[0] + .5, self.position[1] + .5)
        
        if uniform(0, 1) < Enemy.shoot_odds and not enemybullet.EnemyBullet.halt:
        #With Enemy.shoot_odds% of firing...
        #TODO: Use another probability distribution
            b             = enemybullet.get_enemy_bullet()
            b.rect.midtop = self.rect.midbottom
            b.position    = list(b.rect.topleft)
            b.add(enemybullet.EnemyBullet.GROUP)

        if not Enemy.should_flip:
        #If the squadron of enemies is not marked to reverse direction...
            if self.rect.left < 0 or self.rect.right > config.SCREEN_WIDTH:
            #If this enemy touches either end of the screen...
                Enemy.should_flip = True

    def die(self):
        balloflight.get_ball(self.rect.topleft, self.column, self.color).add(Enemy.GROUP)
        _hurt.play()
        self.emitter.burst(20)
        self.kill()
        
        self.position      = [-300.0, -300.0]
        self.rect.topleft  = self.position
        self.change_state(Enemy.STATES.IDLE)
        Enemy.velocity[0] += copysign(0.1, Enemy.velocity[0])
        #^ Increase the enemy squadron's speed (copysign() considers direction)

    def lower(self):
        self.__animate()
        self.amount_lowered += 1
        self.position[1]    += 1
        self.rect.top        = self.position[1]
        if self.amount_lowered == LOWER_INCREMENT:
            self.amount_lowered = 0
            self.change_state(Enemy.STATES.ACTIVE)

    def cheer(self):
        self.__animate()
        self.position[1] -= 2 * sin((Enemy.anim/2) - (pi/4) * self._form_position[0])
        self.rect.top = self.position[1] + .5
    
    def __animate(self):
        self._anim = int(3 - abs(Enemy.anim - 3)) % 4
        self.image = self.current_frame_list[id(self.color)][self._anim]

    actions = {
                STATES.APPEARING: 'appear',
                STATES.LOWERING : 'lower' ,
                STATES.ACTIVE   : 'move'  ,
                STATES.DYING    : 'die'   ,
                STATES.IDLE     : None    ,
                STATES.CHEERING : 'cheer' ,
              }
示例#13
0
class UFO(GameObject):
    STATES = config.Enum(*UFO_STATES)
    GROUP = None
    BLOCK_GROUP = None

    def __init__(self):
        super().__init__()
        self._anim = 0.0
        self.column = None
        self.current_frame_list = UFO_FRAMES
        self.image = config.get_sprite(FRAMES[0])
        self.odds = expovariate(AVG_WAIT)
        self.position = list(START_POS)
        self.rect = Rect(START_POS, self.image.get_size())
        self.state = UFO.STATES.IDLE
        self.emitter = ParticleEmitter(color.random_color_particles, self.rect)

        del self.acceleration

    def appear(self):
        '''
        Appear on-screen, but not for very long!
        '''
        INVADE.play(-1)
        self.position = list(START_POS)
        self.rect.topleft = list(START_POS)
        self.change_state(UFO.STATES.ACTIVE)
        self.velocity[0] = -2.0

    def move(self):
        '''
        Move left on the screen, and oscillate up and down.
        '''
        position = self.position
        rect = self.rect

        self._anim += 0.5
        self.image  = UFO_FRAMES[id(choice(color.LIST))       ] \
                                [int(self._anim) % len(FRAMES)]
        position[0] += self.velocity[0]
        position[1] += sin(self._anim / 4)
        rect.topleft = (position[0] + .5, position[1] + .5)

        if rect.right < 0:
            #If we've gone past the left edge of the screen...
            self.change_state(UFO.STATES.LEAVING)

    def die(self):
        '''
        Vanish and release a special Block that clears lots of other Blocks.
        '''
        self.emitter.rect = self.rect
        self.emitter.burst(30)
        DEATH.play()
        UFO.BLOCK_GROUP.add(get_block((self.rect.centerx, 0), special=True))
        gamedata.score += 90
        self.change_state(UFO.STATES.LEAVING)

    def leave(self):
        INVADE.stop()
        self.velocity[0] = 0
        self.position = list(START_POS)
        self.rect.topleft = START_POS
        self.change_state(UFO.STATES.IDLE)

    def wait(self):
        '''
        Wait off-screen, and only come back with a specific probability.
        '''
        if uniform(0, 1) < self.odds:
            #With a certain probability...
            self.odds = expovariate(AVG_WAIT)
            self.change_state(UFO.STATES.APPEARING)

    actions = {
        STATES.IDLE: 'wait',
        STATES.APPEARING: 'appear',
        STATES.ACTIVE: 'move',
        STATES.DYING: 'die',
        STATES.LEAVING: 'leave',
        STATES.GAMEOVER: None,
    }
示例#14
0
class Block(GameObject):
    '''
    Blocks are left by enemies when they're killed.  Match three of the same
    color, and they'll disappear.
    '''
    STATES = config.Enum(*BLOCK_STATES)
    block_full = False
    GROUP = None
    particle_group = None

    def __init__(self, position, newcolor=choice(color.LIST), special=False):
        GameObject.__init__(self)
        self._anim = 0
        self.color = newcolor
        self.temp_color = self.color
        self.current_frame_list = _block_frames_color_blind if settings.SETTINGS[
            'color_blind'] else _block_frames
        self.image = self.current_frame_list[id(self.color)][0]
        self.position = position
        self.rect = pygame.Rect(position, self.image.get_size())  #(x, y)
        self._special = special
        self.state = Block.STATES.IDLE

    def __str__(self):
        return (
            "<Block - color: %s, cell: %s, position: %s, rect: %s, state: %i>"
            %
            (self.color, self.gridcell, self.position, self.rect, self.state))

    def __repr__(self):
        return self.__str__()

    def appear(self):
        self.position = self.__get_snap()
        self.rect.topleft = self.position
        self.image = self.current_frame_list[id(self.color)][0]
        self.gridcell = [
            self.rect.centerx // self.rect.width,
            self.rect.centery // self.rect.height,
        ]  #(x, y)
        self.emitter = ParticleEmitter(color.color_particles[id(self.color)],
                                       self.rect, 5, Block.particle_group)
        self.change_state(Block.STATES.START_FALLING)
        self.add(Block.GROUP)

    def start_falling(self):
        '''
        Starts the Block falling down.  Only called once before this Block's
        state switches to STATES.FALLING.  Blocks that are falling must not be
        part of matches.
        '''
        self.acceleration[1] = GRAVITY
        blockgrid.check_block(self, False)

        if self.gridcell[1]:
            #If we're not at the top of the grid...
            block_above = blockgrid.blocks[self.gridcell[0]][self.gridcell[1] -
                                                             1]
            if block_above:
                #If there's at least one block above us...
                assert isinstance(block_above, Block), \
                "%s expected a Block, got a %s" % (self, block_above)

                for i in blockgrid.blocks[self.gridcell[0]]:
                    #For all grid cells above us...
                    if i and not i.velocity[1]:
                        #If this is a block that's not moving...
                        i.change_state(Block.STATES.START_FALLING)
                    else:
                        break

        self.change_state(Block.STATES.FALLING)

    def fall(self):
        '''
        Falls down.  For the sake of efficiency, blocks work independently of
        the collision detection system, since they're only going to move
        vertically, and only depend on other blocks for collisions.
        '''
        gridcell = self.gridcell
        position = self.position
        rect = self.rect

        self.velocity[1] = min(MAX_SPEED,
                               self.velocity[1] + self.acceleration[1])
        position[1] += self.velocity[1]
        rect.top = position[1] + 0.5  #Round to the nearest integer
        gridcell[1] = self.rect.centery // self.rect.height
        self.emitter.rect.topleft = rect.topleft

        self.__animate()

        if rect.bottom >= blockgrid.RECT.bottom:
            #If we've hit the bottom of the grid...
            rect.bottom = blockgrid.RECT.bottom
            position[1] = rect.top
            self.change_state(Block.STATES.IMPACT)
        elif self.gridcell[1] + 1 < blockgrid.SIZE[1]:
            #Else if it was another block...
            below = blockgrid.blocks[gridcell[0]][gridcell[1] + 1]
            if below and rect.bottom >= below.rect.top:
                #If we've gone past the block below...
                rect.bottom = below.rect.top
                position[1] = rect.top
                self.change_state(Block.STATES.IMPACT)
            assert isinstance(below, Block) or below is None, \
            "A %s is trying to collide with a stray %s!" % (self, below)

        assert self.state == Block.STATES.FALLING \
        and rect.colliderect(blockgrid.RECT) \
        and blockgrid.RECT.collidepoint(position), \
        "An active %s has somehow left the field!" % self

    def wait(self):
        '''
        Constantly checks to see if this block can fall.
        '''
        gridcell = self.gridcell
        if self.rect.bottom < blockgrid.RECT.bottom:
            #If we're not at the bottom of the grid...
            block_below = blockgrid.blocks[gridcell[0]][gridcell[1] + 1]
            if not block_below or block_below.velocity[1]:
                #If there's no block directly below...
                blockgrid.check_block(self, False)
                self.acceleration[1] = GRAVITY
                self.change_state(Block.STATES.START_FALLING)

        if __debug__ and self.rect.collidepoint(pygame.mouse.get_pos()):
            print(self)

    def stop(self):
        '''
        Handles the Block when it hits the bottom of the grid or another block.
        Changes velocity, plays sounds, etc.
        '''
        self.acceleration[1] = 0.0
        self.velocity[1] = 0.0
        self.position = self.__get_snap()
        self.rect.topleft = self.position
        self.gridcell[1] = self.rect.centery // self.rect.height  #(row, col)
        self.state = Block.STATES.ACTIVE
        blockgrid.blocks[self.gridcell[0]][self.gridcell[1]] = self

        blockgrid.check_block(self, True)
        _bump.play()
        #blockgrid.update()

        if self._special:
            #If this is a special block...
            UFO_BLOCK.play()
            self.emitter.pool = color.random_color_particles
            if self.gridcell[1] < blockgrid.SIZE[1] - 1:
                #If we're not at the bottom of the grid...
                self.color = blockgrid.blocks[self.gridcell[0]][
                    self.gridcell[1] + 1].color
                blockgrid.clear_color(self.color)
            else:
                blockgrid.clear_row(self.gridcell[1])

        elif not self.gridcell[1]:
            #If we go past the the playing field...
            self._anim = len(FRAMES) - 2  #Bring us to the second-to-last frame
            self.__animate()  #And let the animation system finish
            gamedata.lives = 0

    def vanish(self):
        blockgrid.check_block(self, False)
        self.emitter.burst(20)
        self.kill()

        blockgrid.blocks[self.gridcell[0]][self.gridcell[1]] = None
        self._anim = 0
        self.position = [-300.0, -300.0]
        self.rect.topleft = self.position
        self.gridcell = None
        self.change_state(Block.STATES.IDLE)
        self.__replace()

    def __replace(self):
        '''
        Puts this Block back in the set of spares.
        
        @postcondition: This Block is ready to be recycled.
        '''
        _blocks_set.add(self)

    def __animate(self):
        if self._anim < len(FRAMES) - 1:
            #If we haven't hit the last frame of animation...
            self._anim += 1
            self.image = self.current_frame_list[id(self.color)][self._anim]

        if self._special:
            #If this is a _special block...
            self.image = self.current_frame_list[id(choice(
                color.LIST))][self._anim]

    def __get_snap(self):
        '''
        Returns a position list that snaps this block to the grid.
        '''
        size = self.image.get_size()
        return [
            round(self.position[0] // size[0]) * size[0],
            round(self.position[1] // size[1]) * size[1],
        ]

    actions = {
        STATES.IDLE: None,
        STATES.APPEARING: 'appear',
        STATES.ACTIVE: 'wait',
        STATES.FALLING: 'fall',
        STATES.START_FALLING: 'start_falling',
        STATES.IMPACT: 'stop',
        STATES.DYING: 'vanish',
    }
示例#15
0
class UFO(GameObject):
    STATES      = config.Enum(*UFO_STATES)
    GROUP       = None
    BLOCK_GROUP = None

    def __init__(self):
        super().__init__()
        self._anim    = 0.0
        self.column   = None
        self.current_frame_list = UFO_FRAMES
        self.image    = config.get_sprite(FRAMES[0])
        self.odds     = expovariate(AVG_WAIT)
        self.position = list(START_POS)
        self.rect     = Rect(START_POS, self.image.get_size())
        self.state    = UFO.STATES.IDLE
        self.emitter  = ParticleEmitter(color.random_color_particles, self.rect)

        del self.acceleration

    def appear(self):
        '''
        Appear on-screen, but not for very long!
        '''
        INVADE.play(-1)
        self.position     = list(START_POS)
        self.rect.topleft = list(START_POS)
        self.change_state(UFO.STATES.ACTIVE)
        self.velocity[0]  = -2.0

    def move(self):
        '''
        Move left on the screen, and oscillate up and down.
        '''
        position = self.position
        rect     = self.rect
            
        self._anim += 0.5
        self.image  = UFO_FRAMES[id(choice(color.LIST))       ] \
                                [int(self._anim) % len(FRAMES)]
        position[0] += self.velocity[0]
        position[1] += sin(self._anim/4)
        rect.topleft = (position[0] + .5, position[1] + .5)

        if rect.right < 0:
        #If we've gone past the left edge of the screen...
            self.change_state(UFO.STATES.LEAVING)

    def die(self):
        '''
        Vanish and release a special Block that clears lots of other Blocks.
        '''
        self.emitter.rect = self.rect
        self.emitter.burst(30)
        DEATH.play()
        UFO.BLOCK_GROUP.add(get_block((self.rect.centerx, 0), special=True))
        gamedata.score += 90
        self.change_state(UFO.STATES.LEAVING)

    def leave(self):
        INVADE.stop()
        self.velocity[0]  = 0
        self.position     = list(START_POS)
        self.rect.topleft = START_POS
        self.change_state(UFO.STATES.IDLE)

    def wait(self):
        '''
        Wait off-screen, and only come back with a specific probability.
        '''
        if uniform(0, 1) < self.odds:
        #With a certain probability...
            self.odds = expovariate(AVG_WAIT)
            self.change_state(UFO.STATES.APPEARING)

    actions = {
                STATES.IDLE     : 'wait'  ,
                STATES.APPEARING: 'appear',
                STATES.ACTIVE   : 'move'  ,
                STATES.DYING    : 'die'   ,
                STATES.LEAVING  : 'leave' ,
                STATES.GAMEOVER : None    ,
              }
示例#16
0
class Ship(GameObject):
    '''
    The Ship is the player character.  There's only going to be one instance of
    it, but it has to inherit from pygame.sprite.Sprite, so we can't make it a
    true Python singleton (i.e. a module).
    '''
    
    STATES = config.Enum(*SHIP_STATES)
    GROUP  = None

    def __init__(self):
        '''
        @ivar anim: A counter for ship animation
        @ivar image: The graphic
        @ivar invincible: How many frames of invincibility the player has if any
        @ivar my_bullet: The single bullet this ship may fire
        '''
        super().__init__()

        self.anim           = 0.0
        self.appear_emitter = ParticleEmitter(APPEAR_POOL, START_POS.copy(), 2)
        self.emitter        = ParticleEmitter(DEATH_POOL, START_POS.copy(), 2)
        self.flames         = FlameTrail()
        self.image          = FRAMES[0]
        self.invincible     = 0
        self.light_column   = LightColumn()
        self.my_bullet      = ShipBullet()
        self.position       = list(START_POS.topleft)
        self.rect           = START_POS.copy()
        self.respawn_time   = 3 * 60  # In frames
        self.change_state(Ship.STATES.RESPAWN)

    def on_fire_bullet(self):
        bul = self.my_bullet
        if bul.state == ShipBullet.STATES.IDLE and self.state == Ship.STATES.ACTIVE:
        #If our bullet is not already on-screen...
            bul.add(Ship.GROUP)
            self.anim       = 1
            self.image      = FRAMES[self.anim]
            bul.rect.center = self.rect.center
            bul.position    = list(self.rect.topleft)
            bul.change_state(ShipBullet.STATES.FIRED)

    def respawn(self):
        self.appear_emitter.burst(200)
        APPEAR.stop()
        APPEAR.play()
        for i in chain(FRAMES, FlameTrail.FRAMES, {self.light_column.image}): i.set_alpha(128)
        self.invincible   = 250
        self.light_column.rect.midbottom = self.rect.midtop
        self.position     = list(START_POS.topleft)
        self.rect         = START_POS.copy()
        self.respawn_time = 3 * 60
        
        self.change_state(Ship.STATES.ACTIVE)

    def move(self):
        keys  = pygame.key.get_pressed() #Shorthand for which keys are pressed
        rect  = self.rect
        width = self.image.get_width()
        
        if self.state not in {Ship.STATES.DYING, Ship.STATES.DEAD, Ship.STATES.IDLE}:
            if (keys[K_LEFT] or keys[K_a]) and rect.left > 0:
            #If we're pressing left and not at the left edge of the screen...
                self.position[0] -= SPEED
            elif (keys[K_RIGHT] or keys[K_d]) and rect.right < config.SCREEN_RECT.right:
            #If we're pressing right and not at the right edge of the screen...
                self.position[0] += SPEED

        rect.left               = self.position[0] + 0.5
        self.flames.rect.midtop = (rect.midbottom[0], rect.midbottom[1] - 1)
        #Compensate for the gap in the flames                           ^^^
        self.light_column.position[0] = self.position[0]
        self.light_column.rect.left   = round(self.light_column.position[0] / width) * width
        
        if self.invincible:
        #If we're invincible...
            self.invincible -= 1
        elif self.image.get_alpha() == 128:
            for i in chain(FRAMES, FlameTrail.FRAMES): i.set_alpha(255)
            
        self.anim = self.anim + (0 < self.anim < len(FRAMES) - 1) / 3 if self.anim != 4 else 0.0
        self.image = FRAMES[int(self.anim)]
        
        if gamedata.combo_time == gamedata.MAX_COMBO_TIME and gamedata.combo > 1:
            counter                = get_combo_counter(gamedata.combo, self.rect.topleft)
            counter.rect.midbottom = self.rect.midtop
            counter.position       = list(counter.rect.topleft)
            counter.change_state(counter.__class__.STATES.APPEARING)
            Ship.GROUP.add(counter)
            

    def die(self):
        DEATH.play()
        for i in chain(FRAMES, FlameTrail.FRAMES, (self.light_column.image,)): i.set_alpha(0)
        self.emitter.rect = self.rect
        self.emitter.burst(100)
        self.change_state(Ship.STATES.DEAD)
        
    def instadie(self, other):
        if gamedata.lives:
        #If we have any lives...
            gamedata.lives = 0
            self.die()
            
    def wait_to_respawn(self):
        self.respawn_time -= 1
        if not self.respawn_time:
        #If we're done waiting to respawn...
            self.change_state(Ship.STATES.RESPAWN)
            
    actions = {
               STATES.IDLE      : None             ,
               STATES.SPAWNING  : 'respawn'        ,
               STATES.ACTIVE    : 'move'           ,
               STATES.DYING     : 'die'            ,
               STATES.DEAD      : 'wait_to_respawn',
               STATES.RESPAWN   : 'respawn'        ,
              }
    
    collisions = {
                  Enemy: instadie,
                  }
示例#17
0
class Ship(GameObject):
    '''
    The Ship is the player character.  There's only going to be one instance of
    it, but it has to inherit from pygame.sprite.Sprite, so we can't make it a
    true Python singleton (i.e. a module).
    '''

    STATES = config.Enum(*SHIP_STATES)
    GROUP = None

    def __init__(self):
        '''
        @ivar anim: A counter for ship animation
        @ivar image: The graphic
        @ivar invincible: How many frames of invincibility the player has if any
        @ivar my_bullet: The single bullet this ship may fire
        '''
        super().__init__()

        self.anim = 0.0
        self.appear_emitter = ParticleEmitter(APPEAR_POOL, START_POS.copy(), 2)
        self.emitter = ParticleEmitter(DEATH_POOL, START_POS.copy(), 2)
        self.flames = FlameTrail()
        self.image = FRAMES[0]
        self.invincible = 0
        self.light_column = LightColumn()
        self.my_bullet = ShipBullet()
        self.position = list(START_POS.topleft)
        self.rect = START_POS.copy()
        self.respawn_time = 3 * 60  # In frames
        self.change_state(Ship.STATES.RESPAWN)

    def on_fire_bullet(self):
        bul = self.my_bullet
        if bul.state == ShipBullet.STATES.IDLE and self.state == Ship.STATES.ACTIVE:
            #If our bullet is not already on-screen...
            bul.add(Ship.GROUP)
            self.anim = 1
            self.image = FRAMES[self.anim]
            bul.rect.center = self.rect.center
            bul.position = list(self.rect.topleft)
            bul.change_state(ShipBullet.STATES.FIRED)

    def respawn(self):
        self.appear_emitter.burst(200)
        APPEAR.stop()
        APPEAR.play()
        for i in chain(FRAMES, FlameTrail.FRAMES, {self.light_column.image}):
            i.set_alpha(128)
        self.invincible = 250
        self.light_column.rect.midbottom = self.rect.midtop
        self.position = list(START_POS.topleft)
        self.rect = START_POS.copy()
        self.respawn_time = 3 * 60

        self.change_state(Ship.STATES.ACTIVE)

    def move(self):
        keys = pygame.key.get_pressed()  #Shorthand for which keys are pressed
        rect = self.rect
        width = self.image.get_width()

        if self.state not in {
                Ship.STATES.DYING, Ship.STATES.DEAD, Ship.STATES.IDLE
        }:
            if (keys[K_LEFT] or keys[K_a]) and rect.left > 0:
                #If we're pressing left and not at the left edge of the screen...
                self.position[0] -= SPEED
            elif (keys[K_RIGHT]
                  or keys[K_d]) and rect.right < config.SCREEN_RECT.right:
                #If we're pressing right and not at the right edge of the screen...
                self.position[0] += SPEED

        rect.left = self.position[0] + 0.5
        self.flames.rect.midtop = (rect.midbottom[0], rect.midbottom[1] - 1)
        #Compensate for the gap in the flames                           ^^^
        self.light_column.position[0] = self.position[0]
        self.light_column.rect.left = round(
            self.light_column.position[0] / width) * width

        if self.invincible:
            #If we're invincible...
            self.invincible -= 1
        elif self.image.get_alpha() == 128:
            for i in chain(FRAMES, FlameTrail.FRAMES):
                i.set_alpha(255)

        self.anim = self.anim + (
            0 < self.anim < len(FRAMES) - 1) / 3 if self.anim != 4 else 0.0
        self.image = FRAMES[int(self.anim)]

        if gamedata.combo_time == gamedata.MAX_COMBO_TIME and gamedata.combo > 1:
            counter = get_combo_counter(gamedata.combo, self.rect.topleft)
            counter.rect.midbottom = self.rect.midtop
            counter.position = list(counter.rect.topleft)
            counter.change_state(counter.__class__.STATES.APPEARING)
            Ship.GROUP.add(counter)

    def die(self):
        DEATH.play()
        for i in chain(FRAMES, FlameTrail.FRAMES, (self.light_column.image, )):
            i.set_alpha(0)
        self.emitter.rect = self.rect
        self.emitter.burst(100)
        self.change_state(Ship.STATES.DEAD)

    def instadie(self, other):
        if gamedata.lives:
            #If we have any lives...
            gamedata.lives = 0
            self.die()

    def wait_to_respawn(self):
        self.respawn_time -= 1
        if not self.respawn_time:
            #If we're done waiting to respawn...
            self.change_state(Ship.STATES.RESPAWN)

    actions = {
        STATES.IDLE: None,
        STATES.SPAWNING: 'respawn',
        STATES.ACTIVE: 'move',
        STATES.DYING: 'die',
        STATES.DEAD: 'wait_to_respawn',
        STATES.RESPAWN: 'respawn',
    }

    collisions = {
        Enemy: instadie,
    }