Beispiel #1
0
    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 get_next_waypoint_along_path(self, agent_entity: WorldEntity) -> Optional[Tuple[int, int]]:
        if self.path:
            # -----------------------------------------------
            # 1: Remove first waypoint if close enough to it
            # -----------------------------------------------
            # TODO: Does this cause problems for specific entity sizes / movement speeds?
            closeness_margin = 50
            if is_x_and_y_within_distance(agent_entity.get_position(), self.path[0], closeness_margin):
                # print("Popping " + str(self.path[0]) + " as I'm so close to it.")
                self.path.pop(0)
                if self.path:
                    # print("After popping, returning " + str(self.path[0]))
                    return self.path[0]
                else:
                    # print("no path after popping. stopping.")
                    return None

            # -----------------------------------------------
            # 2: Remove first waypoint if it's opposite direction of second waypoint
            # -----------------------------------------------
            if len(self.path) >= 2:
                dir_to_waypoint_0 = get_directions_to_position(agent_entity, self.path[0])[0]
                dir_to_waypoint_1 = get_directions_to_position(agent_entity, self.path[1])[0]
                if dir_to_waypoint_0 == get_opposite_direction(dir_to_waypoint_1):
                    # print("Not gonna go back. Popping " + str(self.path[0]))
                    self.path.pop(0)
                    # print("Popped first position. Next waypoint: " + str(self.path[0]))
                    return self.path[0]
                if self.path:
                    return self.path[0]
        else:
            # print("no path found. stopping.")
            return None
        # print("Leaked through. returning none")
        return None
Beispiel #3
0
 def get_enemies_within_x_y_distance_of(self, distance: int,
                                        position: Tuple[int, int]):
     return [
         e for e in self.non_player_characters
         if e.is_enemy and is_x_and_y_within_distance(
             e.world_entity.get_center_position(), position, distance)
     ]
    def control_npc(self, game_state: GameState, npc: NonPlayerCharacter,
                    player_entity: WorldEntity, is_player_invisible: bool,
                    time_passed: Millis):
        if npc.stun_status.is_stunned():
            return
        self._time_since_attack += time_passed
        self._time_since_updated_path += time_passed
        self._time_since_reevaluated += time_passed

        enemy_entity = npc.world_entity
        target: EnemyTarget = get_target(enemy_entity, game_state)

        if self._time_since_updated_path > self._update_path_interval:
            self._time_since_updated_path = 0
            if not is_player_invisible:
                self.pathfinder.update_path_towards_target(
                    enemy_entity, game_state, target.entity)

        new_next_waypoint = self.pathfinder.get_next_waypoint_along_path(
            enemy_entity)

        should_update_waypoint = self.next_waypoint != new_next_waypoint
        if self._time_since_reevaluated > self._reevaluate_next_waypoint_direction_interval:
            self._time_since_reevaluated = 0
            should_update_waypoint = True

        if should_update_waypoint:
            self.next_waypoint = new_next_waypoint
            if self.next_waypoint:
                direction = self.pathfinder.get_dir_towards_considering_collisions(
                    game_state, enemy_entity, self.next_waypoint)
                _move_in_dir(enemy_entity, direction)
            else:
                enemy_entity.set_not_moving()

        if self._time_since_attack > self._attack_interval:
            if not is_player_invisible:
                enemy_position = enemy_entity.get_center_position()
                target_center_pos = target.entity.get_center_position()
                if is_x_and_y_within_distance(enemy_position,
                                              target_center_pos, 200):
                    self._time_since_attack = 0
                    self.randomize_attack_interval()
                    play_sound(SoundId.ENEMY_ATTACK_ICE_WITCH)
                    damage = random.randint(DAMAGE_MIN, DAMAGE_MAX)
                    deal_npc_damage(damage, DamageType.MAGIC, game_state,
                                    enemy_entity, npc, target)
                    game_state.game_world.visual_effects += [
                        (VisualLine((100, 100, 200), enemy_position,
                                    target_center_pos, Millis(120), 3)),
                        (VisualLine((150, 150, 250), enemy_position,
                                    target_center_pos, Millis(240), 2))
                    ]
                    chance_to_resist_slow = game_state.player_state.get_effective_movement_impairing_resist_chance(
                    )
                    # TODO It's error-prone that we have to check this for every negative debuff that can slow player
                    if random.random() > chance_to_resist_slow:
                        game_state.player_state.gain_buff_effect(
                            get_buff_effect(SLOW_BUFF_TYPE), Millis(1500))
    def control_npc(self, game_state: GameState, npc: NonPlayerCharacter,
                    player_entity: WorldEntity, _is_player_invisible: bool,
                    time_passed: Millis):
        if npc.stun_status.is_stunned():
            return

        self._summon_trait.update(npc, game_state, time_passed)
        self._random_walk_trait.update(npc, game_state, time_passed)

        self._time_since_healing += time_passed
        self._time_since_shoot += time_passed

        if self._time_since_healing > self._healing_cooldown:
            self._time_since_healing = 0
            self._healing_cooldown = self._random_healing_cooldown()
            necro_center_pos = npc.world_entity.get_center_position()
            nearby_hurt_enemies = [
                e for e in game_state.game_world.non_player_characters
                if e.is_enemy and is_x_and_y_within_distance(
                    necro_center_pos, e.world_entity.get_center_position(),
                    200) and e != npc and not e.health_resource.is_at_max()
            ]
            if nearby_hurt_enemies:
                healing_target = nearby_hurt_enemies[0]
                healing_target.health_resource.gain(5)
                healing_target_pos = healing_target.world_entity.get_center_position(
                )
                visual_line = VisualLine((80, 200, 150), necro_center_pos,
                                         healing_target_pos, Millis(350), 3)
                game_state.game_world.visual_effects.append(visual_line)
                play_sound(SoundId.ENEMY_NECROMANCER_HEAL)

        if self._time_since_shoot > self._shoot_cooldown:
            self._time_since_shoot = 0
            self._shoot_cooldown = self._random_shoot_cooldown()
            npc.world_entity.direction = get_directions_to_position(
                npc.world_entity, player_entity.get_position())[0]
            npc.world_entity.set_not_moving()
            center_position = npc.world_entity.get_center_position()
            distance_from_enemy = 35
            projectile_pos = translate_in_direction(
                get_position_from_center_position(center_position,
                                                  PROJECTILE_SIZE),
                npc.world_entity.direction, distance_from_enemy)
            projectile_speed = 0.2
            projectile_entity = WorldEntity(projectile_pos, PROJECTILE_SIZE,
                                            Sprite.NONE,
                                            npc.world_entity.direction,
                                            projectile_speed)
            projectile = Projectile(
                projectile_entity,
                create_projectile_controller(PROJECTILE_TYPE))
            game_state.game_world.projectile_entities.append(projectile)
            play_sound(SoundId.ENEMY_ATTACK_NECRO)
