Beispiel #1
0
    def _handle_gain_exp_events(self, gain_exp_events):
        did_level_up = False
        new_abilities: List[str] = []
        did_unlock_new_talent = False
        for event in gain_exp_events:
            if isinstance(event, PlayerLeveledUp):
                did_level_up = True
            if isinstance(event, PlayerLearnedNewAbility):
                new_abilities.append(ABILITIES[event.ability_type].name)
            if isinstance(event, PlayerUnlockedNewTalent):
                did_unlock_new_talent = True

        if did_level_up:
            play_sound(SoundId.EVENT_PLAYER_LEVELED_UP)
            self.game_state.visual_effects.append(
                VisualCircle(
                    (150, 150, 250),
                    self.game_state.player_entity.get_center_position(), 9, 35,
                    Millis(150), 2))
            self.info_message.set_message(
                "You reached level " + str(self.game_state.player_state.level))
        if new_abilities:
            allocate_input_keys_for_abilities(
                self.game_state.player_state.abilities)
        if len(new_abilities) == 1:
            self.info_message.enqueue_message("New ability: " +
                                              new_abilities[0])
        elif len(new_abilities) > 1:
            self.info_message.enqueue_message("Gained several new abilities")
        if did_unlock_new_talent:
            self.talent_was_unlocked.notify(None)
            self.info_message.enqueue_message("You can pick a talent!")
Beispiel #2
0
 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.game_world.visual_effects += create_teleport_effects(buffed_entity.get_center_position())
         play_sound(SoundId.ABILITY_TELEPORT)
Beispiel #3
0
 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 on_select(self, game_state: GameState):
     if game_state.player_state.item_inventory.has_item_in_inventory(ITEM_TYPE_KEY):
         play_sound(SoundId.EVENT_COMPLETED_QUEST)
         game_state.player_state.complete_quest(QUEST)
     else:
         play_sound(SoundId.WARNING)
         return "You don't have that!"
Beispiel #5
0
 def update(self, npc: NonPlayerCharacter, game_state: GameState,
            time_passed: Millis):
     self._time_since_attack += time_passed
     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,
             game_state.game_world.player_entity.get_position())
         new_direction = directions_to_player[0]
         if random.random(
         ) < self._chance_to_shoot_other_direction 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,
                                               self._projectile_size),
             npc.world_entity.direction, distance_from_enemy)
         projectile = self._create_projectile(projectile_pos,
                                              npc.world_entity.direction)
         game_state.game_world.projectile_entities.append(projectile)
         play_sound(self._sound_id)
    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 on_select(self, game_state: GameState) -> Optional[str]:
     if not game_state.player_state.health_resource.is_at_max():
         health_gained = game_state.player_state.health_resource.gain_to_max()
         game_state.visual_effects.append(create_visual_healing_text(game_state.player_entity, health_gained))
         play_sound(SoundId.CONSUMABLE_POTION)
         return "You feel healthy again!"
     play_sound(SoundId.WARNING)
     return "Already at full health!"
Beispiel #8
0
 def on_select(self, game_state: GameState):
     if game_state.player_state.item_inventory.has_item_in_inventory(
             ItemType.KEY):
         play_sound(SoundId.EVENT_COMPLETED_QUEST)
         game_state.player_state.has_finished_main_quest = True
     else:
         play_sound(SoundId.WARNING)
         return "You don't have that!"
 def _try_pick_up_item_from_ground(self, item: ItemOnGround):
     item_name = item.item_id.name
     did_add_item = self.try_add_item_to_inventory(item.item_id)
     if did_add_item:
         play_sound(SoundId.EVENT_PICKED_UP)
         self.game_state.game_world.items_on_ground.remove(item)
         self.info_message.set_message("You picked up " + item_name)
     else:
         self.info_message.set_message("No space for " + item_name)
 def _save_game(self):
     play_sound(SoundId.EVENT_SAVED_GAME)
     filename = self.save_file_handler.save_to_file(
         self.game_state, self.character_file, self.total_time_played_on_character)
     if self.character_file is None:
         # This is relevant when saving a character for the first time. If we didn't update the field, we would
         # be creating a new file everytime we saved.
         self.character_file = filename
     self.ui_state.set_message("Game was saved.")
