def _apply_scroll(game_state: GameState): player_entity = game_state.player_entity summon_size = NON_PLAYER_CHARACTERS[NpcType.PLAYER_SUMMON_DRAGON].size player_size = game_state.player_entity.pygame_collision_rect.w, game_state.player_entity.h candidate_relative_positions = [ (0, - summon_size[1]), # top (player_size[0], - summon_size[1]), # top right (player_size[0], 0), # right (player_size[0], player_size[1]), # down right (0, player_size[1]), # down (-summon_size[0], player_size[1]), # down left (-summon_size[0], 0), # left (-summon_size[0], -summon_size[1]) # top left ] for relative_pos in candidate_relative_positions: summon_pos = sum_of_vectors(player_entity.get_position(), relative_pos) summon = create_npc(NpcType.PLAYER_SUMMON_DRAGON, summon_pos) is_valid_pos = not game_state.would_entity_collide_if_new_pos(summon.world_entity, summon_pos) if is_valid_pos: game_state.remove_all_player_summons() game_state.add_non_player_character(summon) summon.gain_buff_effect(get_buff_effect(BuffType.SUMMON_DIE_AFTER_DURATION), DURATION_SUMMON) game_state.visual_effects.append( VisualCircle((200, 200, 30), player_entity.get_position(), 40, 70, Millis(140), 3)) game_state.visual_effects.append(VisualCircle((200, 200, 30), summon_pos, 40, 70, Millis(140), 3)) return ConsumableWasConsumed("Summoned dragon") return ConsumableFailedToBeConsumed("No space to summon dragon")
def _apply_ability(game_state: GameState) -> AbilityResult: player_entity = game_state.player_entity rect_w = 28 slash_center_pos = translate_in_direction( player_entity.get_center_position(), player_entity.direction, rect_w / 2 + PLAYER_ENTITY_SIZE[0] * 0.25) slash_rect = Rect(int(slash_center_pos[0] - rect_w / 2), int(slash_center_pos[1] - rect_w / 2), rect_w, rect_w) affected_enemies = game_state.get_enemy_intersecting_rect(slash_rect) if not affected_enemies: return AbilityFailedToExecute(reason="No targets") # Note: Dependency on other ability 'stealth' should_stun = game_state.player_state.has_active_buff(BuffType.STEALTHING) if should_stun: game_state.camera_shake = CameraShake(Millis(50), Millis(150), 4) buff_effect = get_buff_effect(DEBUFF, should_stun) affected_enemies[0].gain_buff_effect(buff_effect, DEBUFF_DURATION) game_state.visual_effects.append( VisualRect((150, 150, 75), slash_center_pos, rect_w, int(rect_w * 0.7), Millis(200), 2, None)) game_state.visual_effects.append( VisualCross((100, 100, 70), slash_center_pos, 6, Millis(100), 2)) game_state.player_state.gain_buff_effect( get_buff_effect(BuffType.RECOVERING_AFTER_ABILITY), Millis(250)) return AbilityWasUsedSuccessfully()
def apply_end_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): game_state.player_state.stun_status.remove_one() hero_center_pos = game_state.player_entity.get_center_position() distance = 80 affected_enemies = game_state.get_enemies_within_x_y_distance_of( distance, hero_center_pos) game_state.visual_effects.append( VisualRect((50, 50, 50), hero_center_pos, distance * 2, int(distance * 2.1), Millis(200), 2, None)) game_state.visual_effects.append( VisualRect((150, 150, 0), hero_center_pos, distance, distance * 2, Millis(150), 3, None)) game_state.visual_effects.append( VisualRect((250, 250, 0), hero_center_pos, distance, distance * 2, Millis(100), 4, None)) for enemy in affected_enemies: damage: float = MIN_DMG + random.random() * (MAX_DMG - MIN_DMG) deal_player_damage_to_enemy(game_state, enemy, damage, DamageType.PHYSICAL) enemy.gain_buff_effect(get_buff_effect(STUNNED_BY_STOMP), STUN_DURATION) game_state.player_state.gain_buff_effect( get_buff_effect(BuffType.RECOVERING_AFTER_ABILITY), Millis(300)) play_sound(SoundId.ABILITY_STOMP_HIT) game_state.camera_shake = CameraShake(Millis(50), Millis(200), 12)
def apply_start_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): game_state.player_state.stun_status.add_one() game_state.modify_hero_stat(HeroStat.MOVEMENT_SPEED, BONUS_SPEED_MULTIPLIER) game_state.game_world.player_entity.set_moving_in_dir( game_state.game_world.player_entity.direction)
def _add_npc(npc_type, game_state: GameState, snapped_mouse_world_position): already_has_npc = any([ x for x in game_state.non_player_characters if x.world_entity.get_position() == snapped_mouse_world_position ]) if not already_has_npc: npc = create_npc(npc_type, snapped_mouse_world_position) game_state.add_non_player_character(npc)
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 _set_game_world(self, game_world: GameWorldState, player_spawn_position: Tuple[int, int]): enabled_portals = { portal.portal_id: portal.world_entity.sprite for portal in game_world.portals if portal.is_enabled } player_state = create_player_state_as_initial(HERO_ID, enabled_portals) self.game_state = GameState(game_world, CAMERA_SIZE, player_state, False, player_spawn_position) self.game_state.game_world.player_entity = create_hero_world_entity( HERO_ID, player_spawn_position) self.game_state.center_camera_on_position(player_spawn_position) self.game_state.snap_camera_to_grid(self.grid_cell_size) if self.ui_view: # UI view may not have been created yet at this time self._notify_ui_of_new_wall_positions()
def _apply_ability(game_state: GameState) -> AbilityResult: player_entity = game_state.player_entity rect_w = 28 slash_center_pos = translate_in_direction( player_entity.get_center_position(), player_entity.direction, rect_w / 2 + PLAYER_ENTITY_SIZE[0] * 0.25) slash_rect = Rect(int(slash_center_pos[0] - rect_w / 2), int(slash_center_pos[1] - rect_w / 2), rect_w, rect_w) affected_enemies = game_state.get_enemy_intersecting_rect(slash_rect) is_stealthed = game_state.player_state.has_active_buff(BuffType.STEALTHING) if is_stealthed: play_sound(SoundId.ABILITY_SHIV_STEALTHED) else: play_sound(SoundId.ABILITY_SHIV) for enemy in affected_enemies: damage: float = MIN_DMG + random.random() * (MAX_DMG - MIN_DMG) # Note: Dependency on other ability 'stealth' if is_stealthed: # Talent: increase the damage bonus that Shiv gets from being used while stealthing has_damage_upgrade = game_state.player_state.has_upgrade( HeroUpgradeId.ABILITY_SHIV_SNEAK_BONUS_DAMAGE) damage *= SHIV_UPGRADED_STEALTH_DAMAGE_MULTIPLIER if has_damage_upgrade else SHIV_STEALTH_DAMAGE_MULTIPLIER game_state.camera_shake = CameraShake(Millis(50), Millis(150), 4) else: # Talent: if attacking an enemy that's at 100% health while not stealthing, deal bonus damage has_damage_upgrade = game_state.player_state.has_upgrade( HeroUpgradeId.ABILITY_SHIV_FULL_HEALTH_BONUS_DAMAGE) if has_damage_upgrade and enemy.health_resource.is_at_max(): damage *= SHIV_TALENT_FULL_HEALTH_DAMAGE_MULTIPLIER deal_player_damage_to_enemy(game_state, enemy, damage, DamageType.PHYSICAL, visual_emphasis=is_stealthed) break game_state.visual_effects.append( VisualRect((150, 150, 75), slash_center_pos, rect_w, int(rect_w * 0.7), Millis(200), 2, None)) game_state.visual_effects.append( VisualCross((100, 100, 70), slash_center_pos, 6, Millis(100), 2)) game_state.player_state.gain_buff_effect( get_buff_effect(BuffType.RECOVERING_AFTER_ABILITY), Millis(250)) return AbilityWasUsedSuccessfully()
def create_dungeon_game_state(player_state: PlayerState, camera_size: Tuple[int, int], difficulty_level: int) -> GameState: dungeon = _generate_dungeon(difficulty_level) player_entity = create_hero_world_entity(player_state.hero_id, dungeon.player_position) game_world = GameWorldState( player_entity=player_entity, non_player_characters=dungeon.npcs, consumables_on_ground=[], items_on_ground=[], money_piles_on_ground=[], walls=dungeon.walls, entire_world_area=dungeon.world_area, decoration_entities=dungeon.decorations, portals=[], chests=[], shrines=[], dungeon_entrances=[], ) return GameState(game_world=game_world, camera_size=camera_size, player_state=player_state, is_dungeon=True, player_spawn_position=dungeon.player_position)
def _apply_ability(game_state: GameState) -> AbilityResult: player_entity = game_state.player_entity rect_w = 36 # Note: We assume that this ability is used by this specific hero hero_entity_size = HEROES[HeroId.WARRIOR].entity_size slash_pos = translate_in_direction(player_entity.get_center_position(), player_entity.direction, rect_w / 2 + hero_entity_size[0] * 0.25) slash_rect = Rect(int(slash_pos[0] - rect_w / 2), int(slash_pos[1] - rect_w / 2), rect_w, rect_w) affected_enemies = game_state.get_enemy_intersecting_rect(slash_rect) has_aoe_upgrade = game_state.player_state.has_upgrade( HeroUpgradeId.ABILITY_SLASH_AOE_BONUS_DAMAGE) hit_multiple_enemies = len(affected_enemies) > 1 for enemy in affected_enemies: if has_aoe_upgrade and hit_multiple_enemies: damage: float = MAX_DMG else: damage: float = MIN_DMG + random.random() * (MAX_DMG - MIN_DMG) deal_player_damage_to_enemy(game_state, enemy, damage, DamageType.PHYSICAL) game_state.visual_effects.append( VisualRect((100, 0, 0), slash_pos, rect_w, int(rect_w * 0.7), Millis(200), 2, None)) game_state.player_state.gain_buff_effect( get_buff_effect(BuffType.RECOVERING_AFTER_ABILITY), Millis(300)) return AbilityWasUsedSuccessfully()
def deserialize(data, player_state: PlayerState, camera_size: Tuple[int, int]) -> GameState: return GameState( player_entity=PlayerJson.deserialize(player_state.hero_id, data["player"]), consumables_on_ground=[ ConsumableJson.deserialize(p) for p in data.get("consumables_on_ground", []) ], items_on_ground=[ ItemJson.deserialize(i) for i in data.get("items_on_ground", []) ], money_piles_on_ground=[ MoneyJson.deserialize(p) for p in data.get("money_piles_on_ground", []) ], non_player_characters=[ NpcJson.deserialize(e) for e in data.get("non_player_characters", []) ], walls=[WallJson.deserialize(w) for w in data.get("walls", [])], camera_size=camera_size, entire_world_area=WorldAreaJson.deserialize( data["entire_world_area"]), player_state=player_state, decoration_entities=[ DecorationJson.deserialize(d) for d in data.get("decorations", []) ], portals=[ PortalJson.deserialize(p) for p in data.get("portals", []) ], chests=[ChestJson.deserialize(c) for c in data.get("chests", [])])
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): 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 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 control_npc(self, game_state: GameState, npc: NonPlayerCharacter, player_entity: WorldEntity, is_player_invisible: bool, time_passed: Millis): self._time_since_updated_path += time_passed self._time_since_reevaluated += time_passed self._time_since_attack += time_passed summon_entity = npc.world_entity if self._time_since_updated_path > self._update_path_interval: self._time_since_updated_path = 0 nearby_enemies = game_state.get_enemies_within_x_y_distance_of(300, npc.world_entity.get_position()) if nearby_enemies: target_entity = nearby_enemies[0].world_entity else: target_entity = game_state.player_entity self.pathfinder.update_path_towards_target(summon_entity, game_state, target_entity) new_next_waypoint = self.pathfinder.get_next_waypoint_along_path(summon_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, summon_entity, self.next_waypoint) _move_in_dir(summon_entity, direction) else: summon_entity.set_not_moving() if self._time_since_attack > self._attack_interval: self._time_since_attack = 0 nearby_enemies = game_state.get_enemies_within_x_y_distance_of(100, summon_entity.get_position()) if nearby_enemies: damage_amount = 3 target = nearby_enemies[0] deal_npc_damage_to_npc(game_state, target, damage_amount) game_state.visual_effects.append( VisualLine((220, 0, 0), summon_entity.get_center_position(), target.world_entity.get_center_position(), Millis(100), damage_amount))
def _apply_ability(game_state: GameState) -> AbilityResult: player_entity = game_state.game_world.player_entity previous_position = player_entity.get_center_position() used_from_stealth = game_state.player_state.has_active_buff( BuffType.STEALTHING) for distance in range(40, 200, 10): new_position = translate_in_direction( (player_entity.x, player_entity.y), player_entity.direction, distance) if game_state.game_world.is_position_within_game_world(new_position) \ and not game_state.game_world.would_entity_collide_if_new_pos(player_entity, new_position): if _would_collide_with_wall(game_state, player_entity, distance): return AbilityFailedToExecute(reason="Wall is blocking") should_regain_mana_and_cd = False enemy_hit = _get_enemy_that_was_hit(game_state, player_entity, distance) if enemy_hit: game_state.camera_shake = CameraShake(Millis(50), Millis(150), 4) deal_player_damage_to_enemy(game_state, enemy_hit, DAMAGE, DamageType.MAGIC) has_reset_upgrade = game_state.player_state.has_upgrade( HeroUpgradeId.ABILITY_DASH_KILL_RESET) enemy_died = enemy_hit.health_resource.is_at_or_below_zero() if has_reset_upgrade and enemy_died: should_regain_mana_and_cd = True player_entity.set_position(new_position) new_center_position = player_entity.get_center_position() color = (250, 140, 80) game_state.game_world.visual_effects.append( VisualCircle(color, previous_position, 17, 35, Millis(150), 1)) game_state.game_world.visual_effects.append( VisualLine(color, previous_position, new_center_position, Millis(250), 2)) game_state.game_world.visual_effects.append( VisualRect(color, previous_position, 37, 46, Millis(150), 1)) game_state.game_world.visual_effects.append( VisualCircle(color, new_center_position, 25, 40, Millis(300), 1, player_entity)) has_speed_upgrade = game_state.player_state.has_upgrade( HeroUpgradeId.ABILITY_DASH_MOVEMENT_SPEED) if has_speed_upgrade: game_state.player_state.gain_buff_effect( get_buff_effect(BUFF_SPEED), BUFF_SPEED_DURATION) if used_from_stealth: game_state.player_state.gain_buff_effect( get_buff_effect(BUFF_FROM_STEALTH), BUFF_FROM_STEALTH_DURATION) return AbilityWasUsedSuccessfully( should_regain_mana_and_cd=should_regain_mana_and_cd) return AbilityFailedToExecute(reason="No space")
def register_game_state_observers(game_state: GameState, ui_view: GameUiView, include_player_state: bool): game_state.game_world.player_movement_speed_was_updated.register_observer(ui_view.on_player_movement_speed_updated) game_state.game_world.notify_movement_speed_observers() # Must notify the initial state game_state.game_world.player_entity.movement_changed = Observable() game_state.game_world.player_entity.movement_changed.register_observer(play_or_stop_footstep_sounds) game_state.game_world.player_entity.position_changed = Observable() game_state.game_world.player_entity.position_changed.register_observer(ui_view.on_player_position_updated) game_state.game_world.player_entity.position_changed.register_observer( lambda _: ui_view.on_walls_seen([w.get_position() for w in game_state.get_walls_in_sight_of_player()])) game_state.game_world.player_entity.notify_position_observers() # Must notify the initial state if include_player_state: _register_player_state_observers(game_state.player_state, ui_view)
def _get_enemy_that_was_hit(game_state: GameState, player_entity: WorldEntity, distance_jumped: int) \ -> Optional[NonPlayerCharacter]: previous_position = (player_entity.x, player_entity.y) partial_distance = 10 while partial_distance < distance_jumped: intermediate_position = translate_in_direction(previous_position, player_entity.direction, partial_distance) enemies_hit = game_state.get_enemy_intersecting_rect( Rect(intermediate_position[0], intermediate_position[1], player_entity.pygame_collision_rect.w, player_entity.pygame_collision_rect.h)) if enemies_hit: return enemies_hit[0] partial_distance += 10 return None
def deserialize(data, player_state: PlayerState, camera_size: Tuple[int, int]) -> MapData: game_state = GameState( player_entity=PlayerJson.deserialize(player_state.hero_id, data["player"]), consumables_on_ground=[ ConsumableJson.deserialize(p) for p in data.get("consumables_on_ground", []) ], items_on_ground=[ ItemJson.deserialize(i) for i in data.get("items_on_ground", []) ], money_piles_on_ground=[ MoneyJson.deserialize(p) for p in data.get("money_piles_on_ground", []) ], non_player_characters=[ NpcJson.deserialize(e) for e in data.get("non_player_characters", []) ], walls=[WallJson.deserialize(w) for w in data.get("walls", [])], camera_size=camera_size, entire_world_area=WorldAreaJson.deserialize( data["entire_world_area"]), player_state=player_state, decoration_entities=[ DecorationJson.deserialize(d) for d in data.get("decorations", []) ], portals=[ PortalJson.deserialize(p) for p in data.get("portals", []) ], chests=[ChestJson.deserialize(c) for c in data.get("chests", [])], shrines=[ ShrineJson.deserialize(s) for s in data.get("shrines", []) ]) map_editor_config = MapEditorConfig( disable_smart_grid=data.get("disable_smart_grid", False)) grid_string = data.get("grid", None) return MapData(game_state, map_editor_config, grid_string)
def apply_middle_effect(self, game_state: GameState, time_passed: Millis): if self.timer.update_and_check_if_ready(time_passed): player_entity = game_state.player_entity player_center_position = player_entity.get_center_position() close_enemies = game_state.get_enemies_within_x_y_distance_of( 140, player_center_position) if close_enemies: damage_amount: float = self.min_dmg + random.random() * ( self.max_dmg - self.min_dmg) deal_player_damage_to_enemy(game_state, close_enemies[0], damage_amount, DamageType.MAGIC) enemy_center_position = close_enemies[ 0].world_entity.get_center_position() game_state.visual_effects.append( VisualCircle((250, 250, 0), player_center_position, 50, 140, Millis(100), 1, player_entity)) game_state.visual_effects.append( VisualLine((250, 250, 0), player_center_position, enemy_center_position, Millis(80), 3))
def _apply_ability(game_state: GameState) -> AbilityResult: player_entity = game_state.player_entity player_center_pos = player_entity.get_center_position() game_state.visual_effects.append( VisualCircle((150, 150, 250), player_center_pos, 95, 190, Millis(200), 3)) affected_enemies = game_state.get_enemies_within_x_y_distance_of( 180, player_center_pos) effect_position = get_position_from_center_position( player_center_pos, EFFECT_SPRITE_SIZE) game_state.visual_effects.append( VisualSprite(Sprite.EFFECT_ABILITY_FROST_NOVA, effect_position, Millis(200), player_entity)) for enemy in affected_enemies: damage_was_dealt = deal_player_damage_to_enemy(game_state, enemy, 5, DamageType.MAGIC) if damage_was_dealt: enemy.gain_buff_effect( get_buff_effect(BuffType.REDUCED_MOVEMENT_SPEED), Millis(4000)) return AbilityWasUsedSuccessfully()
def apply_end_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): game_state.player_state.stun_status.remove_one() game_state.modify_hero_stat(HeroStat.MOVEMENT_SPEED, -BONUS_SPEED_MULTIPLIER)
def _upgrade_mana_regen(game_state: GameState): game_state.modify_hero_stat(HeroStat.MANA_REGEN, 0.5)
def apply_start_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): game_state.player_state.stun_status.add_one() game_state.game_world.player_entity.set_not_moving() game_state.camera_shake = CameraShake(Millis(50), CHANNEL_DURATION, 5)
def apply_end_effect(self, game_state: GameState): for modifier in self.stat_modifiers: game_state.modify_hero_stat(modifier.hero_stat, -modifier.delta)
def apply_end_effect(self, game_state: GameState): for stat, delta in self.stat_modifiers.items(): game_state.modify_hero_stat(stat, -delta)
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()
def apply(self, game_state: GameState): game_state.modify_hero_stat(self.hero_stat, self.amount)
def revert(self, game_state: GameState): game_state.modify_hero_stat(self.hero_stat, -self.amount)
def apply_end_effect(self, game_state: GameState, buffed_entity: WorldEntity, buffed_npc: NonPlayerCharacter): for stat, delta in self.stat_modifiers.items(): game_state.modify_hero_stat(stat, -delta)