Files
roll20-character-sheets/rapidfire_system/system.html
T

1829 lines
97 KiB
HTML

<input name="attr_template_start" value="&{template:rapidfire} {{footer=@{character_name}}} {{character_id=@{character_id}}} {{system=@{system}}}" type="hidden" title="@{template_start}"/>
<input name="attr_collapsed" value="" type="hidden" title="@{collapsed}"/>
<input name="attr_system" value="remnants" type="hidden" title="@{system}"/>
<main class="subsystem-style system-remnant" id="main">
<input class="sheet-state" name="attr_sheet_state" value="settings" type="hidden" title="@{sheet_state}"/>
<input name="attr_system_header" type="hidden" title="@{system_header}"/><img class="system-header" name="attr_system_header" role="heading" aria-level="1" data-i18n="system header" data-i18n-alt="system header" title="@{system_header}"/>
<nav class="subsystem-style system-remnant" id="main-nav">
<button class="nav__button nav__button--tab nav__button--tab--left character" name="act_nav-character" data-i18n="character" role="heading" aria-level="4" type="action" title="%{nav-character}">
</button>
<button class="active nav__button nav__button--settings settings" name="act_nav-settings" type="action" title="%{nav-settings}">y
</button>
<button class="nav__button nav__button--tab nav__button--tab--right battle-remnant" name="act_nav-battle-remnant" data-i18n="battle remnant" role="heading" aria-level="4" type="action" title="%{nav-battle-remnant}">
</button>
</nav>
<article class="subsystem-style system-remnant settings active" id="settings">
<section class="section" id="system-behavior">
<label class="input-label"><span class="input-label__text" data-i18n="setting" role="heading" aria-level="4"></span>
<select class="underlined input-label__input" name="attr_system" title="@{system}">
<option value="remnants" data-i18n="remnants" selected="">
</option>
</select>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="difficulty automation" role="heading" aria-level="4"></span>
<input class="box input-label__input" name="attr_difficulty" value="1" checked="" type="checkbox" title="@{difficulty}"/>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="action penalty automation" role="heading" aria-level="4"></span>
<select class="underlined input-label__input" name="attr_action_penalty_automation" title="@{action_penalty_automation}">
<option value="disabled" data-i18n="disabled">
</option>
<option value="ask" data-i18n="ask">
</option>
<option value="use input" data-i18n="use input">
</option>
</select>
</label>
</section>
<section class="section" id="sheet-behavior">
<label class="input-label"><span class="input-label__text" data-i18n="whisper rolls to gm" role="heading" aria-level="4"></span>
<select class="underlined input-label__input" name="attr_whisper" title="@{whisper}">
<option value=" " data-i18n="never" selected="">
</option>
<option value="/w gm " data-i18n="always">
</option>
<option value="?{Whisper|No, |Yes,/w gm }" data-i18n="ask">
</option>
</select>
</label>
</section>
</article>
<article class="character subsystem-style system-remnant" id="character">
<section class="section" id="character-info">
<h2 data-i18n="character info">
</h2>
<label class="input-label"><span class="input-label__text" data-i18n="character name" role="heading" aria-level="5"></span>
<input class="underlined character_name input-label__input" name="attr_character_name" type="text" title="@{character_name}"/>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="player name" role="heading" aria-level="5"></span>
<input class="underlined player_name input-label__input" name="attr_player_name" type="text" title="@{player_name}"/>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="nationality" role="heading" aria-level="5"></span>
<input class="underlined nationality input-label__input" name="attr_nationality" type="text" title="@{nationality}"/>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="organization affiliations" role="heading" aria-level="5"></span>
<input class="underlined organization_affiliations input-label__input" name="attr_organization_affiliations" type="text" title="@{organization_affiliations}"/>
</label>
<div class="headed-textarea appearance">
<h5 data-i18n="appearance / mannerisms">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_appearance/mannerisms" title="@{appearance/mannerisms}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_appearance/mannerisms" title="@{appearance/mannerisms}"></textarea>
</div>
</div>
<div class="health-track">
<h2 class="health-track__header" data-i18n="health track">
</h2>
<input class="health-track__max-control" name="attr_health_max" value="1" type="hidden" title="@{health|max}"/>
<fieldset class="repeating_health">
<input class="health-track__fill-control" name="attr_fill" hidden="" value="1" type="checkbox" title="@{fill}"/>
<input class="health-track__clear-control health-track__checkbox" name="attr_damaged" value="1" title="@{repeating_health_$X_damaged}" type="checkbox"/>
<button class="health-track__clearer" name="act_clear" title="%{repeating_health_$X_clear}" type="action">
</button>
<input class="boxed health-track__number" name="attr_penalty" title="@{repeating_health_$X_penalty}" type="number"/>
</fieldset>
</div>
</section>
<section class="stat-grid section repeating-container" id="stats">
<h2 data-i18n="stats">
</h2>
<label class="input-label stat_points"><span class="input-label__text" data-i18n="stat points" role="heading" aria-level="4"></span>
<input class="boxed input-label__input" name="attr_stat_points" type="number" title="@{stat_points}"/>
</label>
<label class="input-label reserve"><span class="input-label__text" data-i18n="reserve" role="heading" aria-level="4"></span>
<input class="boxed input-label__input" name="attr_reserve" type="number" title="@{reserve}"/>
</label><!-- body | attrName:body_action | actionName:body-action -->
<button class="roller" name="roll_body" role="heading" aria-level="4" data-i18n="body" value="@{body_action}" title="%{body}" type="roll">
</button><!-- roll_body post block | attrName:body_action | actionName:body-action --><!-- roll_body | attrName:body_action | actionName:body-action | actionTitle:body-action | attrTitle:body_action -->
<button name="act_body-action" hidden="" type="action" title="%{body-action}">
</button>
<input name="attr_body_action" type="hidden" title="@{body_action}"/>
<select class="boxed number" name="attr_body" title="@{body}">
<option value="-3">-3
</option>
<option value="-2">-2
</option>
<option value="-1">-1
</option>
<option value="0" selected="">0
</option>
<option value="1">1
</option>
<option value="2">2
</option>
<option value="3">3
</option>
</select>
<input class="boxed" name="attr_body_mod" type="number" title="@{body_mod}"/><!-- mind | attrName:mind_action | actionName:mind-action -->
<button class="roller" name="roll_mind" role="heading" aria-level="4" data-i18n="mind" value="@{mind_action}" title="%{mind}" type="roll">
</button><!-- roll_mind post block | attrName:mind_action | actionName:mind-action --><!-- roll_mind | attrName:mind_action | actionName:mind-action | actionTitle:mind-action | attrTitle:mind_action -->
<button name="act_mind-action" hidden="" type="action" title="%{mind-action}">
</button>
<input name="attr_mind_action" type="hidden" title="@{mind_action}"/>
<select class="boxed number" name="attr_mind" title="@{mind}">
<option value="-3">-3
</option>
<option value="-2">-2
</option>
<option value="-1">-1
</option>
<option value="0" selected="">0
</option>
<option value="1">1
</option>
<option value="2">2
</option>
<option value="3">3
</option>
</select>
<input class="boxed" name="attr_mind_mod" type="number" title="@{mind_mod}"/><!-- spirit | attrName:spirit_action | actionName:spirit-action -->
<button class="roller" name="roll_spirit" role="heading" aria-level="4" data-i18n="spirit" value="@{spirit_action}" title="%{spirit}" type="roll">
</button><!-- roll_spirit post block | attrName:spirit_action | actionName:spirit-action --><!-- roll_spirit | attrName:spirit_action | actionName:spirit-action | actionTitle:spirit-action | attrTitle:spirit_action -->
<button name="act_spirit-action" hidden="" type="action" title="%{spirit-action}">
</button>
<input name="attr_spirit_action" type="hidden" title="@{spirit_action}"/>
<select class="boxed number" name="attr_spirit" title="@{spirit}">
<option value="-3">-3
</option>
<option value="-2">-2
</option>
<option value="-1">-1
</option>
<option value="0" selected="">0
</option>
<option value="1">1
</option>
<option value="2">2
</option>
<option value="3">3
</option>
</select>
<input class="boxed" name="attr_spirit_mod" type="number" title="@{spirit_mod}"/>
<div class="repeating-container__equipment equipment subsection">
<h2 data-i18n="gear and money">
</h2>
<label class="input-label"><span class="input-label__text" data-i18n="easy living" role="heading" aria-level="4"></span>
<input class="boxed input-label__input" name="attr_easy_living" type="number" title="@{easy_living}"/>
</label>
<div class="repeat-columns">
<h5 data-i18n="name"></h5>
<h5 data-i18n="quantity abbreviation"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-equipment" type="action" title="%{add-equipment}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-equipment" type="action" title="%{edit-equipment}">
</button>
<fieldset class="repeating_equipment">
<input class="collapse" name="attr_collapse" title="@{repeating_equipment_$X_collapse}" type="checkbox"/>
<input class="underlined" name="attr_name" role="heading" aria-level="3" title="@{repeating_equipment_$X_name}" type="text"/>
<input class="underlined" name="attr_quantity" title="@{repeating_equipment_$X_quantity}" type="number"/>
<div class="headed-textarea notes expanded">
<h5 data-i18n="notes">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{notes}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_notes" title="@{repeating_equipment_$X_notes}"></textarea>
</div>
</div>
</fieldset>
</div>
</section>
<section class="section" id="secondary">
<div class="secondaries stat-grid">
<h2 data-i18n="secondary stats">
</h2><!-- initiative | attrName:initiative_action | actionName:initiative-action -->
<button class="initiative-label span-2 roller" name="roll_initiative" data-i18n="initiative" role="heading" aria-level="4" value="@{initiative_action}" title="%{initiative}" type="roll">
</button><!-- roll_initiative post block | attrName:initiative_action | actionName:initiative-action --><!-- roll_initiative | attrName:initiative_action | actionName:initiative-action | actionTitle:initiative-action | attrTitle:initiative_action -->
<button name="act_initiative-action" hidden="" type="action" title="%{initiative-action}">
</button>
<input name="attr_initiative_action" type="hidden" title="@{initiative_action}"/>
<input class="boxed initiative" name="attr_initiative" readonly="" value="0" type="number" title="@{initiative}"/>
<input class="boxed initiative_mod" name="attr_initiative_mod" type="number" title="@{initiative_mod}"/>
<label class="defence-label span-2" for="defence-mod" data-i18n="defence" role="heading" aria-level="4"></label>
<input class="boxed defence" name="attr_defence" readonly="" value="3" type="number" title="@{defence}"/>
<input class="boxed defence_mod" name="attr_defence_mod" id="defence-mod" type="number" title="@{defence_mod}"/>
<label class="resist-label span-2" for="resist-mod" data-i18n="resist" role="heading" aria-level="4"></label>
<input class="boxed resist" name="attr_resist" readonly="" value="0" type="number" title="@{resist}"/>
<input class="boxed resist_mod" name="attr_resist_mod" id="resist-mod" type="number" title="@{resist_mod}"/>
<label class="health-label" for="health" data-i18n="health" role="heading" aria-level="4"></label>
<div class="boxed span-2">
<input name="attr_health" id="health" type="number" title="@{health}"/><span class="slash">/</span>
<input name="attr_health_max" readonly="" type="number" title="@{health|max}"/>
</div>
<input class="boxed health_mod" name="attr_health_mod" type="number" title="@{health_mod}"/>
</div>
<div class="combat-gear stat-grid">
<h2 data-i18n="combat gear">
</h2>
<label class="input-label armour bonus contents"><span class="input-label__text" data-i18n="armour bonus" role="heading" aria-level="4"></span>
<input class="boxed input-label__input" name="attr_armour_bonus" type="number" title="@{armour_bonus}"/>
</label>
<label class="input-label shield bonus contents"><span class="input-label__text" data-i18n="shield bonus" role="heading" aria-level="4"></span>
<input class="boxed input-label__input" name="attr_shield_bonus" type="number" title="@{shield_bonus}"/>
</label>
<label class="input-label action penalty contents"><span class="input-label__text" data-i18n="action penalty" role="heading" aria-level="4"></span>
<input class="boxed input-label__input" name="attr_action_penalty" type="number" title="@{action_penalty}"/>
</label>
</div>
</section>
<section class="repeating-container--skill repeating-container section" id="skill">
<h2 data-i18n="skills"></h2>
<div class="repeat-columns">
<h5 data-i18n="skills"></h5>
<h5 data-i18n="level"></h5>
<h5 data-i18n="stat"></h5>
<h5 data-i18n="xp"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-skill" type="action" title="%{add-skill}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-skill" type="action" title="%{edit-skill}">
</button>
<fieldset class="repeating_skill">
<input class="raw-control" name="attr_raw" hidden="" value="1" title="@{repeating_skill_$X_raw}" type="checkbox"/><!-- roll | attrName:roll_action | actionName:roll-action -->
<button class="raw roller" name="roll_roll" title="%{repeating_skill_$X_roll}" value="@{roll_action}" type="roll"><span name="attr_name" data-i18n-dynamic="" role="heading" aria-level="5" title="@{repeating_skill_$X_name}"></span>
</button><!-- roll_roll post block | attrName:roll_action | actionName:roll-action --><!-- roll_roll | attrName:roll_action | actionName:roll-action | actionTitle:repeating_skill:roll-action | attrTitle:repeating_skill_$x_roll_action -->
<button name="act_roll-action" hidden="" type="action" title="%{roll-action}">
</button>
<input name="attr_roll_action" type="hidden" title="@{roll_action}"/><!-- roll | attrName:roll_action | actionName:roll-action -->
<button class="custom roller" name="roll_roll" title="%{repeating_skill_$X_roll}" value="@{roll_action}" type="roll">
</button><!-- roll_roll post block | attrName:roll_action | actionName:roll-action --><!-- roll_roll | attrName:roll_action | actionName:roll-action | actionTitle:repeating_skill:roll-action | attrTitle:repeating_skill_$x_roll_action -->
<button name="act_roll-action" hidden="" type="action" title="%{roll-action}">
</button>
<input name="attr_roll_action" type="hidden" title="@{roll_action}"/>
<input class="custom underlined" name="attr_name" role="heading" aria-level="5" title="@{repeating_skill_$X_name}" type="text"/>
<input class="underlined" name="attr_level" title="@{repeating_skill_$X_level}" type="number"/>
<select class="underlined" name="attr_stat" title="@{repeating_skill_$X_stat}">
<option value="query" data-i18n="ask">
</option>
<option value="body" data-i18n="body">
</option>
<option value="mind" data-i18n="mind">
</option>
<option value="spirit" data-i18n="spirit">
</option>
</select>
<input class="underlined" name="attr_xp" title="@{repeating_skill_$X_xp}" type="number"/>
</fieldset>
</section>
<section id="adjustments">
<div class="repeating-container repeating-container--advantages advantages subsection section">
<h2 data-i18n="advantages">
</h2>
<div class="repeat-columns">
<h5 data-i18n="name"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-advantage" type="action" title="%{add-advantage}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-advantage" type="action" title="%{edit-advantage}">
</button>
<fieldset class="repeating_advantage">
<input class="collapse" name="attr_collapse" title="@{repeating_advantage_$X_collapse}" type="checkbox"/>
<input class="underlined" name="attr_name" role="heading" aria-level="3" title="@{repeating_advantage_$X_name}" type="text"/>
<div class="headed-textarea notes expanded">
<h5 data-i18n="notes">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{notes}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_notes" title="@{repeating_advantage_$X_notes}"></textarea>
</div>
</div>
</fieldset>
</div>
<div class="repeating-container repeating-container--disadvantages disadvantages subsection section">
<h2 data-i18n="disadvantages">
</h2>
<div class="repeat-columns">
<h5 data-i18n="name"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-disadvantage" type="action" title="%{add-disadvantage}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-disadvantage" type="action" title="%{edit-disadvantage}">
</button>
<fieldset class="repeating_disadvantage">
<input class="collapse" name="attr_collapse" title="@{repeating_disadvantage_$X_collapse}" type="checkbox"/>
<input class="underlined" name="attr_name" role="heading" aria-level="3" title="@{repeating_disadvantage_$X_name}" type="text"/>
<div class="headed-textarea notes expanded">
<h5 data-i18n="notes">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{notes}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_notes" title="@{repeating_disadvantage_$X_notes}"></textarea>
</div>
</div>
</fieldset>
</div>
<div class="repeating-container repeating-container--adjustments adjustments subsection section">
<h2 data-i18n="adjustments">
</h2>
<div class="repeat-columns">
<h5 data-i18n="name"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-adjustment" type="action" title="%{add-adjustment}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-adjustment" type="action" title="%{edit-adjustment}">
</button>
<fieldset class="repeating_adjustment">
<input class="collapse" name="attr_collapse" title="@{repeating_adjustment_$X_collapse}" type="checkbox"/>
<input class="underlined" name="attr_name" role="heading" aria-level="3" title="@{repeating_adjustment_$X_name}" type="text"/>
<div class="headed-textarea mod expanded">
<h5 data-i18n="notes">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{notes}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_notes" title="@{repeating_adjustment_$X_notes}"></textarea>
</div>
</div>
</fieldset>
</div>
</section>
<section class="repeating-container--weapon repeating-container section" id="weapon">
<h2 data-i18n="weapons"></h2>
<div class="repeat-columns">
<h5 data-i18n="weapon"></h5>
<h5 data-i18n="skill"></h5>
<h5 data-i18n="damage"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-weapon" type="action" title="%{add-weapon}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-weapon" type="action" title="%{edit-weapon}">
</button>
<fieldset class="repeating_weapon">
<input class="collapse" name="attr_collapse" type="checkbox" title="@{collapse}"/><!-- roll | attrName:roll_action | actionName:roll-action -->
<button class="roller" name="roll_roll" title="%{repeating_weapon_$X_roll}" value="@{roll_action}" type="roll">
</button><!-- roll_roll post block | attrName:roll_action | actionName:roll-action --><!-- roll_roll | attrName:roll_action | actionName:roll-action | actionTitle:repeating_weapon:roll-action | attrTitle:repeating_weapon_$x_roll_action -->
<button name="act_roll-action" hidden="" type="action" title="%{roll-action}">
</button>
<input name="attr_roll_action" type="hidden" title="@{roll_action}"/>
<input class="underlined" name="attr_name" title="@{repeating_weapon_$X_name}" type="text"/>
<input name="attr_skill" value="select" title="@{repeating_weapon_$X_skill}" type="hidden"/>
<button class="pseudo-select" name="act_skill-action" title="%{repeating_weapon_$X_skill}" type="action">
<input class="translate-control" name="attr_translate_skill" hidden="" value="1" checked="" type="checkbox" title="@{translate_skill}"/><span class="underlined translated" name="attr_skill" data-i18n-dynamic="" title="@{repeating_weapon_$X_skill}"></span><span class="underlined untranslated" name="attr_skill" title="@{repeating_weapon_$X_skill}"></span>
</button>
<div class="roll-plus"><span data-i18n="lead"></span><span class="plus">+</span>
<input class="underlined" name="attr_damage" title="@{repeating_weapon_$X_damage}" type="number"/>
</div>
<div class="headed-textarea notes expanded">
<h5 data-i18n="notes">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{notes}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_notes" title="@{repeating_weapon_$X_notes}"></textarea>
</div>
</div>
</fieldset>
</section>
</article>
<article class="battle-remnant subsystem-style system-remnant" id="remnant">
<section class="section" id="remnant-pic">
<div class="health-track">
<h2 class="health-track__header" data-i18n="structure track">
</h2>
<input class="health-track__max-control" name="attr_structure_max" value="1" type="hidden" title="@{structure|max}"/>
<fieldset class="repeating_structure">
<input class="health-track__fill-control" name="attr_fill" hidden="" value="1" type="checkbox" title="@{fill}"/>
<input class="health-track__clear-control health-track__checkbox" name="attr_damaged" value="1" title="@{repeating_health_$X_damaged}" type="checkbox"/>
<button class="health-track__clearer" name="act_clear" title="%{repeating_health_$X_clear}" type="action">
</button>
<input class="boxed health-track__number" name="attr_penalty" title="@{repeating_health_$X_penalty}" type="number"/>
</fieldset>
</div>
<label class="input-label"><span class="input-label__text" data-i18n="duress" role="heading" aria-level="2"></span>
<input class="boxed input-label__input" name="attr_duress" type="number" title="@{duress}"/>
</label>
<div class="image-container remnant-token">
<label class="input-label image-container__input stacked"><span class="input-label__text" data-i18n="image prompt" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_remnant_token" type="text" value="https://s3.amazonaws.com/files.d20.io/images/259054779/OzN3yQwg7MbfXNtMYs-iOg/original.png" title="@{remnant_token}"/>
</label>
<button class="image-container__button" name="act_remnant-token" type="action" title="%{remnant-token}"><img class="image-container__image" name="attr_remnant_token" src="https://s3.amazonaws.com/files.d20.io/images/259054779/OzN3yQwg7MbfXNtMYs-iOg/original.png" data-i18n-alt="remnant token" title="@{remnant_token}"/>
</button>
</div>
</section>
<section class="stat-grid section" id="remnant-ratings">
<h2 data-i18n="total ratings">
</h2><!-- situational awareness | attrName:situational_awareness_action | actionName:situational-awareness-action -->
<button class="uppercase roller" name="roll_situational_awareness" role="heading" aria-level="4" data-i18n="situation awareness abbreviation" value="@{situational_awareness_action}" title="%{situational_awareness}" type="roll">
</button><!-- roll_situational_awareness post block | attrName:situational_awareness_action | actionName:situational-awareness-action --><!-- roll_situational_awareness | attrName:situational_awareness_action | actionName:situational-awareness-action | actionTitle:situational-awareness-action | attrTitle:situational_awareness_action -->
<button name="act_situational-awareness-action" hidden="" type="action" title="%{situational-awareness-action}">
</button>
<input name="attr_situational_awareness_action" type="hidden" title="@{situational_awareness_action}"/>
<input class="boxed" name="attr_situational_awareness" readonly="" type="number" title="@{situational_awareness}"/>
<input class="boxed" name="attr_situational_awareness_mod" type="number" title="@{situational_awareness_mod}"/>
<h4 data-i18n="defence"></h4>
<input class="boxed" name="attr_remnant_defence" readonly="" type="number" title="@{remnant_defence}"/>
<input class="boxed" name="attr_remnant_defence_mod" type="number" title="@{remnant_defence_mod}"/><!-- remnant initiative | attrName:remnant_initiative_action | actionName:remnant-initiative-action -->
<button class="roller" name="roll_remnant_initiative" data-i18n="initiative" role="heading" aria-level="4" value="@{remnant_initiative_action}" title="%{remnant_initiative}" type="roll">
</button><!-- roll_remnant_initiative post block | attrName:remnant_initiative_action | actionName:remnant-initiative-action --><!-- roll_remnant_initiative | attrName:remnant_initiative_action | actionName:remnant-initiative-action | actionTitle:remnant-initiative-action | attrTitle:remnant_initiative_action -->
<button name="act_remnant-initiative-action" hidden="" type="action" title="%{remnant-initiative-action}">
</button>
<input name="attr_remnant_initiative_action" type="hidden" title="@{remnant_initiative_action}"/>
<input class="boxed" name="attr_remnant_initiative" readonly="" type="number" title="@{remnant_initiative}"/>
<input class="boxed" name="attr_remnant_initiative_mod" type="number" title="@{remnant_initiative_mod}"/>
<div class="remnant-skills">
<h2 data-i18n="skills">
</h2>
<div class="repeat-columns">
<h5 data-i18n="skill">
</h5>
<h5 data-i18n="level">
</h5>
<h5 data-i18n="xp">
</h5>
</div><!-- assault roll | attrName:assault_roll_action | actionName:assault-roll-action -->
<button class="roller" name="roll_assault_roll" data-i18n="assault" role="heading" aria-level="4" value="@{assault_roll_action}" title="%{assault_roll}" type="roll">
</button><!-- roll_assault_roll post block | attrName:assault_roll_action | actionName:assault-roll-action --><!-- roll_assault_roll | attrName:assault_roll_action | actionName:assault-roll-action | actionTitle:assault-roll-action | attrTitle:assault_roll_action -->
<button name="act_assault-roll-action" hidden="" type="action" title="%{assault-roll-action}">
</button>
<input name="attr_assault_roll_action" type="hidden" title="@{assault_roll_action}"/>
<input class="underlined" name="attr_assault_level" type="number" title="@{assault_level}"/>
<input class="underlined" name="attr_assault_experience" type="number" title="@{assault_experience}"/><!-- strike roll | attrName:strike_roll_action | actionName:strike-roll-action -->
<button class="roller" name="roll_strike_roll" data-i18n="strike" role="heading" aria-level="4" value="@{strike_roll_action}" title="%{strike_roll}" type="roll">
</button><!-- roll_strike_roll post block | attrName:strike_roll_action | actionName:strike-roll-action --><!-- roll_strike_roll | attrName:strike_roll_action | actionName:strike-roll-action | actionTitle:strike-roll-action | attrTitle:strike_roll_action -->
<button name="act_strike-roll-action" hidden="" type="action" title="%{strike-roll-action}">
</button>
<input name="attr_strike_roll_action" type="hidden" title="@{strike_roll_action}"/>
<input class="underlined" name="attr_strike_level" type="number" title="@{strike_level}"/>
<input class="underlined" name="attr_strike_experience" type="number" title="@{strike_experience}"/><!-- motion roll | attrName:motion_roll_action | actionName:motion-roll-action -->
<button class="roller" name="roll_motion_roll" data-i18n="motion" role="heading" aria-level="4" value="@{motion_roll_action}" title="%{motion_roll}" type="roll">
</button><!-- roll_motion_roll post block | attrName:motion_roll_action | actionName:motion-roll-action --><!-- roll_motion_roll | attrName:motion_roll_action | actionName:motion-roll-action | actionTitle:motion-roll-action | attrTitle:motion_roll_action -->
<button name="act_motion-roll-action" hidden="" type="action" title="%{motion-roll-action}">
</button>
<input name="attr_motion_roll_action" type="hidden" title="@{motion_roll_action}"/>
<input class="underlined" name="attr_motion_level" type="number" title="@{motion_level}"/>
<input class="underlined" name="attr_motion_experience" type="number" title="@{motion_experience}"/>
</div>
</section>
<section class="stat-grid section" id="remnant-stats">
<h2 data-i18n="remnant stats">
</h2>
<label class="span-2" data-i18n="armour" for="armour" role="heading" aria-level="4"></label>
<input class="boxed" name="attr_armour" type="number" id="armour" title="@{armour}"/>
<label class="span-2" data-i18n="speed" for="speed" role="heading" aria-level="4"></label>
<input class="boxed" name="attr_speed" type="number" id="speed" title="@{speed}"/>
<label class="span-2" data-i18n="inspiring" for="inspiring" role="heading" aria-level="4"></label>
<input class="boxed" name="attr_inspiring" type="number" id="inspiring" title="@{inspiring}"/>
<label class="span-2" data-i18n="terrifying" for="terrifying" role="heading" aria-level="4"></label>
<input class="boxed" name="attr_terrifying" type="number" id="terrifying" title="@{terrifying}"/>
<div class="input-label span-all structure">
<label data-i18n="structure" for="structure" role="heading" aria-level="4"></label>
<div class="boxed span-2">
<input name="attr_structure" id="structure" type="number" title="@{structure}"/><span class="slash">/</span>
<input name="attr_structure_max" type="hidden" title="@{structure|max}"/>
<input name="attr_structure_base" title="@{structure|max}" type="number"/>
</div>
</div>
<label class="assault_damage_label" data-i18n="assault damage" role="heading" aria-level="4" for="assault_damage"></label>
<input class="boxed assault_damage_input" name="attr_assault_damage" type="number" id="assault_damage" title="@{assault_damage}"/>
<label class="strike_damage_label" data-i18n="strike damage" role="heading" aria-level="4" for="strike_damage"></label>
<input class="boxed strike_damage_input" name="attr_strike_damage" type="number" id="strike_damage" title="@{strike_damage}"/>
<label class="strike_range_label" data-i18n="strike range" role="heading" aria-level="4" for="strike_range"></label>
<input class="boxed strike_range_input" name="attr_strike_range" type="number" id="strike_range" title="@{strike_range}"/>
</section>
<section class="repeating-container--specialty repeating-container section" id="remnant-specialty">
<h2 data-i18n="specialty"></h2>
<div class="repeat-columns">
<h5 data-i18n="name"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-specialty" type="action" title="%{add-specialty}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-specialty" type="action" title="%{edit-specialty}">
</button>
<fieldset class="repeating_specialty">
<input class="collapse" name="attr_collapse" type="checkbox" title="@{collapse}"/>
<input class="underlined" name="attr_name" title="@{repeating_specialty_$X_name}" type="text"/>
<div class="headed-textarea description expanded">
<h5 data-i18n="description">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_description" title="@{description}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_description" title="@{repeating_specialty_$X_description}"></textarea>
</div>
</div>
</fieldset>
</section>
<section class="repeating-container--trait repeating-container section" id="remnant-trait">
<h2 data-i18n="traits"></h2>
<div class="repeat-columns">
<h5 data-i18n="name"></h5>
<h5 data-i18n="duress"></h5>
<h5 data-i18n="focus"></h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-trait" type="action" title="%{add-trait}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-trait" type="action" title="%{edit-trait}">
</button>
<fieldset class="repeating_trait">
<input class="collapse" name="attr_collapse" type="checkbox" title="@{collapse}"/>
<input class="underlined" name="attr_name" type="text" title="@{name}"/>
<input class="underlined" name="attr_duress" title="@{repeating_trait_$X_duress}" type="number"/>
<input class="underlined" name="attr_focus" title="@{repeating_trait_$X_focus}" type="number"/>
<div class="headed-textarea description expanded">
<h5 data-i18n="description">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_description" title="@{description}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_description" title="@{repeating_trait_$X_description}"></textarea>
</div>
</div>
</fieldset>
</section>
<section class="repeating-container repeating-container--drone section" id="remnant-drone">
<h2 data-i18n="drones">
</h2>
<button class="repcontrol-button repcontrol-button--add" name="act_add-drone" type="action" title="%{add-drone}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-drone" type="action" title="%{edit-drone}">
</button>
<fieldset class="repeating_drone">
<input class="collapse" name="attr_collapse" type="checkbox" title="@{collapse}"/>
<input class="underlined remnant-drone__name" name="attr_name" role="heading" aria-level="3" title="@{repeating_drone_$X_name}" type="text"/>
<label class="input-label"><span class="input-label__text" data-i18n="# active" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_active" type="number" title="@{active}"/>
</label>
<label class="input-label sa"><span class="input-label__text" data-i18n="situation awareness abbreviation" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_sa" type="number" title="@{repeating_drone_$X_sa}"/>
</label>
<label class="input-label def"><span class="input-label__text" data-i18n="defence" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_def" type="number" title="@{repeating_drone_$X_def}"/>
</label>
<label class="input-label arm"><span class="input-label__text" data-i18n="armour" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_arm" type="number" title="@{repeating_drone_$X_arm}"/>
</label>
<label class="input-label spd"><span class="input-label__text" data-i18n="speed" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_spd" type="number" title="@{repeating_drone_$X_spd}"/>
</label>
<div class="structure"><span role="heading" aria-level="5" data-i18n="structure"></span>
<div class="boxed span-2">
<input name="attr_structure" id="structure" type="number" title="@{structure}"/><span class="slash">/</span>
<input name="attr_structure_max" type="number" title="@{structure|max}"/>
</div>
</div>
<input class="underlined expanded--empty" name="attr_assault" type="hidden" title="@{assault}"/><!-- assault | attrName:assault_action | actionName:assault-action -->
<div class="input-label input-label--button assault">
<button class="roller" name="roll_assault" role="heading" aria-level="5" data-i18n="assault" title="%{repeating_drone_$X_assault}" value="@{assault_action}" type="roll">
</button>
<input class="boxed input-label__input" name="attr_assault" type="number" title="@{repeating_drone_$X_assault}"/>
</div><!-- roll_assault post block | attrName:assault_action | actionName:assault-action --><!-- roll_assault | attrName:assault_action | actionName:assault-action | actionTitle:repeating_drone:assault-action | attrTitle:repeating_drone_$x_assault_action -->
<button name="act_assault-action" hidden="" type="action" title="%{assault-action}">
</button>
<input name="attr_assault_action" type="hidden" title="@{assault_action}"/>
<input class="underlined expanded--empty" name="attr_assault" type="hidden" title="@{assault}"/>
<label class="input-label assault_dam"><span class="input-label__text" data-i18n="assault damage" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_assault_dam" type="number" title="@{repeating_drone_$X_assault_dam}"/>
</label>
<input class="underlined expanded--empty" name="attr_strike" type="hidden" title="@{strike}"/><!-- strike | attrName:strike_action | actionName:strike-action -->
<div class="input-label input-label--button strike">
<button class="roller" name="roll_strike" role="heading" aria-level="5" data-i18n="strike" title="%{repeating_drone_$X_strike}" value="@{strike_action}" type="roll">
</button>
<input class="boxed input-label__input" name="attr_strike" type="number" title="@{repeating_drone_$X_strike}"/>
</div><!-- roll_strike post block | attrName:strike_action | actionName:strike-action --><!-- roll_strike | attrName:strike_action | actionName:strike-action | actionTitle:repeating_drone:strike-action | attrTitle:repeating_drone_$x_strike_action -->
<button name="act_strike-action" hidden="" type="action" title="%{strike-action}">
</button>
<input name="attr_strike_action" type="hidden" title="@{strike_action}"/>
<input class="underlined expanded--empty" name="attr_strike" type="hidden" title="@{strike}"/>
<label class="input-label strike_dam"><span class="input-label__text" data-i18n="strike damage" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_strike_dam" type="number" title="@{repeating_drone_$X_strike_dam}"/>
</label>
<input class="underlined expanded--empty" name="attr_strike" type="hidden" title="@{strike}"/>
<label class="input-label strikerange"><span class="input-label__text" data-i18n="strike range" role="heading" aria-level="5"></span>
<input class="boxed input-label__input" name="attr_strikerange" type="number" title="@{repeating_drone_$X_strikerange}" readonly=""/>
</label>
<div class="headed-textarea description expanded">
<h5 data-i18n="description">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_description" title="@{description}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_description" title="@{repeating_drone_$X_description}"></textarea>
</div>
</div>
</fieldset>
</section>
<section class="repeating-container repeating-container--remnant-weapons section" id="remnant-weapons">
<h2 data-i18n="weapons">
</h2>
<div class="repeat-columns">
<h5 data-i18n="name">
</h5>
<h5 data-i18n="skill">
</h5>
<h5 data-i18n="bonus">
</h5>
<h5 data-i18n="damage">
</h5>
<h5 data-i18n="damage bonus">
</h5>
<h5 data-i18n="type">
</h5>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-remnant-weapon" type="action" title="%{add-remnant-weapon}">
</button>
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-remnant-weapon" type="action" title="%{edit-remnant-weapon}">
</button>
<fieldset class="repeating_remnant-weapon">
<input class="collapse" name="attr_collapse" type="checkbox" title="@{collapse}"/><!-- roll | attrName:roll_action | actionName:roll-action -->
<button class="roller" name="roll_roll" title="%{repeating_remnant-weapon_$X_roll}" value="@{roll_action}" type="roll">
</button><!-- roll_roll post block | attrName:roll_action | actionName:roll-action --><!-- roll_roll | attrName:roll_action | actionName:roll-action | actionTitle:repeating_remnant-weapon:roll-action | attrTitle:repeating_remnant-weapon_$x_roll_action -->
<button name="act_roll-action" hidden="" type="action" title="%{roll-action}">
</button>
<input name="attr_roll_action" type="hidden" title="@{roll_action}"/>
<input class="underlined" name="attr_name" title="@{repeating_remnant-weapon_$X_name}" role="heading" aria-level="4" type="text"/>
<select class="underlined" name="attr_skill" title="@{repeating_remnant-weapon_$X_skill}">
<option value=" " data-i18n="select" selected="">
</option>
<option value="assault" data-i18n="assault">
</option>
<option value="strike" data-i18n="strike">
</option>
</select>
<input class="underlined" name="attr_bonus" title="@{repeating_remnant-weapon_$X_bonus}" type="number"/>
<div class="roll-plus"><span data-i18n="lead"></span><span class="plus">+</span>
<input class="underlined" name="attr_damage" title="@{repeating_weapon_$X_damage}" readonly="" type="number"/>
</div>
<input class="underlined damage-bonus" name="attr_damage_bonus" title="@{repeating_remnant-weapon_$X_damage_bonus}" type="number"/>
<input class="underlined" name="attr_type" title="@{repeating_remnant-weapon_$X_type}" type="text"/>
<div class="headed-textarea description expanded">
<h5 data-i18n="notes">
</h5>
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{notes}"></span>
<textarea class="underlined adaptive-text__textarea" name="attr_notes" title="@{repeating_remnant-weapon_$X_notes}"></textarea>
</div>
</div>
</fieldset>
</section>
</article>
</main>
<rolltemplate class="sheet-rolltemplate-rapidfire">
<div class="template rapidfire system-{{system}}">
<div class="content {{#singleroll}}single-roll{{/singleroll}}">{{#header}}
<div class="header">
<div class="header__background background">
<h3 tabindex="-1">{{header}}</h3>
</div>
<div class="header__content">
<h3>{{header}}</h3>
</div>
<div class="header__image"></div>
</div>{{/header}}{{#roll}}
<div class="hexagon sheet-roll">{{#rollLess() computed::status -2}}<span class="fumble-toggle" hidden=""></span>{{/rollLess() computed::status -2}}{{#rollGreater() computed::status 4}}<span class="crit-toggle" hidden=""></span>{{/rollGreater() computed::status 4}}
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="roll"></h4>{{roll}}
</div>
</div>
</div>
</div>{{/roll}}{{#roll1}}
<div class="hexagon sheet-roll">{{#rollLess() computed::status1 -2}}<span class="fumble-toggle" hidden=""></span>{{/rollLess() computed::status1 -2}}{{#rollGreater() computed::status1 4}}<span class="crit-toggle" hidden=""></span>{{/rollGreater() computed::status1 4}}
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="roll"></h4>{{roll1}}
</div>
</div>
</div>
</div>{{/roll1}}{{#roll2}}
<div class="hexagon sheet-roll">{{#rollLess() computed::status2 -2}}<span class="fumble-toggle" hidden=""></span>{{/rollLess() computed::status2 -2}}{{#rollGreater() computed::status2 4}}<span class="crit-toggle" hidden=""></span>{{/rollGreater() computed::status2 4}}
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="roll"></h4>{{roll2}}
</div>
</div>
</div>
</div>{{/roll2}}{{#roll3}}
<div class="hexagon sheet-roll">{{#rollLess() computed::status3 -2}}<span class="fumble-toggle" hidden=""></span>{{/rollLess() computed::status3 -2}}{{#rollGreater() computed::status3 4}}<span class="crit-toggle" hidden=""></span>{{/rollGreater() computed::status3 4}}
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="roll"></h4>{{roll3}}
</div>
</div>
</div>
</div>{{/roll3}}{{#roll4}}
<div class="hexagon sheet-roll">{{#rollLess() computed::status4 -2}}<span class="fumble-toggle" hidden=""></span>{{/rollLess() computed::status4 -2}}{{#rollGreater() computed::status4 4}}<span class="crit-toggle" hidden=""></span>{{/rollGreater() computed::status4 4}}
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="roll"></h4>{{roll4}}
</div>
</div>
</div>
</div>{{/roll4}}{{#roll5}}
<div class="hexagon sheet-roll">{{#rollLess() computed::status5 -2}}<span class="fumble-toggle" hidden=""></span>{{/rollLess() computed::status5 -2}}{{#rollGreater() computed::status5 4}}<span class="crit-toggle" hidden=""></span>{{/rollGreater() computed::status5 4}}
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="roll"></h4>{{roll5}}
</div>
</div>
</div>
</div>{{/roll5}}{{#roll6}}
<div class="hexagon sheet-roll">{{#rollLess() computed::status6 -2}}<span class="fumble-toggle" hidden=""></span>{{/rollLess() computed::status6 -2}}{{#rollGreater() computed::status6 4}}<span class="crit-toggle" hidden=""></span>{{/rollGreater() computed::status6 4}}
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="roll"></h4>{{roll6}}
</div>
</div>
</div>
</div>{{/roll6}}{{#difficulty}}
<div class="hexagon difficulty">
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">
<h4 data-i18n="difficulty"></h4>{{difficulty}}
</div>
</div>
</div>
</div>{{/difficulty}}{{#damage}}
<div class="hexagon damage">
<div class="hexagon__hex-wrapper">
<div class="hexagon__hex-wrapper__hex-border">
<div class="hexagon__hex-wrapper__hex-border__hex-content">{{#damage_label}}
<h4>{{computed::damage_label}}</h4>{{/damage_label}}{{^damage_label}}
<h4 data-i18n="damage"></h4>{{/damage_label}}{{computed::damage}}
</div>
</div>
</div>
</div>{{/damage}}{{#description}}
<div class="container-background description"></div>
<div class="description text">
<h4 data-i18n="notes"></h4><span>{{description}}</span>{{#allprops() description damage difficulty roll roll1 roll2 roll3 roll4 roll5 roll6 header footer character_id system single-roll custom_description status}}
<h4>{{key}}</h4><span>{{value}}</span>{{/allprops() description damage difficulty roll roll1 roll2 roll3 roll4 roll5 roll6 header footer character_id system single-roll custom_description status}}
</div>{{/description}}{{^description}}{{#custom_description}}
<div class="container-background description"></div>
<div class="description text">{{#allprops() description damage difficulty roll roll1 roll2 roll3 roll4 roll5 roll6 header footer character_id system single-roll custom_description status}}
<h4>{{key}}</h4><span>{{value}}</span>{{/allprops() description damage difficulty roll roll1 roll2 roll3 roll4 roll5 roll6 header footer character_id system single-roll custom_description status}}
</div>{{/custom_description}}{{/description}}{{#footer}}
<div class="footer">
<div class="footer__background background">{{#character_id}}
<h4 class="character_name" tabindex="-1">[{{footer}}](http://journal.roll20.net/character/{{character_id}})</h4>{{/character_id}}{{^character_id}}
<h4 class="character_name" tabindex="-1">{{footer}}</h4>{{/character_id}}
</div>
<div class="footer__content">{{#character_id}}
<h4 class="character_name">[{{footer}}](http://journal.roll20.net/character/{{character_id}})</h4>{{/character_id}}{{^character_id}}
<h4 class="character_name">{{footer}}</h4>{{/character_id}}
</div>
<div class="footer__image"></div>
</div>{{/footer}}
</div>
</div>
<div class="image-border{{#header}} has-header{{/header}}{{#footer}} has-footer{{/footer}}"></div>
</rolltemplate>
<script type="text/worker">
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//# Attribute Obj Proxy handler
const createAttrProxy = function(attributes){
//creates a proxy for the attributes object so that values can be worked with more easily.
const attrTarget = {
updates:{},
attributes:{...attributes},
repOrders:{}
};
const attrHandler = {
get:function(obj,prop){//gets the most value of the attribute.
//If it is a repeating order, returns the array, otherwise returns the update value or the original value
if(prop === 'set'){
return function(){
set(obj.updates,...arguments);
}
}else if(Object.keys(obj).some(key=>key===prop)){
return Reflect.get(...arguments)
}else{
let retValue;
switch(true){
case obj.repOrders.hasOwnProperty(prop):
retValue = obj.repOrders[prop];
break;
case obj.updates.hasOwnProperty(prop):
retValue = obj.updates[prop];
break;
default:
retValue = obj.attributes[prop];
break;
}
let cascRef = prop.replace(/(repeating_[^_]+_)[^_]+/,'$1\$x');
let numRetVal = +retValue;
if(!Number.isNaN(numRetVal)){
retValue = numRetVal;
}else if(cascades[cascRef] && cascades[cascRef].type === 'number'){
retValue = cascades[cascRef].defaultValue || 0;
}
return retValue;
}
},
set:function(obj,prop,value){
//Sets the value. Also verifies that the value is a valid attribute value
//e.g. not undefined, null, or NaN
if(value || value===0 || value===''){
if(/reporder|^repeating_[^_]+$/.test(prop)){
let section = prop.replace(/_reporder_/,'');
obj.repOrders[section] = value;
}else{
obj.updates[prop] = value;
}
}else{
debug(`!!!Warning: Attempted to set ${prop} to an invalid value:${value}; value not stored!!!`);
}
return true;
},
deleteProperty(obj,prop){
//removes the property from the original attributes, updates, and the reporders
Object.keys(obj).forEach((key)=>{
delete obj[key][prop.toLowerCase()];
});
}
};
return new Proxy(attrTarget,attrHandler);
};
const _setSectionOrder = function(section,order){
let trueSection = section.replace(/repeating_/,'');
setSectionOrder(trueSection,order);
};
const _removeRepeatingRow = function(row,attributes,sections){
debug(`removing ${row}`);
Object.keys(attributes.attributes).forEach((key)=>{
if(key.startsWith(row)){
delete attributes[key];
}
});
let [,section,rowID] = row.match(/(repeating_[^_]+)_(.+)/,'');
sections[section] = sections[section].filter((id)=>id!==rowID);
removeRepeatingRow(row);
};
const _getAttrs = function(props=baseGet,callback){
getAttrs(props,(values)=>{
const attributes = createAttrProxy(values);
callback(attributes);
});
};
const getAllAttrs = function({callback,props=baseGet,sectionDetails=repeatingSectionDetails}){
getSections(sectionDetails,(repeats,sections)=>{
getAttrs([...props,...repeats],(values)=>{
const attributes = createAttrProxy(values);
orderSections(attributes,sections);
const casc = expandCascade(cascades,sections,attributes);
callback(attributes,sections,casc);
})
});
};
const getSections = function(section_details,callback){
let queueClone = _.clone(section_details);
const worker = (queue,repeatAttrs=[],sections={})=>{
let detail = queue.shift();
getSectionIDs(detail.section,(IDs)=>{
sections[detail.section] = IDs;
IDs.forEach((id)=>{
detail.fields.forEach((f)=>{
repeatAttrs.push(`${detail.section}_${id}_${f}`);
});
});
repeatAttrs.push(`_reporder_${detail.section}`);
if(queue.length){
worker(queue,repeatAttrs,sections);
}else{
callback(repeatAttrs,sections);
}
});
};
if(!queueClone[0]){
callback([],{});
}
worker(queueClone);
};
// Sets the attributes while always calling with {silent:true}
// Can be awaited to get the values returned from _setAttrs
const set = function(obj,vocal=false){
setAttrs(obj,{silent:!vocal});
};
//sanitizes text for use as a regex
sanitizeForRegex = function(text){
return text.replace(/\.|\||\(|\)|\[|\]|\-|\+|\?|\/|\{|\}|\^|\$|\*/g,'\\$&');
};
//converts a value to a number, it's default value, or 0
const value = function(val,def){
return (+val||def||0);
};
//extracts the section, rowID, and field name from a repeating attribute name
/*
e.g. repeating_equipment_-09Jhu89z_bulk would give:
section: equipment
rowID: -09Jhu89z
field: bulk
*/
const parseRepeatName = function(string){
let match = string.match(/(repeating_[^_]+)_([^_]+)(?:_(.+))?/);
match.shift();
return match;
};
const parseTriggerName = function(string){
let match = string.match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
match.shift();
return match;
};
const parseClickTrigger = function(string){
let match = string.match(/clicked:(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
match.shift();
return match;
};
const parseHTMLName = function(string){
let match = string.match(/(?:attr|act|roll)_(.+)/);
match.shift();
return match[0];
};
const capitalize = function(string){
return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase());
};
const extractQueryResult = async function(query){
debug('entering extractQueryResult');
let queryRoll = await startRoll(`!{{query=[[0[response=?{${query}}]]]}}`);
finishRoll(queryRoll.rollId);
return queryRoll.results.query.expression.replace(/^.+?response=|\]$/g,'');
};
const pseudoQuery = async function(value){
debug('entering pseudoQuery');
let queryRoll = await startRoll(`!{{query=[[0[response=${value}]]]}}`);
finishRoll(queryRoll.rollId);
return queryRoll.results.query.expression.replace(/^.+?response=|\]$/g,'');
};
//Determines which penalty to apply (if any)
const determinePenalty = function(type,attributes,sections){
let damageID = sections[`repeating_${type}`].find((id)=> attributes[`repeating_${type}_${id}_damaged`] === 1);
return damageID ? attributes[`repeating_${type}_${damageID}_penalty`] : 0;
};
//determines which health track's penalties should affect an attribute
const determineHealthType = function(section,field){
const attrCharacterLookup = {
'situational-awareness':'structure',
'remnant-initiative':'structure',
'assault-roll':'structure',
'strike-roll':'structure',
'motion-roll':'structure',
repeating_drone:'structure',
'repeating_remnant-weapon':'structure'
};
return attrCharacterLookup[section] || attrCharacterLookup[field] || 'health';
};
//# Utility Functions #
const log = function(msg){
if(typeof msg === 'string'){
console.log(`%c${sheetName} log| ${msg}`,"background-color:#159ccf");
}else if(typeof msg === 'object'){
Object.keys(msg).forEach((m)=>{
if(typeof msg[m] === 'string'){
console.log(`%c${sheetName} log| ${m}: ${msg[m]}`,"background-color:#159ccf");
}else{
console.log(`%c${sheetName} log| ${typeof msg[m]} ${m}`,"background-color:#159ccf");
console.table(msg[m]);
}
});
}
};
const debug = function(msg,force){
if(!debugMode && !force && version > 0) return;
if(typeof msg === 'string'){
console.log(`%c${sheetName} DEBUG| ${msg}`,"background-color:tan;color:red;");
}else if(typeof msg === 'object'){
Object.keys(msg).forEach((m)=>{
if(typeof msg[m] === 'string'){
console.log(`%c${sheetName} DEBUG| ${m}: ${msg[m]}`,"background-color:tan;color:red;");
}else{
console.log(`%c${sheetName} DEBUG| ${typeof msg[m]} ${m}`,"background-color:tan;color:red;font-weight:bold;");
console.table(msg[m]);
}
});
}
};
//Section ordering
//orders the section id arrays to match the repOrder attribute
const orderSections = function(attributes,sections){
Object.keys(sections).forEach((section)=>{
attributes.attributes[`_reporder_${section}`] = commaArray(attributes[`_reporder_${section}`]);
orderSection(attributes.attributes[`_reporder_${section}`],sections[section]);
});
};
//orders a single ID array
const orderSection = function(repOrder,IDs=[]){
IDs.sort((a,b)=>{
return repOrder.indexOf(a.toLowerCase()) - repOrder.indexOf(b.toLowerCase());
});
};
//splits a comma delimited string into an array
const commaArray = function(string=''){
return string.toLowerCase().split(/\s*,\s*/);
};
//Creates a row ID specific for an add section for pseudo nested repeating sections
const generateCustomID = function(string,rowID){
rowID = rowID || generateRowID();
let re = new RegExp(`^.{${string.length}}`);
return `${string}${rowID.replace(re,'')}`;
};
//Sheet Updaters and styling functions
const updateSheet = function(){
debug('updating sheet');
getAllAttrs({props:['sheet_version','debug_mode','collapsed',...baseGet],callback:(attributes,sections,casc)=>{
debugMode = !!attributes.debug_mode;
attributes.sheet_version = version;
if(!attributes.sheet_version){
initialSetup(attributes,sections);
}
log(`Sheet Update applied. Current Sheet Version ${version}`);
styleOnOpen(attributes,sections);
setActionCalls({},attributes,sections);
attributes.set();
log('Sheet ready for use');
}})
};
const initialSetup = function(attributes,sections){
debug('Initial sheet setup');
};
const styleOnOpen = function(attributes,sections){
navigateSheet({triggerName:`clicked:${attributes.sheet_state}`});
setupSystem({},attributes,sections);
};
/*
Cascade Expansion functions
*/
//Expands the repeating section templates in cascades to reflect the rows actually available
const expandCascade = function(cascade,sections,attributes){
return _.keys(cascade).reduce((memo,key)=>{//iterate through cascades and replace references to repeating attributes with correct row ids.
if(/^repeating/.test(key)){//If the attribute is a repeating attribute, do special logic
expandRepeating(memo,key,cascade,sections,attributes);
}else{//for non repeating attributes do this logic
expandNormal(memo,key,cascade,sections);
}
return memo;
},{});
};
const expandRepeating = function(memo,key,cascade,sections,attributes){
key.replace(/(repeating_[^_]+)_[^_]+?_(.+)/,(match,section,field)=>{
(sections[section]||[]).forEach((id)=>{
memo[`${section}_${id}_${field}`]=_.clone(cascade[key]);//clone the details so that each row's attributes have correct ids
memo[`${section}_${id}_${field}`].name = `${section}_${id}_${field}`;
memo[`${section}_${id}_${field}`].affects = memo[`${section}_${id}_${field}`].affects.reduce((m,affected)=>{
if(section === affected){//otherwise if the affected attribute is in the same section, simply set the affected attribute to have the same row id.
m.push(applyID(affected,id));
}else if(/repeating/.test(affected)){//If the affected attribute isn't in the same repeating section but is still a repeating attribute, add all the rows of that section
addAllRows(affected,m,sections);
}else{//otherwise the affected attribute is a non repeating attribute. Simply add it to the computed affected array
m.push(affected);
}
return m;
},[]);
});
});
};
const applyID = function(affected,id){
return affected.replace(/(repeating_[^_]+_)[^_]+(.+)/,`$1${id}$2`);
};
const expandNormal = function(memo,key,cascade,sections){
memo[key] = _.clone(cascade[key]);
memo[key].affects = memo[key].affects.reduce((m,a)=>{
if(/^repeating/.test(a)){
addAllRows(a,m,sections);
}else{
m.push(a);
}
return m;
},[]);
};
const triggerFunctions = function(obj,attributes,sections){
debug(`triggering functions for ${obj.name}`);
obj.triggeredFuncs && obj.triggeredFuncs.forEach(func=>func(obj,attributes,sections));
};
const initialFunction = function(obj,attributes,sections){
debug(`initial functions for ${obj.name}`);
obj.initialFunc && obj.initialFunc(obj,attributes,sections);
};
const addAllRows = function(affected,memo,sections){
affected.replace(/(repeating_[^_]+?)_[^_]+?_(.+)/,(match,section,field)=>{
sections[section].forEach(id=>memo.push(`${section}_${id}_${field}`));
});
};
let clickDelay = 500;
const defaultThrottle = callback => _.throttle(callback,clickDelay,{trailing:false});
const isDieRoll = function(field){
return /(?:d(?:8|10|12)(?:Crit)?|exertion)/.test(field);
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
/*
Sheet Styling
*/
const navigateSheet = function(event){
debug('navigating sheet');
const setObj = {};
let [,,page] = parseClickTrigger(event.triggerName);
page = page.replace(/nav-|-action/g,'');
debug({page});
navButtons.forEach((button)=>{
let element = button.replace(/-action/,'');
let action = element === page ? 'addClass' : 'removeClass';
$20(`.${element}`)[action]('active');
});
setObj.sheet_state = page;
debug({setObj});
set(setObj);
};
const setupSystem = function(trigger,attributes,sections){
setupAttributes(attributes,sections);
setupSkills(attributes,sections);
setupHealth(attributes,sections);
};
const setupAttributes = function(attributes,sections){
const attrDetails = systemDefaults[attributes.system].attributes;
Object.entries(attrDetails).forEach(([attr,value])=>{
attributes[attr] = value;
});
};
const setupSkills = function(attributes,sections){
let skillDetails = systemDefaults[attributes.system].repeating_skill.values;
let missingSkills = Object.keys(skillDetails).filter((skill)=>sections.repeating_skill.every((id)=>attributes[`repeating_skill_${id}_name`]===skill));
missingSkills.forEach((skill)=>{
let newID = generateRowID();
attributes[`repeating_skill_${newID}_name`] = skill;
attributes[`repeating_skill_${newID}_stat`] = /\//.test(skillDetails[skill]) ? 'query' : skillDetails[skill];
attributes[`repeating_skill_${newID}_raw`] = 1;
});
};
const setupHealth = function(attributes,sections){
let healthDetails = systemDefaults[attributes.system].repeating_health.values;
if(healthDetails && !sections.repeating_health.length){
healthDetails.forEach((penalty)=>{
let newID = generateRowID();
attributes[`repeating_health_${newID}_penalty`] = penalty;
});
attributes.health = healthDetails.length;
attributes.health_max = attributes.health;
}
let structureDetails = systemDefaults[attributes.system].repeating_structure.values;
if(structureDetails && !sections.repeating_structure.length){
structureDetails.forEach((penalty)=>{
let newID = generateRowID();
attributes[`repeating_structure_${newID}_penalty`] = penalty;
});
attributes.structure = structureDetails.length;
attributes.structure_max = attributes.structure;
attributes.structure_base = attributes.structure;
}
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Expands/collapses the sections
const expandHeader = function(jEvent){
debug('entering expandHeader');
const name = jEvent.htmlAttributes.name.replace(/act_/,'');
const target = `.${name}.expandable-header`;
_getAttrs(['collapsed'],(attributes)=>{
attributes.collapsed = attributes.collapsed.split(/\s*,\s*/).filter(b => b!==name && !/^\s*$/.test(b) );
if(!/collapse/.test(jEvent.htmlAttributes.class)){
attributes.collapsed.push(name);
}
attributes.collapsed = attributes.collapsed.join(',');
$20(target).toggleClass('collapse');
attributes.set();
});
};
const imageInput = function(trigger,attributes,sections){
debug('entering imageInput');
let [section,rowID,field] = parseTriggerName(trigger.name);
if(!field) return;
if(!attributes[field]){
attributes[field] = 'https://s3.amazonaws.com/files.d20.io/images/259054779/OzN3yQwg7MbfXNtMYs-iOg/original.png';
}
$20(`.${field.replace(/_/g,'-')} .image-container__input`).removeClass('active');
};
const toggleImageInput = function(jEvent){
let field = parseHTMLName(jEvent.htmlAttributes.name);
$20(`.${field.replace(/_/g,'-')} .image-container__input`).toggleClass('active');
};
//Calls the appropriate dynamic query function for the event
const dynamicSelect = function(event){
let[section,rowID,field] = parseClickTrigger(event.triggerName);
field = field.replace(/-action$/,'');
getAllAttrs({callback:async (attributes,sections)=>{
let selection = await dynamicQueries[field]({section,rowID,field},attributes,sections);
attributes[`${section}_${rowID}_${field}`] = selection;
attributes[`${section}_${rowID}_translate_${field}`] = getTranslationByKey(selection) ? 1 : 0;
attributes.set();
}});
};
const skillQuery = function(click,attributes,sections){
let query = sections.repeating_skill.reduce((prompt,id)=>{
if(attributes[`repeating_skill_${id}_raw`]){
prompt.push(`${capitalize(getTranslationByKey(attributes[`repeating_skill_${id}_name`]))},${attributes[`repeating_skill_${id}_name`]}`);
}else{
prompt.push(capitalize(attributes[`repeating_skill_${id}_name`]));
}
return prompt;
},[`${getTranslationByKey('skill query')}`]).join('|');
query = `${query}`
return extractQueryResult(query);
};
//Click Functions
//These functions are fired in response to an action button being clicked.
//Clears a tracker of all damage
const clearTracker = function(event){
debug('clearing tracker');
let [section,rowID,field] = parseClickTrigger(event.triggerName);
getAllAttrs({callback:(attributes,sections,casc)=>{
sections[section].forEach((id)=>{
attributes[`${section}_${id}_damaged`] = 0;
attributes[`${section}_${id}_fill`] = 0;
});
let attr = section.replace(/repeating_/,'');
attributes[attr] = attributes[`${attr}_max`];
let trigger = casc[attr];
processSheet(trigger,attributes,sections,casc);
}});
};
const addItem = function(event){
debug(`adding repeating item from ${event.triggerName}`);
let [,,section] = parseClickTrigger(event.triggerName);
section = section.replace(/add-/,'');
let rowID = generateRowID();
const setObj = {};
setObj[`repeating_${section}_${rowID}_name`] = '';
set(setObj);
};
const editSection = function(event){
debug(`editing repeating section from ${event.triggerName}`);
let [,,section] = parseClickTrigger(event.triggerName);
section = section.replace(/edit-/,'');
let target = `fieldset.repeating_${section} + .repcontainer`;
$20(target).toggleClass('editmode');
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Roll Functions
//Begins the parsing of a roll
const skillRollDetails = function({section,rowID,sections,attributes,rollObj,actionPenalty}){
rollObj.header = attributes[`${section}_${rowID}_name`];
rollObj.header = attributes[`${section}_${rowID}_raw`] ? `^{${rollObj.header}}` : rollObj.header;
let skill = attributes[`${section}_${rowID}_level`];
let stat = attributes[`${section}_${rowID}_stat`];
let penalty = determinePenalty('health',attributes,sections);
if(stat === 'query'){
stat = `?{${getTranslationByKey('stat query')}|${['body','mind','spirit'].map((s)=>`${getTranslationByKey(s)},@{${s}}[${s}]`).join('|')}}`;
}else{
stat = `[[0@{${stat}}]][${stat}]`;
}
rollObj.roll = `[[1d6 + ${stat} + [[0${skill}]][${rollObj.header}] + ${penalty}[Damage Penalty]${actionPenalty}]]`;
};
const weaponRollDetails = function({section,rowID,field,sections,attributes,rollObj,actionPenalty}){
rollObj.header = attributes[`${section}_${rowID}_name`];
let skill;
let stat;
let healthType = determineHealthType(section,field);
let penalty = determinePenalty(healthType,attributes,sections);
debug({healthType,penalty});
if(section === 'repeating_weapon'){
let skillID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === attributes[`${section}_${rowID}_skill`]);
let skillRef = `repeating_skill_${skillID}`;
skill = attributes[`${skillRef}_level`];
stat = attributes[`${skillRef}_stat`];
if(stat === 'query'){
stat = `?{${getTranslationByKey('stat query')}|${['body','mind','spirit'].map((s)=>`${getTranslationByKey(s)},@{${s}}[${s}]`).join('|')}}`;
}else{
stat = `@{${stat}}[${stat}]`;
}
rollObj.roll = `[[1d6 + ${stat} + ${skill}[${attributes[`${skillRef}_name`]}] + ${penalty}[Damage Penalty]${actionPenalty}]]`;
rollObj.damage = `[[${attributes[`${section}_${rowID}_damage`]} + Lead]]`;
}else if(section === 'repeating_remnant-weapon'){
let skillName = attributes[`${section}_${rowID}_skill`];
skill = attributes[`${skillName}_level`];
stat = attributes.situational_awareness;
rollObj.roll = `[[1d6 + [[0${stat}]][SA] + [[0${skill}]][${skillName}] + [[0${attributes[`${section}_${rowID}_bonus`]}]] + ${penalty}[Damage Penalty]${actionPenalty} ]]`;
rollObj.damage = `[[${attributes[`${section}_${rowID}_damage`]+ attributes[`${section}_${rowID}_damage_bonus`]} + Lead]]`;
}
debug({roll:rollObj.roll});
rollObj.description = `@{${section}_${rowID}_notes}`;
};
const droneRollDetails = async function({section,rowID,field,attributes,rollObj,actionPenalty}){
rollObj.header = attributes[`${section}_${rowID}_name`];
let numQuery = _.range(attributes[`${section}_${rowID}_active`]).map((num)=>{
return num +1;
});
let attackNum;
debug({numQuery});
if(numQuery.length <= 1){
attackNum = await pseudoQuery(1);
}else{
attackNum = await extractQueryResult(`${getTranslationByKey('drone query')}|${numQuery.join('|')}`);
}
debug({attackNum});
attackNum = +attackNum;
let bonus = Math.floor(attackNum / 2);
_.range(attackNum).forEach((n)=>{
let num = n+1;
rollObj[`roll${num}`] = `[[1d6 + [[0@{${section}_${rowID}_${field}}]][${field}] + ${bonus}[multiple attack]${actionPenalty}]]`;
rollObj[`status${num}`] = `[[0[computed value]]]`;
});
rollObj.damage = `[[${attributes[`${section}_${rowID}_${field}_dam`]} + Lead]]`;
rollObj.description = `@{${section}_${rowID}_description}`;
if(field === 'strike'){
rollObj.description = `^{strike range}: @{${section}_${rowID}_strikerange}\n@{${section}_${rowID}_description}`;
}
};
const initiateRoll = function(event){
let [section,rowID,field] = parseClickTrigger(event.triggerName);
field = field.replace(/-action/,'');
const rollSwitch = {
repeating_weapon:weaponRollDetails,
repeating_skill:skillRollDetails,
'repeating_remnant-weapon':weaponRollDetails,
repeating_drone:droneRollDetails
};
getAllAttrs({props:['difficulty','action_penalty_automation',...baseGet],callback:async (attributes,sections)=>{
let rollObj = {status:'[[0[computed value]]]'};
let actionPenalty = determineActionPenalty(attributes);
if(/^(?:assault|strike|motion)-roll$/.test(field)){
let penalty = determinePenalty('structure',attributes,sections);
field = field.replace(/-roll/,'');
rollObj.header = `^{${field}}`;
rollObj.roll = `[[1d6 + [[0@{${field}_level}]][${field}] + [[0@{situational_awareness}]][SA] + ${penalty}[Damage Penalty]${actionPenalty}]]`;
rollObj.damage = /assault|strike/.test(field) ? `[[[[0@{${field}_damage}]] + Lead]]` : undefined;
}else if(!section){
//Base rolling for simple stats
let healthType = determineHealthType(section,field);
let penalty = determinePenalty(healthType,attributes,sections);
rollObj.header = `^{${field.replace(/-/g,' ')}}`;//output translation of name
rollObj.roll = `[[1d6 + [[0@{${field.replace(/-/g,'_')}}]][${field.replace(/-/g,' ')}] + ${penalty}[Damage Penalty]${actionPenalty}${/initiative/.test(field) ? '&{tracker}' : ''}]]`;
}else{
//rolling for more complex fields. These are typically repeating sections
if(section === 'repeating_drone'){
await rollSwitch[section]({section,rowID,field,sections,attributes,rollObj,actionPenalty});
}else{
rollSwitch[section]({section,rowID,field,sections,attributes,rollObj,actionPenalty});
}
}
if(attributes.difficulty && !/initiative/.test(field)){
rollObj.difficulty = `[[?{${getTranslationByKey('difficulty query')}|0}]]`;
rollObj.damage_label = rollObj.damage ? undefined : '[[0[computed value]]]';
rollObj.damage = rollObj.damage || `[[0[computed value]]]`;
}
if(!rollObj.damage && !rollObj.difficulty){
rollObj.singleroll = 'true';
}
executeRoll(rollObj);
}});
};
const determineActionPenalty = function(attributes){
const stateSwitch = {
disabled:'',
ask:`+ [[abs(0?{${getTranslationByKey('action penalty query')}|0}) * -1]][Action Penalty]`,
'use input':`+ ${attributes.action_penalty}[Action Penalty]`
};
return stateSwitch[attributes.action_penalty_automation];
};
const executeRoll = async function(rollObj){
let rollText = Object.entries(rollObj).reduce((text,[field,content])=>{
if(content){
text += `{{${field}=${content}}}`;
}
return text;
},'@{template_start}');
let roll = await startRoll(rollText);
const computeObj = {};
if(roll.results.roll1){
computeMultipleRolls(roll.results,computeObj,rollObj);
}else{
computeSingleRoll(roll.results,computeObj,rollObj);
}
finishRoll(roll.rollId,computeObj);
};
const computeMultipleRolls = function(results,computeObj,rollObj){
let rollNums = [6,5,4,3,2,1].find((num)=>results.hasOwnProperty(`roll${num}`));
computeObj[`damage`] = rollObj[`damage`] && rollObj.difficulty ? 0 : undefined;
_.range(rollNums).forEach((n)=>{
let num = n + 1;
if(rollObj[`damage`] && rollObj.difficulty){
let damage = results[`damage`].result + (results[`roll${num}`].result - results.difficulty.result);
if(damage < results[`damage`].result){
damage = 0;
}
computeObj.damage += damage;
}
if(rollObj.difficulty){
computeObj[`status`] = checkResult(results[`roll${num}`],results.difficulty);
}
});
};
const computeSingleRoll = function(results,computeObj,rollObj){
if(rollObj.damage && rollObj.difficulty){
computeObj.damage = results.damage.result + (results.roll.result - results.difficulty.result);
}
if(results.damage_label){
computeObj.damage_label = getTranslationByKey(computeObj.damage < 0 ? 'trail' : 'lead');
}
if(rollObj.difficulty){
computeObj[`status`] = checkResult(results[`roll`],results.difficulty);
}
};
const checkResult = function(roll,difficulty){
return roll.result - difficulty.result;
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Initial change functions
const syncHealth = function(trigger,attributes,sections){
debug('syncing token and character health');
const type = trigger.name.replace(/_max/,'');
let damage = attributes[`${type}_max`] - attributes[type];
let damageIndex = damage - 1;
sections[`repeating_${type}`].forEach((id,index)=>{
attributes[`repeating_${type}_${id}_damaged`] = index === damageIndex ? 1 : 0;
attributes[`repeating_${type}_${id}_fill`] = index < damageIndex ? 1 : 0;
});
if(attributes[type] > attributes[`${type}_max`]){
attributes[type] = attributes[`${type}_max`];
}
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
// Triggered Functions
const setActionCalls = function(trigger,attributes,sections){
debug('setting action calls');
actionAttributes.forEach((base)=>{
let [section,,field] = parseTriggerName(base);
let fieldAction = field.replace(/_/g,'-');
if(section){
sections[section].forEach((id)=>{
attributes[`${section}_${id}_${field}`] = `%{${attributes.character_name}|${section}_${id}_${fieldAction}}`;
});
}else{
attributes[`${field}`] = `%{${attributes.character_name}|${fieldAction}}`;
}
});
debug({updates:attributes.updates});
};
const checkHealth = function(trigger,attributes,sections){
debug('checking current health trackers')
let [section,rowID] = parseRepeatName(trigger.name);
let currDamage = sections[section].indexOf(rowID);
sections[section].forEach((id,index)=>{
if(index !== currDamage){
attributes[`${section}_${id}_damaged`] = 0;
}
attributes[`${section}_${id}_fill`] = index < currDamage ? 1 : 0;
});
};
const updateTrack = function(section,attributes,sections){
let newTracks = attributes[`${section}_max`] - sections[`repeating_${section}`].length;
if(newTracks > 0){
_.range(newTracks).forEach((n)=>{
let newID = generateRowID();
let priorPenalties = sections[`repeating_${section}`].slice(Math.max(0,sections[`repeating_${section}`].length - 2))
.map((id)=>{
return attributes[`repeating_${section}_${id}_penalty`];
});
let newPenalty;
if(priorPenalties[0] !== priorPenalties[1]){
newPenalty = priorPenalties[1] || priorPenalties[0];
}else{
let penStart = priorPenalties[1] || priorPenalties[0];
newPenalty = Math.max(Math.min(-3,penStart),penStart - 1);
}
attributes[`repeating_${section}_${newID}_damaged`] = 0;
attributes[`repeating_${section}_${newID}_penalty`] = newPenalty;
sections[`repeating_${section}`].push(newID);
});
}else if(newTracks < 0){
let slicePoint = Math.max(0,sections[`repeating_${section}`].length + newTracks);
let extraTracks = sections[`repeating_${section}`].slice(slicePoint);
extraTracks.forEach((id)=>{
_removeRepeatingRow(`repeating_${section}_${id}`,attributes,sections);
});
}
};
const maxHealth = function(type,attributes){
const typeSwitch = {
structure:()=> attributes.structure_base,
health:()=> 3 + attributes.body + attributes.spirit + attributes.health_mod
}
return Math.max(typeSwitch[type](),1);
};
const calcHealth = function(trigger,attributes,sections){
const type = trigger.name.replace(/_max/,'');
debug(`calculating ${type}`);
attributes[`${type}_max`] = maxHealth(type,attributes);
let healthDiff = attributes[`${type}_max`] - value(attributes.attributes[`${type}_max`]);
updateTrack(type,attributes,sections);
attributes[type] = attributes[type] + healthDiff;
syncHealth(trigger,attributes,sections);
};
const skillEffects = function(trigger,attributes,sections){
let [section,rowID,field] = parseRepeatName(trigger.name);
let skillName = attributes[`${section}_${rowID}_name`];
const skillSwitch = {
dodge:['defence'],
awareness:['initiative']
};
if(skillSwitch[skillName]){
trigger.affects = [...trigger.affects,...skillSwitch[skillName]];
}
};
const validateActionPenalty = function(trigger,attributes,sections){
if(attributes.action_penalty > 0){
attributes.action_penalty = attributes.action_penalty * -1;
}
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Calculation Functions
const calcInitiative = function(trigger,attributes,sections){
debug('calculating initiative');
let awarenessID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === 'awareness');
let awareness = attributes[`repeating_skill_${awarenessID}_level`];
return awareness + attributes.mind + attributes.initiative_mod;
};
const calcDefence = function(trigger,attributes,sections){
debug('calculating defense');
let dodgeID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === 'dodge');
let dodge = attributes[`repeating_skill_${dodgeID}_level`];
let damagePenalty = determinePenalty('health',attributes,sections);
let total = 3 + attributes.body + dodge + attributes.shield_bonus + damagePenalty + attributes.defence_mod;
return Math.max(total,0);
};
const totalHealth = function(trigger,attributes,sections){
const type = trigger.name.replace(/_max/,'');
debug(`calculating total ${type}`);
return sections[`repeating_${type}`].reduce((total,id)=>{
total -= attributes[`repeating_${type}_${id}_damaged`] + attributes[`repeating_${type}_${id}_fill`];
return total;
},attributes[`${type}_max`]);
};
const calcResist = function(trigger,attributes,sections){
debug('calculating resist');
return Math.max(attributes.spirit + attributes.armour_bonus + attributes.resist_mod,0);
};
const calcSA = function(trigger,attributes,sections){
debug('calculating situational awareness');
return ['body','mind','spirit'].reduce((m,attr)=>{
return m + attributes[attr];
},0);
};
const calcRemnantInitiative = function(trigger,attributes,sections){
debug('calculating remnant initiative');
let awarenessID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === 'awareness');
let awareness = attributes[`repeating_skill_${awarenessID}_level`];
let penalty = determinePenalty('structure',attributes,sections);
return attributes.situational_awareness + awareness + penalty;
};
const calcRemnantDefence = function(trigger,attributes,sections){
debug('calculating remnant defence');
let damagePenalty = determinePenalty('structure',attributes,sections);
debug({damagePenalty});
return Math.max(attributes.speed + attributes.situational_awareness + attributes.motion_level + damagePenalty,0);
};
const calcStrikeRange = function(trigger,attributes,sections){
debug('calculating strike range');
const [section,rowID,field] = parseTriggerName(trigger.name);
let strike = section ? `${section}_${rowID}_strike` : 'strike_level';
return attributes[strike] * 100;
};
const calcRemnantDamage = function(trigger,attributes,sections){
debug('calculating remnant damage');
let [section,rowID,field] = parseTriggerName(trigger.name);
let skill = attributes[`${section}_${rowID}_skill`];
return attributes[`${skill}_damage`] + attributes[`${section}_${rowID}_damage_bonus`];
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//accesses the sheet and iterates through all changes necessary before calling setAttrs
const accessSheet = function(event){
debug({event});
getAllAttrs({callback:(attributes,sections,casc)=>{
let trigger = casc[event.sourceAttribute];
processSheet(trigger,attributes,sections,casc);
}});
};
const processSheet = function(trigger,attributes,sections,casc){
debug({trigger});
if(!trigger){
return;
}
initialFunction(trigger,attributes,sections);//functions that should only be run if the attribute was the thing changed
triggerFunctions(trigger,attributes,sections);//functions that cause complex changes to the character
let queue = [...trigger.affects];
while(queue.length){
let name = queue.shift();
debug(`processing ${name}`);
let obj = casc[name];
triggerFunctions(obj,attributes,sections);
if(obj.calculation){
attributes[name] = obj.calculation(obj,attributes,sections);//Basic calculation of an attribute value
}
queue = [...queue,...obj.affects];
}
debug('sheet accessed, setting attributes');
attributes.set();
};
const registerEventHandlers = function(){
on('sheet:opened',updateSheet);
toMonitor.forEach((m)=>{
on(`change:${m}`,accessSheet);
});
//Click Listeners
navButtons.forEach((button)=>{
on(`clicked:nav-${button}`,navigateSheet);
});
trackerMonitors.forEach((button)=>{
on(`clicked:${button}`,clearTracker);
});
queryButtons.forEach((button)=>{
on(`clicked:${button}`,dynamicSelect);
});
rollMonitor.forEach((button)=>{
on(`clicked:${button}`,initiateRoll);
});
repeatAddMonitors.forEach((button)=>{
on(`clicked:add-${button}`,addItem);
on(`clicked:edit-${button}`,editSection);
})
//jQuery listeners
$20('.expandable-header').on('click',expandHeader);
$20('.image-container__button').on('click',toggleImageInput);
};
const trackerMonitors = ["repeating_health:clear","repeating_structure:clear"];
const navButtons = ["character","settings","battle-remnant"];
const rollMonitor = ["body-action","mind-action","spirit-action","initiative-action","repeating_skill:roll-action","repeating_weapon:roll-action","situational-awareness-action","remnant-initiative-action","assault-roll-action","strike-roll-action","motion-roll-action","repeating_drone:assault-action","repeating_drone:strike-action","repeating_remnant-weapon:roll-action"];
const actionAttributes = ["body_action","mind_action","spirit_action","initiative_action","repeating_skill_$x_roll_action","repeating_weapon_$x_roll_action","situational_awareness_action","remnant_initiative_action","assault_roll_action","strike_roll_action","motion_roll_action","repeating_drone_$x_assault_action","repeating_drone_$x_strike_action","repeating_remnant-weapon_$x_roll_action"];
const repeatAddMonitors = ["equipment","skill","advantage","disadvantage","adjustment","weapon","specialty","trait","drone","remnant-weapon"];
const queryButtons = ["repeating_weapon:skill-action"];
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Sheet Variables
const sheetName = 'Rapidfire System';
const version = 1.0;
let debugMode = false;
//Object that defines how the attributes affect each other
const repeatingSectionDetails = [
{section:'repeating_health',fields:['damaged','penalty','fill']},
{section:'repeating_structure',fields:['damaged','penalty','fill']},
{section:'repeating_skill',fields:['name','level','stat','xp','raw','roll_action']},
{section:'repeating_weapon',fields:['name','skill','stat','translate skill','damage','notes','collapse','roll_action']},
{section:'repeating_remnant-weapon',fields:['name','skill','bonus','damage','damage_bonus','type','notes','collapse','roll_action','notes']},
{section:'repeating_drone',fields:['name','active','sa','collapse','def','arm','spd','struct','assault','assault_dam','strike','strike_dam','strikerange','strike_action','assault_action','description']}
];
//const actionAttributes = ['body','mind','spirit','initiative','repeating_skill_$x_roll','repeating_weapon_$x_roll','situational_awareness','remnant_initiative','assault_roll','strike_roll','motion_roll','repeating_remnant-weapon_$x_roll','repeating_drone_$x_assault','repeating_drone_$x_strike'];
const systemDefaults = {
'remnants':{
repeating_skill:{field:'name',additional:true,values:{"archery / throwing":"body","athletics":"body","awareness":"mind","bureaucracy":"mind","command":"mind or spirit","craft":"body / mind / spirit","dodge":"body","etiquette":"spirit","history":"mind","investigation":"mind / spirit","language":"mind","larceny":"mind / body / spirit","lore":"mind","medicine":"mind","melee":"body","perform":"spirit","ride":"body / spirit","sail":"mind / spirit","sciences":"mind","social sciences":"mind","stealth":"body","survival":"mind","unarmed combat":"body"}},
repeating_health:{field:'penalty',additional:true,values:[0,-1,-1],alternate:true},
repeating_structure:{field:'penalty',additional:true,values:[0,0,-1,-1,-2],alternate:true},
attributes:{system_header:'https://s3.amazonaws.com/files.d20.io/images/260489274/xCVgiDGF-dMYpVGKXL5UXA/original.png',armour:5,speed:3,assault_damage:4,strike_damage:2,inspiring:1,terrifying:1}
}
};
const cascades = {
/*template
attribute_name:{
name:'attribute_name',
affects:['array','of','attributes','affected'],
calculation:funcCall,//Calculation for this attribute; optional property
triggeredFuncs:[func,func]
}*/
// ## Character stats ##
character_name:{name:'character_name',type:'text',affects:[],triggeredFuncs:[setActionCalls]},
sheet_version:{name:'sheet_version',type:'number',defaultValue:0,affects:[]},
system:{name:'system',affects:[],triggeredFuncs:[setupSystem]},
//Stats
body:{name:'body',type:'number',defaultValue:0,affects:['defence','health_max','situational_awareness'],triggerdFuncs:[syncHealth]},
mind:{name:'mind',type:'number',defaultValue:0,affects:['initiative','situational_awareness']},
spirit:{name:'spirit',type:'number',defaultValue:0,affects:['resist','health_max','situational_awareness'],triggerdFuncs:[syncHealth]},
//Secondary Stats
initiative:{name:'initiative',type:'number',defaultValue:0,affects:[],calculation:calcInitiative},
initiative_mod:{name:'initiative_mod',type:'number',defaultValue:0,affects:['initiative']},
defence:{name:'defence',type:'number',defaultValue:3,affects:[],calculation:calcDefence},
resist:{name:'resist',type:'number',defaultValue:0,affects:[],calculation:calcResist},
defence_mod:{name:'defence_mod',type:'number',defaultValue:0,affects:['defence']},
resist_mod:{name:'resist_mod',type:'number',defaultValue:0,affects:['resist']},
health:{name:'health',type:'number',defaultValue:3,affects:['defence'],calculation:totalHealth,initialFunc:syncHealth},
health_max:{name:'health_max',type:'number',defaultValue:3,affects:['defence'],triggeredFuncs:[calcHealth]},
health_mod:{name:'health_mod',type:'number',defaultValue:0,affects:['health_max']},
//Combat Gear
armour_bonus:{name:'armour_bonus',type:'number',defaultValue:0,affects:['resist']},
shield_bonus:{name:'shield_bonus',type:'number',defaultValue:0,affects:['defence']},
action_penalty:{name:'action_penalty',type:'number',defaultValue:0,affects:[],triggeredFuncs:[validateActionPenalty]},
//Health Track
repeating_health_$x_damaged:{name:'repeating_health_$x_damaged',type:'number',defaultValue:0,affects:['health'],triggeredFuncs:[checkHealth]},
repeating_health_$x_fill:{name:'repeating_health_$x_fill',type:'number',defaultValue:0,affects:[]},
repeating_health_$x_penalty:{name:'repeating_health_$x_penalty',type:'number',defaultValue:0,affects:['defence']},
//Skills
repeating_skill_$x_level:{name:'repeating_skill_$x_level',type:'number',defaultValue:0,affects:[],triggeredFuncs:[skillEffects]},
// ## Remnant Attributes ##
remnant_token:{name:'structure',type:'number',defaultValue:3,affects:[],triggeredFuncs:[imageInput]},
//Ratings
situational_awareness:{name:'situational_awareness',type:'number',defaultValue:0,affects:['remnant_initiative','remnant_defence'],calculation:calcSA},
remnant_defence:{name:'remnant_defence',type:'number',defaultValue:0,affects:[],calculation:calcRemnantDefence},
remnant_initiative:{name:'remnant_initiative',type:'number',defaultValue:0,affects:[],calculation:calcRemnantInitiative},
//Stats
speed:{name:'speed',type:'number',defaultValue:0,affects:['remnant_defence']},
strike_range:{name:'strike_range',type:'number',defaultValue:0,affects:[],calculation:calcStrikeRange},
structure:{name:'structure',type:'number',defaultValue:5,affects:['remnant_defence'],calculation:totalHealth,initialFunc:syncHealth},
structure_base:{name:'structure_base',type:'number',defaultValue:5,affects:['structure_max']},
structure_max:{name:'structure_max',type:'number',defaultValue:5,affects:['remnant_defence'],triggeredFuncs:[calcHealth]},
strike_damage:{name:'strike_damage',type:'number',defaultValue:0,affects:['repeating_remnant-weapon_$x_damage']},
assault_damage:{name:'assault_damage',type:'number',defaultValue:0,affects:['repeating_remnant-weapon_$x_damage']},
//Remnant Skills
strike_level:{name:'strike_level',type:'number',defaultValue:0,affects:['strike_range']},
assault_level:{name:'assault_level',type:'number',defaultValue:0,affects:[]},
motion_level:{name:'motion_level',type:'number',defaultValue:0,affects:['remnant_defence']},
//Structure Track
repeating_structure_$x_damaged:{name:'repeating_structure_$x_damaged',type:'number',defaultValue:0,affects:['structure'],triggeredFuncs:[checkHealth]},
repeating_structure_$x_fill:{name:'repeating_structure_$x_fill',type:'number',defaultValue:0,affects:[]},
repeating_structure_$x_penalty:{name:'repeating_structure_$x_penalty',type:'number',defaultValue:0,affects:['remnant_defence']},
//remnant-weapons
'repeating_remnant-weapon_$x_damage':{name:'repeating_remnant-weapon_$x_damage',type:'number',defaultValue:0,affects:[],calculation:calcRemnantDamage},
'repeating_remnant-weapon_$x_skill':{name:'repeating_remnant-weapon_$x_skill',type:'text',defaultValue:'assault',affects:['repeating_remnant-weapon_$x_damage']},
'repeating_remnant-weapon_$x_damage_bonus':{name:'repeating_remnant-weapon_$x_damage_bonus',type:'number',defaultValue:0,affects:['repeating_remnant-weapon_$x_damage']},
//remnant Drones
'repeating_drone_$x_strike':{name:'repeating_drone_$x_strike',type:'number',defaultValue:0,affects:['repeating_drone_$x_strikerange']},
'repeating_drone_$x_active':{name:'repeating_drone_$x_active',type:'number',defaultValue:0,affects:[]},
'repeating_drone_$x_strikerange':{name:'repeating_drone_$x_strikerange',type:'number',defaultValue:0,affects:[],calculation:calcStrikeRange},
};
const systems = ['remnants'];
const repeatMonitor = [];
//const rollMonitor = ['body-action','mind-action','spirit-action','initiative-action','situational-awareness-action','remnant-initiative-action','assault-roll-action','strike-roll-action','motion-roll-action','repeating_skill:roll-action','repeating_weapon:roll-action','repeating_drone:assault-action','repeating_drone:strike-action','repeating_remnant-weapon:roll-action'];
//const navButtons = ['character','settings','battle-remnant'];
//const repeatAddMonitors = ['skill','weapon','advantage','disadvantage','adjustment','specialty','trait','drone','remnant-weapon'];
//const queryButtons = ['repeating_weapon:skill'];
//const trackerMonitors = ['repeating_structure:clear','repeating_health:clear'];
const dynamicQueries = {skill:skillQuery};
const baseGet = ['sheet_state','section_state','character_name'];
let toMonitor = Object.keys(cascades).reduce((m,k)=>{
if(!/repeating/.test(k)){
baseGet.push(k);
if(!/max/.test(k)){
m.push(k);
}
}else if(/repeating/.test(k)){
k.replace(/(repeating_[^_]+?)_[^_]+?_(.+)/,(match,section,field)=>{
repeatMonitor.push(`${section}:${field}`);
});
}
return m;
},[]);
debug({cascades,baseGet});
toMonitor = [...toMonitor,...repeatMonitor];
registerEventHandlers();
log(`Sheet Loaded`);
</script>