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
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
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
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
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
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 {}
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 []
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