def handle_nearby_entities(self, player_entity: WorldEntity, game_state: GameState, game_engine: GameEngine): self.entity_to_interact_with = None player_position = player_entity.get_position() distance_to_closest_entity = sys.maxsize for npc in game_state.non_player_characters: if has_npc_dialog(npc.npc_type): close_to_player = is_x_and_y_within_distance( player_position, npc.world_entity.get_position(), 75) distance = get_manhattan_distance_between_rects( player_entity.rect(), npc.world_entity.rect()) if close_to_player and distance < distance_to_closest_entity: self.entity_to_interact_with = npc distance_to_closest_entity = distance lootables_on_ground: List[LootableOnGround] = list( game_state.items_on_ground) lootables_on_ground += game_state.consumables_on_ground for lootable in lootables_on_ground: if boxes_intersect(player_entity.rect(), lootable.world_entity.rect()): self.entity_to_interact_with = lootable distance_to_closest_entity = 0 for portal in game_state.portals: close_to_player = is_x_and_y_within_distance( player_position, portal.world_entity.get_position(), 75) distance = get_manhattan_distance_between_rects( player_entity.rect(), portal.world_entity.rect()) if close_to_player: game_engine.handle_being_close_to_portal(portal) if close_to_player and distance < distance_to_closest_entity: self.entity_to_interact_with = portal distance_to_closest_entity = distance for warp_point in game_state.warp_points: close_to_player = is_x_and_y_within_distance( player_position, warp_point.world_entity.get_position(), 75) distance = get_manhattan_distance_between_rects( player_entity.rect(), warp_point.world_entity.rect()) if close_to_player and distance < distance_to_closest_entity: self.entity_to_interact_with = warp_point distance_to_closest_entity = distance for chest in game_state.chests: close_to_player = is_x_and_y_within_distance( player_position, chest.world_entity.get_position(), 75) distance = get_manhattan_distance_between_rects( player_entity.rect(), chest.world_entity.rect()) if close_to_player and distance < distance_to_closest_entity: self.entity_to_interact_with = chest distance_to_closest_entity = distance
def run_one_frame(self, time_passed: Millis) -> List[EngineEvent]: events = [] for npc in self.game_state.non_player_characters: # NonPlayerCharacter AI shouldn't run if enemy is too far out of sight if self._is_npc_close_to_camera( npc) and not npc.stun_status.is_stunned(): npc.npc_mind.control_npc( self.game_state, npc, self.game_state.player_entity, self.game_state.player_state.is_invisible, time_passed) for projectile in self.game_state.projectile_entities: projectile.projectile_controller.notify_time_passed( self.game_state, projectile, time_passed) for visual_effect in self.game_state.visual_effects: visual_effect.notify_time_passed(time_passed) self.game_state.handle_camera_shake(time_passed) npcs_that_died = self.game_state.remove_dead_npcs() enemies_that_died = [e for e in npcs_that_died if e.is_enemy] if enemies_that_died: exp_gained = sum([ NON_PLAYER_CHARACTERS[e.npc_type].exp_reward for e in enemies_that_died ]) self.game_state.visual_effects.append( create_visual_exp_text(self.game_state.player_entity, exp_gained)) gain_exp_events = self.game_state.player_state.gain_exp(exp_gained) self._handle_gain_exp_events(gain_exp_events) for enemy_that_died in enemies_that_died: if enemy_that_died.death_sound_id: play_sound(enemy_that_died.death_sound_id) else: play_sound(SoundId.EVENT_ENEMY_DIED) loot = enemy_that_died.enemy_loot_table.generate_loot() enemy_death_position = enemy_that_died.world_entity.get_position( ) self._put_loot_on_ground(enemy_death_position, loot) self.game_state.player_state.notify_about_event( EnemyDiedEvent(), self.game_state) events.append(EngineEvent.ENEMY_DIED) self.game_state.remove_expired_projectiles() self.game_state.remove_expired_visual_effects() self.game_state.remove_opened_chests() player_buffs_update = self.game_state.player_state.handle_buffs( time_passed) for buff in player_buffs_update.buffs_that_started: buff.buff_effect.apply_start_effect(self.game_state, self.game_state.player_entity, None) for buff in player_buffs_update.buffs_that_were_active: buff_should_end = buff.buff_effect.apply_middle_effect( self.game_state, self.game_state.player_entity, None, time_passed) if buff_should_end: buff.force_cancel() for buff in player_buffs_update.buffs_that_ended: buff.buff_effect.apply_end_effect(self.game_state, self.game_state.player_entity, None) for enemy in self.game_state.non_player_characters: enemy.health_resource.regenerate(time_passed) buffs_update = handle_buffs(enemy.active_buffs, time_passed) for buff in buffs_update.buffs_that_started: buff.buff_effect.apply_start_effect(self.game_state, enemy.world_entity, enemy) for buff in buffs_update.buffs_that_were_active: buff.buff_effect.apply_middle_effect(self.game_state, enemy.world_entity, enemy, time_passed) for buff in buffs_update.buffs_that_ended: buff.buff_effect.apply_end_effect(self.game_state, enemy.world_entity, enemy) for item_effect in self.game_state.player_state.item_inventory.get_all_active_item_effects( ): item_effect.apply_middle_effect(self.game_state, time_passed) self.game_state.player_state.health_resource.regenerate(time_passed) self.game_state.player_state.mana_resource.regenerate(time_passed) self.game_state.player_state.recharge_ability_cooldowns(time_passed) self.game_state.player_entity.update_movement_animation(time_passed) for npc in self.game_state.non_player_characters: npc.world_entity.update_movement_animation(time_passed) for projectile in self.game_state.projectile_entities: projectile.world_entity.update_movement_animation(time_passed) for warp_point in self.game_state.warp_points: warp_point.world_entity.update_animation(time_passed) for npc in self.game_state.non_player_characters: # Enemies shouldn't move towards player when they are out of sight if self._is_npc_close_to_camera( npc) and not npc.stun_status.is_stunned(): self.game_state.update_npc_position_within_game_world( npc, time_passed) # player can still move when stunned (could be charging) self.game_state.update_world_entity_position_within_game_world( self.game_state.player_entity, time_passed) for projectile in self.game_state.projectile_entities: new_pos = projectile.world_entity.get_new_position_according_to_dir_and_speed( time_passed) projectile.world_entity.set_position(new_pos) for visual_effect in self.game_state.visual_effects: visual_effect.update_position_if_attached_to_entity() if visual_effect.attached_to_entity: npcs = [ e.world_entity for e in self.game_state.non_player_characters ] projectiles = [ p.world_entity for p in self.game_state.projectile_entities ] if not visual_effect.attached_to_entity in npcs + projectiles + [ self.game_state.player_entity ]: visual_effect.has_expired = True # ------------------------------------ # HANDLE COLLISIONS # ------------------------------------ for money_pile in self.game_state.money_piles_on_ground: if boxes_intersect(self.game_state.player_entity.rect(), money_pile.world_entity.rect()): play_sound(SoundId.EVENT_PICKED_UP_MONEY) money_pile.has_been_picked_up_and_should_be_removed = True self.game_state.player_state.modify_money(money_pile.amount) for enemy in [ e for e in self.game_state.non_player_characters if e.is_enemy ]: for projectile in self.game_state.get_projectiles_intersecting_with( enemy.world_entity): if not projectile.has_collided_and_should_be_removed: projectile.projectile_controller.apply_enemy_collision( enemy, self.game_state, projectile) for player_summon in [ npc for npc in self.game_state.non_player_characters if npc.npc_category == NpcCategory.PLAYER_SUMMON ]: for projectile in self.game_state.get_projectiles_intersecting_with( player_summon.world_entity): if not projectile.has_collided_and_should_be_removed: projectile.projectile_controller.apply_player_summon_collision( player_summon, self.game_state, projectile) for projectile in self.game_state.get_projectiles_intersecting_with( self.game_state.player_entity): if not projectile.has_collided_and_should_be_removed: projectile.projectile_controller.apply_player_collision( self.game_state, projectile) for projectile in self.game_state.projectile_entities: if not projectile.has_collided_and_should_be_removed: if self.game_state.walls_state.does_entity_intersect_with_wall( projectile.world_entity): projectile.projectile_controller.apply_wall_collision( self.game_state, projectile) self.game_state.remove_money_piles_that_have_been_picked_up() self.game_state.remove_projectiles_that_have_been_destroyed() # ------------------------------------ # UPDATE CAMERA POSITION # ------------------------------------ self.game_state.center_camera_on_player() if self.game_state.player_state.health_resource.is_at_or_below_zero(): events.append(EngineEvent.PLAYER_DIED) return events
def get_projectiles_intersecting_with( self, entity: WorldEntity) -> List[Projectile]: return [ p for p in self.projectile_entities if boxes_intersect(entity.rect(), p.world_entity.rect()) ]
def get_enemy_intersecting_with( self, entity: WorldEntity) -> List[NonPlayerCharacter]: return [ e for e in self.non_player_characters if e.is_enemy and boxes_intersect(e.world_entity.rect(), entity.rect()) ]
def does_entity_intersect_with_wall(self, entity: WorldEntity): nearby_walls = self.get_walls_close_to_position(entity.get_position()) return any([ w for w in nearby_walls if boxes_intersect(w.rect(), entity.rect()) ])