Beispiel #6
0
 def update_npc_position_within_game_world(self, npc: NonPlayerCharacter, time_passed: Millis):
     entity = npc.world_entity
     new_position = entity.get_new_position_according_to_dir_and_speed(time_passed)
     if new_position:
         if npc.max_distance_allowed_from_start_position:
             is_close_to_start_position = is_x_and_y_within_distance(
                 npc.start_position, new_position, npc.max_distance_allowed_from_start_position)
             if not is_close_to_start_position:
                 return
         new_pos_within_world = self.get_within_world(
             new_position, (entity.pygame_collision_rect.w, entity.pygame_collision_rect.h))
         if not self.would_entity_collide_if_new_pos(entity, new_pos_within_world):
             entity.set_position(new_pos_within_world)
Beispiel #7
0
    def control_npc(self, game_state: GameState, npc: NonPlayerCharacter,
                    player_entity: WorldEntity, is_player_invisible: bool,
                    time_passed: Millis):
        if npc.stun_status.is_stunned():
            return
        self._time_since_attack += time_passed
        self._time_since_updated_path += time_passed
        self._time_since_reevaluated += time_passed

        enemy_entity = npc.world_entity
        target: EnemyTarget = get_target(enemy_entity, game_state)

        if self._time_since_updated_path > self._update_path_interval:
            self._time_since_updated_path = 0
            if not is_player_invisible:
                self.pathfinder.update_path_towards_target(
                    enemy_entity, game_state, target.entity)

        new_next_waypoint = self.pathfinder.get_next_waypoint_along_path(
            enemy_entity)

        should_update_waypoint = self.next_waypoint != new_next_waypoint
        if self._time_since_reevaluated > self._reevaluate_next_waypoint_direction_interval:
            self._time_since_reevaluated = 0
            should_update_waypoint = True

        if should_update_waypoint:
            self.next_waypoint = new_next_waypoint
            if self.next_waypoint:
                direction = self.pathfinder.get_dir_towards_considering_collisions(
                    game_state, enemy_entity, self.next_waypoint)
                if random.random(
                ) < self.chance_to_stray_from_path and direction:
                    direction = random.choice(
                        get_perpendicular_directions(direction))
                _move_in_dir(enemy_entity, direction)
            else:
                enemy_entity.set_not_moving()

        if self._time_since_attack > self._attack_interval:
            if not is_player_invisible:
                enemy_position = enemy_entity.get_center_position()
                target_center_pos = target.entity.get_center_position()
                if is_x_and_y_within_distance(enemy_position,
                                              target_center_pos, 80):
                    self._time_since_attack = 0
                    self.randomize_attack_interval()
                    deal_npc_damage(self.damage_amount, DamageType.PHYSICAL,
                                    game_state, enemy_entity, npc, target)
