class SpeechBubble(Entity): def __init__(self, x, y, source, sprite_type=SpriteType.NONE): super(SpeechBubble, self).__init__(x, y, 1, 1) self.sprite = Sprite(x, y, SpriteType.SPEECH_BUBBLE) self.__content = Sprite(x + 8, y + 8, sprite_type) self.source = source def set_location(self, x, y): super(SpeechBubble, self).set_location(x, y) self.sprite.set_location(x, y) self.__content.set_location(x + 8, y + 8) def set_content(self, sprite_type): self.__content.set_sprite(sprite_type) self.__content.set_location(self.x + 8, self.y + 8) def update(self, delta_time, entities): pass def draw(self, surface): self.sprite.draw(surface, CameraType.DYNAMIC) self.__content.draw(surface, CameraType.DYNAMIC)
class BossCrab(Entity): def __init__(self): super(BossCrab, self).__init__(96, 128, 128, 8) self.body = Sprite(self.x - 64, self.y - 32, SpriteType.CRAB_BOSS_BODY) self.bandaid = Sprite(self.x + 2 * 16, self.y - 1 * 16, SpriteType.CRAB_BOSS_BANDAID) self.face = Sprite(self.x + 2 * 16, self.y + 2 * 16, SpriteType.CRAB_FACE_SLEEPING) self.emote = Sprite(self.x + 5 * 16, self.y - 2 * 16, SpriteType.CRAB_BOSS_EMOTE_SLEEPY) self.state_index = 0 self.total_flashes = 3 self.flashes = 0 self.flash_duration = 200 self.invinsibility_flash_timer = Timer(self.flash_duration) self.hurt = False self.flashing = False self.injured = False self.crab_smash = False self.sync_smash = 0 self.special_attack = False self.idle_timer = Timer(5000) def bop_on_head(self): if not self.hurt: self.state_index += 1 self.hurt = True self.invinsibility_flash_timer.start() if self.state_index < 5: self.face.set_sprite(SpriteType.CRAB_FACE_HURT) self.emote.set_sprite(SpriteType.CRAB_BOSS_EMOTE_MAD) def __update_ai(self, delta_time, scene_data): if self.state_index > 1 and self.state_index < 5: self.idle_timer.start() self.idle_timer.update(delta_time) if self.idle_timer.done: self.crab_smash = True self.idle_timer.reset() def __update_health(self, delta_time): if self.hurt: self.idle_timer.reset() self.invinsibility_flash_timer.update(delta_time) if self.invinsibility_flash_timer.done: self.flashes += 1 self.flashing = not self.flashing if self.flashes >= self.total_flashes * 2: self.hurt = False self.flashing = False self.flashes = 0 if self.state_index == 1: self.state_index += 1 if self.state_index < 5: self.face.set_sprite(SpriteType.CRAB_FACE_HAPPY) self.emote.set_sprite(SpriteType.NONE) self.crab_smash = True self.invinsibility_flash_timer.reset() self.invinsibility_flash_timer.start() def __update_expression(self): if self.state_index >= 5: self.emote.set_sprite(SpriteType.CRAB_BOSS_EMOTE_THIRSTY) self.face.set_sprite(SpriteType.CRAB_FACE_INJURED) self.injured = True def update(self, delta_time, scene_data): self.__update_ai(delta_time, scene_data) self.__update_health(delta_time) self.__update_expression() def draw(self, surface): if globals.debugging: draw_rectangle(surface, self.bounds, CameraType.DYNAMIC, self.color, 4) else: if not self.flashing: self.body.draw(surface, CameraType.STATIC) self.bandaid.draw(surface, CameraType.STATIC) self.face.draw(surface, CameraType.STATIC) self.emote.draw(surface, CameraType.STATIC)
class Boat(Actor): def __init__(self, x, y, beans=50): super(Boat, self).__init__(x, y, 83, 16, 50) self.beans = beans self.playbounds = Rectangle(0, 16 * 3, Camera.BOUNDS.width, Camera.BOUNDS.height - 16 * 3) self.sprite = Sprite(x - 16, y - 48, SpriteType.BOAT) self.shadow = Sprite(x - 16 - 16, y - 16, SpriteType.BOAT_SHADOW) self.blinks = 5 self.invis_duration = 1600 self.invis_timer = Timer(self.invis_duration) self.blink_timer = Timer(self.invis_duration / self.blinks / 2) self.damaged = False self.flashing = False self.dead = False def set_location(self, x, y): super(Boat, self).set_location(x, y) self.sprite.set_location(self.x - 16, self.y - 48) self.shadow.set_location(self.x - 16 - 16, self.y - 16) def _collision(self, entities): for e in entities: if not self.damaged: if isinstance(e, Bullet) or isinstance(e, Octopus): if self.bounds.colliderect(e.bounds): e.dead = True self.__decrease_health(5) elif isinstance(e, Rock): if self.bounds.colliderect(e.bounds): self.__decrease_health(10) self.__bounds_collision() def __decrease_health(self, amount): self.damaged = True self.beans -= amount self.invis_timer.start() self.blink_timer.start() self.sprite.set_sprite(SpriteType.BOAT_OWO) def _move(self, direction=Direction.NONE): self.facing = direction if self.facing == Direction.UP: self.set_location(self.x, self.y - self.move_speed) self.velocity.y = -1 if self.facing == Direction.DOWN: self.set_location(self.x, self.y + self.move_speed) self.velocity.y = 1 if self.facing == Direction.LEFT: self.set_location(self.x - self.move_speed, self.y) self.velocity.x = -1 if self.facing == Direction.RIGHT: self.set_location(self.x + self.move_speed, self.y) self.velocity.x = 1 def _update_input(self, delta_time): self.input.update(delta_time) if self.input.pressing(InputType.UP): self._move(Direction.UP) if self.input.pressing(InputType.DOWN): self._move(Direction.DOWN) if self.input.pressing(InputType.LEFT): self._move(Direction.LEFT) if self.input.pressing(InputType.RIGHT): self._move(Direction.RIGHT) def __update_health(self, delta_time): if self.damaged: self.invis_timer.update(delta_time) self.blink_timer.update(delta_time) if self.blink_timer.done: self.flashing = not self.flashing self.blink_timer.reset() self.blink_timer.start() if self.invis_timer.done: self.damaged = False self.flashing = False self.invis_timer.reset() self.sprite.set_sprite(SpriteType.BOAT) def __bounds_collision(self): if self.x < self.playbounds.x: self.x = self.playbounds.x elif self.x + self.width > self.playbounds.x + self.playbounds.width: self.x = self.playbounds.x + self.playbounds.width - self.width if self.y < self.playbounds.y: self.y = self.playbounds.y elif self.y + self.height > self.playbounds.y + self.playbounds.height: self.y = self.playbounds.y + self.playbounds.height - self.height def __check_death(self): if self.beans <= 0: # TODO: death logic here, maybe display transition and change scene? self.dead = True def update(self, delta_time, entities): self._calculate_scaled_speed(delta_time) if self.dead: self.flashing = False self.set_location(self.x - self.move_speed / 2 + randint(-2, 0), self.y + self.move_speed) return self._update_input(delta_time) self._collision(entities) self.__update_health(delta_time) self.__check_death() def draw(self, surface): if pygine.globals.debug: self._draw_bounds(surface, CameraType.DYNAMIC) else: if not self.flashing: self.shadow.draw(surface, CameraType.DYNAMIC) self.sprite.draw(surface, CameraType.DYNAMIC)
class NPC(Kinetic): def __init__(self, x, y, type, can_move=True, horizontal=True, start_direction=1, walk_duration=5000): super(NPC, self).__init__(x, y, 10, 10, 25) self.type = type self.sprite = Sprite(self.x - 3, self.y - 22, SpriteType.NONE) self.shadow = Sprite(self.x - 3, self.y - 21, SpriteType.PLAYER_SHADOW) self.speech_bubble = SpeechBubble(self.x - 11, self.y - 32 - 11, self) self.radius = 32 self.show_prompt = False self.set_color(Color.RED) self.walk_direction = 1 if start_direction >= 0 else -1 self.horizontal = horizontal self.can_move = can_move self._walk_timer = Timer(walk_duration, True) self.animation_walk = Animation(6, 6, 100) self.walking = True self._set_walking_sprite() self._set_random_emotion() def set_location(self, x, y): super(NPC, self).set_location(x, y) self.sprite.set_location(self.x - 3, self.y - 22) self.shadow.set_location(self.x - 3, self.y - 21) self.speech_bubble.set_location(self.x - 11, self.y - 32 - 11) def _set_random_emotion(self): rand = randint(1, 4) if rand == 1: self.speech_bubble.set_content(SpriteType.FACE_HAPPY) elif rand == 2: self.speech_bubble.set_content(SpriteType.FACE_SAD) elif rand == 3: self.speech_bubble.set_content(SpriteType.FACE_MAD) elif rand == 4: self.speech_bubble.set_content(SpriteType.FACE_SURPRISED) def _stop_walking(self): if not self.horizontal: if self.type == NPCType.MALE: self.sprite.set_sprite(SpriteType.NPC_M_F) else: self.sprite.set_sprite(SpriteType.NPC_F_F) self._set_random_emotion() self.walking = not self.walking def _walk(self, delta_time): self._walk_timer.update(delta_time) if self._walk_timer.done: if random() < 0.25: self._stop_walking() if self.walking: self.walk_direction = -self.walk_direction self._set_walking_sprite() self._walk_timer.reset() self._walk_timer.start() if self.walking: if self.horizontal: self.set_location( self.x + self.move_speed * self.walk_direction, self.y) else: self.set_location( self.x, self.y + self.move_speed * self.walk_direction) def _set_walking_sprite(self): if self.can_move: if self.horizontal: if self.walk_direction > 0: if self.type == NPCType.MALE: self.sprite.set_sprite(SpriteType.NPC_M_R) else: self.sprite.set_sprite(SpriteType.NPC_F_R) else: if self.type == NPCType.MALE: self.sprite.set_sprite(SpriteType.NPC_M_L) else: self.sprite.set_sprite(SpriteType.NPC_F_L) else: if self.walk_direction > 0: if self.type == NPCType.MALE: self.sprite.set_sprite(SpriteType.NPC_M_F) else: self.sprite.set_sprite(SpriteType.NPC_F_F) else: if self.type == NPCType.MALE: self.sprite.set_sprite(SpriteType.NPC_M_B) else: self.sprite.set_sprite(SpriteType.NPC_F_B) else: if self.type == NPCType.MALE: self.sprite.set_sprite(SpriteType.NPC_M_F) else: self.sprite.set_sprite(SpriteType.NPC_F_F) def _within_radius(self, e): if distance_between(self.center, e.center) <= self.radius: self.show_prompt = True else: self.show_prompt = False def _update_conversation(self, entities): for e in entities: if isinstance(e, Player): last = self.show_prompt self._within_radius(e) if last != self.show_prompt: if self.show_prompt: entities.append(self.speech_bubble) else: entities.remove(self.speech_bubble) def _update_animation(self, delta_time): if self.walking: self.animation_walk.update(delta_time) self.sprite.set_frame(self.animation_walk.current_frame, self.animation_walk.columns) else: self.sprite.set_frame(0, self.animation_walk.columns) def _rectangle_collision_logic(self, entity): # Bottom if self.collision_rectangles[0].colliderect( entity.bounds) and self.velocity.y < 0: self.set_location(self.x, entity.bounds.bottom) # Top elif self.collision_rectangles[1].colliderect( entity.bounds) and self.velocity.y > 0: self.set_location(self.x, entity.bounds.top - self.bounds.height) # Right elif self.collision_rectangles[2].colliderect( entity.bounds) and self.velocity.x < 0: self.set_location(entity.bounds.right, self.y) # Left elif self.collision_rectangles[3].colliderect( entity.bounds) and self.velocity.x > 0: self.set_location(entity.bounds.left - self.bounds.width, self.y) def _collision(self, entities): for e in entities: if (isinstance(e, Building) or isinstance(e, Tree)): self._rectangle_collision_logic(e) def update(self, delta_time, entities): self._update_conversation(entities) self._calculate_scaled_speed(delta_time) if self.can_move: self._walk(delta_time) # self._update_collision_rectangles() # self._collision(entities) if self.can_move: self._update_animation(delta_time) def draw(self, surface): if pygine.globals.debug: self._draw_bounds(surface, CameraType.DYNAMIC) else: self.shadow.draw(surface, CameraType.DYNAMIC) self.sprite.draw(surface, CameraType.DYNAMIC)
class Player(Actor): def __init__(self, x, y, width=10, height=10, speed=50): super(Player, self).__init__(x, y, width, height, speed) self.sprite = Sprite(self.x - 3, self.y - 22, SpriteType.PLAYER_F) self.arms = Sprite(self.x - 3, self.y - 22, SpriteType.PLAYER_ARM_SIDE_F) self.shadow = Sprite(self.x - 3, self.y - 21, SpriteType.PLAYER_SHADOW) self.set_color(Color.RED) self.item_carrying = None self.animation_walk = Animation(6, 6, 100) self.walking = False def set_location(self, x, y): super(Player, self).set_location(x, y) self.sprite.set_location(self.x - 3, self.y - 22) self.arms.set_location(self.x - 3, self.y - 22) self.shadow.set_location(self.x - 3, self.y - 21) if self.item_carrying != None: self.item_carrying.set_location(self.x - 3, self.sprite.y - 8) def _move(self, direction=Direction.NONE): self.facing = direction self.walking = True if self.facing == Direction.UP: self.sprite.set_sprite(SpriteType.PLAYER_B) self.arms.set_sprite(SpriteType.PLAYER_ARM_SIDE_B) self.set_location(self.x, self.y - self.move_speed) self.velocity.y = -1 if self.facing == Direction.DOWN: self.sprite.set_sprite(SpriteType.PLAYER_F) self.arms.set_sprite(SpriteType.PLAYER_ARM_SIDE_F) self.set_location(self.x, self.y + self.move_speed) self.velocity.y = 1 if self.facing == Direction.LEFT: self.sprite.set_sprite(SpriteType.PLAYER_L) self.arms.set_sprite(SpriteType.PLAYER_ARM_SIDE_L) self.set_location(self.x - self.move_speed, self.y) self.velocity.x = -1 if self.facing == Direction.RIGHT: self.sprite.set_sprite(SpriteType.PLAYER_R) self.arms.set_sprite(SpriteType.PLAYER_ARM_SIDE_R) self.set_location(self.x + self.move_speed, self.y) self.velocity.x = 1 def _update_input(self, delta_time): self.input.update(delta_time) self.walking = False if self.input.pressing( InputType.UP) and not self.input.pressing(InputType.DOWN): self._move(Direction.UP) if self.input.pressing( InputType.DOWN) and not self.input.pressing(InputType.UP): self._move(Direction.DOWN) if self.input.pressing( InputType.LEFT) and not self.input.pressing(InputType.RIGHT): self._move(Direction.LEFT) if self.input.pressing( InputType.RIGHT) and not self.input.pressing(InputType.LEFT): self._move(Direction.RIGHT) def _rectangle_collision_logic(self, entity): # Bottom if self.collision_rectangles[0].colliderect( entity.bounds) and self.velocity.y < 0: self.set_location(self.x, entity.bounds.bottom) # Top elif self.collision_rectangles[1].colliderect( entity.bounds) and self.velocity.y > 0: self.set_location(self.x, entity.bounds.top - self.bounds.height) # Right elif self.collision_rectangles[2].colliderect( entity.bounds) and self.velocity.x < 0: self.set_location(entity.bounds.right, self.y) # Left elif self.collision_rectangles[3].colliderect( entity.bounds) and self.velocity.x > 0: self.set_location(entity.bounds.left - self.bounds.width, self.y) def _collision(self, entities): for e in entities: if (not e.ignore and (isinstance(e, Building) or isinstance(e, Furniture) or isinstance(e, Wall) or isinstance(e, Tree)) # isinstance(e, NPC) ): self._rectangle_collision_logic(e) def _update_animation(self, delta_time): if self.walking: self.animation_walk.update(delta_time) self.sprite.set_frame(self.animation_walk.current_frame, self.animation_walk.columns) self.arms.set_frame(self.animation_walk.current_frame, self.animation_walk.columns) else: self.sprite.set_frame(0, self.animation_walk.columns) self.arms.set_frame(0, self.animation_walk.columns) def _update_item(self): if self.item_carrying != None: self.arms.increment_sprite_x(16 * 6) def update(self, delta_time, entities): self._calculate_scaled_speed(delta_time) self._update_input(delta_time) self._update_collision_rectangles() self._collision(entities) self._update_animation(delta_time) self._update_item() def draw(self, surface): if pygine.globals.debug: self._draw_bounds(surface, CameraType.DYNAMIC) self._draw_collision_rectangles(surface) else: self.shadow.draw(surface, CameraType.DYNAMIC) self.sprite.draw(surface, CameraType.DYNAMIC) self.arms.draw(surface, CameraType.DYNAMIC) if (self.item_carrying != None): self.item_carrying.draw(surface)
class Fishy(Kinetic): def __init__(self, y, left): super(Fishy, self).__init__(-32 if left else Camera.BOUNDS.width + 16, y, 16, 16, randint(1, 10) * 10) self.type = randint(0, 1) self.velocity.x = -1 if left else 1 if self.velocity.x > 0: if self.type == 0: self.sprite = Sprite( self.x, self.y, SpriteType.FISH_SMALL_L if left else SpriteType.FISH_SMALL_R) elif self.type == 1: self.sprite = Sprite( self.x, self.y, SpriteType.FISH_LARGE_L if left else SpriteType.FISH_LARGE_R) else: if self.type == 0: self.sprite = Sprite( self.x, self.y, SpriteType.FISH_SMALL_L if left else SpriteType.FISH_SMALL_L) elif self.type == 1: self.sprite = Sprite( self.x, self.y, SpriteType.FISH_LARGE_L if left else SpriteType.FISH_LARGE_L) self.captured = False def set_location(self, x, y): super(Fishy, self).set_location(x, y) self.sprite.set_location(self.x, self.y) def hook_fish(self): self.captured = True def _update_ai(self): self.set_location(self.x + self.move_speed * self.velocity.x, self.y) def flip_velocity(self): self.velocity.x *= -1 if self.velocity.x > 0: if self.type == 0: self.sprite.set_sprite(SpriteType.FISH_SMALL_R) elif self.type == 1: self.sprite.set_sprite(SpriteType.FISH_LARGE_R) else: if self.type == 0: self.sprite.set_sprite(SpriteType.FISH_SMALL_L) elif self.type == 1: self.sprite.set_sprite(SpriteType.FISH_LARGE_L) def _collision(self, entities): if self.captured: for e in entities: if isinstance(e, Hook): if self.velocity.x > 0: self.set_location(e.x - 6 + randint(-3, 3), e.y + 4 + randint(-3, 3)) else: self.set_location(e.x + 2 + randint(-3, 3), e.y + 4 + randint(-3, 3)) if randint(1, 10) <= 1: self.flip_velocity() return if (self.bounds.left < -32 and self.velocity.x < 0) or ( self.bounds.right > Camera.BOUNDS.width + 32 and self.velocity.x > 0): self.flip_velocity() def update(self, delta_time, entities): self._calculate_scaled_speed(delta_time) # self._update_collision_rectangles() self._update_ai() self._collision(entities) def draw(self, surface): if pygine.globals.debug: self._draw_bounds(surface, CameraType.DYNAMIC) self._draw_collision_rectangles(surface) else: self.sprite.draw(surface, CameraType.DYNAMIC)