def __init__(self, name=None): super(Sprite, self).__init__() # Signals self.moved = Signal() self.dead = Signal() # State self.quad_trees = set() self.layer = None self.name = name or self.NAME assert self.name self.started = False self.direction = Direction.SOUTH self.velocity = (0, 0) self.speed = self.MOVE_SPEED self.can_move = True self.use_quadtrees = True self.autoset_velocity = True self.frame_state = 'default' self.anim_frame = 0 self.anim_timer = None
def __init__(self, level): self.level = level self.rects = [] self._moved_cnxs = [] level.register_for_events(self) self.entered_objects = set() # Signals self.event_fired = Signal() self.object_moved = Signal() self.object_entered = Signal() self.object_exited = Signal()
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()
class Wife(ChaseMixin, Human, WalkingSprite): MOVE_SPEED = 1 CHASE_SPEED = 1 WANDER_KEY_NAME = 'walking' DEFAULT_HEALTH = 1 CLEAN_NAME = 'wife' INFECTED_NAME = 'infectedwife' NAME = INFECTED_NAME TRANSITION_MS = 250 MAX_TRANSITIONS = 6 def __init__(self): super(Wife, self).__init__() self.transitioned = Signal() self.showed_exclamation = False def on_collision(self, dx, dy, obj, self_rect, obj_rect): if obj == get_engine().player and not self.showed_exclamation: get_engine().ui.close_monologues() self.show_exclamation('heart_exclamation', self._transition_clean) self.showed_exclamation = True def _transition_clean(self): self.transition_count = 0 self.transition_timer = Timer(ms=self.TRANSITION_MS, cb=lambda: self._on_transition()) def _on_transition(self): self.transition_count += 1 if self.name == self.INFECTED_NAME: self.name = self.CLEAN_NAME else: self.name = self.INFECTED_NAME if self.transition_count == self.MAX_TRANSITIONS: self.name = self.CLEAN_NAME self.transition_timer.stop() self.transitioned.emit()
class EventBox(object): def __init__(self, level): self.level = level self.rects = [] self._moved_cnxs = [] level.register_for_events(self) self.entered_objects = set() # Signals self.event_fired = Signal() self.object_moved = Signal() self.object_entered = Signal() self.object_exited = Signal() def disconnect(self): self.level.unregister_for_events(self) for cnx in self._moved_cnxs: cnx.disconnect() self._moved_cnxs = [] def watch_object_moves(self, obj): self._moved_cnxs.append(obj.moved.connect(lambda dx, dy: self.handle_object_move(obj))) def handle_event(self, event): return self.event_fired.emit(event) def handle_object_move(self, obj): if obj.layer.parent != self.level: return obj_rects = obj.get_absolute_collision_rects() for obj_rect in obj_rects: if obj_rect.collidelist(self.rects) != -1: if obj not in self.entered_objects: self.entered_objects.add(obj) self.object_entered.emit(obj) self.object_moved.emit(obj) elif obj in self.entered_objects: self.entered_objects.remove(obj) self.object_exited.emit(obj)
class Sprite(BaseSprite): NAME = None MOVE_SPEED = 4 RUN_SPEED = 8 ANIM_MS = 150 DEATH_BLINK_MS = 250 MAX_BLINKS = 4 LETHAL = False SPRITESHEET_ROWS = 1 SPRITESHEET_COLS = 1 SPRITESHEET_FRAMES = { Direction.SOUTH: { 'default': [(0, 0)], }, Direction.WEST: { 'default': [(0, 0)], }, Direction.EAST: { 'default': [(0, 0)], }, Direction.NORTH: { 'default': [(0, 0)], }, } NEED_TICKS = True def __init__(self, name=None): super(Sprite, self).__init__() # Signals self.moved = Signal() self.dead = Signal() # State self.quad_trees = set() self.layer = None self.name = name or self.NAME assert self.name self.started = False self.direction = Direction.SOUTH self.velocity = (0, 0) self.speed = self.MOVE_SPEED self.can_move = True self.use_quadtrees = True self.autoset_velocity = True self.frame_state = 'default' self.anim_frame = 0 self.anim_timer = None def start(self): self.anim_timer = Timer(ms=self.ANIM_MS, cb=self._on_anim_tick, start_automatically=False) self.started = True def stop(self): self.stop_moving() self.started = False def damage(self, damage_value): if self.health > 0: self.health -= damage_value if self.health <= 0: self.die() return True return False def die(self, on_done=None): self.stop() self.blink(self.DEATH_BLINK_MS, lambda: self._stop_and_die(on_done)) def _stop_and_die(self, on_done): self.dead.emit() self.remove() if on_done: on_done() def blink(self, ms, on_done): self.blink_count = 0 self.visible = 0 self.blink_timer = Timer(ms=ms, cb=lambda: self._on_blinked(on_done)) def _on_blinked(self, on_done): self.blink_count += 1 if self.visible: self.visible = 0 else: self.visible = 1 if self.blink_count == self.MAX_BLINKS: self.visible = 1 self.blink_timer.stop() if on_done: on_done() def remove(self): self.stop() self.collidable = False self.visible = 0 self.dirty = 1 self.layer.remove(self) self.layer = None def update_image(self): self.image = self.generate_image() assert self.image self.rect.size = self.image.get_size() self.update_collision_rects() def generate_image(self): return load_spritesheet_frame( self.name, self._get_spritesheet_frames()[self.anim_frame], self.SPRITESHEET_ROWS, self.SPRITESHEET_COLS) def _get_spritesheet_frames(self): return self.SPRITESHEET_FRAMES[self.direction][self.frame_state] def move_by(self, dx, dy, check_collisions=True): super(Sprite, self).move_by(dx, dy, check_collisions=check_collisions) self.moved.emit(dx, dy) def set_direction(self, direction): if self.direction != direction: self.direction = direction self.update_velocity() self.update_image() def start_animation(self, name): self.frame_state = name self.anim_frame = 0 if self.anim_timer: self.anim_timer.stop() self.anim_timer = Timer(ms=self.ANIM_MS, cb=self._on_anim_tick) def stop_moving(self): self.velocity = (0, 0) self.frame_state = 'default' self.anim_frame = 0 if self.anim_timer: self.anim_timer.stop() self.anim_timer = None def recompute_direction(self): if abs(self.velocity[0]) > abs(self.velocity[1]): if self.velocity[0] > 0: self.set_direction(Direction.EAST) elif self.velocity[0] < 0: self.set_direction(Direction.WEST) elif abs(self.velocity[1]) >= abs(self.velocity[0]): if self.velocity[1] > 0: self.set_direction(Direction.SOUTH) elif self.velocity[1] < 0: self.set_direction(Direction.NORTH) def update_collision_rects(self): pass def update_velocity(self): if not self.started or not self.autoset_velocity: return x, y = { Direction.WEST: (-1, None), Direction.EAST: (1, None), Direction.NORTH: (None, -1), Direction.SOUTH: (None, 1), }[self.direction] if x: x *= self.speed else: x = self.velocity[0] if y: y *= self.speed else: y = self.velocity[1] self.velocity = (x, y) def tick(self): if self.started and self.velocity != (0, 0): self.move_by(*self.velocity) def _on_anim_tick(self): frames = self._get_spritesheet_frames() self.anim_frame += 1 if self.anim_frame == len(frames): self.anim_frame = 0 self.dirty = 2 self.update_image()
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()
def __init__(self): super(Wife, self).__init__() self.transitioned = Signal() self.showed_exclamation = False