def apply_start_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): buffed_npc.stun_status.add_one() buffed_entity.set_not_moving() game_state.visual_effects.append( create_visual_stun_text(buffed_entity))
def update_path_towards_target(self, agent_entity: WorldEntity, game_state: GameState, target_entity: WorldEntity): agent_cell = _translate_world_position_to_cell( agent_entity.get_position(), game_state.entire_world_area) target_cell = _translate_world_position_to_cell( target_entity.get_position(), game_state.entire_world_area) agent_cell_size = ( agent_entity.pygame_collision_rect.w // GRID_CELL_WIDTH + 1, agent_entity.pygame_collision_rect.h // GRID_CELL_WIDTH + 1) self.global_path_finder.register_entity_size(agent_cell_size) path_with_cells = self.global_path_finder.run(agent_cell_size, agent_cell, target_cell) if path_with_cells: # Note: Cells are expressed in non-negative values (and need to be translated to game world coordinates) path = [ _translate_cell_to_world_position(cell, game_state.entire_world_area) for cell in path_with_cells ] if DEBUG_RENDER_PATHFINDING: _add_visual_lines_along_path(game_state, path) self.path = path else: self.path = None
def apply_start_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): visual_effect = VisualCircle((220, 220, 50), buffed_entity.get_center_position(), 9, 16, Millis(250), 2) game_state.visual_effects.append(visual_effect) game_state.visual_effects.append( create_visual_stun_text(buffed_entity)) buffed_npc.stun_status.add_one() buffed_entity.set_not_moving()
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 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() < 0.5 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: self._time_since_attack = 0 self._update_attack_interval() directions_to_player = get_directions_to_position(npc.world_entity, player_entity.get_position()) new_direction = directions_to_player[0] if random.random() < 0.3 and directions_to_player[1] is not None: new_direction = directions_to_player[1] npc.world_entity.direction = new_direction 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.11 projectile_entity = WorldEntity(projectile_pos, PROJECTILE_SIZE, PROJECTILE_SPRITE, 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_GOBLIN_WARLOCK)
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_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() if not npc.health_resource.is_at_max(): healing_amount = random.randint(10, 20) npc.health_resource.gain(healing_amount) circle_effect = VisualCircle( (80, 200, 150), npc.world_entity.get_center_position(), 30, 50, Millis(350), 3) game_state.visual_effects.append(circle_effect) number_effect = create_visual_healing_text( npc.world_entity, healing_amount) game_state.visual_effects.append(number_effect) play_sound(SoundId.ENEMY_SKELETON_MAGE_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_SKELETON_MAGE) 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()
def apply_start_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): game_state.player_state.stun_status.add_one() game_state.player_entity.set_not_moving() game_state.visual_effects += create_teleport_effects( buffed_entity.get_center_position()) game_state.player_entity.visible = False
def create_npc(npc_type: NpcType, pos: Tuple[int, int]) -> NonPlayerCharacter: data: NpcData = NON_PLAYER_CHARACTERS[npc_type] entity = WorldEntity(pos, data.size, data.sprite, Direction.LEFT, data.speed) npc_mind = create_npc_mind(npc_type, global_path_finder) health_resource = HealthOrManaResource(data.max_health, data.health_regen) return NonPlayerCharacter(npc_type, entity, health_resource, npc_mind, data.npc_category, data.enemy_loot_table, data.death_sound_id, data.max_distance_allowed_from_start_position, is_boss=data.is_boss)
def create_money_pile_on_ground(amount: int, pos: Tuple[int, int]) -> MoneyPileOnGround: if amount == 1: sprite = Sprite.COINS_1 elif amount == 2: sprite = Sprite.COINS_2 else: sprite = Sprite.COINS_5 return MoneyPileOnGround(WorldEntity(pos, ITEM_ENTITY_SIZE, sprite), amount)
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): if self.timer.update_and_check_if_ready(time_passed): deal_player_damage_to_enemy(game_state, buffed_npc, DAMAGE_PER_TICK, DamageType.PHYSICAL) if self.should_stun: effect_position = buffed_entity.get_center_position() game_state.visual_effects.append( VisualRect((250, 250, 50), effect_position, 30, 40, Millis(100), 1, buffed_entity))
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): self.time_since_start += time_passed if not self.has_teleport_happened and self.time_since_start > PORTAL_DELAY / 2: self.has_teleport_happened = True game_state.warp_points = [] game_state.player_entity.set_position(self.destination) game_state.visual_effects += create_teleport_effects(buffed_entity.get_center_position()) play_sound(SoundId.WARP)
def deal_npc_damage(damage_amount: float, damage_type: DamageType, game_state: GameState, attacker_entity: WorldEntity, attacker_npc: NonPlayerCharacter, target: EnemyTarget): attacker_position = attacker_entity.get_center_position() game_state.visual_effects.append( VisualRect((200, 0, 0), attacker_position, 50, 50, Millis(200), 3, attacker_entity)) if target.non_enemy_npc: deal_npc_damage_to_npc(game_state, target.non_enemy_npc, damage_amount) else: deal_damage_to_player(game_state, damage_amount, damage_type, attacker_npc)
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): if self.timer.update_and_check_if_ready(time_passed): visual_effect = VisualCircle((250, 150, 250), buffed_entity.get_center_position(), 18, 25, Millis(220), 1, buffed_entity) game_state.visual_effects.append(visual_effect)
def _get_entity_text_positions( entity: WorldEntity, text_y_travel_distance: int ) -> Tuple[Tuple[int, int], Tuple[int, int]]: sprite_size = ENTITY_SPRITE_SIZES[entity.sprite] y_start = entity.y + entity.pygame_collision_rect.h - sprite_size[1] random_x_offset = random.randint(-10, 10) x = entity.get_center_position()[0] - 5 + random_x_offset start_position = (x, y_start) end_position = (x, y_start - text_y_travel_distance) return start_position, end_position
def apply_start_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): game_state.player_state.stun_status.add_one() player_entity = game_state.player_entity player_entity.set_not_moving() player_entity.visible = False game_state.visual_effects += create_teleport_effects(buffed_entity.get_center_position()) player_collision_rect = (player_entity.pygame_collision_rect.w, player_entity.pygame_collision_rect.h) home_warp_point = create_warp_point(game_state.player_spawn_position, player_collision_rect) remote_warp_point = create_warp_point(player_entity.get_center_position(), player_collision_rect) game_state.warp_points = [home_warp_point, remote_warp_point]
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): self._time_since_graphics += time_passed if self._time_since_graphics > 800: game_state.visual_effects.append( VisualRect((0, 100, 200), buffed_entity.get_center_position(), 50, 50, Millis(400), 1, buffed_entity)) self._time_since_graphics = 0
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): if self.timer.update_and_check_if_ready(time_passed): deal_player_damage_to_enemy(game_state, buffed_npc, 1, DamageType.MAGIC) game_state.visual_effects.append( VisualCircle((0, 150, 0), buffed_entity.get_center_position(), 30, 55, Millis(150), 2, buffed_entity))
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): self.time_since_start += time_passed if not self.has_spawn_happened and self.time_since_start > DELAY / 2: self.has_spawn_happened = True game_state.visual_effects += create_teleport_effects( buffed_entity.get_center_position()) play_sound(SoundId.ABILITY_TELEPORT)
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): if self.timer.update_and_check_if_ready(time_passed): player_center_position = game_state.player_entity.get_center_position() projectile_pos = get_position_from_center_position(player_center_position, PROJECTILE_SIZE) entity = WorldEntity(projectile_pos, PROJECTILE_SIZE, Sprite.PROJECTILE_PLAYER_ARCANE_FIRE, game_state.player_entity.direction, PROJECTILE_SPEED) projectile = Projectile(entity, create_projectile_controller(ProjectileType.PLAYER_ARCANE_FIRE)) game_state.projectile_entities.append(projectile) game_state.visual_effects.append(VisualRect((250, 0, 250), player_center_position, 45, 60, Millis(250), 1))
def render_world(self, all_entities_to_render: List[WorldEntity], decorations_to_render: List[DecorationEntity], camera_world_area, non_player_characters: List[NonPlayerCharacter], is_player_invisible: bool, player_active_buffs: List[BuffWithDuration], player_entity: WorldEntity, visual_effects, render_hit_and_collision_boxes, player_health, player_max_health, entire_world_area: Rect, entity_action_text: Optional[EntityActionText]): self.camera_world_area = camera_world_area self.screen_render.fill(COLOR_BACKGROUND) self._world_ground(entire_world_area) all_entities_to_render.sort(key=lambda entry: (-entry.view_z, entry.y)) for decoration_entity in decorations_to_render: self._world_entity(decoration_entity) for entity in all_entities_to_render: self._world_entity(entity) if entity == player_entity and is_player_invisible: self.world_render.rect((200, 100, 250), player_entity.rect(), 2) player_sprite_y_relative_to_entity = \ ENTITY_SPRITE_INITIALIZERS[player_entity.sprite][Direction.DOWN].position_relative_to_entity[1] if player_entity.visible: self._stat_bar_for_world_entity(player_entity, 5, player_sprite_y_relative_to_entity - 5, player_health / player_max_health, (100, 200, 0)) # Buffs related to channeling something are rendered above player's head with progress from left to right for buff in player_active_buffs: if buff.buff_effect.get_buff_type() in CHANNELING_BUFFS: ratio = 1 - buff.get_ratio_duration_remaining() self._stat_bar_for_world_entity(player_entity, 3, player_sprite_y_relative_to_entity - 11, ratio, (150, 150, 250)) if render_hit_and_collision_boxes: for entity in all_entities_to_render: # hit box self.world_render.rect((250, 250, 250), entity.rect(), 1) for npc in non_player_characters: healthbar_color = COLOR_RED if npc.is_enemy else (250, 250, 0) npc_sprite_y_relative_to_entity = \ ENTITY_SPRITE_INITIALIZERS[npc.world_entity.sprite][Direction.DOWN].position_relative_to_entity[1] if not npc.is_neutral: self._stat_bar_for_world_entity(npc.world_entity, 3, npc_sprite_y_relative_to_entity - 5, npc.health_resource.get_partial(), healthbar_color) if npc.active_buffs: buff = npc.active_buffs[0] if buff.should_duration_be_visualized_on_enemies(): self._stat_bar_for_world_entity(npc.world_entity, 2, npc_sprite_y_relative_to_entity - 9, buff.get_ratio_duration_remaining(), (250, 250, 250)) for visual_effect in visual_effects: self._visual_effect(visual_effect) if entity_action_text: self._entity_action_text(entity_action_text)
def control_npc(self, game_state: GameState, npc: NonPlayerCharacter, player_entity: WorldEntity, is_player_invisible: bool, time_passed: Millis): super().control_npc(game_state, npc, player_entity, is_player_invisible, time_passed) self.sprint_cooldown_remaining -= time_passed sprint_distance_limit = 250 if self.sprint_cooldown_remaining <= 0: is_far_away = get_manhattan_distance( npc.world_entity.get_position(), player_entity.get_position()) > sprint_distance_limit if is_far_away: npc.gain_buff_effect(get_buff_effect(BuffType.ENEMY_GOBLIN_SPEARMAN_SPRINT), Millis(2500)) self.sprint_cooldown_remaining = self.random_cooldown()
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): if self.graphics_timer.update_and_check_if_ready(time_passed): position = buffed_entity.get_center_position() visual_effect1 = VisualCircle((0, 40, 100), position, 9, 16, Millis(400), 2, buffed_entity) visual_effect2 = VisualCircle((0, 90, 180), position, 9, 16, Millis(500), 2, buffed_entity) game_state.visual_effects.append(visual_effect1) game_state.visual_effects.append(visual_effect2)
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis) -> bool: if self.timer.update_and_check_if_ready(time_passed): visual_effect = VisualCircle( (250, 250, 250), buffed_entity.get_center_position(), self.graphics_size, self.graphics_size + 10, Millis(70), 2, None) self.graphics_size -= 7 game_state.visual_effects.append(visual_effect) return False
def _would_collide_with_dir(direction: Direction, agent_entity: WorldEntity, game_state: GameState): # TODO Is this too naive to work? future_time = Millis(100) future_pos = agent_entity.get_new_position_according_to_other_dir_and_speed( direction, future_time) future_pos_within_world = game_state.get_within_world( future_pos, (agent_entity.pygame_collision_rect.w, agent_entity.pygame_collision_rect.h)) would_collide = game_state.would_entity_collide_if_new_pos( agent_entity, future_pos_within_world) return would_collide
def get_target(agent_entity: WorldEntity, game_state: GameState) -> EnemyTarget: # Enemies should prioritize attacking a summon over attacking the player player_summons = [npc for npc in game_state.non_player_characters if npc.npc_category == NpcCategory.PLAYER_SUMMON] if player_summons: player_summon = player_summons[0] agent_position = agent_entity.get_position() distance_to_npc_target = get_manhattan_distance(player_summon.world_entity.get_position(), agent_position) distance_to_player = get_manhattan_distance(game_state.player_entity.get_position(), agent_position) if distance_to_npc_target < distance_to_player: return EnemyTarget.npc(player_summon.world_entity, player_summon) return EnemyTarget.player(game_state.player_entity)
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 apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis) -> Optional[bool]: self.time_since_start += time_passed charger_center_pos = buffed_entity.get_center_position() if self.graphics_timer.update_and_check_if_ready(time_passed): visual_circle = VisualCircle((250, 250, 250), charger_center_pos, 15, 25, Millis(120), 2, None) game_state.visual_effects.append(visual_circle) rect_w = 32 # NOTE: We assume that this ability is used by this specific hero hero_entity_size = HEROES[HeroId.WARRIOR].entity_size impact_pos = translate_in_direction( charger_center_pos, buffed_entity.direction, rect_w / 2 + hero_entity_size[0] / 2) impact_rect = Rect(int(impact_pos[0] - rect_w / 2), int(impact_pos[1] - rect_w / 2), rect_w, rect_w) affected_enemies = game_state.get_enemy_intersecting_rect(impact_rect) for enemy in affected_enemies: visual_impact_pos = get_middle_point(charger_center_pos, enemy.world_entity.get_center_position()) damage = MIN_DMG # Talent: Apply damage bonus even if using charge in melee range has_melee_upgrade = game_state.player_state.has_upgrade(HeroUpgradeId.ABILITY_CHARGE_MELEE) damage_increased = self.time_since_start > float(CHARGE_DURATION) * 0.3 or has_melee_upgrade if damage_increased: # TODO Stun target as a bonus here damage = MAX_DMG deal_player_damage_to_enemy(game_state, enemy, damage, DamageType.PHYSICAL, visual_emphasis=damage_increased) game_state.visual_effects.append( VisualRect((250, 170, 0), visual_impact_pos, 45, 25, IMPACT_STUN_DURATION, 2, None)) game_state.visual_effects.append( VisualRect((150, 0, 0), visual_impact_pos, 35, 20, IMPACT_STUN_DURATION, 2, None)) game_state.player_state.gain_buff_effect(get_buff_effect(BUFF_TYPE_STUNNED), IMPACT_STUN_DURATION) enemy.gain_buff_effect(get_buff_effect(BUFF_TYPE_STUNNED), IMPACT_STUN_DURATION) game_state.camera_shake = CameraShake(Millis(50), Millis(150), 12) play_sound(SoundId.ABILITY_CHARGE_HIT) has_stomp_cooldown_upgrade = game_state.player_state.has_upgrade( HeroUpgradeId.ABILITY_CHARGE_RESET_STOMP_COOLDOWN) if has_stomp_cooldown_upgrade: game_state.player_state.set_ability_cooldown_to_zero(AbilityType.STOMP) # The buff should end upon impact return True return False
def apply_middle_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter, time_passed: Millis): if self.dmg_timer.update_and_check_if_ready(time_passed): deal_player_damage_to_enemy(game_state, buffed_npc, 1, DamageType.MAGIC, damage_source=DAMAGE_SOURCE) if self.graphics_timer.update_and_check_if_ready(time_passed): position = buffed_entity.get_center_position() visual_effect1 = VisualCircle((0, 100, 40), position, 9, 16, Millis(400), 2, buffed_entity) visual_effect2 = VisualCircle((0, 180, 90), position, 9, 16, Millis(500), 2, buffed_entity) game_state.visual_effects.append(visual_effect1) game_state.visual_effects.append(visual_effect2)
def _apply_ability(game_state: GameState) -> AbilityResult: player_entity = game_state.player_entity distance_from_player = 35 projectile_pos = translate_in_direction( get_position_from_center_position(player_entity.get_center_position(), PROJECTILE_SIZE), player_entity.direction, distance_from_player) projectile_speed = 0.2 entity = WorldEntity(projectile_pos, PROJECTILE_SIZE, PROJECTILE_SPRITE, player_entity.direction, projectile_speed) projectile = Projectile(entity, create_projectile_controller(PROJECTILE_TYPE)) game_state.projectile_entities.append(projectile) effect_position = (projectile_pos[0] + PROJECTILE_SIZE[0] // 2, projectile_pos[1] + PROJECTILE_SIZE[1] // 2) game_state.visual_effects.append( VisualCircle((250, 150, 50), effect_position, 9, 18, Millis(80), 0)) return AbilityWasUsedSuccessfully()
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
def _apply_ability(game_state: GameState) -> AbilityResult: player_entity = game_state.player_entity distance_from_player = 35 projectile_pos = translate_in_direction( get_position_from_center_position(player_entity.get_center_position(), PROJECTILE_SIZE), player_entity.direction, distance_from_player) projectile_speed = 0.3 entity = WorldEntity(projectile_pos, PROJECTILE_SIZE, Sprite.PROJECTILE_PLAYER_FIREBALL, player_entity.direction, projectile_speed) projectile = Projectile( entity, create_projectile_controller(ProjectileType.PLAYER_FIREBALL)) game_state.projectile_entities.append(projectile) effect_position = (projectile_pos[0] + PROJECTILE_SIZE[0] // 2, projectile_pos[1] + PROJECTILE_SIZE[1] // 2) game_state.visual_effects.append( VisualCircle((250, 150, 50), effect_position, 15, 5, Millis(300), 0)) has_lightfooted_upgrade = game_state.player_state.has_upgrade( HeroUpgradeId.MAGE_LIGHT_FOOTED) if not has_lightfooted_upgrade: game_state.player_state.gain_buff_effect( get_buff_effect(BuffType.RECOVERING_AFTER_ABILITY), Millis(300)) return AbilityWasUsedSuccessfully()