class Crab(Kinetic): def __init__(self, x, y): super(Crab, self).__init__(x, y, 11, 9, 25) self.sprite = Sprite(self.x - 18, self.y - 19, SpriteType.CRAB) self.walk_animation = Animation(4, 4, 150) self.direction = Direction.RIGHT self.area = None self.query_result = None self.default_jump_height = 16 * 1.5 self.jump_duration = 0.5 self.jump_initial_velocity = 0 self.gravity = 0 self.lateral_acceleration = 0 self.aggravated_move_speed = 100 self.internal_bounds = Rect(self.x + 5, self.y + 5, 1, 1) self.grounded = False self.aggravated = False self.dead = False def set_location(self, x, y): super(Crab, self).set_location(x, y) self.sprite.set_location(self.x - 18, self.y - 19) self.internal_bounds = Rect(self.x + 5, self.y + 5, 1, 1) def toggle_aggravation(self): if self.dead: return self.aggravated = not self.aggravated def squish(self): self.dead = True self.velocity.y = -self.jump_initial_velocity * 0.75 self.velocity.x = - \ self.move_speed if randint(1, 10) % 2 == 0 else self.move_speed def _calculate_scaled_speed(self, delta_time): if self.aggravated: self.move_speed = self.aggravated_move_speed * delta_time else: self.move_speed = self.default_move_speed * delta_time time = 1 / delta_time * self.jump_duration self.jump_initial_velocity = 4 * self.default_jump_height / time self.gravity = 8 * self.default_jump_height / time**2 def _apply_force(self, delta_time): self.velocity.y += self.gravity if self.direction == Direction.RIGHT: self.velocity.x = self.move_speed if self.direction == Direction.LEFT: self.velocity.x = -self.move_speed self.set_location(self.x + self.velocity.x, self.y + self.velocity.y) def _update_collision_rectangles(self): self.collision_width = 3 self.collision_rectangles = [ Rect(self.x + 2, self.y - self.collision_width * 2, self.width - 4, self.collision_width * 2), Rect(self.x + 2, self.y + self.height, self.width - 4, self.collision_width * 2), Rect(self.x - self.collision_width, self.y + self.collision_width, self.collision_width, self.height - self.collision_width * 2), Rect(self.x + self.width, self.y + self.collision_width, self.collision_width, self.height - self.collision_width * 2) ] def __rectanlge_collision_logic(self, entity): # Bottom if self.velocity.y < 0 and self.collision_rectangles[0].colliderect( entity.bounds): self.set_location(self.x, entity.bounds.bottom) self.velocity.y = 0 # Top if self.velocity.y > 0 and self.collision_rectangles[1].colliderect( entity.bounds): self.set_location(self.x, entity.bounds.top - self.bounds.height) self.velocity.y = 0 self.grounded = True # Right if self.velocity.x < 0 and self.collision_rectangles[2].colliderect( entity.bounds): self.set_location(entity.bounds.right, self.y) self.velocity.x = 0 self.direction = Direction.RIGHT # Left if self.velocity.x > 0 and self.collision_rectangles[3].colliderect( entity.bounds): self.set_location(entity.bounds.left - self.bounds.width, self.y) self.velocity.x = 0 self.direction = Direction.LEFT def _collision(self, scene_data): if self.dead: return if self.x < 3: self.set_location(3, self.y) self.velocity.x = 0 self.direction = Direction.RIGHT if self.x + self.width > scene_data.scene_bounds.width: self.set_location(scene_data.scene_bounds.width - self.width, self.y) self.velocity.x = 0 self.direction = Direction.LEFT if self.y > scene_data.scene_bounds.height + 64: self.squish() self.area = Rect(self.x - 16, self.y - 16, self.width + 16 * 2, self.height + 16 * 2) self.query_result = scene_data.entity_quad_tree.query(self.area) self.grounded = False for e in self.query_result: if e is self: continue if isinstance(e, Block): self.__rectanlge_collision_logic(e) self._update_collision_rectangles() elif isinstance(e, QBlock): if e.active: if not self.dead and self.internal_bounds.colliderect( e.bounds): self.squish() self.__rectanlge_collision_logic(e) self._update_collision_rectangles() self.query_result = scene_data.kinetic_quad_tree.query(self.area) for e in self.query_result: if e is self: continue if isinstance(e, Crab): self.__rectanlge_collision_logic(e) self._update_collision_rectangles() def __update_ai(self, scene_data): if self.dead: if self.y > scene_data.scene_bounds.height + 64: self.remove = True if self.aggravated: if self.grounded: self.velocity.y = -self.jump_initial_velocity def __update_animation(self, delta_time): self.walk_animation.update(delta_time) self.sprite.set_frame(self.walk_animation.current_frame, self.walk_animation.columns) if self.aggravated: self.sprite.increment_sprite_y(32) if self.dead: self.sprite.flip_vertically(True) def update(self, delta_time, scene_data): self._calculate_scaled_speed(delta_time) self.__update_ai(scene_data) self._apply_force(delta_time) self._update_collision_rectangles() self._collision(scene_data) self.__update_animation(delta_time) def draw(self, surface): if globals.debugging: self._draw_collision_rectangles(surface) draw_rectangle(surface, self.bounds, CameraType.DYNAMIC, self.color) else: 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): super(Player, self).__init__(x, y, 12, 28, 100) self.sprite = Sprite(self.x - 10, self.y - 16, SpriteType.PLAYER) self.walk_animation = Animation(6, 6, 100) self.direction = Direction.NONE self.area = None self.query_result = None self.default_jump_height = 16 * 4 self.jump_duration = 1 self.default_run_acceleration = 10 self.default_ground_friction = 7 self.default_air_friction = 1 self.jump_initial_velocity = 0 self.gravity = 0 self.lateral_acceleration = 0 self.ground_friction = 0 self.air_friction = 0 self.grounded = False self.jumping = False self.attempt_block_shift = False self.attacked = False self.restart = False self.pause = False self.transitioning = False self.restart_delay = Timer(2600) self.queue_restart = False def revive(self): self.grounded = False self.jumping = False self.attempt_block_shift = False self.attacked = False self.restart = False self.queue_restart = False self.velocity = Vector2(0, 0) self.sprite.set_frame(0, 6) def get_yeeted(self): self.__finessed_by_enemy() def _calculate_scaled_speed(self, delta_time): super(Player, self)._calculate_scaled_speed(delta_time) time = 1 / delta_time * self.jump_duration self.jump_initial_velocity = 4 * self.default_jump_height / time self.gravity = 8 * self.default_jump_height / time**2 self.lateral_acceleration = self.default_run_acceleration * delta_time self.ground_friction = self.default_ground_friction * delta_time self.air_friction = self.default_air_friction * delta_time def set_location(self, x, y): super(Player, self).set_location(x, y) self.sprite.set_location(self.x - 10, self.y - 16) def _apply_force(self, delta_time): if self.transitioning: return if not self.pause: self.velocity.y += self.gravity else: self.velocity.y += self.gravity * 0.6 self.set_location(self.x + self.velocity.x, self.y + self.velocity.y) def _update_input(self, delta_time): if self.attacked: return if not self.pause and pressing( InputType.LEFT) and not pressing(InputType.RIGHT): self.velocity.x -= self.lateral_acceleration if self.velocity.x < -self.move_speed: self.velocity.x = -self.move_speed if not self.jumping: self.sprite.set_frame(self.walk_animation.current_frame, 6) self.direction = Direction.LEFT elif not self.pause and pressing( InputType.RIGHT) and not pressing(InputType.LEFT): self.velocity.x += self.lateral_acceleration if self.velocity.x > self.move_speed: self.velocity.x = self.move_speed if not self.jumping: self.sprite.set_frame(self.walk_animation.current_frame, 6) self.direction = Direction.RIGHT elif ((not pressing(InputType.LEFT) and not pressing(InputType.RIGHT)) or (pressing(InputType.LEFT) and pressing(InputType.RIGHT))): if self.grounded: self.velocity.lerp(Vector2(0, self.velocity.y), self.ground_friction) else: self.velocity.lerp(Vector2(0, self.velocity.y), self.air_friction) if self.velocity.x > -0.1 and self.velocity.x < 0.1: self.velocity.x = 0 self.sprite.set_frame(0, 6) if not self.grounded: if self.velocity.y < 0: self.sprite.set_frame(6, 6) if self.velocity.y > 0: self.sprite.set_frame(7, 6) else: if self.velocity.x == 0: if pressing(InputType.UP): self.sprite.set_frame(8, 6) if pressing(InputType.DOWN): self.sprite.set_frame(9, 6) if self.direction == Direction.LEFT: self.sprite.flip_horizontally(True) elif self.direction == Direction.RIGHT: self.sprite.flip_horizontally(False) if pressed(InputType.A) and self.grounded and not self.jumping: self.__jump(delta_time) self.jumping = True if self.jumping and self.velocity.y < -self.jump_initial_velocity / 2 and not pressing( InputType.A): self.velocity.y = -self.jump_initial_velocity / 2 self.jumping = False if pressed(InputType.X): self.attempt_block_shift = True def __rectanlge_collision_logic(self, entity): # Bottom if self.velocity.y < 0 and self.collision_rectangles[0].colliderect( entity.bounds): self.set_location(self.x, entity.bounds.bottom) self.velocity.y = 0 # Top if self.velocity.y > 0 and self.collision_rectangles[1].colliderect( entity.bounds): self.set_location(self.x, entity.bounds.top - self.bounds.height) self.grounded = True self.jumping = False self.velocity.y = 0 # Right if self.velocity.x < 0 and self.collision_rectangles[2].colliderect( entity.bounds): self.set_location(entity.bounds.right, self.y) self.velocity.x = 0 # Left if self.velocity.x > 0 and self.collision_rectangles[3].colliderect( entity.bounds): self.set_location(entity.bounds.left - self.bounds.width, self.y) self.velocity.x = 0 def _collision(self, scene_data): if self.attacked: return if self.x < 3: self.set_location(3, self.y) if (globals.debugging): for e in scene_data.entities: e.set_color(Color.WHITE) self.area = Rect(self.x - 16, self.y - 32, self.width + 16 * 2, self.height + 32 * 2) self.grounded = False self.query_result = scene_data.entity_quad_tree.query(self.area) if self.attempt_block_shift: self.__shift_blocks(scene_data) self.attempt_block_shift = False for e in self.query_result: if e is self: continue if (globals.debugging): e.set_color(Color.RED) if isinstance(e, Block): self.__rectanlge_collision_logic(e) self._update_collision_rectangles() if isinstance(e, QBlock): if e.active: self.__rectanlge_collision_logic(e) self._update_collision_rectangles() if isinstance(e, BossCrab): if (not e.hurt and not self.grounded and self.velocity.y > 0 and self.collision_rectangles[1].colliderect( e.bounds)): e.bop_on_head() self.velocity.y = -self.jump_initial_velocity * 0.35 if not e.injured: play_sound("pain.wav", 0.4) else: play_sound("pain.wav", 0.2) self.query_result = scene_data.kinetic_quad_tree.query(self.area) for e in self.query_result: if e is self: continue if (globals.debugging): e.set_color(Color.RED) if isinstance(e, Crab): if not e.dead: if (not e.aggravated and not self.grounded and self.velocity.y > 0 and self.collision_rectangles[1].colliderect( e.bounds)): e.squish() self.velocity.y = -self.jump_initial_velocity * 0.35 play_sound("bop.wav") elif e.aggravated and self.bounds.colliderect(e.bounds): self.__finessed_by_enemy() def __jump(self, delta_time): self.velocity.y = -self.jump_initial_velocity play_sound("jump.wav") def __shift_blocks(self, scene_data): for e in self.query_result: if e is self: continue if isinstance(e, QBlock): if self.bounds.colliderect(e.bounds): play_sound("shift_fail.wav") return for e in scene_data.entities: if isinstance(e, QBlock): e.toggle() elif isinstance(e, Crab): e.toggle_aggravation() play_sound("shift.wav") def __finessed_by_enemy(self): self.attacked = True self.velocity.y = -self.jump_initial_velocity * 0.5 self.sprite.set_frame(10, 6) self.__play_mocking_music() def __play_mocking_music(self): if not self.queue_restart: play_song("fin.wav") self.queue_restart = True self.restart_delay.reset() self.restart_delay.start() def __update_death(self, delta_time, scene_data): if self.y + self.height > scene_data.scene_bounds.height: self.attacked = True self.__play_mocking_music() #if self.y + self.height > scene_data.scene_bounds.height + 64 + 128 + 64: # self.restart = True if self.queue_restart: self.restart_delay.update(delta_time) if self.restart_delay.done: self.restart = True self.queue_restart = False def __update_animation(self, delta_time): self.walk_animation.update(delta_time) def update(self, delta_time, scene_data): self._calculate_scaled_speed(delta_time) self._update_input(delta_time) self._apply_force(delta_time) self._update_collision_rectangles() self._collision(scene_data) self.__update_death(delta_time, scene_data) self.__update_animation(delta_time) def draw(self, surface): if globals.debugging: self._draw_collision_rectangles(surface) draw_rectangle(surface, self.bounds, CameraType.DYNAMIC, self.color) if self.area != None: draw_rectangle(surface, self.area, CameraType.DYNAMIC, Color.BLACK, 1) else: 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)