Beispiel #8
0
    def control_npc(self, game_state: GameState, npc: NonPlayerCharacter,
                    player_entity: WorldEntity, is_player_invisible: bool,
                    time_passed: Millis):
        self._time_since_attack += time_passed
        self._time_since_updated_path += time_passed
        self._time_since_reevaluated += time_passed
        self._time_since_shield += time_passed

        enemy_entity = npc.world_entity

        if self._time_since_updated_path > self._update_path_interval:
            self._time_since_updated_path = 0
            self.pathfinder.update_path_towards_target(
                enemy_entity, game_state, game_state.player_entity)

        new_next_waypoint = self.pathfinder.get_next_waypoint_along_path(
            enemy_entity)

        should_update_waypoint = self.next_waypoint != new_next_waypoint
        if self._time_since_reevaluated > self._reevaluate_next_waypoint_direction_interval:
            self._time_since_reevaluated = 0
            should_update_waypoint = True

        if should_update_waypoint:
            self.next_waypoint = new_next_waypoint
            if self.next_waypoint:
                direction = self.pathfinder.get_dir_towards_considering_collisions(
                    game_state, enemy_entity, self.next_waypoint)
                if random.random() < 0.1 and direction:
                    direction = random.choice(
                        get_perpendicular_directions(direction))
                _move_in_dir(enemy_entity, direction)
            else:
                enemy_entity.set_not_moving()

        enemy_center_pos = enemy_entity.get_center_position()

        if self._time_since_attack > self._attack_interval:
            self._time_since_attack = 0
            game_state.visual_effects.append(
                VisualCircle((100, 100, 100), enemy_center_pos, 180, 180,
                             Millis(self._attack_interval), 1, enemy_entity))
            if not is_player_invisible:
                player_center_pos = game_state.player_entity.get_center_position(
                )
                if is_x_and_y_within_distance(enemy_center_pos,
                                              player_center_pos, 160):
                    deal_damage_to_player(game_state, 3, DamageType.MAGIC, npc)
                    game_state.visual_effects += [
                        VisualCircle((0, 0, 0), enemy_center_pos, 25, 50,
                                     Millis(200), 2, enemy_entity),
                        VisualLine((0, 100, 0), enemy_center_pos,
                                   player_center_pos, Millis(200), 2),
                        VisualCircle((0, 100, 0), player_center_pos, 20, 40,
                                     Millis(150), 2, player_entity),
                        VisualCircle((0, 150, 0), player_center_pos, 25, 50,
                                     Millis(200), 2, player_entity),
                        VisualCircle((0, 200, 0), player_center_pos, 30, 60,
                                     Millis(300), 2, player_entity),
                    ]

        if self._time_since_shield > self._shield_interval:
            self._time_since_shield = 0

            game_state.visual_effects.append(
                VisualCircle((0, 0, 150), enemy_center_pos, 60, 20,
                             Millis(self._shield_duration), 2, enemy_entity))
            npc.gain_buff_effect(get_buff_effect(BUFF_TYPE_INVULN),
                                 Millis(self._shield_duration))
    def control_npc(self, game_state: GameState, npc: NonPlayerCharacter, player_entity: WorldEntity,
                    _is_player_invisible: bool, time_passed: Millis):
        self._time_since_decision += time_passed
        self._time_since_summoning += time_passed
        self._time_since_healing += time_passed
        self._time_since_shoot += time_passed
        if self._time_since_summoning > self._summoning_cooldown:
            necro_center_pos = npc.world_entity.get_center_position()
            self._time_since_summoning = 0
            self._alive_summons = [summon for summon in self._alive_summons
                                   if summon in game_state.non_player_characters]
            if len(self._alive_summons) < 3:
                relative_pos_from_summoner = (random.randint(-150, 150), random.randint(-150, 150))
                summon_center_pos = sum_of_vectors(necro_center_pos, relative_pos_from_summoner)
                summon_type = random.choice([NpcType.ZOMBIE, NpcType.MUMMY])
                summon_size = NON_PLAYER_CHARACTERS[summon_type].size
                summon_pos = game_state.get_within_world(
                    get_position_from_center_position(summon_center_pos, summon_size), summon_size)
                summon_enemy = create_npc(summon_type, summon_pos)
                is_wall_blocking = game_state.walls_state.does_rect_intersect_with_wall(
                    rect_from_corners(necro_center_pos, summon_center_pos))
                is_position_blocked = game_state.would_entity_collide_if_new_pos(summon_enemy.world_entity, summon_pos)
                if not is_wall_blocking and not is_position_blocked:
                    self._summoning_cooldown = self._random_summoning_cooldown()
                    game_state.add_non_player_character(summon_enemy)
                    self._alive_summons.append(summon_enemy)
                    game_state.visual_effects.append(
                        VisualCircle((80, 150, 100), necro_center_pos, 40, 70, Millis(120), 3))
                    game_state.visual_effects.append(
                        VisualCircle((80, 150, 100), summon_center_pos, 40, 70, Millis(120), 3))
                    play_sound(SoundId.ENEMY_NECROMANCER_SUMMON)
                else:
                    # Failed to summon, so try again without waiting full duration
                    self._summoning_cooldown = 500
            else:
                self._summoning_cooldown = self._random_summoning_cooldown()

        if self._time_since_healing > self._healing_cooldown:
            self._time_since_healing = 0
            self._healing_cooldown = self._random_healing_cooldown()
            necro_center_pos = npc.world_entity.get_center_position()
            nearby_hurt_enemies = [
                e for e in game_state.non_player_characters
                if e.is_enemy
                   and is_x_and_y_within_distance(necro_center_pos, e.world_entity.get_center_position(), 200)
                   and e != npc and not e.health_resource.is_at_max()
            ]
            if nearby_hurt_enemies:
                healing_target = nearby_hurt_enemies[0]
                healing_target.health_resource.gain(5)
                healing_target_pos = healing_target.world_entity.get_center_position()
                visual_line = VisualLine((80, 200, 150), necro_center_pos, healing_target_pos, Millis(350), 3)
                game_state.visual_effects.append(visual_line)
                play_sound(SoundId.ENEMY_NECROMANCER_HEAL)

        if self._time_since_shoot > self._shoot_cooldown:
            self._time_since_shoot = 0
            self._shoot_cooldown = self._random_shoot_cooldown()
            npc.world_entity.direction = get_directions_to_position(npc.world_entity, player_entity.get_position())[0]
            npc.world_entity.set_not_moving()
            center_position = npc.world_entity.get_center_position()
            distance_from_enemy = 35
            projectile_pos = translate_in_direction(
                get_position_from_center_position(center_position, PROJECTILE_SIZE),
                npc.world_entity.direction, distance_from_enemy)
            projectile_speed = 0.2
            projectile_entity = WorldEntity(projectile_pos, PROJECTILE_SIZE, Sprite.NONE, npc.world_entity.direction,
                                            projectile_speed)
            projectile = Projectile(projectile_entity, create_projectile_controller(PROJECTILE_TYPE))
            game_state.projectile_entities.append(projectile)
            play_sound(SoundId.ENEMY_ATTACK_NECRO)

        if self._time_since_decision > self._decision_interval:
            self._time_since_decision = 0
            if random.random() < 0.2:
                direction = random_direction()
                npc.world_entity.set_moving_in_dir(direction)
            else:
                npc.world_entity.set_not_moving()