def add_creature(template): #t = CreatureTemplate(template=template) #t.set_loadout(Loadout([WEAPON_CLUB])) c = Creature(template) try_equip_best_weapons(c) c.set_action_loop(loop) return c
def resolve_opposed_initiative(a: Creature, b: Creature) -> Optional[Creature]: a_initiative = a.get_initiative_modifier() b_initiative = b.get_initiative_modifier() a_roll = dice(1, 10).get_roll_result() b_roll = dice(1, 10).get_roll_result() a_total = a_roll + a_initiative b_total = b_roll + b_initiative a_crit = 1 if a_roll >= 10 else (-1 if a_roll <= 1 else 0) b_crit = 1 if b_roll >= 10 else (-1 if b_roll <= 1 else 0) winner = None if a_crit != b_crit: winner = a if a_crit > b_crit else b elif a_total != b_total: winner = a if a_total > b_total else b elif round(a_initiative) != round(b_initiative): winner = a if a_initiative > b_initiative else b success_text = f'{winner} takes initiative!' if winner is not None else 'Tie!' print( f'[Initiative] {a} vs {b} RESULT: {a_roll}{a_initiative:+.0f}={a_total:.0f} vs ' f'{b_roll}{b_initiative:+.0f}={b_total:.0f} {success_text}') return winner
def apply_loadout(self, creature: Creature) -> None: for group in self.loadout: for item in group: if isinstance(item, EquipmentTemplate): creature.inventory.add(Equipment(item)) if isinstance(item, CreatureTrait): creature.add_trait(item)
def get_attribute_modifier(self, protagonist: Creature) -> int: if protagonist.has_trait(FinesseTrait): return sum( max(protagonist.get_attribute(attr), protagonist.get_attribute(PrimaryAttribute.DEX)) if attr == PrimaryAttribute.STR else protagonist.get_attribute(attr) for attr in self.key_attr ) return super().get_attribute_modifier(protagonist)
def get_opposed_chance(protagonist: Creature, pro_contest: Contest, antagonist: Creature, ant_contest: Contest = None, modifier: int = 0): ant_contest = ant_contest or pro_contest pro_level = protagonist.get_skill_level(pro_contest) pro_mod = pro_contest.get_attribute_modifier(protagonist) + pro_contest.get_skill_modifier(pro_level) ant_level = antagonist.get_skill_level(ant_contest) ant_mod = ant_contest.get_attribute_modifier(antagonist) + ant_contest.get_skill_modifier(ant_level) target = ant_mod - pro_mod - modifier roll_table = get_opposed_roll_table(pro_level.bonus_dice, ant_level.bonus_dice) return sum(p for result, p in roll_table.items() if result > target)
def _get_standing_bodyparts(creature: Creature) -> Iterable[BodyPart]: if creature.stance == Stance.Prone: return () current, total = creature.get_stance_count() used = total if creature.stance == Stance.Crouching: used = int(total / 2) elif creature.stance == Stance.Standing: used = min(total / 2 + 1, total - 1) used = min(used, current) stance_parts = list(bp for bp in creature.get_bodyparts() if bp.is_stance_part()) return random.sample(stance_parts, used)
def can_attack(self, combatant: Creature) -> bool: opponent = self.get_opponent(combatant) if opponent is None: return False return any( attack.can_attack(self.get_separation()) for attack in combatant.get_melee_attacks())
def get_melee_threat_value(self, opponent: Creature) -> float: threat_priority = get_melee_attack_priority(opponent, self.parent, opponent.get_melee_attacks()) threat_value = max(threat_priority.values(), default=0.0) attack_priority = get_melee_attack_priority(self.parent, opponent, self.parent.get_melee_attacks()) attack_value = max(attack_priority.values(), default=0.0) return threat_value * attack_value / (opponent.health + opponent.max_health)
def can_interrupt_action(combatant: Creature) -> bool: action = combatant.get_current_action() if action is None: return True # idle if isinstance(action, CreatureAction): return action.can_interrupt return False
def join_melee_combat(a: Creature, b: Creature) -> MeleeCombat: """Used to setup a melee combat between two combatants""" a_mount = a.get_mount() a_base = a_mount or a b_mount = b.get_mount() b_base = b_mount or b separation = MeleeSeparation(a_base, b_base) a_combatants = [a_base, *a_base.get_riders()] b_combatants = [b_base, *b_base.get_riders()] for i in a_combatants: for j in b_combatants: if i is not None and j is not None: melee = MeleeCombat(i, j, separation) i.add_melee_combat(j, melee) j.add_melee_combat(i, melee) return a.get_melee_combat(b)
def get_combat_difficulty(creature: Creature) -> DifficultyGrade: grade = DifficultyGrade.Standard if creature.stance == Stance.Crouching: grade = DifficultyGrade.Hard elif creature.stance == Stance.Prone: grade = DifficultyGrade.Formidable if creature.is_seriously_wounded(): grade = grade.get_step(+1) return grade
def get_range_change_desire(self, opponent: Creature, from_range: MeleeRange, to_range: MeleeRange) -> float: """-1.0 to 1.0 scale rating how favorable the given range change is""" range_scores = self.get_melee_range_priority(opponent) from_score = range_scores.get(from_range, 0) to_score = range_scores.get(to_range, 0) if from_score == 0: return 1.0 modifier = ChangeMeleeRangeAction.get_contest_modifier(self.parent) - ChangeMeleeRangeAction.get_contest_modifier(opponent) success_chance = Contest.get_opposed_chance(self.parent, SKILL_EVADE, opponent, SKILL_EVADE, modifier.contest) score = min(max(-1.0, (to_score/from_score - 1.0) * success_chance), 1.0) # account for possible attack of opportunity if score > 0 and from_range > to_range and can_interrupt_action(opponent): attack_ranges = list(MeleeRange.between(from_range, to_range)) attacks = (attack for attack in opponent.get_melee_attacks() if any(attack.can_attack(r) for r in attack_ranges)) attack = max(attacks, key=lambda a: opponent.get_skill_level(a.combat_test), default=None) safety = 1.0 - Contest.get_opposed_chance(opponent, attack.combat_test, self.parent, SKILL_EVADE) if attack is not None: return min(score, safety**2) return score
def get_melee_range_priority(self, opponent: Creature, *, caution: float = 1.0) -> Mapping[MeleeRange, float]: range_priority = {} melee = self.parent.get_melee_combat(opponent) for reach in MeleeRange.between(melee.get_min_separation(), self.parent.get_melee_engage_distance()): pro_attacks = (attack for attack in self.parent.get_melee_attacks() if attack.can_attack(reach)) attack_priority = get_melee_attack_priority(self.parent, opponent, pro_attacks) power = max(attack_priority.values(), default=0.0) ant_attacks = (attack for attack in opponent.get_melee_attacks() if attack.can_attack(reach)) threat_priority = get_melee_attack_priority(opponent, self.parent, ant_attacks) threat = max(threat_priority.values(), default=0.0) health = self.parent.health + self.parent.max_health danger = (2 * caution * threat + health) / health range_priority[reach] = power/danger return range_priority
def get_melee_attack_value(attack: MeleeAttack, attacker: Creature, target: Creature) -> float: expected_damage = get_expected_damage(attack, target) skill_factor = SKILL_FACTOR[attacker.get_skill_level(attack.combat_test)] return expected_damage * skill_factor
def get_expected_damage(attack: MeleeAttack, target: Creature) -> float: result = 0 for bp in target.get_bodyparts(): result += bp.exposure * bp.get_effective_damage(attack.damage.mean(), attack.armpen.mean()) return result
def __init__(self, protagonist: Creature, contest: Contest, modifier: ContestModifier = ContestModifier()): self.contest = contest self.protagonist = protagonist self.skill_level = protagonist.get_skill_level(contest) self.modifier = modifier self.reroll()
def __init__(self, a: Creature, b: Creature): self._pair = (a, b) combatants = [a, *a.get_riders(), b, *b.get_riders()] self._separation = max(c.get_melee_engage_distance() for c in combatants)
def get_attribute_modifier(self, protagonist: Creature) -> int: return sum(protagonist.get_attribute(attr) for attr in self.key_attr)
def get_success_chance(self, protagonist: Creature, target: int) -> float: skill_level = protagonist.get_skill_level(self) target -= self.get_attribute_modifier(protagonist) + self.get_skill_modifier(skill_level) roll_table = get_roll_table(skill_level.bonus_dice) return sum(p for roll, p in roll_table.items() if roll > target)
def print_melee_attacks(creature: Creature) -> None: for attack in creature.get_melee_attacks(): print(attack)
def get_attack_value(creature: Creature, attack: MeleeAttackTemplate) -> float: return attack.damage.mean() * SKILL_FACTOR[creature.get_skill_level(attack.combat_test)]
def get_random_hitloc(creature: Creature) -> Optional[BodyPart]: bodyparts = [(bp, bp.exposure) for bp in creature.get_bodyparts()] result = random.choices(*zip(*bodyparts)) if len(result) > 0: return result[0] return None
# (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from core.creature import Creature from core import barf import sys #for arg in sys.argv: # cgtype = arg barf.Barf("DEF", "-----------------------------------------------------", False) barf.Barf("DEF", "roguekit-uniquecreatures (v0.0.1) Copyright (C) 2014", False) barf.Barf("DEF", "-----------------------------------------------------", False) barf.Barf("DEF", "Tristy H. \"KittyTristy\" <*****@*****.**>", False) barf.Barf("DEF", "Sina Mashek \"CptMashek\" <*****@*****.**>", False) barf.Barf("DEF", "-----------------------------------------------------", False) creature = Creature() try: creature.generate(sys.argv[1:][0]) except IndexError: creature.generate('humanoid') barf.Barf("DEF", "------------------------------------------------------", False)
def get_block_difficulty(creature: Creature) -> DifficultyGrade: grade = DifficultyGrade.Standard if creature.is_seriously_wounded(): grade = grade.get_step(+1) return grade