Пример #1
0
    def death(self, game):

        ent = self.owner
        ent_old_color = ent.color
        blood = colors.blood_red if ent.color_blood is None else ent.color_blood

        if game.debug['invincibility'] and ent.is_player:
            self.hp = self.max_hp
            return M(f'Invincibility enabled')

        # Strip ent of components and set it as a corpse
        # x, y = ent.x, ent.y
        ent.char = '%'
        ent.color = blood
        ent.bg_color = colors.black
        ent.render_order = RenderOrder.CORPSE
        ent.blocks[BlockLevel.WALK] = False
        ent.blocks[BlockLevel.FLOOR] = False
        ent.ai = None
        if not self.owner.is_player:
            ent.fighter = None
        #ent.name = f'{ent.name.title()} remains'

        # Create gibs
        # TODO Consider force of impact (amount of damage done beyond 0 hp?) to vary spread
        game.map.gib_area(ent.x, ent.y, randint(3, 5), blood, chunks=True)

        type = MessageType.GOOD if not ent.is_player else MessageType.BAD
        message = M(
            f'The %{ent_old_color}%{ent.name}%% {ent.state_verb_present} dead!',
            type=type,
            category=MessageCategory.OBSERVATION)

        return message
Пример #2
0
    def toggle_blocking(self):
        results = []

        if self.is_blocking:
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} stop blocking.',
                  type=MessageType.COMBAT_INFO)
            })
            self.stances[Stance.BLOCKING] = False
        elif self.shield:
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} ready your {self.shield.owner.owner.name_colored}.',
                  type=MessageType.COMBAT_INFO)
            })
            self.stances[Stance.BLOCKING] = True
        else:
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} need a shield to block.',
                  type=MessageType.COMBAT_INFO)
            })

        return results
Пример #3
0
    def dash(self, dx, dy, game, distance: int = 2):
        results = []

        if self.can_dash:
            moved = animate_move_line(self.owner,
                                      dx,
                                      dy,
                                      distance,
                                      game,
                                      anim_delay=0.05)
            results.append(
                self.exert(self.defense * cfg.DASH_EXERT_MULTIPL, 'dash'))
            if moved is not True and not isinstance(
                    moved, bool
            ):  # If dash was stopped by another entity, proceed to tackle
                target = moved
                if target.f is not None:
                    results.extend(self.tackle(target, game))
        else:
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} are too exhausted!',
                  type=MessageType.COMBAT_BAD)
            })

        return results
Пример #4
0
    def toggle_dashing(self):
        results = []

        if self.is_dashing:
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} stop dashing.',
                  type=MessageType.COMBAT_INFO)
            })
            self.stances[Stance.DASHING] = False
        else:
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} prepare to dash.',
                  type=MessageType.COMBAT_INFO)
            })
            self.stances[Stance.DASHING] = True

        return results
Пример #5
0
    def attack_execute(self, target, power, attack_string):
        results = []

        damage = round(power - (target.f.modded_defense))
        deflect_exertion = power * cfg.DEFLECT_EXERT_MULTIPL

        if target == self.owner:
            target_string = 'itself' if not target.is_player else 'yourself'
        else:
            target_string = target.address_colored

        if damage > 0:
            msg_type = MessageType.COMBAT_BAD if target.is_player else MessageType.COMBAT_INFO
            atk_dmg_string = target.f.hpdmg_string(damage)
            col = target.f.hpdmg_color(damage)
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} {attack_string} {target_string}, {choice(hpdmg_string_data["verbs"])} %{col}%{atk_dmg_string}%% damage.',
                  category=MessageCategory.COMBAT,
                  type=msg_type)
            })
            results.extend(target.f.take_damage(damage))

            # STATISTICS
            self.owner.statistics.dmg_done += damage
            if target.f.hp <= 0:
                self.owner.statistics.killed += [target.name]

        else:
            msg_type = MessageType.COMBAT_BAD if not target.is_player else MessageType.COMBAT_GOOD
            results.append({
                'message':
                M(f'{self.owner.address_colored.title()} {attack_string} {target_string} but can not penetrate armor.',
                  category=MessageCategory.COMBAT,
                  type=msg_type)
            })
            results.append(target.f.exert(deflect_exertion, 'deflection'))

        logging.debug(
            f'{self.owner.name.title()} attacks {target.name.title()} with {power} power against {target.f.defense} defense for {damage} damage. Target has {target.f.stamina} stamina left.)'
        )
        return results
Пример #6
0
 def exert(self, amount, string='action'):
     self.stamina -= round(amount)
     logging.debug(f'{self.owner.name} exerted by {string} for {amount}')
     if self.owner.is_player:
         sta_dmg_string = self.stadmg_string(amount)
         col = self.stadmg_color(amount)
         message = M(
             f'{self.owner.possessive_colored.title()} {string} causes %{col}%{sta_dmg_string}%% exertion.',
             category=MessageCategory.OBSERVATION,
             type=MessageType.COMBAT_INFO)
         return {'message': message}
     return {}
