mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 19:52:29 +00:00
1828 lines
122 KiB
HTML
1828 lines
122 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}"><!-- triggerObj: {"value":"remnants","data-i18n":"remnants","selected":"","trigger":{"triggeredFuncs":["setupSystem"],"name":"system"},"name":"attr_system","title":"@{system}","type":"select"} -->
|
|
<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}"><!-- triggerObj: {"value":"disabled","data-i18n":"disabled","name":"attr_action_penalty_automation","title":"@{action_penalty_automation}","type":"select"} -->
|
|
<option value="disabled" data-i18n="disabled">
|
|
</option><!-- triggerObj: {"value":"ask","data-i18n":"ask","name":"attr_action_penalty_automation","title":"@{action_penalty_automation}","type":"select"} -->
|
|
<option value="ask" data-i18n="ask">
|
|
</option><!-- triggerObj: {"value":"use input","data-i18n":"use input","name":"attr_action_penalty_automation","title":"@{action_penalty_automation}","type":"select"} -->
|
|
<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}"><!-- triggerObj: {"value":" ","data-i18n":"never","selected":"","name":"attr_whisper","title":"@{whisper}","type":"select"} -->
|
|
<option value=" " data-i18n="never" selected="">
|
|
</option><!-- triggerObj: {"value":"/w gm ","data-i18n":"always","name":"attr_whisper","title":"@{whisper}","type":"select"} -->
|
|
<option value="/w gm " data-i18n="always">
|
|
</option><!-- triggerObj: {"value":"?{Whisper|No, |Yes,/w gm }","data-i18n":"ask","name":"attr_whisper","title":"@{whisper}","type":"select"} -->
|
|
<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="@{repeating_health_$X_fill}"/>
|
|
<input class="health-track__clear-control health-track__checkbox" name="attr_damaged" value="1" type="checkbox" title="@{repeating_health_$X_damaged}"/>
|
|
<button class="health-track__clearer" name="act_clear" type="action" title="%{repeating_health_$X_clear}">
|
|
</button>
|
|
<input class="boxed health-track__number" name="attr_penalty" type="number" title="@{repeating_health_$X_penalty}"/>
|
|
</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>
|
|
<button class="roller" name="roll_body" role="heading" aria-level="4" data-i18n="body" value="@{body_action}" title="%{body}" type="roll">
|
|
</button>
|
|
<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}"><!-- triggerObj: {"value":3,"name":"attr_body","title":"@{body}","type":"select"} -->
|
|
<option value="3">3
|
|
</option><!-- triggerObj: {"value":2,"name":"attr_body","title":"@{body}","type":"select"} -->
|
|
<option value="2">2
|
|
</option><!-- triggerObj: {"value":1,"name":"attr_body","title":"@{body}","type":"select"} -->
|
|
<option value="1">1
|
|
</option><!-- triggerObj: {"value":0,"selected":"","trigger":{"affects":["defence","health_max","situational_awareness"],"name":"body"},"name":"attr_body","title":"@{body}","type":"select"} -->
|
|
<option value="0" selected="">0
|
|
</option><!-- triggerObj: {"value":-1,"name":"attr_body","title":"@{body}","type":"select"} -->
|
|
<option value="-1">-1
|
|
</option><!-- triggerObj: {"value":-2,"name":"attr_body","title":"@{body}","type":"select"} -->
|
|
<option value="-2">-2
|
|
</option><!-- triggerObj: {"value":-3,"name":"attr_body","title":"@{body}","type":"select"} -->
|
|
<option value="-3">-3
|
|
</option>
|
|
</select>
|
|
<input class="boxed" name="attr_body_mod" type="number" title="@{body_mod}"/>
|
|
<button class="roller" name="roll_mind" role="heading" aria-level="4" data-i18n="mind" value="@{mind_action}" title="%{mind}" type="roll">
|
|
</button>
|
|
<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}"><!-- triggerObj: {"value":3,"name":"attr_mind","title":"@{mind}","type":"select"} -->
|
|
<option value="3">3
|
|
</option><!-- triggerObj: {"value":2,"name":"attr_mind","title":"@{mind}","type":"select"} -->
|
|
<option value="2">2
|
|
</option><!-- triggerObj: {"value":1,"name":"attr_mind","title":"@{mind}","type":"select"} -->
|
|
<option value="1">1
|
|
</option><!-- triggerObj: {"value":0,"selected":"","trigger":{"affects":["initiative","situational_awareness"],"name":"mind"},"name":"attr_mind","title":"@{mind}","type":"select"} -->
|
|
<option value="0" selected="">0
|
|
</option><!-- triggerObj: {"value":-1,"name":"attr_mind","title":"@{mind}","type":"select"} -->
|
|
<option value="-1">-1
|
|
</option><!-- triggerObj: {"value":-2,"name":"attr_mind","title":"@{mind}","type":"select"} -->
|
|
<option value="-2">-2
|
|
</option><!-- triggerObj: {"value":-3,"name":"attr_mind","title":"@{mind}","type":"select"} -->
|
|
<option value="-3">-3
|
|
</option>
|
|
</select>
|
|
<input class="boxed" name="attr_mind_mod" type="number" title="@{mind_mod}"/>
|
|
<button class="roller" name="roll_spirit" role="heading" aria-level="4" data-i18n="spirit" value="@{spirit_action}" title="%{spirit}" type="roll">
|
|
</button>
|
|
<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}"><!-- triggerObj: {"value":3,"name":"attr_spirit","title":"@{spirit}","type":"select"} -->
|
|
<option value="3">3
|
|
</option><!-- triggerObj: {"value":2,"name":"attr_spirit","title":"@{spirit}","type":"select"} -->
|
|
<option value="2">2
|
|
</option><!-- triggerObj: {"value":1,"name":"attr_spirit","title":"@{spirit}","type":"select"} -->
|
|
<option value="1">1
|
|
</option><!-- triggerObj: {"value":0,"selected":"","trigger":{"affects":["resist","health_max","situational_awareness"],"name":"spirit"},"name":"attr_spirit","title":"@{spirit}","type":"select"} -->
|
|
<option value="0" selected="">0
|
|
</option><!-- triggerObj: {"value":-1,"name":"attr_spirit","title":"@{spirit}","type":"select"} -->
|
|
<option value="-1">-1
|
|
</option><!-- triggerObj: {"value":-2,"name":"attr_spirit","title":"@{spirit}","type":"select"} -->
|
|
<option value="-2">-2
|
|
</option><!-- triggerObj: {"value":-3,"name":"attr_spirit","title":"@{spirit}","type":"select"} -->
|
|
<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" type="checkbox" title="@{repeating_equipment_$X_collapse}"/>
|
|
<input class="underlined" name="attr_name" role="heading" aria-level="3" type="text" title="@{repeating_equipment_$X_name}"/>
|
|
<input class="underlined" name="attr_quantity" type="number" title="@{repeating_equipment_$X_quantity}"/>
|
|
<div class="headed-textarea notes expanded">
|
|
<h5 data-i18n="notes">
|
|
</h5>
|
|
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{repeating_equipment_$X_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>
|
|
<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>
|
|
<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" type="checkbox" title="@{repeating_skill_$X_raw}"/>
|
|
<button class="raw roller" name="roll_roll" value="@{roll_action}" title="%{repeating_skill_$X_roll}" type="roll"><span name="attr_name" data-i18n-dynamic="" role="heading" aria-level="5" title="@{repeating_skill_$X_name}"></span>
|
|
</button>
|
|
<button name="act_roll-action" hidden="" type="action" title="%{repeating_skill_$X_roll-action}">
|
|
</button>
|
|
<input name="attr_roll_action" type="hidden" title="@{repeating_skill_$X_roll_action}"/>
|
|
<button class="custom roller" name="roll_roll" value="@{roll_action}" title="%{repeating_skill_$X_roll}" type="roll">
|
|
</button>
|
|
<button name="act_roll-action" hidden="" type="action" title="%{repeating_skill_$X_roll-action}">
|
|
</button>
|
|
<input name="attr_roll_action" type="hidden" title="@{repeating_skill_$X_roll_action}"/>
|
|
<input class="custom underlined" name="attr_name" role="heading" aria-level="5" type="text" title="@{repeating_skill_$X_name}"/>
|
|
<input class="underlined" name="attr_level" type="number" title="@{repeating_skill_$X_level}"/>
|
|
<select class="underlined" name="attr_stat" title="@{repeating_skill_$X_stat}"><!-- triggerObj: {"value":"query","data-i18n":"ask","selected":"","trigger":{"triggeredFuncs":["skillEffects"],"name":"repeating_skill_$X_stat","listener":"change:repeating_skill:stat","listenerFunc":"accessSheet","type":"select","defaultValue":"query","affects":[]},"name":"attr_stat","title":"@{repeating_skill_$X_stat}","type":"select"} -->
|
|
<option value="query" data-i18n="ask" selected="">
|
|
</option><!-- triggerObj: {"value":"body","data-i18n":"body","name":"attr_stat","title":"@{repeating_skill_$X_stat}","type":"select"} -->
|
|
<option value="body" data-i18n="body">
|
|
</option><!-- triggerObj: {"value":"mind","data-i18n":"mind","name":"attr_stat","title":"@{repeating_skill_$X_stat}","type":"select"} -->
|
|
<option value="mind" data-i18n="mind">
|
|
</option><!-- triggerObj: {"value":"spirit","data-i18n":"spirit","name":"attr_stat","title":"@{repeating_skill_$X_stat}","type":"select"} -->
|
|
<option value="spirit" data-i18n="spirit">
|
|
</option>
|
|
</select>
|
|
<input class="underlined" name="attr_xp" type="number" title="@{repeating_skill_$X_xp}"/>
|
|
</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" type="checkbox" title="@{repeating_advantage_$X_collapse}"/>
|
|
<input class="underlined" name="attr_name" role="heading" aria-level="3" type="text" title="@{repeating_advantage_$X_name}"/>
|
|
<div class="headed-textarea notes expanded">
|
|
<h5 data-i18n="notes">
|
|
</h5>
|
|
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{repeating_advantage_$X_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" type="checkbox" title="@{repeating_disadvantage_$X_collapse}"/>
|
|
<input class="underlined" name="attr_name" role="heading" aria-level="3" type="text" title="@{repeating_disadvantage_$X_name}"/>
|
|
<div class="headed-textarea notes expanded">
|
|
<h5 data-i18n="notes">
|
|
</h5>
|
|
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{repeating_disadvantage_$X_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" type="checkbox" title="@{repeating_adjustment_$X_collapse}"/>
|
|
<input class="underlined" name="attr_name" role="heading" aria-level="3" type="text" title="@{repeating_adjustment_$X_name}"/>
|
|
<div class="headed-textarea mod expanded">
|
|
<h5 data-i18n="notes">
|
|
</h5>
|
|
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{repeating_adjustment_$X_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="@{repeating_weapon_$X_collapse}"/>
|
|
<button class="roller" name="roll_roll" value="@{roll_action}" title="%{repeating_weapon_$X_roll}" type="roll">
|
|
</button>
|
|
<button name="act_roll-action" hidden="" type="action" title="%{repeating_weapon_$X_roll-action}">
|
|
</button>
|
|
<input name="attr_roll_action" type="hidden" title="@{repeating_weapon_$X_roll_action}"/>
|
|
<input class="underlined" name="attr_name" type="text" title="@{repeating_weapon_$X_name}"/>
|
|
<input name="attr_skill" value="select" type="hidden" title="@{repeating_weapon_$X_skill}"/>
|
|
<button class="pseudo-select" name="act_skill-action" type="action" title="%{repeating_weapon_$X_skill-action}">
|
|
<input class="translate-control" name="attr_translate_skill" hidden="" value="1" checked="" type="checkbox" title="@{repeating_weapon_$X_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" type="number" title="@{repeating_weapon_$X_damage}"/>
|
|
</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="@{repeating_weapon_$X_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="@{repeating_structure_$X_fill}"/>
|
|
<input class="health-track__clear-control health-track__checkbox" name="attr_damaged" value="1" type="checkbox" title="@{repeating_structure_$X_damaged}"/>
|
|
<button class="health-track__clearer" name="act_clear" type="action" title="%{repeating_structure_$X_clear}">
|
|
</button>
|
|
<input class="boxed health-track__number" name="attr_penalty" type="number" title="@{repeating_structure_$X_penalty}"/>
|
|
</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>
|
|
<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>
|
|
<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}"/>
|
|
<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>
|
|
<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>
|
|
<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>
|
|
<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}"/>
|
|
<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>
|
|
<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}"/>
|
|
<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>
|
|
<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" value="5" type="hidden" title="@{structure|max}"/>
|
|
<input name="attr_structure_base" value="5" type="number" title="@{structure_base}"/>
|
|
</div>
|
|
</div>
|
|
<label class="assault_damage_label" data-i18n="assault damage" role="heading" aria-level="4" for="assault_damage"></label><!-- inputObj: {"name":"assault damage","class":"boxed assault_damage_input","type":"number","id":"assault_damage","trigger":{"affects":["repeating_remnant-weapon_$x_damage"]}}-->
|
|
<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><!-- inputObj: {"name":"strike damage","class":"boxed strike_damage_input","type":"number","id":"strike_damage","trigger":{"affects":["repeating_remnant-weapon_$x_damage"]}}-->
|
|
<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><!-- inputObj: {"name":"strike range","class":"boxed strike_range_input","type":"number","id":"strike_range","trigger":{"calculation":"calcStrikeRange"}}-->
|
|
<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="@{repeating_specialty_$X_collapse}"/>
|
|
<input class="underlined" name="attr_name" type="text" title="@{repeating_specialty_$X_name}"/>
|
|
<div class="headed-textarea description expanded">
|
|
<h5 data-i18n="description">
|
|
</h5>
|
|
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_description" title="@{repeating_specialty_$X_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="@{repeating_trait_$X_collapse}"/>
|
|
<input class="underlined" name="attr_name" type="text" title="@{repeating_trait_$X_name}"/>
|
|
<input class="underlined" name="attr_duress" type="number" title="@{repeating_trait_$X_duress}"/>
|
|
<input class="underlined" name="attr_focus" type="number" title="@{repeating_trait_$X_focus}"/>
|
|
<div class="headed-textarea description expanded">
|
|
<h5 data-i18n="description">
|
|
</h5>
|
|
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_description" title="@{repeating_trait_$X_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="@{repeating_drone_$X_collapse}"/>
|
|
<input class="underlined remnant-drone__name" name="attr_name" role="heading" aria-level="3" type="text" title="@{repeating_drone_$X_name}"/>
|
|
<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="@{repeating_drone_$X_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_struct" type="number" title="@{repeating_drone_$X_struct}"/><span class="slash">/</span>
|
|
<input name="attr_struct_max" type="number" title="@{repeating_drone_$X_struct|max}"/>
|
|
</div>
|
|
</div>
|
|
<input class="underlined expanded--empty" name="attr_assault" type="hidden" title="@{repeating_drone_$X_assault}"/>
|
|
<div class="input-label input-label--button assault">
|
|
<button class="roller" name="roll_assault" role="heading" aria-level="5" data-i18n="assault" value="@{assault_action}" title="%{repeating_drone_$X_assault}" type="roll">
|
|
</button>
|
|
<input class="boxed input-label__input" name="attr_assault" type="number" title="@{repeating_drone_$X_assault}"/>
|
|
</div>
|
|
<button name="act_assault-action" hidden="" type="action" title="%{repeating_drone_$X_assault-action}">
|
|
</button>
|
|
<input name="attr_assault_action" type="hidden" title="@{repeating_drone_$X_assault_action}"/>
|
|
<input class="underlined expanded--empty" name="attr_assault" type="hidden" title="@{repeating_drone_$X_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="@{repeating_drone_$X_strike}"/>
|
|
<div class="input-label input-label--button strike">
|
|
<button class="roller" name="roll_strike" role="heading" aria-level="5" data-i18n="strike" value="@{strike_action}" title="%{repeating_drone_$X_strike}" type="roll">
|
|
</button>
|
|
<input class="boxed input-label__input" name="attr_strike" type="number" title="@{repeating_drone_$X_strike}"/>
|
|
</div>
|
|
<button name="act_strike-action" hidden="" type="action" title="%{repeating_drone_$X_strike-action}">
|
|
</button>
|
|
<input name="attr_strike_action" type="hidden" title="@{repeating_drone_$X_strike_action}"/>
|
|
<input class="underlined expanded--empty" name="attr_strike" type="hidden" title="@{repeating_drone_$X_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="@{repeating_drone_$X_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" readonly="" title="@{repeating_drone_$X_strikerange}"/>
|
|
</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="@{repeating_drone_$X_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="@{repeating_remnant-weapon_$X_collapse}"/>
|
|
<button class="roller" name="roll_roll" value="@{roll_action}" title="%{repeating_remnant-weapon_$X_roll}" type="roll">
|
|
</button>
|
|
<button name="act_roll-action" hidden="" type="action" title="%{repeating_remnant-weapon_$X_roll-action}">
|
|
</button>
|
|
<input name="attr_roll_action" type="hidden" title="@{repeating_remnant-weapon_$X_roll_action}"/>
|
|
<input class="underlined" name="attr_name" role="heading" aria-level="4" type="text" title="@{repeating_remnant-weapon_$X_name}"/>
|
|
<select class="underlined" name="attr_skill" title="@{repeating_remnant-weapon_$X_skill}"><!-- triggerObj: {"value":" ","data-i18n":"select","selected":"","trigger":{"affects":["repeating_remnant-weapon_$x_damage"],"name":"repeating_remnant-weapon_$X_skill","listener":"change:repeating_remnant-weapon:skill","listenerFunc":"accessSheet","type":"select","defaultValue":" ","triggeredFuncs":[]},"name":"attr_skill","title":"@{repeating_remnant-weapon_$X_skill}","type":"select"} -->
|
|
<option value=" " data-i18n="select" selected="">
|
|
</option><!-- triggerObj: {"value":"assault","data-i18n":"assault","name":"attr_skill","title":"@{repeating_remnant-weapon_$X_skill}","type":"select"} -->
|
|
<option value="assault" data-i18n="assault">
|
|
</option><!-- triggerObj: {"value":"strike","data-i18n":"strike","name":"attr_skill","title":"@{repeating_remnant-weapon_$X_skill}","type":"select"} -->
|
|
<option value="strike" data-i18n="strike">
|
|
</option>
|
|
</select>
|
|
<input class="underlined" name="attr_bonus" type="number" title="@{repeating_remnant-weapon_$X_bonus}"/>
|
|
<div class="roll-plus"><span data-i18n="lead"></span><span class="plus">+</span>
|
|
<input class="underlined" name="attr_damage" readonly="" type="number" title="@{repeating_remnant-weapon_$X_damage}"/>
|
|
</div>
|
|
<input class="underlined damage-bonus" name="attr_damage_bonus" type="number" title="@{repeating_remnant-weapon_$X_damage_bonus}"/>
|
|
<input class="underlined" name="attr_type" type="text" title="@{repeating_remnant-weapon_$X_type}"/>
|
|
<div class="headed-textarea description expanded">
|
|
<h5 data-i18n="notes">
|
|
</h5>
|
|
<div class="adaptive-text"><span class="adaptive-text__span" name="attr_notes" title="@{repeating_remnant-weapon_$X_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*/
|
|
//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;
|
|
};
|
|
|
|
//Parses out the components of a trigger name. Giving:
|
|
//section: e.g repeating_inventory
|
|
//rowID: e.g. -189Jlkjw-89
|
|
//Field: e.g. strength
|
|
//section and rowID may return as undefined
|
|
const parseTriggerName = function(string){
|
|
let match = string.replace(/^clicked:/,'').match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
|
|
match.shift();
|
|
return match;
|
|
};
|
|
const parseClickTrigger = parseTriggerName;
|
|
|
|
//parses out the attribute name from the htmlattribute name property
|
|
const parseHTMLName = function(string){
|
|
let match = string.match(/(?:attr|act|roll)_(.+)/);
|
|
match.shift();
|
|
return match[0];
|
|
};
|
|
|
|
//Capitalize each word in a string
|
|
const capitalize = function(string){
|
|
return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase());
|
|
};
|
|
|
|
//extracts a roll query result for use in later functions
|
|
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,'');
|
|
};
|
|
|
|
//Simulates a query for ensuring that async/await works correctly in the sheetworker environment
|
|
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,'');
|
|
};
|
|
|
|
//# 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,'')}`;
|
|
};
|
|
|
|
const repeatingSectionDetails = [{"section":"repeating_health","fields":["fill","damaged","penalty"]},{"section":"repeating_equipment","fields":["collapse","name","quantity","notes"]},{"section":"repeating_skill","fields":["raw","name","roll_action","level","stat","xp"]},{"section":"repeating_advantage","fields":["collapse","name","notes"]},{"section":"repeating_disadvantage","fields":["collapse","name","notes"]},{"section":"repeating_adjustment","fields":["collapse","name","notes"]},{"section":"repeating_weapon","fields":["collapse","roll_action","name","skill","translate_skill","damage","notes"]},{"section":"repeating_structure","fields":["fill","damaged","penalty"]},{"section":"repeating_specialty","fields":["collapse","name","description"]},{"section":"repeating_trait","fields":["collapse","name","duress","focus","description"]},{"section":"repeating_drone","fields":["collapse","name","active","sa","def","arm","spd","struct","struct_max","assault","assault_action","assault_dam","strike","strike_action","strike_dam","strikerange","description"]},{"section":"repeating_remnant-weapon","fields":["collapse","roll_action","name","skill","bonus","damage","damage_bonus","type","notes"]}];
|
|
|
|
const cascades = {"attr_template_start":{"name":"template_start","type":"hidden","defaultValue":"&{template:rapidfire} {{footer=@{character_name}}} {{character_id=@{character_id}}} {{system=@{system}}}","triggeredFuncs":[],"affects":[]},"attr_collapsed":{"name":"collapsed","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_system":{"name":"system","type":"hidden","defaultValue":"remnants","triggeredFuncs":["setupSystem"],"affects":[],"listener":"change:system","listenerFunc":"accessSheet"},"attr_sheet_state":{"name":"sheet_state","type":"hidden","defaultValue":"settings","triggeredFuncs":[],"affects":[]},"attr_system_header":{"name":"system_header","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_nav-character":{"listenerFunc":"navigateSheet","name":"nav-character","listener":"clicked:nav-character","type":"action"},"act_nav-settings":{"listenerFunc":"navigateSheet","name":"nav-settings","listener":"clicked:nav-settings","type":"action"},"act_nav-battle-remnant":{"listenerFunc":"navigateSheet","name":"nav-battle-remnant","listener":"clicked:nav-battle-remnant","type":"action"},"attr_difficulty":{"name":"difficulty","type":"checkbox","defaultValue":"1","triggeredFuncs":[],"affects":[]},"attr_action_penalty_automation":{"name":"action_penalty_automation","type":"select","defaultValue":"disabled","triggeredFuncs":[],"affects":[]},"attr_whisper":{"name":"whisper","type":"select","defaultValue":" ","triggeredFuncs":[],"affects":[]},"attr_character_name":{"listener":"change:character_name","triggeredFuncs":["setActionCalls"],"name":"character_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_player_name":{"name":"player_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_nationality":{"name":"nationality","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_organization_affiliations":{"name":"organization_affiliations","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_appearance/mannerisms":{"name":"appearance/mannerisms","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_health_max":{"name":"health_max","type":"hidden","defaultValue":"1","triggeredFuncs":["calcHealth"],"affects":["defence"],"listener":"change:health_max","listenerFunc":"accessSheet"},"attr_repeating_health_$X_fill":{"name":"repeating_health_$X_fill","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_health_$X_damaged":{"affects":["health"],"triggeredFuncs":["checkHealth"],"name":"repeating_health_$X_damaged","listener":"change:repeating_health:damaged","listenerFunc":"accessSheet","type":"checkbox","defaultValue":0},"act_repeating_health_$X_clear":{"listenerFunc":"clearTracker","name":"repeating_health_$X_clear","listener":"clicked:repeating_health:clear","type":"action"},"attr_repeating_health_$X_penalty":{"affects":["defence"],"name":"repeating_health_$X_penalty","listener":"change:repeating_health:penalty","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_stat_points":{"name":"stat_points","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_reserve":{"name":"reserve","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_body-action":{"listenerFunc":"initiateRoll","name":"body-action","listener":"clicked:body-action","type":"action"},"attr_body_action":{"name":"body_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_body":{"name":"body","type":"select","defaultValue":3,"triggeredFuncs":[],"affects":["defence","health_max","situational_awareness"],"listener":"change:body","listenerFunc":"accessSheet"},"attr_body_mod":{"affects":["defence","health_max","situational_awareness"],"name":"body_mod","listener":"change:body_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"act_mind-action":{"listenerFunc":"initiateRoll","name":"mind-action","listener":"clicked:mind-action","type":"action"},"attr_mind_action":{"name":"mind_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_mind":{"name":"mind","type":"select","defaultValue":3,"triggeredFuncs":[],"affects":["initiative","situational_awareness"],"listener":"change:mind","listenerFunc":"accessSheet"},"attr_mind_mod":{"affects":["initiative","situational_awareness"],"name":"mind_mod","listener":"change:mind_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"act_spirit-action":{"listenerFunc":"initiateRoll","name":"spirit-action","listener":"clicked:spirit-action","type":"action"},"attr_spirit_action":{"name":"spirit_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_spirit":{"name":"spirit","type":"select","defaultValue":3,"triggeredFuncs":[],"affects":["resist","health_max","situational_awareness"],"listener":"change:spirit","listenerFunc":"accessSheet"},"attr_spirit_mod":{"affects":["resist","health_max","situational_awareness"],"name":"spirit_mod","listener":"change:spirit_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_easy_living":{"name":"easy_living","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-equipment":{"listenerFunc":"addItem","name":"add-equipment","listener":"clicked:add-equipment","type":"action"},"act_edit-equipment":{"listenerFunc":"editSection","name":"edit-equipment","listener":"clicked:edit-equipment","type":"action"},"attr_repeating_equipment_$X_collapse":{"name":"repeating_equipment_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_equipment_$X_name":{"name":"repeating_equipment_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_equipment_$X_quantity":{"name":"repeating_equipment_$X_quantity","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_equipment_$X_notes":{"name":"repeating_equipment_$X_notes","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_initiative-action":{"listenerFunc":"initiateRoll","name":"initiative-action","listener":"clicked:initiative-action","type":"action"},"attr_initiative_action":{"name":"initiative_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_initiative":{"calculation":"calcInitiative","name":"initiative","type":"number","defaultValue":"0","triggeredFuncs":[],"affects":[]},"attr_initiative_mod":{"affects":["initiative"],"name":"initiative_mod","listener":"change:initiative_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_defence":{"calculation":"calcDefence","name":"defence","type":"number","defaultValue":3,"triggeredFuncs":[],"affects":[]},"attr_defence_mod":{"affects":["defence"],"name":"defence_mod","listener":"change:defence_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_resist":{"calculation":"calcResist","name":"resist","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_resist_mod":{"affects":["resist"],"name":"resist_mod","listener":"change:resist_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_health":{"affects":["defence"],"calculation":"totalHealth","initialFunc":"syncHealth","name":"health","listener":"change:health","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_health_mod":{"affects":["health_max"],"name":"health_mod","listener":"change:health_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_armour_bonus":{"affects":["resist"],"name":"armour_bonus","listener":"change:armour_bonus","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_shield_bonus":{"affects":["defence"],"name":"shield_bonus","listener":"change:shield_bonus","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_action_penalty":{"triggeredFuncs":["validateActionPenalty"],"name":"action_penalty","listener":"change:action_penalty","listenerFunc":"accessSheet","type":"number","defaultValue":"","affects":[]},"act_add-skill":{"listenerFunc":"addItem","name":"add-skill","listener":"clicked:add-skill","type":"action"},"act_edit-skill":{"listenerFunc":"editSection","name":"edit-skill","listener":"clicked:edit-skill","type":"action"},"attr_repeating_skill_$X_raw":{"name":"repeating_skill_$X_raw","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_skill_$X_name":{"name":"repeating_skill_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_repeating_skill_$X_roll-action":{"listenerFunc":"initiateRoll","name":"repeating_skill_$X_roll-action","listener":"clicked:repeating_skill:roll-action","type":"action"},"attr_repeating_skill_$X_roll_action":{"name":"repeating_skill_$X_roll_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_skill_$X_level":{"triggeredFuncs":["skillEffects"],"name":"repeating_skill_$X_level","listener":"change:repeating_skill:level","listenerFunc":"accessSheet","type":"number","defaultValue":"","affects":[]},"attr_repeating_skill_$X_stat":{"triggeredFuncs":["skillEffects"],"name":"repeating_skill_$X_stat","listener":"change:repeating_skill:stat","listenerFunc":"accessSheet","type":"select","defaultValue":"query","affects":[]},"attr_repeating_skill_$X_xp":{"name":"repeating_skill_$X_xp","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-advantage":{"listenerFunc":"addItem","name":"add-advantage","listener":"clicked:add-advantage","type":"action"},"act_edit-advantage":{"listenerFunc":"editSection","name":"edit-advantage","listener":"clicked:edit-advantage","type":"action"},"attr_repeating_advantage_$X_collapse":{"name":"repeating_advantage_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_advantage_$X_name":{"name":"repeating_advantage_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_advantage_$X_notes":{"name":"repeating_advantage_$X_notes","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-disadvantage":{"listenerFunc":"addItem","name":"add-disadvantage","listener":"clicked:add-disadvantage","type":"action"},"act_edit-disadvantage":{"listenerFunc":"editSection","name":"edit-disadvantage","listener":"clicked:edit-disadvantage","type":"action"},"attr_repeating_disadvantage_$X_collapse":{"name":"repeating_disadvantage_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_disadvantage_$X_name":{"name":"repeating_disadvantage_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_disadvantage_$X_notes":{"name":"repeating_disadvantage_$X_notes","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-adjustment":{"listenerFunc":"addItem","name":"add-adjustment","listener":"clicked:add-adjustment","type":"action"},"act_edit-adjustment":{"listenerFunc":"editSection","name":"edit-adjustment","listener":"clicked:edit-adjustment","type":"action"},"attr_repeating_adjustment_$X_collapse":{"name":"repeating_adjustment_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_adjustment_$X_name":{"name":"repeating_adjustment_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_adjustment_$X_notes":{"name":"repeating_adjustment_$X_notes","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-weapon":{"listenerFunc":"addItem","name":"add-weapon","listener":"clicked:add-weapon","type":"action"},"act_edit-weapon":{"listenerFunc":"editSection","name":"edit-weapon","listener":"clicked:edit-weapon","type":"action"},"attr_repeating_weapon_$X_collapse":{"name":"repeating_weapon_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_repeating_weapon_$X_roll-action":{"listenerFunc":"initiateRoll","name":"repeating_weapon_$X_roll-action","listener":"clicked:repeating_weapon:roll-action","type":"action"},"attr_repeating_weapon_$X_roll_action":{"name":"repeating_weapon_$X_roll_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_weapon_$X_name":{"name":"repeating_weapon_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_weapon_$X_skill":{"name":"repeating_weapon_$X_skill","type":"hidden","defaultValue":"select","triggeredFuncs":[],"affects":[]},"act_repeating_weapon_$X_skill-action":{"listenerFunc":"dynamicSelect","name":"repeating_weapon_$X_skill-action","listener":"clicked:repeating_weapon:skill-action","type":"action"},"attr_repeating_weapon_$X_translate_skill":{"name":"repeating_weapon_$X_translate_skill","type":"checkbox","defaultValue":"1","triggeredFuncs":[],"affects":[]},"attr_repeating_weapon_$X_damage":{"name":"repeating_weapon_$X_damage","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_weapon_$X_notes":{"name":"repeating_weapon_$X_notes","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_structure_max":{"name":"structure_max","type":"hidden","defaultValue":"1","triggeredFuncs":["calcHealth"],"affects":["remnant_defence"],"listener":"change:structure_max","listenerFunc":"accessSheet"},"attr_repeating_structure_$X_fill":{"name":"repeating_structure_$X_fill","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_structure_$X_damaged":{"affects":["structure"],"triggeredFuncs":["checkHealth"],"name":"repeating_structure_$X_damaged","listener":"change:repeating_structure:damaged","listenerFunc":"accessSheet","type":"checkbox","defaultValue":0},"act_repeating_structure_$X_clear":{"listenerFunc":"clearTracker","name":"repeating_structure_$X_clear","listener":"clicked:repeating_structure:clear","type":"action"},"attr_repeating_structure_$X_penalty":{"affects":["defence"],"name":"repeating_structure_$X_penalty","listener":"change:repeating_structure:penalty","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_duress":{"name":"duress","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_remnant_token":{"triggeredFuncs":["imageInput"],"name":"remnant_token","listener":"change:remnant_token","listenerFunc":"accessSheet","type":"text","defaultValue":"https://s3.amazonaws.com/files.d20.io/images/259054779/OzN3yQwg7MbfXNtMYs-iOg/original.png","affects":[]},"act_remnant-token":{"listenerFunc":"toggleImageInput","name":"remnant-token","listener":"clicked:remnant-token","type":"action"},"act_situational-awareness-action":{"listenerFunc":"initiateRoll","name":"situational-awareness-action","listener":"clicked:situational-awareness-action","type":"action"},"attr_situational_awareness_action":{"name":"situational_awareness_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_situational_awareness":{"affects":["remnant_initiative","remnant_defence"],"calculation":"calcSA","name":"situational_awareness","listener":"change:situational_awareness","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_situational_awareness_mod":{"affects":["situational_awareness"],"name":"situational_awareness_mod","listener":"change:situational_awareness_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_remnant_defence":{"affects":[],"calculation":"calcRemnantDefence","name":"remnant_defence","listener":"change:remnant_defence","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_remnant_defence_mod":{"affects":["remnant_defence"],"name":"remnant_defence_mod","listener":"change:remnant_defence_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"act_remnant-initiative-action":{"listenerFunc":"initiateRoll","name":"remnant-initiative-action","listener":"clicked:remnant-initiative-action","type":"action"},"attr_remnant_initiative_action":{"name":"remnant_initiative_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_remnant_initiative":{"calculation":"calcRemnantInitiative","name":"remnant_initiative","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_remnant_initiative_mod":{"affects":["remnant_initiative"],"name":"remnant_initiative_mod","listener":"change:remnant_initiative_mod","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"act_assault-roll-action":{"listenerFunc":"initiateRoll","name":"assault-roll-action","listener":"clicked:assault-roll-action","type":"action"},"attr_assault_roll_action":{"name":"assault_roll_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_assault_level":{"name":"assault_level","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_assault_experience":{"name":"assault_experience","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_strike-roll-action":{"listenerFunc":"initiateRoll","name":"strike-roll-action","listener":"clicked:strike-roll-action","type":"action"},"attr_strike_roll_action":{"name":"strike_roll_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_strike_level":{"affects":["strike_range"],"name":"strike_level","listener":"change:strike_level","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_strike_experience":{"name":"strike_experience","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_motion-roll-action":{"listenerFunc":"initiateRoll","name":"motion-roll-action","listener":"clicked:motion-roll-action","type":"action"},"attr_motion_roll_action":{"name":"motion_roll_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_motion_level":{"affects":["remnant_defence"],"name":"motion_level","listener":"change:motion_level","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_motion_experience":{"name":"motion_experience","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_armour":{"name":"armour","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_speed":{"affects":["remnant_defence"],"name":"speed","listener":"change:speed","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_inspiring":{"name":"inspiring","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_terrifying":{"name":"terrifying","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_structure":{"affects":["remnant_defence"],"calculation":"totalHealth","initialFunc":"syncHealth","name":"structure","listener":"change:structure","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_structure_base":{"affects":["structure_max"],"name":"structure_base","listener":"change:structure_base","listenerFunc":"accessSheet","type":"number","defaultValue":5,"triggeredFuncs":[]},"attr_assault_damage":{"affects":["repeating_remnant-weapon_$x_damage"],"name":"assault_damage","listener":"change:assault_damage","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_strike_damage":{"affects":["repeating_remnant-weapon_$x_damage"],"name":"strike_damage","listener":"change:strike_damage","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_strike_range":{"calculation":"calcStrikeRange","name":"strike_range","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-specialty":{"listenerFunc":"addItem","name":"add-specialty","listener":"clicked:add-specialty","type":"action"},"act_edit-specialty":{"listenerFunc":"editSection","name":"edit-specialty","listener":"clicked:edit-specialty","type":"action"},"attr_repeating_specialty_$X_collapse":{"name":"repeating_specialty_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_specialty_$X_name":{"name":"repeating_specialty_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_specialty_$X_description":{"name":"repeating_specialty_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-trait":{"listenerFunc":"addItem","name":"add-trait","listener":"clicked:add-trait","type":"action"},"act_edit-trait":{"listenerFunc":"editSection","name":"edit-trait","listener":"clicked:edit-trait","type":"action"},"attr_repeating_trait_$X_collapse":{"name":"repeating_trait_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_trait_$X_name":{"name":"repeating_trait_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_trait_$X_duress":{"name":"repeating_trait_$X_duress","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_trait_$X_focus":{"name":"repeating_trait_$X_focus","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_trait_$X_description":{"name":"repeating_trait_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-drone":{"listenerFunc":"addItem","name":"add-drone","listener":"clicked:add-drone","type":"action"},"act_edit-drone":{"listenerFunc":"editSection","name":"edit-drone","listener":"clicked:edit-drone","type":"action"},"attr_repeating_drone_$X_collapse":{"name":"repeating_drone_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_name":{"name":"repeating_drone_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_active":{"name":"repeating_drone_$X_active","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_sa":{"name":"repeating_drone_$X_sa","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_def":{"name":"repeating_drone_$X_def","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_arm":{"name":"repeating_drone_$X_arm","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_spd":{"name":"repeating_drone_$X_spd","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_struct":{"name":"repeating_drone_$X_struct","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_struct_max":{"name":"repeating_drone_$X_struct_max","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_assault":{"name":"repeating_drone_$X_assault","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_repeating_drone_$X_assault-action":{"listenerFunc":"initiateRoll","name":"repeating_drone_$X_assault-action","listener":"clicked:repeating_drone:assault-action","type":"action"},"attr_repeating_drone_$X_assault_action":{"name":"repeating_drone_$X_assault_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_assault_dam":{"name":"repeating_drone_$X_assault_dam","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_strike":{"name":"repeating_drone_$X_strike","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":["repeating_drone_$x_strikerange"],"listener":"change:repeating_drone:strike","listenerFunc":"accessSheet"},"act_repeating_drone_$X_strike-action":{"listenerFunc":"initiateRoll","name":"repeating_drone_$X_strike-action","listener":"clicked:repeating_drone:strike-action","type":"action"},"attr_repeating_drone_$X_strike_action":{"name":"repeating_drone_$X_strike_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_strike_dam":{"name":"repeating_drone_$X_strike_dam","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_strikerange":{"calculation":"calcStrikeRange","name":"repeating_drone_$X_strikerange","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_drone_$X_description":{"name":"repeating_drone_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-remnant-weapon":{"listenerFunc":"addItem","name":"add-remnant-weapon","listener":"clicked:add-remnant-weapon","type":"action"},"act_edit-remnant-weapon":{"listenerFunc":"editSection","name":"edit-remnant-weapon","listener":"clicked:edit-remnant-weapon","type":"action"},"attr_repeating_remnant-weapon_$X_collapse":{"name":"repeating_remnant-weapon_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_repeating_remnant-weapon_$X_roll-action":{"listenerFunc":"initiateRoll","name":"repeating_remnant-weapon_$X_roll-action","listener":"clicked:repeating_remnant-weapon:roll-action","type":"action"},"attr_repeating_remnant-weapon_$X_roll_action":{"name":"repeating_remnant-weapon_$X_roll_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_remnant-weapon_$X_name":{"name":"repeating_remnant-weapon_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_remnant-weapon_$X_skill":{"affects":["repeating_remnant-weapon_$x_damage"],"name":"repeating_remnant-weapon_$X_skill","listener":"change:repeating_remnant-weapon:skill","listenerFunc":"accessSheet","type":"select","defaultValue":" ","triggeredFuncs":[]},"attr_repeating_remnant-weapon_$X_bonus":{"name":"repeating_remnant-weapon_$X_bonus","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_remnant-weapon_$X_damage":{"calculation":"calcRemnantDamage","name":"repeating_remnant-weapon_$X_damage","type":"number","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_remnant-weapon_$X_damage_bonus":{"affects":["repeating_remnant-weapon_$x_damage"],"name":"repeating_remnant-weapon_$X_damage_bonus","listener":"change:repeating_remnant-weapon:damage_bonus","listenerFunc":"accessSheet","type":"number","defaultValue":"","triggeredFuncs":[]},"attr_repeating_remnant-weapon_$X_type":{"name":"repeating_remnant-weapon_$X_type","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_remnant-weapon_$X_notes":{"name":"repeating_remnant-weapon_$X_notes","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]}};
|
|
|
|
const trackerMonitors = ["repeating_health:clear","repeating_structure:clear"];
|
|
|
|
const navButtons = ["character","settings","battle-remnant"];
|
|
|
|
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"];
|
|
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
//Sheet Variables
|
|
const sheetName = 'Rapidfire System';
|
|
const version = 1.01;
|
|
let debugMode = false;
|
|
//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 systems = ['remnants'];
|
|
const repeatMonitor = [];
|
|
const listeners = {};
|
|
const dynamicQueries = {};
|
|
const baseGet = Object.entries(cascades).reduce((memo,[attrName,detailObj])=>{
|
|
if(!/repeating/.test(attrName) && detailObj.type !== 'action'){
|
|
memo.push(detailObj.name);
|
|
}
|
|
if(detailObj.listener){
|
|
listeners[detailObj.listener] = detailObj.listenerFunc;
|
|
}
|
|
return memo;
|
|
},['sheet_state','section_state','character_name']);
|
|
const updateHandlers = {};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
//# Attribute Obj Proxy handler
|
|
const createAttrProxy = function(attrs){
|
|
//creates a proxy for the attributes object so that values can be worked with more easily.
|
|
const getCascObj = function(event,casc){
|
|
let typePrefix = event.htmlAttributes ? 'act_' : 'attr_';
|
|
let cascName = `${typePrefix}${event.sourceAttribute}`;
|
|
let cascObj = casc[cascName];
|
|
return cascObj;
|
|
};
|
|
|
|
const triggerFunctions = function(obj,attributes,sections){
|
|
if(obj.triggeredFuncs && obj.triggeredFuncs.length){
|
|
debug(`triggering functions for ${obj.name}`);
|
|
obj.triggeredFuncs && obj.triggeredFuncs.forEach(func=>funcs[func] ?
|
|
funcs[func]({trigger:obj,attributes,sections}) :
|
|
debug(`!!!Warning!!! no function named ${func} found. Triggered function not called for ${obj.name}`,true));
|
|
}
|
|
};
|
|
|
|
const initialFunction = function(obj,attributes,sections){
|
|
if(obj.initialFunc){
|
|
debug(`initial functions for ${obj.name}`);
|
|
funcs[obj.initialFunc] ?
|
|
funcs[obj.initialFunc]({trigger:obj,attributes,sections}) :
|
|
debug(`!!!Warning!!! no function named ${obj.initialFunc} found. Initial function not called for ${obj.name}`,true);
|
|
}
|
|
};
|
|
const processChange = function({event,trigger,attributes,sections,casc}){
|
|
debug({trigger});
|
|
if(event && !trigger){
|
|
debug('initial change detected. No trigger found');
|
|
return;
|
|
}
|
|
if(!attributes || !sections || !casc){
|
|
debug(`!!! Insufficient arguments || attributes > ${!!attributes} | sections > ${!!sections} | casc > ${!!casc} !!!`);
|
|
return;
|
|
}
|
|
//store the queue in attributes.
|
|
if(event){
|
|
debug('checking for initial functions');
|
|
initialFunction(trigger,attributes,sections);//functions that should only be run if the attribute was the thing changed by the user
|
|
attributes.queue = [...trigger.affects];
|
|
}
|
|
if(trigger){
|
|
debug(`processing ${trigger.name}`);
|
|
triggerFunctions(trigger,attributes,sections);
|
|
if(!event && trigger.calculation){
|
|
attributes[trigger.name] = funcs[trigger.calculation]({trigger,attributes,sections});
|
|
}
|
|
}
|
|
if(event || attributes.updates.hasOwnProperty(trigger.name)){
|
|
attributes.queue.push(...trigger.affects);
|
|
}
|
|
attributes.set({attributes,sections,casc});
|
|
};
|
|
const attrTarget = {
|
|
updates:{},
|
|
attributes:{...attrs},
|
|
repOrders:{},
|
|
queue: [],
|
|
casc:{},
|
|
processChange,
|
|
triggerFunctions,
|
|
initialFunction,
|
|
getCascObj
|
|
};
|
|
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(){
|
|
let {attributes,sections,casc,callback,vocal} = arguments[0] ? arguments[0] : {};
|
|
if(attributes && attributes.queue.length && sections && casc){
|
|
let triggerName = attributes.queue.shift();
|
|
let trigger = getCascObj({sourceAttribute:triggerName},casc);
|
|
attributes.processChange({trigger,attributes,sections,casc});
|
|
}else{
|
|
debug('sheet accessed, all changes handled, setting attributes');
|
|
set(obj.updates,vocal,callback);
|
|
}
|
|
}
|
|
}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 = `attr_${prop.replace(/(repeating_[^_]+_)[^_]+/,'$1\$x')}`;
|
|
let numRetVal = +retValue;
|
|
if(!Number.isNaN(numRetVal)){
|
|
retValue = numRetVal;
|
|
}else if(cascades[cascRef] && typeof cascades[cascRef].defaultValue === 'number'){
|
|
retValue = cascades[cascRef].defaultValue;
|
|
}
|
|
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 if(`${obj.attributes}` !== `${value}` ||
|
|
(obj.updates[prop] && `${obj.updates}` !== `${value}`)
|
|
){
|
|
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 funcs = new Proxy({},{
|
|
set:function(obj,prop,value){
|
|
if(obj[prop]){
|
|
debug(`!!! Duplicate function name for ${prop} !!!`);
|
|
return false;
|
|
}else{
|
|
obj[prop] = value;
|
|
return true;
|
|
}
|
|
},
|
|
get:function(obj,prop){
|
|
return function(){
|
|
debug(`running ${prop}`);
|
|
return obj[prop](...arguments);
|
|
};
|
|
}
|
|
});//Sheet Updaters and styling functions
|
|
const updateSheet = function(){
|
|
log('updating sheet');
|
|
getAllAttrs({props:['sheet_version','debug_mode','collapsed',...baseGet],callback:(attributes,sections,casc)=>{
|
|
debugMode = !!attributes.debug_mode;
|
|
if(!attributes.sheet_version){
|
|
initialSetup(attributes,sections);
|
|
}else{
|
|
Object.entries(updateHandlers).forEach(([ver,handler])=>{
|
|
if(attributes.version < +ver){
|
|
handler({attributes,sections,casc});
|
|
}
|
|
});
|
|
}
|
|
attributes.sheet_version = version;
|
|
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);
|
|
};
|
|
|
|
//These functions access the sheet and iterate through all changes necessary before calling setAttrs
|
|
const accessSheet = function(event){
|
|
debug({event});
|
|
getAllAttrs({event,callback:(attributes,sections,casc)=>{
|
|
let trigger = attributes.getCascObj(event,casc);
|
|
attributes.processChange({event,trigger,attributes,sections,casc});
|
|
}});
|
|
};
|
|
funcs.accessSheet = accessSheet;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
/*
|
|
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(/^(?:act|attr)_repeating_/.test(key)){//If the attribute is a repeating attribute, do special logic
|
|
expandRepeating(memo,key,cascade,sections,attributes);
|
|
}else if(key){//for non repeating attributes do this logic
|
|
expandNormal(memo,key,cascade,sections);
|
|
}
|
|
return memo;
|
|
},{});
|
|
};
|
|
|
|
const expandRepeating = function(memo,key,cascade,sections,attributes){
|
|
key.replace(/((?:attr|act)_)(repeating_[^_]+)_[^_]+?_(.+)/,(match,type,section,field)=>{
|
|
(sections[section]||[]).forEach((id)=>{
|
|
memo[`${type}${section}_${id}_${field}`]=_.clone(cascade[key]);//clone the details so that each row's attributes have correct ids
|
|
memo[`${type}${section}_${id}_${field}`].name = `${section}_${id}_${field}`;
|
|
if(key.startsWith('attr_')){
|
|
memo[`${type}${section}_${id}_${field}`].affects = memo[`${type}${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]);
|
|
if(key.startsWith('attr_')){
|
|
memo[key].affects = memo[key].affects.reduce((m,a)=>{
|
|
if(/^repeating/.test(a)){
|
|
addAllRows(a,m,sections);
|
|
}else{
|
|
m.push(a);
|
|
}
|
|
return m;
|
|
},[]);
|
|
}
|
|
};
|
|
|
|
const addAllRows = function(affected,memo,sections){
|
|
affected.replace(/(repeating_[^_]+?)_[^_]+?_(.+)/,(match,section,field)=>{
|
|
sections[section].forEach(id=>memo.push(`${section}_${id}_${field}`));
|
|
});
|
|
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
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,event}){
|
|
getAttrs(props,(values)=>{
|
|
const attributes = createAttrProxy(values,event);
|
|
callback(attributes);
|
|
});
|
|
};
|
|
|
|
const getAllAttrs = function({callback,props=baseGet,sectionDetails=repeatingSectionDetails}){
|
|
debug({props});
|
|
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});
|
|
};/*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);
|
|
};
|
|
funcs.navigateSheet = navigateSheet;
|
|
|
|
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();
|
|
});
|
|
};
|
|
funcs.expandHeader = expandHeader;
|
|
|
|
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');
|
|
};
|
|
funcs.imageInput = imageInput;
|
|
|
|
const toggleImageInput = function(jEvent){
|
|
debug({jEvent});
|
|
let field = parseHTMLName(jEvent.htmlAttributes.name);
|
|
debug({field});
|
|
$20(`.${field.replace(/_/g,'-')} .image-container__input`).toggleClass('active');
|
|
};
|
|
funcs.toggleImageInput = toggleImageInput;
|
|
|
|
//Calls the appropriate dynamic query function for the event
|
|
const dynamicSelect = function(event){
|
|
let[section,rowID,field] = parseClickTrigger(event.triggerName);
|
|
field = field.replace(/-action$/,'');
|
|
if(dynamicQueries[field]){
|
|
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();
|
|
}});
|
|
}
|
|
};
|
|
funcs.dynamicSelect = dynamicSelect;
|
|
|
|
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);
|
|
};
|
|
funcs.skillQuery = skillQuery;
|
|
dynamicQueries.skill = skillQuery;
|
|
//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_${attr}`];
|
|
attributes.processChange({trigger,attributes,sections,casc});
|
|
}});
|
|
};
|
|
funcs.clearTracker = clearTracker;
|
|
|
|
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);
|
|
};
|
|
funcs.addItem = addItem;
|
|
|
|
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');
|
|
};
|
|
funcs.editSection = editSection;/*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}]]`;
|
|
};
|
|
|
|
//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';
|
|
};
|
|
|
|
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] = parseTriggerName(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);
|
|
}});
|
|
};
|
|
funcs.initiateRoll = initiateRoll;
|
|
|
|
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');
|
|
debug({sections,trigger});
|
|
const type = trigger.name.replace(/_(?:mod|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`];
|
|
}
|
|
};
|
|
funcs.syncHealth = syncHealth;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
// Triggered Functions
|
|
const setActionCalls = function({attributes,sections}){
|
|
debug('setting action calls');
|
|
debug({actionAttributes});
|
|
debug({attributes});
|
|
debug({character_name:attributes.character_name,assault_roll_action:attributes.assault_roll_action});
|
|
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}}`;
|
|
}
|
|
});
|
|
};
|
|
funcs.setActionCalls = setActionCalls;
|
|
|
|
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;
|
|
});
|
|
};
|
|
funcs.checkHealth = checkHealth;
|
|
|
|
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);
|
|
});
|
|
}
|
|
};
|
|
funcs.updateTrack = updateTrack;
|
|
|
|
const maxHealth = function({type,attributes}){
|
|
const typeSwitch = {
|
|
structure:()=> attributes.structure_base,
|
|
health:()=> 3 + attributes.body + attributes.body_mod + attributes.spirit + attributes.spirit_mod + attributes.health_mod
|
|
}
|
|
return Math.max(typeSwitch[type](),1);
|
|
};
|
|
funcs.maxHealth = maxHealth;
|
|
|
|
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({section:type,attributes,sections});
|
|
attributes[type] = attributes[type] + healthDiff;
|
|
syncHealth({trigger,attributes,sections});
|
|
};
|
|
funcs.calcHealth = calcHealth;
|
|
|
|
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]];
|
|
}
|
|
};
|
|
funcs.skillEffects = skillEffects;
|
|
|
|
const validateActionPenalty = function({attributes}){
|
|
if(attributes.action_penalty > 0){
|
|
attributes.action_penalty = attributes.action_penalty * -1;
|
|
}
|
|
};
|
|
funcs.validateActionPenalty = validateActionPenalty;/*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.mind_mod + attributes.initiative_mod;
|
|
};
|
|
funcs.calcInitiative = calcInitiative;
|
|
|
|
//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;
|
|
};
|
|
|
|
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 + attributes.body_mod + dodge + attributes.shield_bonus + damagePenalty + attributes.defence_mod;
|
|
return Math.max(total,0);
|
|
};
|
|
funcs.calcDefence = calcDefence;
|
|
|
|
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`]);
|
|
};
|
|
funcs.totalHealth = totalHealth;
|
|
|
|
const calcResist = function({trigger,attributes,sections}){
|
|
debug('calculating resist');
|
|
return Math.max(attributes.spirit + attributes.spirit_mod + attributes.armour_bonus + attributes.resist_mod,0);
|
|
};
|
|
funcs.calcResist = calcResist;
|
|
|
|
const calcSA = function({trigger,attributes,sections}){
|
|
debug('calculating situational awareness');
|
|
return ['body','mind','spirit'].reduce((m,attr)=>{
|
|
return m + attributes[attr] + attributes[`${attr}_mod`];
|
|
},0);
|
|
};
|
|
funcs.calcSA = calcSA;
|
|
|
|
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;
|
|
};
|
|
funcs.calcRemnantInitiative = calcRemnantInitiative;
|
|
|
|
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);
|
|
};
|
|
funcs.calcRemnantDefence = calcRemnantDefence;
|
|
|
|
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;
|
|
};
|
|
funcs.calcStrikeRange = calcStrikeRange;
|
|
|
|
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`];
|
|
};
|
|
funcs.calcRemnantDamage = calcRemnantDamage;const update1x01 = function({attributes,sections}){
|
|
sections.repeating_drone.forEach((id)=>{
|
|
attributes[`repeating_drone_${id}_struct`] = attributes[`repeating_drone_${id}_structure`];
|
|
attributes[`repeating_drone_${id}_struct_max`] = attributes[`repeating_drone_${id}_structure_max`];
|
|
});
|
|
};
|
|
updateHandlers['1.01'] = update1x01;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
|
|
const registerEventHandlers = function(){
|
|
on('sheet:opened',updateSheet);
|
|
|
|
//Roll20 change and click listeners
|
|
Object.entries(listeners).forEach(([event,funcName])=>{
|
|
if(funcs[funcName]){
|
|
on(event,funcs[funcName]);
|
|
}else{
|
|
debug(`!!!Warning!!! no function named ${funcName} found. No listener created for ${event}`,true);
|
|
}
|
|
});
|
|
|
|
//jQuery listeners
|
|
$20('.expandable-header').on('click',expandHeader);
|
|
$20('.image-container__button').on('click',toggleImageInput);
|
|
};
|
|
registerEventHandlers();
|
|
log(`Sheet Loaded`);
|
|
</script> |