Пример #1
0
class Player(WalkingSprite):
    MAX_LIVES = 3
    MAX_HEALTH = 8

    SHOOT_MS = 500
    FALL_SPEED = 10
    HURT_BLINK_MS = 250

    SPRITESHEET_COLS = 4
    SPRITESHEET_FRAMES = {
        Direction.SOUTH: dict(
            WalkingSprite.SPRITESHEET_FRAMES[Direction.SOUTH], **{
                'shooting': [(3, 0)],
                'falling': [(1, 0)],
            }),
        Direction.WEST: dict(
            WalkingSprite.SPRITESHEET_FRAMES[Direction.WEST], **{
                'shooting': [(3, 1)],
                'falling': [(1, 1)],
            }),
        Direction.EAST: dict(
            WalkingSprite.SPRITESHEET_FRAMES[Direction.EAST], **{
                'shooting': [(3, 2)],
                'falling': [(1, 2)],
            }),
        Direction.NORTH: dict(
            WalkingSprite.SPRITESHEET_FRAMES[Direction.NORTH], **{
                'shooting': [(3, 3)],
                'falling': [(1, 3)],
            }),
    }

    def __init__(self):
        super(Player, self).__init__('player')

        # Signals
        self.health_changed = Signal()
        self.lives_changed = Signal()

        # State
        self.human_kill_count = 0
        self.shoot_timer = Timer(ms=self.SHOOT_MS,
                                 cb=self.shoot,
                                 start_automatically=False)
        self.reset()

    def update_collision_rects(self):
        self.collision_rects = [
            pygame.Rect(0, self.rect.height / 2,
                        self.rect.width, self.rect.height / 2),
        ]

    def reset(self):
        self.health = self.MAX_HEALTH
        self.lives = self.MAX_LIVES
        self.invulnerable = False
        self.running = False
        self.falling = False
        self.shooting = False
        self.can_run = True
        self.collidable = True
        self.allow_player_control = True

        self.health_changed.emit()
        self.lives_changed.emit()

        self.set_running(True)
        self.stop_moving()
        self._update_animation()

    def handle_event(self, event):
        if not self.allow_player_control:
            return

        if event.type == KEYDOWN:
            if event.key == K_RIGHT:
                self.move_direction(Direction.EAST)
            elif event.key == K_LEFT:
                self.move_direction(Direction.WEST)
            elif event.key == K_UP:
                self.move_direction(Direction.NORTH)
            elif event.key == K_DOWN:
                self.move_direction(Direction.SOUTH)
            elif event.key == K_c:
                self.set_shooting(True)
            elif event.key == K_F4:
                self.collidable = not self.collidable
        elif event.type == KEYUP:
            if event.key == K_RIGHT:
                self.stop_moving_direction(Direction.EAST)
            elif event.key == K_LEFT:
                self.stop_moving_direction(Direction.WEST)
            elif event.key == K_UP:
                self.stop_moving_direction(Direction.NORTH)
            elif event.key == K_DOWN:
                self.stop_moving_direction(Direction.SOUTH)
            elif event.key == K_c:
                self.set_shooting(False)

    def move_direction(self, direction):
        self.direction = direction

        self.update_velocity()
        self._update_animation()

        self.update_image()

    def stop_moving_direction(self, direction):
        if direction in (Direction.WEST, Direction.EAST):
            self.velocity = (0, self.velocity[1])
        elif direction in (Direction.NORTH, Direction.SOUTH):
            self.velocity = (self.velocity[0], 0)

        # The direction may not make any sense anymore, so recompute it.
        self.recompute_direction()
        self._update_animation()

    def set_shooting(self, shooting):
        if self.shooting == shooting:
            return

        self.shooting = shooting

        if shooting:
            self.shoot_timer.start()
            self.shoot()
        else:
            self.shoot_timer.stop()

        self._update_animation()

    def shoot(self):
        bullet = Bullet(self)
        self.layer.add(bullet)

        bullet.move_beside(self, self.direction)
        bullet.start()
        bullet.set_direction(self.direction)
        bullet.update_velocity()

    def should_adjust_position_with(self, obj, dx, dy):
        return not isinstance(obj, Bullet) or obj.owner_sprite != self

    def set_running(self, running):
        self.running = running

        if running:
            self.speed = self.RUN_SPEED
        else:
            self.speed = self.MOVE_SPEED

        if self.velocity != (0, 0):
            self.update_velocity()

        self._update_animation()

    def stop_running(self):
        self.running = False
        self._update_animation()

    def fall(self):
        self.collidable = False
        self.stop_running()
        self.set_direction(Direction.SOUTH)
        self.velocity = (0, self.FALL_SPEED)
        self.falling = True
        self._update_animation()

    def _update_animation(self):
        if self.velocity == (0, 0):
            if self.shooting and self.frame_state != 'shooting':
                self.frame_state = 'shooting'
            elif not self.shooting and self.frame_state != 'default':
                self.frame_state = 'default'
            else:
                return

            self.anim_timer.stop()
        else:
            new_state = None

            if self.running and self.frame_state != 'running':
                new_state = 'running'
            elif self.falling:
                new_state = 'falling'
            elif self.frame_state != 'walking':
                new_state = 'walking'
            else:
                return

            if new_state:
                self.start_animation(new_state)

        self.anim_frame = 0
        self.update_image()

    def on_collision(self, dx, dy, obj, self_rect, obj_rect):
        if obj.LETHAL and self.health > 0:
            if not self.invulnerable:
                self.health -= 1
                self.health_changed.emit()

                if self.health == 0:
                    self.on_dead()
                else:
                    self.invulnerable = True
                    self.blink(self.HURT_BLINK_MS, self._on_hurt_blink_done)

            return True

    def _on_hurt_blink_done(self):
        self.invulnerable = False

    def on_dead(self):
        self.lives -= 1
        self.lives_changed.emit()
        self.stop_moving()

        engine = get_engine()

        if self.lives == 0:
            self.die(engine.game_over)
        else:
            self.health = self.MAX_HEALTH
            self.health_changed.emit()
            engine.dead()