async def dice_roll_save(self, gctx, roll_request): """Save: Display like ``!s``.""" save_name = roll_request.action if save_name in constants.STAT_ABBREVIATIONS: save_name = verbose_stat(save_name) await self._dice_roll_embed_common(gctx, roll_request, "{name} makes {save} Save!", save=a_or_an(save_name.title()))
async def dice_roll_check(self, gctx, roll_request): """Check: Display like ``!c``. Requires character - if not imported falls back to default roll.""" check_name = roll_request.action if check_name in constants.STAT_ABBREVIATIONS: check_name = verbose_stat(check_name) await self._dice_roll_embed_common(gctx, roll_request, "{name} makes {check} check!", check=a_or_an(check_name.title()))
def run_save(save_key, caster, args, embed): """ Runs a caster's saving throw, building on an existing embed and handling most arguments. Also handles save bonuses from ieffects if caster is a combatant. :type save_key: str :type caster: cogs5e.models.sheet.statblock.StatBlock :type args: utils.argparser.ParsedArguments :type embed: discord.Embed :return: The total of each save. :rtype: SaveResult """ if save_key.startswith('death'): save = Skill(0) stat_name = stat = 'Death' save_name = 'Death Save' else: try: save = caster.saves.get(save_key) stat = save_key[:3] stat_name = verbose_stat(stat).title() save_name = f"{stat_name} Save" except ValueError: raise InvalidArgument('That\'s not a valid save.') # -title if args.last('title'): embed.title = args.last('title', '') \ .replace('[name]', caster.get_title_name()) \ .replace('[sname]', save_name) elif args.last('h'): embed.title = f"An unknown creature makes {a_or_an(save_name)}!" else: embed.title = f'{caster.get_title_name()} makes {a_or_an(save_name)}!' # ieffect handling if isinstance(caster, init.Combatant): # -sb args['b'] = args.get('b') + caster.active_effects('sb') # -sadv/sdis sadv_effects = caster.active_effects('sadv') sdis_effects = caster.active_effects('sdis') if 'all' in sadv_effects or stat in sadv_effects: args[ 'adv'] = True # Because adv() only checks last() just forcibly add them if 'all' in sdis_effects or stat in sdis_effects: args['dis'] = True result = _run_common(save, args, embed, rr_format="Save {}") return SaveResult(rolls=result.rolls, skill=save, skill_name=stat_name, skill_roll_result=result)
def run_save(save_key, caster, args, embed): """ Runs a caster's saving throw, building on an existing embed and handling most arguments. Also handles save bonuses from ieffects if caster is a combatant. :type save_key: str :type caster: cogs5e.models.sheet.statblock.StatBlock :type args: utils.argparser.ParsedArguments :type embed: discord.Embed :return: The total of each save. :rtype: SaveResult """ try: save = caster.saves.get(save_key) stat_name = verbose_stat(save_key[:3]).title() save_name = f"{stat_name} Save" except ValueError: raise InvalidArgument('That\'s not a valid save.') # -title if args.last('title'): embed.title = args.last('title', '') \ .replace('[name]', caster.get_title_name()) \ .replace('[sname]', save_name) elif args.last('h'): embed.title = f"An unknown creature makes {a_or_an(save_name)}!" else: embed.title = f'{caster.get_title_name()} makes {a_or_an(save_name)}!' # ieffect -sb if isinstance(caster, init.Combatant): args['b'] = args.get('b') + caster.active_effects('sb') result = _run_common(save, args, embed, rr_format="Save {}") return SaveResult(rolls=result.rolls, skill=save, skill_name=stat_name, skill_roll_result=result)
def parse_stat_str(stat_list): if 'all' in stat_list: return 'All' return ', '.join(verbose_stat(s) for s in stat_list)
def run(self, autoctx): super().run(autoctx) if autoctx.target is None: raise TargetException("Tried to make a save without a target! Make sure all Save effects are inside " "of a Target effect.") # ==== args ==== save = autoctx.args.last('save') or self.stat sb = autoctx.args.get('sb', ephem=True) auto_pass = autoctx.args.last('pass', type_=bool, ephem=True) auto_fail = autoctx.args.last('fail', type_=bool, ephem=True) hide = autoctx.args.last('h', type_=bool) # ==== dc ==== dc_override = None if self.dc: try: dc_override = autoctx.parse_intexpression(self.dc) except Exception: raise AutomationException(f"{self.dc!r} cannot be interpreted as a DC.") # dc hierarchy: arg > self.dc > spell cast override > spellbook dc dc = dc_override or autoctx.dc_override or autoctx.caster.spellbook.dc if 'dc' in autoctx.args: dc = maybe_mod(autoctx.args.last('dc'), dc) if dc is None: raise NoSpellDC("No spell save DC found. Use the `-dc` argument to specify one!") try: save_skill = next(s for s in ('strengthSave', 'dexteritySave', 'constitutionSave', 'intelligenceSave', 'wisdomSave', 'charismaSave') if save.lower() in s.lower()) stat = save_skill[:3] except StopIteration: raise InvalidSaveType() # ==== ieffects ==== if autoctx.target.combatant: # Combine args/ieffect advantages - adv/dis (#1552) sadv_effects = autoctx.target.combatant.active_effects('sadv') sdis_effects = autoctx.target.combatant.active_effects('sdis') sadv = 'all' in sadv_effects or stat in sadv_effects sdis = 'all' in sdis_effects or stat in sdis_effects adv = reconcile_adv( adv=autoctx.args.last('sadv', type_=bool, ephem=True) or sadv, dis=autoctx.args.last('sdis', type_=bool, ephem=True) or sdis ) else: adv = autoctx.args.adv(custom={'adv': 'sadv', 'dis': 'sdis'}) # ==== execution ==== save_roll = None autoctx.metavars['lastSaveRollTotal'] = 0 autoctx.metavars['lastSaveNaturalRoll'] = 0 # 1495 autoctx.metavars['lastSaveDC'] = dc autoctx.metavars['lastSaveAbility'] = verbose_stat(stat) autoctx.meta_queue(f"**DC**: {dc}") if not autoctx.target.is_simple: save_blurb = f'{stat.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: save_dice = autoctx.target.get_save_dice(save_skill, adv=adv, sb=sb) save_roll = d20.roll(save_dice) is_success = save_roll.total >= dc # get natural roll d20_value = d20.utils.leftmost(save_roll.expr).total autoctx.metavars['lastSaveRollTotal'] = save_roll.total # 1362 autoctx.metavars['lastSaveNaturalRoll'] = d20_value # 1495 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(f'{stat.upper()} Save') is_success = False # Disable critical damage state for children (#1556) original = autoctx.in_save autoctx.in_save = True if is_success: children = self.on_success(autoctx) else: children = self.on_fail(autoctx) autoctx.in_save = original # Restore proper crit state (#1556) return SaveResult(dc=dc, ability=save_skill, save_roll=save_roll, adv=adv, did_save=is_success, children=children)