Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
# add X critdice to the leftmost node if it's dice
left = d20.utils.leftmost(dice_ast)
if isinstance(left, d20.ast.Dice):
left.num += int(critdice)
# -c #
if c_arg and in_crit:
c_ast = d20.parse(c_arg)
dice_ast.roll = d20.ast.BinOp(dice_ast.roll, '+', c_ast.roll)
# max
if max_arg:
dice_ast = d20.utils.tree_map(_max_mapper, dice_ast)
# evaluate damage
dmgroll = roll(dice_ast)
# magic arg (#853)
always = {'magical'} if (autoctx.is_spell or magic_arg) else None
# dtype transforms/overrides (#876)
transforms = {}
for dtype in dtype_args:
if '>' in dtype:
*froms, to = dtype.split('>')
for frm in froms:
transforms[frm.strip()] = to.strip()
else:
transforms[None] = dtype
# display damage transforms (#1103)
if None in transforms:
autoctx.meta_queue(f"**Damage Type**: {transforms[None]}")
elif transforms:
if in_crit:
dice_ast = d20.utils.tree_map(_crit_mapper, dice_ast)
if critdice and not autoctx.is_spell:
# add X critdice to the leftmost node if it's dice
left = d20.utils.leftmost(dice_ast)
if isinstance(left, d20.ast.Dice):
left.num += int(critdice)
# -c #
if c_arg and in_crit:
c_ast = d20.parse(c_arg)
dice_ast.roll = d20.ast.BinOp(dice_ast.roll, '+', c_ast.roll)
# max
if max_arg:
dice_ast = d20.utils.tree_map(_max_mapper, dice_ast)
# evaluate damage
dmgroll = roll(dice_ast)
# magic arg (#853)
always = {'magical'} if (autoctx.is_spell or magic_arg) else None
# dtype transforms/overrides (#876)
transforms = {}
for dtype in dtype_args:
if '>' in dtype:
*froms, to = dtype.split('>')
for frm in froms:
transforms[frm.strip()] = to.strip()
else:
transforms[None] = dtype
# display damage transforms (#1103)
Note that any neutrals in the resistances will be explicitly ignored.
:type damage_expr: d20.Expression
:type resistances: Resistances
:param always: If passed, damage is always this type in addition to whatever it's annotated as.
:type always: set[str]
:param transforms: a dict representing damage type transforms. If None is a key, all damage types become that.
:type transforms: dict[str or None, str]
"""
if always is None:
always = set()
if transforms is None:
transforms = {}
# simplify damage types
d20.utils.simplify_expr_annotations(damage_expr.roll, ambig_inherit='left')
# depth first visit expression nodes: if it has an annotation, add the appropriate binops and move to the next
def do_visit(node):
if node.annotation:
# handle transforms
if None in transforms:
tokens = _resist_tokenize(transforms[None])
else:
tokens = []
for t in _resist_tokenize(node.annotation.lower()):
tokens.extend(_resist_tokenize(transforms.get(t, t)))
original_annotation = node.annotation
dtype = f"{' '.join(always)} {' '.join(tokens)}".strip()
node.annotation = f"[{dtype}]"
# handle tree modification
# handle tree modification
ann = set(tokens) | always
if any(n.applies_to(ann) for n in resistances.neutral): # neutral overrides all
return node
if any(v.applies_to(ann) for v in resistances.vuln):
node = d20.BinOp(d20.Parenthetical(node), '*', d20.Literal(2))
if original_annotation.startswith('[^') or original_annotation.endswith('^]'):
node.annotation = original_annotation
# break here - don't handle resist/immune
return node
if any(r.applies_to(ann) for r in resistances.resist):
node = d20.BinOp(d20.Parenthetical(node), '/', d20.Literal(2))
if any(im.applies_to(ann) for im in resistances.immune):
node = d20.BinOp(d20.Parenthetical(node), '*', d20.Literal(0))
return node
for i, child in enumerate(node.children):
replacement = do_visit(child)
if replacement and replacement is not child:
node.set_child(i, replacement)
return None
if any(n.applies_to(ann) for n in resistances.neutral): # neutral overrides all
return node
if any(v.applies_to(ann) for v in resistances.vuln):
node = d20.BinOp(d20.Parenthetical(node), '*', d20.Literal(2))
if original_annotation.startswith('[^') or original_annotation.endswith('^]'):
node.annotation = original_annotation
# break here - don't handle resist/immune
return node
if any(r.applies_to(ann) for r in resistances.resist):
node = d20.BinOp(d20.Parenthetical(node), '/', d20.Literal(2))
if any(im.applies_to(ann) for im in resistances.immune):
node = d20.BinOp(d20.Parenthetical(node), '*', d20.Literal(0))
return node
for i, child in enumerate(node.children):
replacement = do_visit(child)
if replacement and replacement is not child:
node.set_child(i, replacement)
return None
else:
tokens = []
for t in _resist_tokenize(node.annotation.lower()):
tokens.extend(_resist_tokenize(transforms.get(t, t)))
original_annotation = node.annotation
dtype = f"{' '.join(always)} {' '.join(tokens)}".strip()
node.annotation = f"[{dtype}]"
# handle tree modification
ann = set(tokens) | always
if any(n.applies_to(ann) for n in resistances.neutral): # neutral overrides all
return node
if any(v.applies_to(ann) for v in resistances.vuln):
node = d20.BinOp(d20.Parenthetical(node), '*', d20.Literal(2))
if original_annotation.startswith('[^') or original_annotation.endswith('^]'):
node.annotation = original_annotation
# break here - don't handle resist/immune
return node
if any(r.applies_to(ann) for r in resistances.resist):
node = d20.BinOp(d20.Parenthetical(node), '/', d20.Literal(2))
if any(im.applies_to(ann) for im in resistances.immune):
node = d20.BinOp(d20.Parenthetical(node), '*', d20.Literal(0))
return node
for i, child in enumerate(node.children):
replacement = do_visit(child)
mi/ma (min/max result)
e (explode dice of value)
ra (reroll and add)
__Supported Selectors__
X (literal X)
lX (lowest X)
hX (highest X)
>X (greater than X)
1999:
out = f"{ctx.author.mention} :game_die:\n" \
f"{str(res)[:100]}...\n" \
f"**Total:** {res.total}"
await try_delete(ctx.message)
await ctx.send(out)
await Stats.increase_stat(ctx, "dice_rolled_life")
:param str ability: The type of save ("str", "dexterity", etc).
:param bool adv: Whether to roll the save with advantage. Rolls with advantage if ``True``, disadvantage if ``False``, or normally if ``None``.
:returns: A SimpleRollResult describing the rolled save.
:rtype: :class:`~cogs5e.funcs.scripting.functions.SimpleRollResult`
"""
try:
save = self._combatant.saves.get(ability)
except ValueError:
raise InvalidSaveType
sb = self._combatant.active_effects('sb')
saveroll = save.d20(base_adv=adv)
if sb:
saveroll = f'{saveroll}+{"+".join(sb)}'
save_roll = roll(saveroll)
return SimpleRollResult(save_roll)
save.lower() in s.lower())
except StopIteration:
raise InvalidSaveType()
autoctx.meta_queue(f"**DC**: {dc}")
if not autoctx.target.is_simple:
save_blurb = f'{save_skill[:3].upper()} Save'
if auto_pass:
is_success = True
autoctx.queue(f"**{save_blurb}:** Automatic success!")
elif auto_fail:
is_success = False
autoctx.queue(f"**{save_blurb}:** Automatic failure!")
else:
saveroll = autoctx.target.get_save_dice(save_skill, adv=autoctx.args.adv(boolwise=True))
save_roll = roll(saveroll)
is_success = save_roll.total >= dc
success_str = ("; Success!" if is_success else "; Failure!")
out = f"**{save_blurb}**: {save_roll.result}{success_str}"
if not hide:
autoctx.queue(out)
else:
autoctx.add_pm(str(autoctx.ctx.author.id), out)
autoctx.queue(f"**{save_blurb}**: 1d20...{success_str}")
else:
autoctx.meta_queue('{} Save'.format(save_skill[:3].upper()))
is_success = False
if is_success:
damage = self.on_success(autoctx)
else:
damage = self.on_fail(autoctx)
return await ctx.send("You must pass in a positive, nonzero HP with the -hp tag.")
if args.last('ac'):
ac = args.last('ac', type_=int)
for k in ('resist', 'immune', 'vuln'):
resists[k] = args.get(k)
combat = await Combat.from_ctx(ctx)
if combat.get_combatant(name) is not None:
await ctx.send("Combatant already exists.")
return
if not place:
init_skill = Skill(modifier, adv=adv)
init_roll = roll(init_skill.d20())
init = init_roll.total
init_roll_skeleton = init_roll.result
else:
init_skill = Skill(0, adv=adv)
init = place
init_roll_skeleton = str(init)
me = Combatant.new(name, controller, init, init_skill, hp, ac, private, Resistances.from_dict(resists), ctx,
combat)
# -thp (#1142)
if thp and thp > 0:
me.temp_hp = thp
if group is None:
combat.add_combatant(me)