Пример #7
0
 def set_effect(self,
                state: State,
                value: bool = True,
                duration: int = 0,
                msg=True):
     self.effects[state] = value
     if duration > 0:  # If duration > 0, set a plan to disable the current presence in n turns
         self.owner.actionplan.add_to_queue(
             execute_in=duration,
             planned_function=self.set_effect,
             planned_function_args=(state, False))
     message = M(
         f'{self.owner.address_colored.title()} {self.owner.state_verb_present}{" no longer " if not value else " "}{state.name}!',
         category=MessageCategory.COMBAT)
     return [{'message': message}] if msg else []
Пример #8
0
    def attack_setup(self,
                     target,
                     game,
                     dmg_mod_multipl: float = 1,
                     verb: str = 'hit',
                     ignore_moveset: bool = False):
        results = []
        extra_attacks = []
        #atk_exertion_divider = cfg.ATK_EXERT_MULTIPL
        daze_chance = 50  # chance to daze attacker on successful block # TODO dynamically calculated

        if self.active_weapon is not None:  # if a weapon is equipped, check the type
            melee_attack = True if self.active_weapon.type == ItemType.MELEE_WEAPON else False
            #ranged_attack = True if self.active_weapon.type == ItemType.RANGED_WEAPON else False
        else:  # unarmed attack
            melee_attack = True
            ranged_attack = False
            ignore_moveset = True

        # Apply moveset modifers #
        attack_power = self.get_attack_power(dmg_mod_multipl, ignore_moveset)
        attack_exertion = self.get_attack_exertion(attack_power,
                                                   ignore_moveset)
        if not ignore_moveset and self.active_weapon is not None:
            move_results = self.active_weapon.moveset.execute(
                self.owner, target)
            verb = move_results.get('attack_verb', verb)
            extra_attacks = move_results.get('extra_attacks', [])
            self.active_weapon.moveset.cycle_moves()
        # if ignore_moveset:
        #     attack_power = choice(self.base_dmg_potential) * dmg_mod_multipl
        #     attack_exertion = attack_power / atk_exertion_divider
        # else:
        #     if self.active_weapon is not None:
        #         move_results = self.active_weapon.moveset.execute(self.owner, target)
        #         verb = move_results.get('attack_verb', verb)
        #         extra_attacks = move_results.get('extra_attacks', [])
        #
        #     attack_power = self.dmg_roll * dmg_mod_multipl
        #     attack_exertion = attack_power / atk_exertion_divider * self.active_weapon.moveset.exert_multipl
        #     self.active_weapon.moveset.cycle_moves()

        logging.debug(
            f'{self.owner.name} prepares to attack {target.name} with base damage {self.base_dmg_potential},'
            f' (modded {self.modded_dmg_potential}) for a total power of {attack_power} and {attack_exertion}exert'
        )

        # Make sure attacker has enough stamina #
        if self.stamina < attack_exertion:
            message = M(f'You are too exhausted to attack!',
                        category=MessageCategory.COMBAT,
                        type=MessageType.ALERT)
            results.append({'message': message})
            logging.debug(
                f'Canceling {self}\'s attack: stamina of {self.stamina} too low.'
            )
            return results

        # Blocking #
        attack_blocked = False
        if target.f.is_blocking:
            attack_blocked = target.f.attempt_block(self, attack_power)

        if attack_blocked:
            # TODO should attacker also take sta damage?
            sta_dmg_multipl = self.active_weapon.moveset.get_modifier(
                Mod.BLOCK_STA_DMG_MULTIPL
            )  # Some weapons afflict a higher stamina damage
            sta_dmg = round((attack_power / 2) * sta_dmg_multipl)
            logging.debug(
                f'{target.name} block exert multiplied by {sta_dmg_multipl} due to {self.owner.name} attack mod'
            )
            results.append(target.f.exert(sta_dmg, 'block'))

            if melee_attack and randint(0, 100) > daze_chance:
                if target.is_player:
                    message = M(
                        f'{target.address_colored.title()} block the attack, dazing {self.owner.address_colored}!',
                        category=MessageCategory.COMBAT,
                        type=MessageType.COMBAT_GOOD)
                else:
                    message = M(
                        f'{self.owner.address_colored.title()} blocks your attack, dazing {target.owner.address_colored}!',
                        category=MessageCategory.COMBAT,
                        type=MessageType.COMBAT_BAD)
                self.set_effect(State.DAZED, True, 1)
                results.append({'message': message})
        else:
            attack_string = self.owner.verb_declination(verb)
            results.extend(
                self.attack_execute(target, attack_power, attack_string))

        for attack_pos in extra_attacks:
            extra_target = entity_at_pos(game.npc_ents, *attack_pos)
            if extra_target:
                results.extend(
                    self.attack_execute(extra_target, attack_power // 2,
                                        'also hit')
                )  # TODO currently flat half damage and can't be blocked; can be replaced with moveset-tailored multiplier

        self.exert(attack_exertion, 'attack')

        return results