def test_damage_mod_overrides_strength(randint, monster): randint.return_value = 5 weapon = Weapon( "test", Dice("1d1"), None, damage_mod=5, owner=monster ) roll, _ = weapon.damage_roll() assert roll == 10
def activate(self, caster, level, **kwargs): # Get piped healing or roll the healing healing = super().activate(caster, level, **kwargs) if not healing: healing = sum((Dice("1d8") * level).roll()) + caster.spellcasting for target in self.get_targets(caster=caster, **kwargs): actual_healing = target.heal(healing) EventLog.log(f"\thealing {target} by {actual_healing}") return actual_healing
def activate(self, caster, level, **kwargs): # Get piped damage or roll the damage damage = super().activate(caster, level, **kwargs) if not damage: damage = sum((Dice("1d8") * level).roll()) + caster.spellcasting for target in self.get_targets(caster=caster, **kwargs): actual_damage = target.take_damage(damage, self.damage_type) EventLog.log(f"\tdamaging {target} by {actual_damage}") return actual_damage
def saving_throw(self, attribute, dc): """ Rolls a saving throw for an attribute. Returns: bool: True if saved, False otherwise. """ saving_throw = (Dice("1d20") + self.attributes[attribute]).roll()[0] if saving_throw >= dc: EventLog.log(f"\t{self} saved against {attribute} with a {saving_throw}") return True else: return False
def attack_roll(self, advantage=False, disadvantage=False): if advantage and disadvantage: advantage = False disadvantage = False # Determine modification if self.attack_mod: dice_mod = self.attack_mod else: dice_mod = self._get_ability() if self.owner.is_proficient(self): dice_mod += self.owner.proficiency # Roll the d20 dice = Dice("1d20") if advantage: roll = max((dice * 2).roll()) elif disadvantage: roll = min((dice * 2).roll()) else: roll = dice.roll()[0] return roll + dice_mod, roll == 20
def roll(self): """ Checks stack for a function with roll value attached. This method will traverse up the current frame stack for a code object that has had a roll value attached to it with the set_roll method. If a value has been attached, that value will be returned. Otherwise, we return a randomized value using the default Dice class. """ print("ROLL:", self._dice) for f in inspect.stack(): if 'self' in f.frame.f_locals: obj_id = id(f.frame.f_locals['self']) # Case: Method if ( obj_id in MockDice.roll_vals and f.function in MockDice.roll_vals[obj_id] and self._dice in MockDice.roll_vals[obj_id][f.function] ): print(f"MOCK DICE::{self._dice}::self:: {MockDice.roll_vals[obj_id][f.function][self._dice]}") return MockDice.roll_vals[obj_id][f.function][self._dice] # Case: Class klass = f.frame.f_locals['self'].__class__.__name__ if ( klass in MockDice.roll_vals and self._dice in MockDice.roll_vals[klass] ): print(f"MOCK DICE::{self._dice}::class:: {MockDice.roll_vals[klass][self._dice]}") return MockDice.roll_vals[klass][self._dice] # Case: Default filename = f.frame.f_code.co_filename lineno = f.frame.f_code.co_firstlineno if type(self._dice) not in [str, int]: print("!!!-------------", self._dice) if ( filename in MockDice.roll_vals and lineno in MockDice.roll_vals[filename] and self._dice in MockDice.roll_vals[filename][lineno] ): print(f"MOCK DICE::{self._dice}::def:: {MockDice.roll_vals[filename][lineno][self._dice]}") return MockDice.roll_vals[filename][lineno][self._dice] # Case: Not found print(f"MOCK DICE::{self._dice}::not_found") return Dice(self._dice).roll()
def test_player_max_hp_is_at_least_1(): player = Character(level=1, hd=Dice("1d1"), constitution=2) assert player.max_hp == 1
def test_creature_max_hp_is_at_least_1(): creature = Creature(level=1, hd=Dice("1d1"), constitution=2) assert creature.max_hp == 1
""" My own custom monsters for testing encounters. """ from combatsim.dice import Dice from combatsim.tactics import Healer, Mage from combatsim.spells import acid_splash, cure_wounds, drain from combatsim.items import Weapon simple_cleric = { 'name': "Cleric", 'level': 5, 'weapons': [Weapon("Staff", Dice("1d6"), "bludgeoning", attack_mod=4, damage_mod=2)], 'spell_slots': [3], 'spells': [cure_wounds], 'tactics': Healer } commoner = {'name': "Commoner"} knight = { 'name': "Knight", 'ac': 14, 'level': 5, 'strength': 14, 'weapons': [Weapon("Longsword", Dice("1d8"), "slashing")] } mage = {
'level': 2, 'strength': 11, 'dexterity': 12, 'constitution': 12, 'intelligence': 10, 'wisdom': 10, 'charisma': 10, 'hd': Dice("1d8"), 'armor': Armor("Leather", 11), 'weapons': [ Weapon("Scimitar", Dice("1d6"), "slashing", attack_mod=3, damage_mod=1), Weapon("Light Crossbow", Dice("1d8"), "piercing", melee=False, attack_mod=3, damage_mod=1) ] } # TODO (phillip): Implement pack tactics
def test_add_dynamic_modifiers_to_dice(): modifier = Modifier(1) dice = Dice("d1") + modifier assert dice.roll() == [2] modifier.mod = 2 assert dice.roll() == [3]
def __init__(self, dice, type_=None, **kwargs): super().__init__(**kwargs) self.damage_type = type_ self.damage_dice = Dice(dice)
def test_sub_int(): dice = Dice("1d20") - 1 assert dice == Dice("1d20") + Modifier(-1)
def test_add_int(): dice = Dice("1d20") + 1 assert dice == Dice("1d20") + Modifier(1)
def test_initiative_order(): medium = Monster(name="med", initiative=Dice("d1") + Modifier(3)) fast = Monster(name="fast", initiative=Dice("d1") + Modifier(5)) slow = Monster(name="slow", initiative=Dice("d1")) encounter = Encounter([medium, fast, slow]) assert encounter.roll_initiative()[0][1].name == fast.name
def __init__(self, **kwargs): self.name = kwargs.get('name', "nameless") self.xp = kwargs.get('xp', None) self.level = kwargs.get('level', 1) self.proficiency = kwargs.get( 'proficiency', 1 + math.ceil(self.level / 4) ) self.strength = Ability("Strength", kwargs.get('strength', 10)) self.dexterity = Ability("Dexterity", kwargs.get('dexterity', 10)) self.constitution = Ability( "Constitution", kwargs.get('constitution', 10) ) self.intelligence = Ability( "Intelligence", kwargs.get('intelligence', 10) ) self.wisdom = Ability("Wisdom", kwargs.get('wisdom', 10)) self.charisma = Ability("Charisma", kwargs.get('charisma', 10)) self.attributes = { 'strength': self.strength, 'dexterity': self.dexterity, 'constitution': self.constitution, 'intelligence': self.intelligence, 'wisdom': self.wisdom, 'charisma': self.charisma } self.hd = kwargs.get('hd', Dice('1d8')) # Must come after hd and abilities so _calc_hp works properly self.max_hp = kwargs.get('max_hp', self._calc_hp()) self.hp = kwargs.get('hp', self.max_hp) self.armor = None armor = kwargs.get('armor', None) if 'ac' in kwargs: armor = Armor("Default", kwargs['ac'], 0) self.equip(armor) self.initiative = kwargs.get( 'initiative', Dice("d20") + self.dexterity ) # TODO (phillip): This part looks very similar to the armor part above. # Is there a way we can make this more elegant for both? self.weapons = [] weapons = kwargs.get('weapons', []) for weapon in weapons: self.equip(weapon) if not self.weapons: self.equip(Weapon("Fists", Dice("1d4"), "bludgeoning")) self.tactics = kwargs.get('tactics', TargetWeakest)(self) self.team = kwargs.get('team', None) self.resistances = kwargs.get('resistances', []) self.vulnerabilities = kwargs.get('vulnerabilities', []) self.spellcasting = self.attributes[ kwargs.get('spellcasting', 'wisdom') ] self.spells = kwargs.get('spells', []) self.spell_slots = kwargs.get('spell_slots', []) # Set up grid if it is necessary self.grid = kwargs.get('grid', None) self.x, self.y = kwargs.get('pos', (None,None)) if self.x is not None and self.y is not None: self.grid[self.x, self.y] = self
import pytest import unittest from combatsim.dice import Dice, Modifier @pytest.mark.parametrize("dice,result", [(Dice("d1"), [1]), (Dice(["d1", "d1"]), [1, 1]), (Dice(["5d1"]), [5]), (Dice("d1") + Modifier(1), [2]), (Dice(["2d1", "d1"]) + Modifier(1), [3, 2]), (Dice(["1d1"]) + Modifier(1) + Modifier(2), [4]), (Dice([]), [])]) def test_roll_d1s(dice, result): assert dice.roll() == result @pytest.mark.parametrize("dice,average", [(Dice("d6"), 3.5), (Dice("d6") + Modifier(1), 4.5), (Dice(["2d6", "2d8"]), 16), (Dice(["1d6", "1d8"]) + Modifier(1), 10)]) def test_dice_averages(dice, average): assert dice.average == average @pytest.mark.parametrize("dice,expected", [(Dice("1d20"), 20), (Dice(["1d20", "2d20"]), 60), (Dice(["1d20", "1d10"]) + Modifier(5), 40)]) def test_dice_maxes(dice, expected): assert dice.max == expected