def get_value(self) -> Union[int, float]: value = 0 for mod in self.mods: if isinstance(mod, int): value += mod elif isinstance(mod, str): if '$' in mod: # this contains character variables mod = self.character.parse_vars(mod) value += basic(mod) elif isinstance(mod, list): if len(mod) == 0: # Add zero to the value continue largest = None for candidate in mod: if isinstance(candidate, str): if '$' in mod: # this contains character variables mod = self.character.parse_vars(mod) candidate = basic(candidate) # Take largest seen so far if largest is None: largest = candidate elif candidate > largest: largest = candidate value += largest return value
def startup_end(self): name = self.levelgain['Character Name?'] cl = self.levelgain['Class to gain a level in?'] path = 'character/' + h.sanitize_filename(name) + '.character' if os.path.exists(iface.JsonInterface.OBJECTSPATH / path): self.record = iface.JsonInterface(path) else: gui.ErrorMessage('A character with that name was not found.') clpath = 'class/' + h.sanitize_filename(cl) + '.class' if not os.path.exists(iface.JsonInterface.OBJECTSPATH / path): gui.ErrorMessage('A class with that name was not found.') self.character = c.Character(self.record) pattern = r'\s*([a-zA-Z\']+)\s*(\(([a-zA-Z\'\s]+)\))?' desc_ = re.match(pattern, cl).groups() desc = [str(item) for item in desc_ if item is not None] # desc should be a class and possibly a subclass name (rec, level) = self.character.classes.level_up(*desc) self.core = FeaturesAtLevel(self.f, rec.record, level) # Set new number of hit dice size = rec.hit_dice hdpath = '/HP/HD/' + size + '/maxnumber' hdn = self.character.get(hdpath) self.character.set(hdpath, hdn + 1) # Set new number of hit points conmod = h.modifier(self.character.get('/abilities/Constitution')) if self.levelgain['Average or roll for HP?'] == 'average': gain = d.basic(size, d.Mode.AVERAGE) + .5 elif self.levelgain['Average or roll for HP?'] == 'roll': gain = d.basic(size) current = self.character.get('/HP/max') self.character.set('/HP/max', current + gain + conmod) self.draw_static() self.container.deiconify()
def __init__(self, data): self.name = data['name'] self.HP = int( d.basic(data['HP'], mode=d.Mode.from_string( 'average' if data.get('average') else 'normal'))) self.maxHP = self.HP self.AC = data['AC'] self.abilities = data['abilities'] self.initiative = d.basic(h.d20_roll(), modifiers=h.modifier( data['abilities']['Dexterity'])) self.saves = data.get('saves', {})
def test_basic(self): self.assertEqual(basic('3d4'), 12) self.assertEqual(basic('3d4', mode=Mode.AVERAGE), 7.5) self.assertEqual(basic('3d6', mode=Mode.MAX), 18) self.assertEqual(basic('3d4', mode=Mode.CRIT), 24) self.assertEqual(basic('3d4', modifiers=4), 16) self.assertEqual(basic(2), 2) self.assertEqual(basic(2, modifiers=4), 6) with self.assertRaises(InputTypeError): basic([40, 2])
def change(self, amount): """Change the HP :param amount: A rollable amount :return: The actual change in HP """ delta = basic(amount) if delta == 0: return 0 if delta < 0: if abs(delta) > self.temp: # overflow beyond temp delta += self.temp self.temp = 0 self.current += delta return delta else: # Temp absorbs it all self.temp += delta return 0 else: # healing if self.current + delta > self.max: delta = self.max - self.current self.current += delta return delta
def add_temp(self, amount): """Add some temp HP :param amount: A rollable amount :return: The actual change in HP (always 0 because temp HP is not real) """ delta = basic(amount) if delta > self.temp: self.temp = delta return 0
def use(self, number): if self.number < number: raise LowOnResource(self) self.number -= number if isinstance(self.value, str): return basic('+'.join([self.value] * number)) elif isinstance(self.value, int): return number * self.value else: return 0
def death_save(self): if self.hp.current > 0: return '' roll = d.basic(h.d20_roll(luck=self.bonuses.get('lucky'))) if roll == 20: self.hp.current = 1 self.deathSaveFailures = 0 if roll < 10: self.deathSaveFailures += 1 if roll == 1: # add a second failure self.deathSaveFailures += 1 if self.deathSaveFailures >= 3: raise ex.CharacterDead()
async def massroll(ctx, amt: int, atk: str, dmg: str = '0', ac: int = 0, short: str = ''): text = f'''```Massroll: {amt} rolls against AC {ac}''' textEnd = '' #emb = discord.Embed(title=f'Mass roll: {amt} rolls against AC {ac}') sumNum = 0 sumCrits = 0 try: dndice.basic(atk) except: ctx.send(f'{atk} is not valid roll syntax') try: dndice.basic(dmg) except: ctx.send(f'{dmg} is not valid roll syntax') for x in range(amt): atkRoll = dndice.basic(atk) dmgRoll = dndice.basic(dmg) if atkRoll == dice.roll_max(atk): critical = ', Critical!' sumCrits += 1 dmgRoll = dndice.basic(dmg.replace('d', 'dc')) else: critical = '' if dmg != '0': textEnd += f"Attack n°{x}: {atkRoll}, damage: {dmgRoll}{critical}\n" #emb.add_field(name=f'Attack {x}', value=f'{critical}{atkRoll}, damage: {dmgRoll}') else: textEnd += f"Attack {x}: {critical}{atkRoll}\n" #emb.add_field(name=f'Attack {x}', value=f'{critical}{atkRoll}') if (atkRoll >= ac or critical == 'Critical Attack!: '): sumNum += dmgRoll text += f'''\n Sum of the Damage: {sumNum} damage''' text += f"\n Amount of critical attacks: {sumCrits}\n\n" if short == "short": pass elif (len(text) + len(textEnd) <= 1997): text += textEnd else: text += "Text is too long, detail doesnt fit." #emb.add_field(name=f'Sum of the Damage', value=f'{sumNum} damage') text += "```" await ctx.send(text)
def attack(self, which): advantage = self.adv.get() disadvantage = self.dis.get() attack_bonus = self.character.parse_vars(self.atkbonus.get(), False) damage_bonus = self.character.parse_vars(self.dambonus.get(), False) if which is None: attack = h.d20_roll(advantage, disadvantage, self.character.bonuses.get('lucky', False)) attack += d.compile(attack_bonus) attackRoll = attack.evaluate() if attack.is_critical(): attackRoll = 'Critical Hit!' damageRoll = d.basic(damage_bonus, mode=d.Mode.CRIT) elif attack.is_fail(): attackRoll = 'Critical Miss.' damageRoll = 0 else: # mode = 'execute' attackRoll = attack.verbose_result() damageRoll = d.basic(damage_bonus) result = ('Attack Roll: ' + str(attackRoll), 'Damage Roll: ' + str(damageRoll), '') # result = ('Attack Roll: ' + str(atk), 'Damage Roll: ' + str(dam), '') else: result = which.attack(self.character, advantage, disadvantage, attack_bonus, damage_bonus) self.output.update(*result)
def __init__(self, jf: DataInterface, character: 'char.Character', definition: DataInterface = None): super().__init__(jf, definition) self.owner = character val = character.parse_vars(self.value) if isinstance(val, str): # Evaluate parenthetical expressions for ease & clean expression later pattern = r'\(.*\)' rep = lambda m: str(basic(m.group(0))) new = re.sub(pattern, rep, val) self.value = new else: self.value = val
def __init__(self, data: dict): super().__init__(data['name']) self.record = iface.DataInterface(data) self.abilities = abil.Abilities(self.record.cd('/abilities')) self.initiative += self.abilities[abil.AbilityName.DEX].modifier self.hpRoll = data['HP'] mode = Mode.from_string('average' if data.get('average') else 'normal') maxHP = int(basic(self.hpRoll, mode=mode)) self.HP = hp.HP( iface.DataInterface({ "current": maxHP, "max": maxHP, "temp": 0, })) self.AC = data['AC']
def alter_HP(self, amount): self.HP += d.basic(amount) self.HP = 0 if self.HP < 0 else self.maxHP if self.HP > self.maxHP else self.HP
def roll(self, advantage=False, disadvantage=False, lucky=False): d20 = d20_roll(advantage, disadvantage, lucky) return basic(d20, modifiers=self.ability.modifier)
def __init__(self, field, value, character: 'char.Character'): super().__init__(field, value) self.owner = character self.value = basic(self.owner.parse_vars(self.value))
def rollToHit(self, adv=False, dis=False, luck=False): rollstr = h.d20_string(adv, dis, luck) + ''.join( self.jf.get(self.path + '/roll_suffix')) mod = self.modifiers.get_value() return basic(rollstr, modifiers=mod)
def __init__(self, data): self.name = data['name'] self.initiative = d.basic(data['init'])
def roll_initiative(custom=None) -> int: if custom: return basic(custom) else: return basic(h.d20_roll())
"(1d4-1)|(1d3-2>0)", "1dc8+1dc4+3", "1dm6+1d6", "2d4c2", "2da6", "3da6", "2d10%2", "1d4=4|1d4=3", "1d8>=6", "10d8r>4", "10d8R>4", "10d[3,3,3,5]", "10d[3, 3, 3, 5]", "15d6t5", "15d6T1", ] for expr in testCases: tree = EvalTree(expr) print('EVALUATING ' + expr) print('EVALUATING USING TREE DIRECTLY') print(tree.evaluate()) print('EVALUATING USING ROLL FUNCTION') print(basic(expr)) print('EVALUATING USING ROLL FUNCTION IN VERBOSE MODE') print(verbose(expr)) print('EVALUATING USING ROLL FUNCTION AND MODIFIER') print(basic(expr, modifiers=3)) print('EVALUATING USING ROLL FUNCTION IN VERBOSE MODE AND MODIFIER') print(verbose(expr, modifiers=3)) print()