예제 #1
0
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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
    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()
예제 #7
0
def test_player_max_hp_is_at_least_1():
    player = Character(level=1, hd=Dice("1d1"), constitution=2)
    assert player.max_hp == 1
예제 #8
0
def test_creature_max_hp_is_at_least_1():
    creature = Creature(level=1, hd=Dice("1d1"), constitution=2)
    assert creature.max_hp == 1
예제 #9
0
""" 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 = {
예제 #10
0
    '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
예제 #11
0
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]
예제 #12
0
 def __init__(self, dice, type_=None, **kwargs):
     super().__init__(**kwargs)
     self.damage_type = type_
     self.damage_dice = Dice(dice)
예제 #13
0
def test_sub_int():
    dice = Dice("1d20") - 1
    assert dice == Dice("1d20") + Modifier(-1)
예제 #14
0
def test_add_int():
    dice = Dice("1d20") + 1
    assert dice == Dice("1d20") + Modifier(1)
예제 #15
0
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
예제 #16
0
    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
예제 #17
0
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