Beispiel #11
0
 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 handle_event(self, event: EngineEvent) -> Optional[SceneTransition]:
     if event == EngineEvent.PLAYER_DIED:
         self.game_state.player_entity.set_position(self.game_state.player_spawn_position)
         self.game_state.player_state.health_resource.set_to_partial_of_max(0.5)
         self.game_state.player_state.lose_exp_from_death()
         self.game_state.player_state.force_cancel_all_buffs()
         self.info_message.set_message("Lost exp from dying")
         play_sound(SoundId.EVENT_PLAYER_DIED)
         self.game_state.player_state.gain_buff_effect(get_buff_effect(BuffType.BEING_SPAWNED), Millis(1000))
     return None
 def _on_click_toggle(self, clicked_toggle: ToggleButton):
     play_sound(SoundId.UI_TOGGLE)
     if clicked_toggle.is_open:
         self.enabled_toggle.close()
         self.enabled_toggle = None
     else:
         if self.enabled_toggle is not None:
             self.enabled_toggle.close()
         self.enabled_toggle = clicked_toggle
         self.enabled_toggle.open()
    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)
 def apply_enemy_collision(self, npc: NonPlayerCharacter, game_state: GameState, projectile: Projectile):
     damage_was_dealt = deal_player_damage_to_enemy(game_state, npc, 1, DamageType.MAGIC)
     if damage_was_dealt:
         npc.gain_buff_effect(get_buff_effect(BUFF_TYPE), DEBUFF_DURATION)
         victim_center_pos = npc.world_entity.get_center_position()
         visual_effect_pos = (victim_center_pos[0] - ENTANGLING_ROOTS_SIZE[0] // 2,
                              victim_center_pos[1] - ENTANGLING_ROOTS_SIZE[1] // 2)
         debuff_visual_effect = VisualSprite(Sprite.DECORATION_ENTANGLING_ROOTS_EFFECT, visual_effect_pos,
                                             DEBUFF_DURATION, npc.world_entity)
         game_state.game_world.visual_effects.append(debuff_visual_effect)
         play_sound(SoundId.ABILITY_ENTANGLING_ROOTS_HIT)
     projectile.has_collided_and_should_be_removed = True
Beispiel #16
0
    def _change_option(self, delta: int):
        num_options = len(self._saved_characters) + 1
        self._selected_option_index = (self._selected_option_index +
                                       delta) % num_options

        if self._first_shown_option_index + NUM_SHOWN_SAVE_FILES <= self._selected_option_index \
                < len(self._saved_characters):
            self._first_shown_option_index = self._selected_option_index - NUM_SHOWN_SAVE_FILES + 1
        elif self._selected_option_index < self._first_shown_option_index:
            self._first_shown_option_index = self._selected_option_index

        play_sound(SoundId.DIALOG)
Beispiel #17
0
 def on_select(self, game_state: GameState) -> Optional[str]:
     player_has_it = game_state.player_state.item_inventory.has_item_in_inventory(
         self.item_id)
     if player_has_it:
         game_state.player_state.item_inventory.lose_item_from_inventory(
             self.item_id)
         game_state.player_state.modify_money(self.price)
         play_sound(SoundId.EVENT_SOLD_SOMETHING)
         return "Sold " + self.name
     else:
         play_sound(SoundId.WARNING)
         return "You don't have that!"
Beispiel #18
0
    def on_select(self, game_engine: Any) -> Optional[str]:
        game_state = game_engine.game_state
        player_state = game_state.player_state
        can_afford = player_state.money >= self.price
        if not can_afford:
            play_sound(SoundId.WARNING)
            return "Not enough gold!"

        player_state.modify_money(-self.price)
        reset_talents(game_state)
        play_sound(SoundId.EVENT_RESET_TALENT)
        return "Talents reset"
 def handle_being_close_to_portal(self, portal: Portal):
     # When finding a new portal out on the map, it's enough to walk close to it, to activate its sibling
     if portal.is_enabled:
         destination_portal = self.game_state.game_world.get_portal_with_id(
             portal.leads_to)
         if not destination_portal.is_enabled:
             play_sound(SoundId.EVENT_PORTAL_ACTIVATED)
             self.info_message.set_message("Portal was activated")
             self.activate_portal(destination_portal,
                                  portal.world_entity.sprite)
             self.game_state.game_world.visual_effects += create_teleport_effects(
                 portal.world_entity.get_center_position())
Beispiel #20
0
 def _try_pick_up_item_from_ground(self, item: ItemOnGround):
     item_effect = get_item_effect(item.item_type)
     item_data = ITEMS[item.item_type]
     item_equipment_category = item_data.item_equipment_category
     did_add_item = try_add_item_to_inventory(self.game_state, item_effect,
                                              item_equipment_category)
     if did_add_item:
         play_sound(SoundId.EVENT_PICKED_UP)
         self.game_state.items_on_ground.remove(item)
         self.info_message.set_message("You picked up " + item_data.name)
     else:
         self.info_message.set_message("No space for " + item_data.name)
    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 deal_damage_to_player(game_state: GameState, base_amount: float,
                          damage_type: DamageType,
                          npc_attacker: Optional[NonPlayerCharacter]):
    player_state = game_state.player_state
    if npc_attacker:
        player_state.notify_about_event(PlayerWasAttackedEvent(npc_attacker),
                                        game_state)
    damage_reduction = 0
    # Armor only reduces physical damage
    if damage_type == DamageType.PHYSICAL:
        dodge_chance = player_state.get_effective_dodge_chance()
        block_chance = player_state.get_effective_block_chance()
        if random.random() < dodge_chance:
            game_state.game_world.visual_effects.append(
                create_visual_dodge_text(game_state.game_world.player_entity))
            play_sound(SoundId.ENEMY_ATTACK_WAS_DODGED)
            player_state.notify_about_event(PlayerDodgedEvent(npc_attacker),
                                            game_state)
            return
        elif random.random() < block_chance:
            if player_state.block_damage_reduction > 0:
                game_state.game_world.visual_effects.append(
                    create_visual_block_text(
                        game_state.game_world.player_entity))
            damage_reduction += player_state.block_damage_reduction
            player_state.notify_about_event(PlayerBlockedEvent(npc_attacker),
                                            game_state)
        # Armor has a random element to it. Example: 5 armor absorbs 0-5 damage
        damage_reduction += random.randint(0,
                                           player_state.get_effective_armor())
    elif damage_type == DamageType.MAGIC:
        resist_chance = player_state.get_effective_magic_resist_chance()
        if random.random() < resist_chance:
            game_state.game_world.visual_effects.append(
                create_visual_resist_text(game_state.game_world.player_entity))
            play_sound(SoundId.MAGIC_DAMAGE_WAS_RESISTED)
            return

    amount = max(0.0, base_amount - damage_reduction)
    health_lost_integer = player_state.health_resource.lose(amount)
    if health_lost_integer > 0:
        game_state.game_world.visual_effects.append(
            create_visual_damage_text(game_state.game_world.player_entity,
                                      health_lost_integer))
        play_sound(SoundId.ENEMY_ATTACK)
        if random.random() < 0.3:
            play_sound(SoundId.PLAYER_PAIN)
        player_state.notify_about_event(
            PlayerLostHealthEvent(health_lost_integer, npc_attacker),
            game_state)
    else:
        play_sound(SoundId.ENEMY_ATTACK_WAS_BLOCKED)
Beispiel #23
0
    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()
Beispiel #24
0
 def apply_enemy_collision(self, npc: NonPlayerCharacter,
                           game_state: GameState, projectile: Projectile):
     damage_amount: float = MIN_DMG + random.random() * (MAX_DMG - MIN_DMG)
     deal_player_damage_to_enemy(game_state, npc, damage_amount,
                                 DamageType.MAGIC)
     _create_visual_splash(npc.world_entity.get_center_position(),
                           game_state)
     has_burn_upgrade = game_state.player_state.has_upgrade(
         HeroUpgradeId.ABILITY_FIREBALL_BURN)
     if has_burn_upgrade:
         npc.gain_buff_effect(get_buff_effect(BUFF_TYPE),
                              FIREBALL_TALENT_BURN_DURATION)
     play_sound(SoundId.ABILITY_FIREBALL_HIT)
     projectile.has_collided_and_should_be_removed = True
Beispiel #25
0
 def _try_pick_up_consumable_from_ground(self,
                                         consumable: ConsumableOnGround):
     # TODO move some logic into ConsumableInventory class
     has_space = self.game_state.player_state.consumable_inventory.has_space_for_more(
     )
     consumable_type = consumable.consumable_type
     consumable_name = CONSUMABLES[consumable_type].name
     if has_space:
         self.game_state.player_state.consumable_inventory.add_consumable(
             consumable_type)
         self.info_message.set_message("You picked up " + consumable_name)
         play_sound(SoundId.EVENT_PICKED_UP)
         self.game_state.consumables_on_ground.remove(consumable)
     else:
         self.info_message.set_message("No space for " + consumable_name)
Beispiel #26
0
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()
Beispiel #27
0
    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
Beispiel #28
0
 def update(self, npc: NonPlayerCharacter, game_state: GameState,
            time_passed: Millis):
     self._time_since_summoning += 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.game_world.non_player_characters
         ]
         if len(self._alive_summons) < self._max_summons:
             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(self._summon_npc_types)
             summon_size = NON_PLAYER_CHARACTERS[summon_type].size
             summon_pos = game_state.game_world.get_within_world(
                 get_position_from_center_position(summon_center_pos,
                                                   summon_size),
                 summon_size)
             summon_enemy = self._create_npc(summon_type, summon_pos)
             is_wall_blocking = game_state.game_world.walls_state.does_rect_intersect_with_wall(
                 rect_from_corners(necro_center_pos, summon_center_pos))
             is_position_blocked = game_state.game_world.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.game_world.add_non_player_character(
                     summon_enemy)
                 self._alive_summons.append(summon_enemy)
                 game_state.game_world.visual_effects.append(
                     VisualCircle((80, 150, 100), necro_center_pos, 40, 70,
                                  Millis(120), 3))
                 game_state.game_world.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()
 def on_select(self, game_engine: GameEngine) -> Optional[str]:
     game_state = game_engine.game_state
     player_has_it = game_state.player_state.item_inventory.has_item_in_inventory(
         plain_item_id(self.quest_item_type))
     if player_has_it:
         game_state.player_state.item_inventory.lose_item_from_inventory(plain_item_id(self.quest_item_type))
         play_sound(SoundId.EVENT_COMPLETED_QUEST)
         game_state.player_state.complete_quest(self.quest)
         reward_item_id = self.reward_item_id(game_state)
         if reward_item_id:
             did_add_item = game_engine.try_add_item_to_inventory(reward_item_id)
             if not did_add_item:
                 game_state.game_world.items_on_ground.append(
                     create_item_on_ground(reward_item_id, game_state.game_world.player_entity.get_position()))
             return "Quest completed! Reward gained: " + reward_item_id.name
         return "Quest completed!"
     else:
         play_sound(SoundId.WARNING)
         return "You don't have that!"
    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
        super().control_npc(game_state, npc, player_entity, is_player_invisible, time_passed)

        self._time_since_state_change += time_passed

        if self._state == State.BASE:
            if self._time_since_state_change > NpcMind.STATE_DURATION_BASE:
                self._time_since_state_change -= NpcMind.STATE_DURATION_BASE
                self._state = State.FIRING

                npc.gain_buff_effect(get_buff_effect(BUFF_STUNNED),
                                     Millis(NpcMind.STATE_DURATION_FIRING))
                return
        elif self._state == State.FIRING:
            if self._time_since_state_change > NpcMind.STATE_DURATION_FIRING:
                self._time_since_state_change -= NpcMind.STATE_DURATION_FIRING
                self._state = State.BASE
                return
            self._time_since_fired += time_passed
            if self._time_since_fired > NpcMind.FIRE_COOLDOWN:
                self._time_since_fired -= NpcMind.FIRE_COOLDOWN
                directions_to_player = get_directions_to_position(npc.world_entity, player_entity.get_position())
                new_direction = directions_to_player[0]
                if random.random() < 0.1 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.3
                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_MAGIC_SKELETON_BOSS)