mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-09-11 09:32:33 +00:00
3463 lines
192 KiB
HTML
3463 lines
192 KiB
HTML
|
|
<input name="attr_sheet_state" value="settings" type="hidden" title="@{sheet_state}"/>
|
|
<input name="attr_collapsed" value="" type="hidden" title="@{collapsed}"/>
|
|
<div class="subsystem-style compendium-drop-target threats" id="main">
|
|
<input name="attr_drop_name" accept="Name" value="" type="hidden" title="@{drop_name}"/>
|
|
<input name="attr_drop_data" accept="data" value="" type="hidden" title="@{drop_data}"/>
|
|
<nav class="sheet-nav" id="main-nav">
|
|
<button class="subsystem-style sheet-nav__tab sheet-nav__tab--possible sheet-nav__tab--possible--character all" name="act_nav-all" data-i18n="all pages" data-i18n-title="View all sections" role="heading" aria-level="5" type="action" title="%{nav-all}">
|
|
</button>
|
|
<button class="subsystem-style sheet-nav__tab sheet-nav__tab--possible sheet-nav__tab--possible--character general" name="act_nav-general" data-i18n="general pages" data-i18n-title="View general information" role="heading" aria-level="5" type="action" title="%{nav-general}">
|
|
</button>
|
|
<button class="subsystem-style sheet-nav__tab sheet-nav__tab--possible sheet-nav__tab--possible--character powers" name="act_nav-powers" data-i18n="powers pages" data-i18n-title="View powers information" role="heading" aria-level="5" type="action" title="%{nav-powers}">
|
|
</button>
|
|
<button class="subsystem-style sheet-nav__tab sheet-nav__tab--possible sheet-nav__tab--possible--antagonist antagonist" name="act_nav-antagonist" data-i18n="threat" data-i18n-title="View threat" role="heading" aria-level="5" type="action" title="%{nav-antagonist}">
|
|
</button>
|
|
<button class="subsystem-style sheet-nav__tab sheet-nav__tab--possible sheet-nav__tab--possible--gm_screen gm_screen" name="act_nav-gm-screen" data-i18n="gm screen" data-i18n-title="View gm screen" role="heading" aria-level="5" type="action" title="%{nav-gm-screen}">
|
|
</button>
|
|
<button class="subsystem-style sheet-nav__tab sheet-nav__tab--possible sheet-nav__tab--possible--party_screen party_screen" name="act_nav-party-screen" data-i18n="party screen" data-i18n-title="View party screen" role="heading" aria-level="5" type="action" title="%{nav-party-screen}">
|
|
</button>
|
|
<button class="pictos active sheet-nav__tab settings" name="act_nav-settings" data-i18n-title="open the settings page" role="heading" aria-level="5" type="action" title="%{nav-settings}">y
|
|
</button>
|
|
</nav>
|
|
<article class="navigable-section--settings navigable-section settings active" id="settings">
|
|
<section class="section-system-panel" id="system-panel">
|
|
<h2 data-i18n="system options"></h2>
|
|
<label class="input-label"><span data-i18n="system"></span>
|
|
<select class="underlined input-label__input" name="attr_system" title="@{system}">
|
|
<option value="beneath-the-sea" data-i18n="beneath-the-sea" selected="">
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label class="input-label"><span data-i18n="sheet type"></span>
|
|
<select class="underlined input-label__input" name="attr_sheet_type" title="@{sheet_type}">
|
|
<option value="character" data-i18n="character" selected="">
|
|
</option>
|
|
<option value="antagonist" data-i18n="threat">
|
|
</option>
|
|
<option value="gm_screen" data-i18n="gm screen">
|
|
</option>
|
|
<option value="party_screen" data-i18n="party screen">
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label class="input-label"><span data-i18n="target number"></span>
|
|
<input class="underlined input-label__input" name="attr_target_number" type="number" value="8" title="@{target_number}"/>
|
|
</label>
|
|
<label class="input-label"><span data-i18n="again"></span>
|
|
<input class="underlined input-label__input" name="attr_again" type="number" value="10" min="2" max="10" title="@{again}"/>
|
|
</label>
|
|
</section>
|
|
<section class="section-sheet-behavior" id="sheet-behavior">
|
|
<h2 data-i18n="sheet behavior"></h2>
|
|
<label class="input-label"><span data-i18n="difficulty automation"></span>
|
|
<select class="underlined input-label__input" name="attr_difficulty_query" title="@{difficulty_query}">
|
|
<option value="off" data-i18n="off" selected="">
|
|
</option>
|
|
<option value="on" data-i18n="on">
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label class="input-label"><span data-i18n="enhancement automation"></span>
|
|
<select class="underlined input-label__input" name="attr_enhancement_query" title="@{enhancement_query}">
|
|
<option value="off" data-i18n="off" selected="">
|
|
</option>
|
|
<option value="on" data-i18n="on">
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label class="input-label"><span data-i18n="rolls are"></span>
|
|
<select class="underlined input-label__input" name="attr_whisper" title="@{whisper}">
|
|
<option value="" data-i18n="public" selected="">
|
|
</option>
|
|
<option value="/w gm " data-i18n="whispered to gm">
|
|
</option>
|
|
</select>
|
|
</label>
|
|
</section>
|
|
</article>
|
|
<article class="navigable-section sheet navigable-section--all navigable-section--general navigable-section--powers subsystem-style system-beneath-the-sea" id="sheet">
|
|
<section class="section-image-head" id="image-head"><img class="subsystem-display beneath-the-sea active" data-i18n-alt="they came from beneath the sea" role="heading" aria-level="1" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/f9a2525f4d31787a29939df4b72610354a1bd4fe/they_came_from/assets/logo.png"/></section>
|
|
<section class="section-character_details" id="character_details">
|
|
<label class="input-label stacked"><span data-i18n="name"></span>
|
|
<input class="underlined input-label__input" type="text" name="attr_character_name" role="heading" aria-level="4" title="@{character_name}"/>
|
|
</label>
|
|
<label class="input-label stacked"><span data-i18n="player"></span>
|
|
<input class="underlined input-label__input" type="text" name="attr_player" role="heading" aria-level="4" title="@{player}"/>
|
|
</label>
|
|
<label class="input-label stacked"><span data-i18n="concept"></span>
|
|
<input class="underlined input-label__input" type="text" name="attr_concept" role="heading" aria-level="4" title="@{concept}"/>
|
|
</label>
|
|
<label class="input-label stacked"><span data-i18n="archetype"></span>
|
|
<input class="underlined input-label__input" type="text" name="attr_archetype" role="heading" aria-level="4" title="@{archetype}"/>
|
|
</label>
|
|
</section>
|
|
<section class="section-skills navigable-section navigable-section--all navigable-section--general" id="skills">
|
|
<button class="subsystem-style expandable-header skills" name="act_skills" data-i18n="skills" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{skills}">
|
|
</button><span class="locked column-2" hidden=""></span>
|
|
<fieldset class="repeating_skill">
|
|
<input name="attr_name" hidden="" type="text" title="@{repeating_skill_$X_name}"/>
|
|
<input name="attr_path" type="checkbox" title="@{repeating_skill_$X_path}"/>
|
|
<div class="dot-row">
|
|
<button class="roller" name="roll_roll" value="@{action}" title="%{repeating_skill_$X_roll}" type="roll"><span name="attr_name" data-i18n-dynamic="" title="@{repeating_skill_$X_name}"></span>
|
|
</button>
|
|
<button name="act_action" hidden="" type="action" title="%{repeating_skill_$X_action}">
|
|
</button>
|
|
<input name="attr_action" type="hidden" title="@{repeating_skill_$X_action}"/>
|
|
<input class="subsystem-display scion-origin scion-hero active underlined" name="attr_specialty" type="text" title="@{repeating_skill_$X_specialty}"/>
|
|
<select class="dot-number underlined" name="attr_rating" title="@{repeating_skill_$X_rating}">
|
|
<option value="0" selected="">5
|
|
</option>
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="clearer dot" name="attr_rating" checked="" value="0" type="radio" title="@{repeating_skill_$X_rating}"/>
|
|
<input class="dot" name="attr_rating" value="1" type="radio" title="@{repeating_skill_$X_rating}"/>
|
|
<input class="dot" name="attr_rating" value="2" type="radio" title="@{repeating_skill_$X_rating}"/>
|
|
<input class="dot" name="attr_rating" value="3" type="radio" title="@{repeating_skill_$X_rating}"/>
|
|
<input class="dot" name="attr_rating" value="4" type="radio" title="@{repeating_skill_$X_rating}"/>
|
|
<input class="dot" name="attr_rating" value="5" type="radio" title="@{repeating_skill_$X_rating}"/>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-attributes navigable-section navigable-section--all navigable-section--general" id="attributes">
|
|
<button class="subsystem-style expandable-header attributes" name="act_attributes" data-i18n="attributes" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{attributes}">
|
|
</button>
|
|
<h3 class="subsystem-display scion-hero scion-origin active" data-i18n="mental"></h3>
|
|
<h3 class="subsystem-display scion-hero scion-origin active" data-i18n="physical"></h3>
|
|
<h3 class="subsystem-display scion-hero scion-origin" data-i18n="social"></h3>
|
|
<div class="approach-container power">
|
|
<h4 class="subsystem-display scion-hero scion-origin active" data-i18n="power"></h4>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_intellect" role="header" aria-level="5" data-i18n="intellect" value="@{intellect_action}" title="%{intellect}" type="roll">
|
|
</button>
|
|
<button name="act_intellect-action" hidden="" type="action" title="%{intellect-action}">
|
|
</button>
|
|
<input name="attr_intellect_action" type="hidden" title="@{intellect_action}"/>
|
|
<select class="dot-number underlined" name="attr_intellect" title="@{intellect}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_intellect" value="1" type="radio" title="@{intellect}"/>
|
|
<input class="no-clear dot" name="attr_intellect" value="2" type="radio" title="@{intellect}"/>
|
|
<input class="no-clear dot" name="attr_intellect" value="3" type="radio" title="@{intellect}"/>
|
|
<input class="no-clear dot" name="attr_intellect" value="4" type="radio" title="@{intellect}"/>
|
|
<input class="no-clear dot" name="attr_intellect" value="5" type="radio" title="@{intellect}"/>
|
|
</div>
|
|
</div>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_might" role="header" aria-level="5" data-i18n="might" value="@{might_action}" title="%{might}" type="roll">
|
|
</button>
|
|
<button name="act_might-action" hidden="" type="action" title="%{might-action}">
|
|
</button>
|
|
<input name="attr_might_action" type="hidden" title="@{might_action}"/>
|
|
<select class="dot-number underlined" name="attr_might" title="@{might}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_might" value="1" type="radio" title="@{might}"/>
|
|
<input class="no-clear dot" name="attr_might" value="2" type="radio" title="@{might}"/>
|
|
<input class="no-clear dot" name="attr_might" value="3" type="radio" title="@{might}"/>
|
|
<input class="no-clear dot" name="attr_might" value="4" type="radio" title="@{might}"/>
|
|
<input class="no-clear dot" name="attr_might" value="5" type="radio" title="@{might}"/>
|
|
</div>
|
|
</div>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_presence" role="header" aria-level="5" data-i18n="presence" value="@{presence_action}" title="%{presence}" type="roll">
|
|
</button>
|
|
<button name="act_presence-action" hidden="" type="action" title="%{presence-action}">
|
|
</button>
|
|
<input name="attr_presence_action" type="hidden" title="@{presence_action}"/>
|
|
<select class="dot-number underlined" name="attr_presence" title="@{presence}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_presence" value="1" type="radio" title="@{presence}"/>
|
|
<input class="no-clear dot" name="attr_presence" value="2" type="radio" title="@{presence}"/>
|
|
<input class="no-clear dot" name="attr_presence" value="3" type="radio" title="@{presence}"/>
|
|
<input class="no-clear dot" name="attr_presence" value="4" type="radio" title="@{presence}"/>
|
|
<input class="no-clear dot" name="attr_presence" value="5" type="radio" title="@{presence}"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="approach-container finesse">
|
|
<h4 class="subsystem-display scion-hero scion-origin active" data-i18n="finesse"></h4>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_cunning" role="header" aria-level="5" data-i18n="cunning" value="@{cunning_action}" title="%{cunning}" type="roll">
|
|
</button>
|
|
<button name="act_cunning-action" hidden="" type="action" title="%{cunning-action}">
|
|
</button>
|
|
<input name="attr_cunning_action" type="hidden" title="@{cunning_action}"/>
|
|
<select class="dot-number underlined" name="attr_cunning" title="@{cunning}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_cunning" value="1" type="radio" title="@{cunning}"/>
|
|
<input class="no-clear dot" name="attr_cunning" value="2" type="radio" title="@{cunning}"/>
|
|
<input class="no-clear dot" name="attr_cunning" value="3" type="radio" title="@{cunning}"/>
|
|
<input class="no-clear dot" name="attr_cunning" value="4" type="radio" title="@{cunning}"/>
|
|
<input class="no-clear dot" name="attr_cunning" value="5" type="radio" title="@{cunning}"/>
|
|
</div>
|
|
</div>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_dexterity" role="header" aria-level="5" data-i18n="dexterity" value="@{dexterity_action}" title="%{dexterity}" type="roll">
|
|
</button>
|
|
<button name="act_dexterity-action" hidden="" type="action" title="%{dexterity-action}">
|
|
</button>
|
|
<input name="attr_dexterity_action" type="hidden" title="@{dexterity_action}"/>
|
|
<select class="dot-number underlined" name="attr_dexterity" title="@{dexterity}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_dexterity" value="1" type="radio" title="@{dexterity}"/>
|
|
<input class="no-clear dot" name="attr_dexterity" value="2" type="radio" title="@{dexterity}"/>
|
|
<input class="no-clear dot" name="attr_dexterity" value="3" type="radio" title="@{dexterity}"/>
|
|
<input class="no-clear dot" name="attr_dexterity" value="4" type="radio" title="@{dexterity}"/>
|
|
<input class="no-clear dot" name="attr_dexterity" value="5" type="radio" title="@{dexterity}"/>
|
|
</div>
|
|
</div>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_manipulation" role="header" aria-level="5" data-i18n="manipulation" value="@{manipulation_action}" title="%{manipulation}" type="roll">
|
|
</button>
|
|
<button name="act_manipulation-action" hidden="" type="action" title="%{manipulation-action}">
|
|
</button>
|
|
<input name="attr_manipulation_action" type="hidden" title="@{manipulation_action}"/>
|
|
<select class="dot-number underlined" name="attr_manipulation" title="@{manipulation}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_manipulation" value="1" type="radio" title="@{manipulation}"/>
|
|
<input class="no-clear dot" name="attr_manipulation" value="2" type="radio" title="@{manipulation}"/>
|
|
<input class="no-clear dot" name="attr_manipulation" value="3" type="radio" title="@{manipulation}"/>
|
|
<input class="no-clear dot" name="attr_manipulation" value="4" type="radio" title="@{manipulation}"/>
|
|
<input class="no-clear dot" name="attr_manipulation" value="5" type="radio" title="@{manipulation}"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="approach-container resistance">
|
|
<h4 class="subsystem-display scion-hero scion-origin active" data-i18n="resilience"></h4>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_resolve" role="header" aria-level="5" data-i18n="resolve" value="@{resolve_action}" title="%{resolve}" type="roll">
|
|
</button>
|
|
<button name="act_resolve-action" hidden="" type="action" title="%{resolve-action}">
|
|
</button>
|
|
<input name="attr_resolve_action" type="hidden" title="@{resolve_action}"/>
|
|
<select class="dot-number underlined" name="attr_resolve" title="@{resolve}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_resolve" value="1" type="radio" title="@{resolve}"/>
|
|
<input class="no-clear dot" name="attr_resolve" value="2" type="radio" title="@{resolve}"/>
|
|
<input class="no-clear dot" name="attr_resolve" value="3" type="radio" title="@{resolve}"/>
|
|
<input class="no-clear dot" name="attr_resolve" value="4" type="radio" title="@{resolve}"/>
|
|
<input class="no-clear dot" name="attr_resolve" value="5" type="radio" title="@{resolve}"/>
|
|
</div>
|
|
</div>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_stamina" role="header" aria-level="5" data-i18n="stamina" value="@{stamina_action}" title="%{stamina}" type="roll">
|
|
</button>
|
|
<button name="act_stamina-action" hidden="" type="action" title="%{stamina-action}">
|
|
</button>
|
|
<input name="attr_stamina_action" type="hidden" title="@{stamina_action}"/>
|
|
<select class="dot-number underlined" name="attr_stamina" title="@{stamina}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_stamina" value="1" type="radio" title="@{stamina}"/>
|
|
<input class="no-clear dot" name="attr_stamina" value="2" type="radio" title="@{stamina}"/>
|
|
<input class="no-clear dot" name="attr_stamina" value="3" type="radio" title="@{stamina}"/>
|
|
<input class="no-clear dot" name="attr_stamina" value="4" type="radio" title="@{stamina}"/>
|
|
<input class="no-clear dot" name="attr_stamina" value="5" type="radio" title="@{stamina}"/>
|
|
</div>
|
|
</div>
|
|
<div class="dot-row size-number subsystem-display scion-hero scion-origin active">
|
|
<button class="roller" name="roll_composure" role="header" aria-level="5" data-i18n="composure" value="@{composure_action}" title="%{composure}" type="roll">
|
|
</button>
|
|
<button name="act_composure-action" hidden="" type="action" title="%{composure-action}">
|
|
</button>
|
|
<input name="attr_composure_action" type="hidden" title="@{composure_action}"/>
|
|
<select class="dot-number underlined" name="attr_composure" title="@{composure}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_composure" value="1" type="radio" title="@{composure}"/>
|
|
<input class="no-clear dot" name="attr_composure" value="2" type="radio" title="@{composure}"/>
|
|
<input class="no-clear dot" name="attr_composure" value="3" type="radio" title="@{composure}"/>
|
|
<input class="no-clear dot" name="attr_composure" value="4" type="radio" title="@{composure}"/>
|
|
<input class="no-clear dot" name="attr_composure" value="5" type="radio" title="@{composure}"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<section class="section-health" id="health">
|
|
<button class="subsystem-style expandable-header health" name="act_health" data-i18n="health" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{health}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-injury" data-i18n-title="create a injury" type="action" title="%{add-injury}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-injury" data-i18n-title="edit section" type="action" title="%{edit-injury}">
|
|
</button>
|
|
<div class="input-label input-label--button initiative-label">
|
|
<button class="roller" name="roll_pc_initiative" data-i18n="initiative" role="heading" aria-level="5" value="@{pc_initiative_action}" title="%{pc_initiative}" type="roll">
|
|
</button>
|
|
<button name="act_pc-initiative-action" hidden="" type="action" title="%{pc-initiative-action}">
|
|
</button>
|
|
<input name="attr_pc_initiative_action" type="hidden" title="@{pc_initiative_action}"/>
|
|
<select class="underlined" name="attr_initiative_skill" id="initiative-skill-select" title="@{initiative_skill}">
|
|
<option value="" data-i18n="select one">
|
|
</option>
|
|
</select>
|
|
</div><span class="pseudo-control" hidden=""></span>
|
|
<fieldset class="repeating_injury">
|
|
<input class="type-control" name="attr_display_state" value="bruised" type="hidden" title="@{repeating_injury_$X_display_state}"/>
|
|
<div class="type">
|
|
<input class="collapse-control" name="attr_collapse" value="1" type="hidden" title="@{repeating_injury_$X_collapse}"/>
|
|
<label class="collapse-interface">
|
|
<input name="attr_collapse" value="1" hidden="" checked="" type="checkbox" title="@{repeating_injury_$X_collapse}"/>
|
|
</label>
|
|
<input name="attr_affected" type="checkbox" title="@{repeating_injury_$X_affected}"/>
|
|
<select class="underlined" name="attr_type" title="@{repeating_injury_$X_type}">
|
|
<option value="Just a Flesh Wound" data-i18n="Just a Flesh Wound" selected="">
|
|
</option>
|
|
<option value="That'll Leave a Scar" data-i18n="That'll Leave a Scar">
|
|
</option>
|
|
<option value="Last-Ditch Effort" data-i18n="Last-Ditch Effort">
|
|
</option>
|
|
<option value="Don't Forget Me" data-i18n="Don't Forget Me">
|
|
</option>
|
|
<option value="Death Scene" data-i18n="Death Scene">
|
|
</option>
|
|
</select>
|
|
<div class="adaptive adaptive--text description"><span class="adaptive--text__span" name="attr_effect" title="@{repeating_injury_$X_effect}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_effect" data-i18n-placeholder="injury effect" title="@{repeating_injury_$X_effect}"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="taken-out">
|
|
<input name="attr_affected" type="checkbox" title="@{repeating_injury_$X_affected}"/><span name="attr_type" data-i18n-dynamic="" title="@{repeating_injury_$X_type}"></span>
|
|
</div>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-aspirations subsystem-display scion-hero scion-origin navigable-section navigable-section--all navigable-section--general" id="aspirations">
|
|
<button class="subsystem-style expandable-header aspirations" name="act_aspirations" data-i18n="aspirations" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{aspirations}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-aspiration" data-i18n-title="create a aspiration" type="action" title="%{add-aspiration}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-aspiration" data-i18n-title="edit section" type="action" title="%{edit-aspiration}">
|
|
</button>
|
|
<div class="experience">
|
|
<h5 data-i18n="experience"></h5>
|
|
<div class="input-label"><span data-i18n="used"></span>
|
|
<input class="underlined" name="attr_experience_used" type="number" title="@{experience_used}"/>
|
|
</div>
|
|
<div class="input-label"><span data-i18n="earned"></span>
|
|
<input class="underlined" name="attr_experience_earned" type="number" title="@{experience_earned}"/>
|
|
</div>
|
|
<div class="input-label"><span data-i18n="remaining"></span>
|
|
<input class="underlined" name="attr_experience_remaining" readonly="" type="number" title="@{experience_remaining}"/>
|
|
</div>
|
|
</div><span class="pseudo-control" hidden=""></span>
|
|
<fieldset class="repeating_aspiration">
|
|
<select name="attr_type" title="@{repeating_aspiration_$X_type}">
|
|
<option value="short" data-i18n="short" selected="">
|
|
</option>
|
|
<option value="long" data-i18n="long">
|
|
</option>
|
|
</select>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_name" title="@{repeating_aspiration_$X_name}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_name" data-i18n-placeholder="aspiration description" title="@{repeating_aspiration_$X_name}"></textarea>
|
|
</div>
|
|
<input name="attr_completed" value="1" type="checkbox" title="@{repeating_aspiration_$X_completed}"/>
|
|
</fieldset>
|
|
<button class="subsystem-style completed-aspiration expandable-header completed aspiration" name="act_completed-aspiration" data-i18n="completed aspirations" aria-level="3" role="heading" data-i18n-title="collapse/expand the section" type="action" title="%{completed-aspiration}">
|
|
</button><span class="pseudo-control" hidden=""></span>
|
|
<fieldset class="repeating_completed-aspiration">
|
|
<select name="attr_type" title="@{repeating_completed-aspiration_$X_type}">
|
|
<option value="short" data-i18n="short" selected="">
|
|
</option>
|
|
<option value="long" data-i18n="long">
|
|
</option>
|
|
</select>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_name" title="@{repeating_completed-aspiration_$X_name}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_name" data-i18n-placeholder="aspiration description" title="@{repeating_completed-aspiration_$X_name}"></textarea>
|
|
</div>
|
|
<input name="attr_completed" value="1" type="checkbox" title="@{repeating_completed-aspiration_$X_completed}"/>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-paths navigable-section navigable-section--all navigable-section--powers" id="paths">
|
|
<button class="subsystem-style expandable-header paths" name="act_paths" data-i18n="paths" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{paths}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-path" data-i18n-title="create a path" type="action" title="%{add-path}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-path" data-i18n-title="edit section" type="action" title="%{edit-path}">
|
|
</button><span class="pseudo-control image-borders" hidden=""></span>
|
|
<fieldset class="repeating_path">
|
|
<input class="border-control" name="attr_display_state" type="hidden" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="image-border">
|
|
<div class="upper-blur"></div>
|
|
<div class="lower-blur"></div>
|
|
</div>
|
|
<input name="attr_master_id" type="hidden" title="@{repeating_path_$X_master_id}"/>
|
|
<input class="display-control" name="attr_display_state" value="short-master" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target short-master">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label><span name="attr_type" role="heading" aria-level="5" title="@{repeating_path_$X_type}"></span><span name="attr_name" title="@{repeating_path_$X_name}"></span>
|
|
<select name="attr_status" title="@{repeating_path_$X_status}">
|
|
<option value="available" data-i18n="available" selected="">
|
|
</option>
|
|
<option value="invoked" data-i18n="invoked">
|
|
</option>
|
|
<option value="suspended" data-i18n="suspended">
|
|
</option>
|
|
<option value="revoked" data-i18n="revoked">
|
|
</option>
|
|
</select><span class="short-text description" name="attr_condition" title="@{repeating_path_$X_condition}"></span>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="master" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target master">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<input class="underlined" name="attr_type" data-i18n-placeholder="type" role="heading" aria-level="5" type="text" title="@{repeating_path_$X_type}"/>
|
|
<input class="underlined" name="attr_name" data-i18n-placeholder="name of the path" type="text" title="@{repeating_path_$X_name}"/>
|
|
<input name="attr_status" value="available" type="hidden" title="@{repeating_path_$X_status}"/>
|
|
<select name="attr_status" title="@{repeating_path_$X_status}">
|
|
<option value="available" data-i18n="available" selected="">
|
|
</option>
|
|
<option value="invoked" data-i18n="invoked">
|
|
</option>
|
|
<option value="suspended" data-i18n="suspended">
|
|
</option>
|
|
<option value="revoked" data-i18n="revoked">
|
|
</option>
|
|
</select>
|
|
<input class="underlined skills" name="attr_skill" data-i18n-placeholder="Associated skills" type="text" title="@{repeating_path_$X_skill}"/>
|
|
<div class="adaptive adaptive--text description"><span class="adaptive--text__span" name="attr_condition" title="@{repeating_path_$X_condition}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_condition" data-i18n-placeholder="Path condition" title="@{repeating_path_$X_condition}"></textarea>
|
|
</div>
|
|
<div class="adaptive adaptive--text description"><span class="adaptive--text__span" name="attr_description" title="@{repeating_path_$X_description}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_description" data-i18n-placeholder="describe the path" title="@{repeating_path_$X_description}"></textarea>
|
|
</div>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="short-contact" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target short-contact">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="contact"></h5><span name="attr_name" title="@{repeating_path_$X_name}"></span>
|
|
<input class="underlined" name="attr_tags" data-i18n-placeholder="tags" type="text" title="@{repeating_path_$X_tags}"/>
|
|
<select class="dot-number underlined" name="attr_rating" title="@{repeating_path_$X_rating}">
|
|
<option value="0">5
|
|
</option>
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="contact" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target contact">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="contact"></h5>
|
|
<input class="underlined" name="attr_name" data-i18n-placeholder="name" type="text" title="@{repeating_path_$X_name}"/>
|
|
<input class="underlined" name="attr_tags" data-i18n-placeholder="tags" type="text" title="@{repeating_path_$X_tags}"/>
|
|
<div class="dot-row size-number">
|
|
<select class="dot-number underlined" name="attr_rating" title="@{repeating_path_$X_rating}">
|
|
<option value="1">5
|
|
</option>
|
|
<option value="2">5
|
|
</option>
|
|
<option value="3">5
|
|
</option>
|
|
<option value="4">5
|
|
</option>
|
|
<option value="5">5
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="no-clear dot" name="attr_rating" value="1" type="radio" title="@{repeating_path_$X_rating}"/>
|
|
<input class="no-clear dot" name="attr_rating" value="2" type="radio" title="@{repeating_path_$X_rating}"/>
|
|
<input class="no-clear dot" name="attr_rating" value="3" type="radio" title="@{repeating_path_$X_rating}"/>
|
|
<input class="no-clear dot" name="attr_rating" value="4" type="radio" title="@{repeating_path_$X_rating}"/>
|
|
<input class="no-clear dot" name="attr_rating" value="5" type="radio" title="@{repeating_path_$X_rating}"/>
|
|
</div>
|
|
</div>
|
|
<div class="adaptive adaptive--text description"><span class="adaptive--text__span" name="attr_description" title="@{repeating_path_$X_description}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_description" data-i18n-placeholder="describe your contact" title="@{repeating_path_$X_description}"></textarea>
|
|
</div>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="short-group" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target short-group">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="group"></h5><span class="short-text" name="attr_group" title="@{repeating_path_$X_group}"></span>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="group" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target group">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="group"></h5>
|
|
<div class="adaptive adaptive--text description"><span class="adaptive--text__span" name="attr_group" title="@{repeating_path_$X_group}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_group" data-i18n-placeholder="describe the group" title="@{repeating_path_$X_group}"></textarea>
|
|
</div>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="short-access" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target short-access">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="access"></h5><span class="short-text" name="attr_access" title="@{repeating_path_$X_access}"></span>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="access" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target access">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="access"></h5>
|
|
<div class="adaptive adaptive--text description"><span class="adaptive--text__span" name="attr_access" title="@{repeating_path_$X_access}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_access" data-i18n-placeholder="describe the access" title="@{repeating_path_$X_access}"></textarea>
|
|
</div>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="short-obligation" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target short-obligation">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="obligation"></h5><span class="short-text" name="attr_obligation" title="@{repeating_path_$X_obligation}"></span>
|
|
<select class="underlined" name="attr_obligation_status" title="@{repeating_path_$X_obligation_status}">
|
|
<option value="pending" data-i18n="pending" selected="">
|
|
</option>
|
|
<option value="completed" data-i18n="completed">
|
|
</option>
|
|
<option value="failed" data-i18n="failed">
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="obligation" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target obligation">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_path_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_path_$X_collapse}"/>
|
|
</label>
|
|
<h5 data-i18n="obligation"></h5>
|
|
<input name="attr_obligation_status" value="pending" type="hidden" title="@{repeating_path_$X_obligation_status}"/>
|
|
<select class="underlined" name="attr_obligation_status" title="@{repeating_path_$X_obligation_status}">
|
|
<option value="pending" data-i18n="pending" selected="">
|
|
</option>
|
|
<option value="completed" data-i18n="completed">
|
|
</option>
|
|
<option value="failed" data-i18n="failed">
|
|
</option>
|
|
</select>
|
|
<div class="adaptive adaptive--text description"><span class="adaptive--text__span" name="attr_obligation" title="@{repeating_path_$X_obligation}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_obligation" data-i18n-placeholder="describe the obligation" title="@{repeating_path_$X_obligation}"></textarea>
|
|
</div>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="add-detail" hidden="" type="radio" title="@{repeating_path_$X_display_state}"/>
|
|
<div class="display-target add-detail">
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-contact" data-i18n-title="create a contact" data-i18n="contact" type="action" title="%{repeating_path_$X_add-contact}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-group" data-i18n-title="create a group" data-i18n="group" type="action" title="%{repeating_path_$X_add-group}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-access" data-i18n-title="create a access" data-i18n="access" type="action" title="%{repeating_path_$X_add-access}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-obligation" data-i18n-title="create a obligation" data-i18n="obligation" type="action" title="%{repeating_path_$X_add-obligation}">
|
|
</button>
|
|
</div>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-trademark navigable-section navigable-section--all navigable-section--powers" id="trademark">
|
|
<button class="subsystem-style beneath-the-sea expandable-header trademarks" name="act_trademarks" data-i18n="trademarks" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{trademarks}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-trademark" data-i18n-title="create a trademark" type="action" title="%{add-trademark}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-trademark" data-i18n-title="edit section" type="action" title="%{edit-trademark}">
|
|
</button><span class="pseudo-control" hidden=""></span>
|
|
<fieldset class="repeating_trademark">
|
|
<input name="attr_display_state" value="trademark" type="hidden" title="@{repeating_trademark_$X_display_state}"/>
|
|
<input class="display-control" name="attr_display_state" value="short-trademark" hidden="" type="radio" title="@{repeating_trademark_$X_display_state}"/>
|
|
<div class="display-target short-trademark">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_trademark_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_trademark_$X_collapse}"/>
|
|
</label><span name="attr_name" title="@{repeating_trademark_$X_name}"></span>
|
|
<input name="attr_used_session" value="1" type="checkbox" title="@{repeating_trademark_$X_used_session}"/>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="trademark" hidden="" type="radio" title="@{repeating_trademark_$X_display_state}"/>
|
|
<div class="display-target trademark">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_trademark_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_trademark_$X_collapse}"/>
|
|
</label>
|
|
<input class="underlined" name="attr_name" type="text" value="" title="@{repeating_trademark_$X_name}"/>
|
|
<input name="attr_used_session" value="1" type="checkbox" title="@{repeating_trademark_$X_used_session}"/>
|
|
<label class="input-label"><span class="input-label__text" data-i18n="skill"></span>
|
|
<select class="underlined input-label__input" name="attr_skill" title="@{repeating_trademark_$X_skill}">
|
|
<option value="" data-i18n="select one">
|
|
</option>
|
|
<option value="aim" data-i18n="aim">
|
|
</option>
|
|
<option value="athletics" data-i18n="athletics">
|
|
</option>
|
|
<option value="close combat" data-i18n="close combat">
|
|
</option>
|
|
<option value="command" data-i18n="command">
|
|
</option>
|
|
<option value="culture" data-i18n="culture">
|
|
</option>
|
|
<option value="empathy" data-i18n="empathy">
|
|
</option>
|
|
<option value="enigmas" data-i18n="enigmas">
|
|
</option>
|
|
<option value="humanities" data-i18n="humanities">
|
|
</option>
|
|
<option value="integrity" data-i18n="integrity">
|
|
</option>
|
|
<option value="larceny" data-i18n="larceny">
|
|
</option>
|
|
<option value="medicine" data-i18n="medicine">
|
|
</option>
|
|
<option value="persuasion" data-i18n="persuasion">
|
|
</option>
|
|
<option value="pilot" data-i18n="pilot">
|
|
</option>
|
|
<option value="science" data-i18n="science">
|
|
</option>
|
|
<option value="survival" data-i18n="survival">
|
|
</option>
|
|
<option value="technology" data-i18n="technology">
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_description" title="@{repeating_trademark_$X_description}"></span>
|
|
<textarea class="adaptive--text__textarea" name="attr_description" data-i18n-placeholder="trademark description" title="@{repeating_trademark_$X_description}"></textarea>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-stunt navigable-section navigable-section--all navigable-section--powers" id="stunt">
|
|
<button class="subsystem-style beneath-the-sea expandable-header stunts" name="act_stunts" data-i18n="favored stunts" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{stunts}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-stunt" data-i18n-title="create a stunt" type="action" title="%{add-stunt}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-stunt" data-i18n-title="edit section" type="action" title="%{edit-stunt}">
|
|
</button><span class="pseudo-control" hidden=""></span>
|
|
<fieldset class="repeating_stunt">
|
|
<input name="attr_display_state" value="stunt" type="hidden" title="@{repeating_stunt_$X_display_state}"/>
|
|
<input class="display-control" name="attr_display_state" value="short-stunt" hidden="" type="radio" title="@{repeating_stunt_$X_display_state}"/>
|
|
<div class="display-target short-stunt">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_stunt_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_stunt_$X_collapse}"/>
|
|
</label><span name="attr_name" title="@{repeating_stunt_$X_name}"></span><span class="capitalized" name="attr_type" data-i18n-dynamic="" title="@{repeating_stunt_$X_type}"></span>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="stunt" hidden="" type="radio" title="@{repeating_stunt_$X_display_state}"/>
|
|
<div class="display-target stunt">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_stunt_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_stunt_$X_collapse}"/>
|
|
</label>
|
|
<input class="underlined" name="attr_name" type="text" value="" title="@{repeating_stunt_$X_name}"/>
|
|
<select class="underlined" name="attr_type" title="@{repeating_stunt_$X_type}">
|
|
<option value="" data-i18n="select one">
|
|
</option>
|
|
<option value="simple" data-i18n="simple">
|
|
</option>
|
|
<option value="flashy" data-i18n="flashy">
|
|
</option>
|
|
<option value="daring" data-i18n="daring">
|
|
</option>
|
|
</select>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_description" title="@{repeating_stunt_$X_description}"></span>
|
|
<textarea class="adaptive--text__textarea" name="attr_description" data-i18n-placeholder="stunt description" title="@{repeating_stunt_$X_description}"></textarea>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-quip navigable-section navigable-section--all navigable-section--powers" id="quip">
|
|
<button class="subsystem-style beneath-the-sea expandable-header quips" name="act_quips" data-i18n="quips" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{quips}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-quip" data-i18n-title="create a quip" type="action" title="%{add-quip}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-quip" data-i18n-title="edit section" type="action" title="%{edit-quip}">
|
|
</button><span class="pseudo-control" hidden=""></span>
|
|
<fieldset class="repeating_quip">
|
|
<input name="attr_display_state" value="quip" type="hidden" title="@{repeating_quip_$X_display_state}"/>
|
|
<input class="display-control" name="attr_display_state" value="short-quip" hidden="" type="radio" title="@{repeating_quip_$X_display_state}"/>
|
|
<div class="display-target short-quip">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_quip_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_quip_$X_collapse}"/>
|
|
</label><span name="attr_name" title="@{repeating_quip_$X_name}"></span>
|
|
<input name="attr_used_session" value="1" type="checkbox" title="@{repeating_quip_$X_used_session}"/>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="quip" hidden="" type="radio" title="@{repeating_quip_$X_display_state}"/>
|
|
<div class="display-target quip">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_quip_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_quip_$X_collapse}"/>
|
|
</label>
|
|
<input class="underlined" name="attr_name" type="text" value="" title="@{repeating_quip_$X_name}"/>
|
|
<input name="attr_used_session" value="1" type="checkbox" title="@{repeating_quip_$X_used_session}"/>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_description" title="@{repeating_quip_$X_description}"></span>
|
|
<textarea class="adaptive--text__textarea" name="attr_description" data-i18n-placeholder="quip description" title="@{repeating_quip_$X_description}"></textarea>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-trope navigable-section navigable-section--all navigable-section--powers" id="trope">
|
|
<button class="subsystem-style beneath-the-sea expandable-header tropes" name="act_tropes" data-i18n="tropes" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{tropes}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--add" name="act_add-trope" data-i18n-title="create a trope" type="action" title="%{add-trope}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit" name="act_edit-trope" data-i18n-title="edit section" type="action" title="%{edit-trope}">
|
|
</button><span class="pseudo-control" hidden=""></span>
|
|
<fieldset class="repeating_trope">
|
|
<input name="attr_display_state" value="quip" type="hidden" title="@{repeating_trope_$X_display_state}"/>
|
|
<input class="display-control" name="attr_display_state" value="short-trope" hidden="" type="radio" title="@{repeating_trope_$X_display_state}"/>
|
|
<div class="display-target short-trope">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_trope_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_trope_$X_collapse}"/>
|
|
</label><span name="attr_name" title="@{repeating_trope_$X_name}"></span>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="trope" hidden="" type="radio" title="@{repeating_trope_$X_display_state}"/>
|
|
<div class="display-target trope">
|
|
<input class="collapse-control" name="attr_collapse" value="0" type="hidden" title="@{repeating_trope_$X_collapse}"/>
|
|
<label class="collapse-interface" data-i18n-title="collapse subsection">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_trope_$X_collapse}"/>
|
|
</label>
|
|
<input class="underlined" name="attr_name" type="text" value="" title="@{repeating_trope_$X_name}"/>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_description" title="@{repeating_trope_$X_description}"></span>
|
|
<textarea class="adaptive--text__textarea" name="attr_description" data-i18n-placeholder="trope description" title="@{repeating_trope_$X_description}"></textarea>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
</section>
|
|
<section class="section-notes navigable-section navigable-section--all navigable-section--general" id="notes">
|
|
<button class="subsystem-style expandable-header notes" name="act_notes" data-i18n="notes" role="heading" aria-level="2" data-i18n-title="collapse/expand the section" type="action" title="%{notes}">
|
|
</button>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_notes" title="@{notes}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_notes" title="@{notes}"></textarea>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
<article class="navigable-section navigable-section--antagonist subsystem-style system-tcfbts statblock" id="antagonist">
|
|
<section class="antagonist" id="antagonist-details">
|
|
<div class="statblock__row statblock__row--header">
|
|
<input name="attr_character_name" role="heading" aria-level="1" type="text" title="@{character_name}"/>
|
|
</div>
|
|
<div class="statblock__row">
|
|
<div class="headed-textarea">
|
|
<h2 data-i18n="goals"></h2>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_goals" title="@{goals}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_goals" title="@{goals}"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="statblock__row--flex collapse-container skill-row">
|
|
<input class="collapse" name="attr_skill_collapse" value="1" type="checkbox" title="@{skill_collapse}"/>
|
|
<h2 class="colon" data-i18n="skills"></h2><span class="locked column-2" hidden=""></span>
|
|
<fieldset class="repeating_skill">
|
|
<input name="attr_name" type="hidden" title="@{repeating_skill_$X_name}"/>
|
|
<div class="label-input">
|
|
<button class="roller" name="roll_roll" value="@{action}" title="%{repeating_skill_$X_roll}" type="roll"><span name="attr_name" data-i18n-dynamic="" title="@{repeating_skill_$X_name}"></span>
|
|
</button>
|
|
<button name="act_action" hidden="" type="action" title="%{repeating_skill_$X_action}">
|
|
</button>
|
|
<input name="attr_action" type="hidden" title="@{repeating_skill_$X_action}"/>
|
|
<input class="underlined" name="attr_rating" type="number" title="@{repeating_skill_$X_rating}"/>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
<div class="statblock__row--flex">
|
|
<h2 class="colon" data-i18n="attributes"></h2>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_intellect" data-i18n="intellect" value="@{intellect_action}" title="%{intellect}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_intellect" type="number" value="1" title="@{intellect}"/>
|
|
</div>
|
|
<button name="act_intellect-action" hidden="" type="action" title="%{intellect-action}">
|
|
</button>
|
|
<input name="attr_intellect_action" type="hidden" title="@{intellect_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_cunning" data-i18n="cunning" value="@{cunning_action}" title="%{cunning}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_cunning" type="number" value="1" title="@{cunning}"/>
|
|
</div>
|
|
<button name="act_cunning-action" hidden="" type="action" title="%{cunning-action}">
|
|
</button>
|
|
<input name="attr_cunning_action" type="hidden" title="@{cunning_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_resolve" data-i18n="resolve" value="@{resolve_action}" title="%{resolve}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_resolve" type="number" value="1" title="@{resolve}"/>
|
|
</div>
|
|
<button name="act_resolve-action" hidden="" type="action" title="%{resolve-action}">
|
|
</button>
|
|
<input name="attr_resolve_action" type="hidden" title="@{resolve_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_might" data-i18n="might" value="@{might_action}" title="%{might}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_might" type="number" value="1" title="@{might}"/>
|
|
</div>
|
|
<button name="act_might-action" hidden="" type="action" title="%{might-action}">
|
|
</button>
|
|
<input name="attr_might_action" type="hidden" title="@{might_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_dexterity" data-i18n="dexterity" value="@{dexterity_action}" title="%{dexterity}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_dexterity" type="number" value="1" title="@{dexterity}"/>
|
|
</div>
|
|
<button name="act_dexterity-action" hidden="" type="action" title="%{dexterity-action}">
|
|
</button>
|
|
<input name="attr_dexterity_action" type="hidden" title="@{dexterity_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_stamina" data-i18n="stamina" value="@{stamina_action}" title="%{stamina}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_stamina" type="number" value="1" title="@{stamina}"/>
|
|
</div>
|
|
<button name="act_stamina-action" hidden="" type="action" title="%{stamina-action}">
|
|
</button>
|
|
<input name="attr_stamina_action" type="hidden" title="@{stamina_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_presence" data-i18n="presence" value="@{presence_action}" title="%{presence}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_presence" type="number" value="1" title="@{presence}"/>
|
|
</div>
|
|
<button name="act_presence-action" hidden="" type="action" title="%{presence-action}">
|
|
</button>
|
|
<input name="attr_presence_action" type="hidden" title="@{presence_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_manipulation" data-i18n="manipulation" value="@{manipulation_action}" title="%{manipulation}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_manipulation" type="number" value="1" title="@{manipulation}"/>
|
|
</div>
|
|
<button name="act_manipulation-action" hidden="" type="action" title="%{manipulation-action}">
|
|
</button>
|
|
<input name="attr_manipulation_action" type="hidden" title="@{manipulation_action}"/>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_composure" data-i18n="composure" value="@{composure_action}" title="%{composure}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_composure" type="number" value="1" title="@{composure}"/>
|
|
</div>
|
|
<button name="act_composure-action" hidden="" type="action" title="%{composure-action}">
|
|
</button>
|
|
<input name="attr_composure_action" type="hidden" title="@{composure_action}"/>
|
|
</div>
|
|
<div class="statblock__row statblock__row--auto-grid">
|
|
<label class="input-label"><span class="input-label__text" data-i18n="health" role="heading" aria-level="3"></span>
|
|
<input class="underlined input-label__input" name="attr_health" type="number" title="@{health}"/>
|
|
</label>
|
|
<label class="input-label"><span class="input-label__text" data-i18n="defense" role="heading" aria-level="3"></span>
|
|
<input class="underlined input-label__input" name="attr_defense" type="number" title="@{defense}"/>
|
|
</label>
|
|
<div class="input-label input-label--button">
|
|
<button class="roller" name="roll_initiative" data-i18n="initiative" role="heading" aria-level="3" value="@{initiative_action}" title="%{initiative}" type="roll">
|
|
</button>
|
|
<input class="underlined input-label__input" name="attr_initiative" type="number" title="@{initiative}"/>
|
|
</div>
|
|
<button name="act_initiative-action" hidden="" type="action" title="%{initiative-action}">
|
|
</button>
|
|
<input name="attr_initiative_action" type="hidden" title="@{initiative_action}"/>
|
|
</div>
|
|
<div class="statblock__row statblock__row--inline-container">
|
|
<h3 data-i18n="special rules"></h3><span class="inline-fieldset" hidden=""></span>
|
|
<fieldset class="repeating_special-rules">
|
|
<input class="display-control" name="attr_display_state" value="short-display" hidden="" type="radio" title="@{repeating_special-rules_$X_display_state}"/>
|
|
<div class="inline-fieldset__summary display-target">
|
|
<label class="pointer">
|
|
<input name="attr_collapse" value="1" hidden="" type="checkbox" title="@{repeating_special-rules_$X_collapse}"/><span name="attr_name" title="@{repeating_special-rules_$X_name}"></span>
|
|
</label>
|
|
</div>
|
|
<input class="display-control" name="attr_display_state" value="display" checked="" hidden="" type="radio" title="@{repeating_special-rules_$X_display_state}"/>
|
|
<div class="inline-fieldset__detail display-target">
|
|
<input class="underlined" name="attr_name" role="heading" aria-level="3" data-i18n-placeholder="special rules name" type="text" title="@{repeating_special-rules_$X_name}"/>
|
|
<div class="statblock__row statblock__row--flex display-target">
|
|
<label class="input-label input-label--adaptive"><span class="input-label__text" data-i18n="cost" role="heading" aria-level="4"></span>
|
|
<div class="adaptive adaptive--input"><span class="adaptive--input__span" name="attr_cost" title="@{repeating_special-rules_$X_cost}"></span>
|
|
<input class="underlined input-label__input adaptive--input__input" name="attr_cost" type="text" title="@{repeating_special-rules_$X_cost}"/>
|
|
</div>
|
|
</label>
|
|
<label class="input-label input-label--adaptive"><span class="input-label__text" data-i18n="duration" role="heading" aria-level="4"></span>
|
|
<div class="adaptive adaptive--input"><span class="adaptive--input__span" name="attr_duration" title="@{repeating_special-rules_$X_duration}"></span>
|
|
<input class="underlined input-label__input adaptive--input__input" name="attr_duration" type="text" title="@{repeating_special-rules_$X_duration}"/>
|
|
</div>
|
|
</label>
|
|
<label class="input-label input-label--adaptive"><span class="input-label__text" data-i18n="subject" role="heading" aria-level="4"></span>
|
|
<div class="adaptive adaptive--input"><span class="adaptive--input__span" name="attr_subject" title="@{repeating_special-rules_$X_subject}"></span>
|
|
<input class="underlined input-label__input adaptive--input__input" name="attr_subject" type="text" title="@{repeating_special-rules_$X_subject}"/>
|
|
</div>
|
|
</label>
|
|
<label class="input-label input-label--adaptive"><span class="input-label__text" data-i18n="range" role="heading" aria-level="4"></span>
|
|
<div class="adaptive adaptive--input"><span class="adaptive--input__span" name="attr_range" title="@{repeating_special-rules_$X_range}"></span>
|
|
<input class="underlined input-label__input adaptive--input__input" name="attr_range" type="text" title="@{repeating_special-rules_$X_range}"/>
|
|
</div>
|
|
</label>
|
|
<label class="input-label input-label--adaptive"><span class="input-label__text" data-i18n="action" role="heading" aria-level="4"></span>
|
|
<div class="adaptive adaptive--input"><span class="adaptive--input__span" name="attr_action" title="@{repeating_special-rules_$X_action}"></span>
|
|
<input class="underlined input-label__input adaptive--input__input" name="attr_action" type="text" title="@{repeating_special-rules_$X_action}"/>
|
|
</div>
|
|
</label>
|
|
<label class="input-label input-label--adaptive"><span class="input-label__text" data-i18n="cooldown" role="heading" aria-level="4"></span>
|
|
<div class="adaptive adaptive--input"><span class="adaptive--input__span" name="attr_cooldown" title="@{repeating_special-rules_$X_cooldown}"></span>
|
|
<input class="underlined input-label__input adaptive--input__input" name="attr_cooldown" type="text" title="@{repeating_special-rules_$X_cooldown}"/>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_description" title="@{repeating_special-rules_$X_description}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_description" data-i18n-placeholder="special rules description" title="@{repeating_special-rules_$X_description}"></textarea>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
<button class="repcontrol-button repcontrol-button--add repcontrol-button--inline" name="act_add-special-rules" type="action" title="%{add-special-rules}">
|
|
</button>
|
|
<button class="repcontrol-button repcontrol-button--edit repcontrol-button--inline" name="act_edit-special-rules" type="action" title="%{edit-special-rules}">
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
<article class="navigable-section navigable-section--gm-screen gm-screen subsystem-style system-scion-origin statblock" id="gm-screen">
|
|
<section class="section-tension subsystem-style" id="tension">
|
|
<h2 class="subsystem-style scion-hero" data-i18n="tension"></h2>
|
|
<div class="dot-row twelve-dots momentum-content boxes">
|
|
<select class="dot-number underlined" name="attr_tension" title="@{tension}">
|
|
<option value="0" selected="">12
|
|
</option>
|
|
<option value="1">12
|
|
</option>
|
|
<option value="2">12
|
|
</option>
|
|
<option value="3">12
|
|
</option>
|
|
<option value="4">12
|
|
</option>
|
|
<option value="5">12
|
|
</option>
|
|
<option value="6">12
|
|
</option>
|
|
<option value="7">12
|
|
</option>
|
|
<option value="8">12
|
|
</option>
|
|
<option value="9">12
|
|
</option>
|
|
<option value="10">12
|
|
</option>
|
|
<option value="11">12
|
|
</option>
|
|
<option value="12">12
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="clearer dot" name="attr_tension" checked="" value="0" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="1" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="2" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="3" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="4" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="5" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="6" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="7" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="8" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="9" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="10" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="11" type="radio" title="@{tension}"/>
|
|
<input class="dot" name="attr_tension" value="12" type="radio" title="@{tension}"/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
<article class="navigable-section navigable-section--party-screen party-screen subsystem-style system-scion-origin statblock" id="party-screen">
|
|
<section class="section-party-momentum subsystem-style" id="party-momentum">
|
|
<h2 class="subsystem-style scion-hero" data-i18n="momentum"></h2>
|
|
<div class="dot-row twelve-dots momentum-content boxes">
|
|
<select class="dot-number underlined" name="attr_party_momentum" title="@{party_momentum}">
|
|
<option value="0" selected="">12
|
|
</option>
|
|
<option value="1">12
|
|
</option>
|
|
<option value="2">12
|
|
</option>
|
|
<option value="3">12
|
|
</option>
|
|
<option value="4">12
|
|
</option>
|
|
<option value="5">12
|
|
</option>
|
|
<option value="6">12
|
|
</option>
|
|
<option value="7">12
|
|
</option>
|
|
<option value="8">12
|
|
</option>
|
|
<option value="9">12
|
|
</option>
|
|
<option value="10">12
|
|
</option>
|
|
<option value="11">12
|
|
</option>
|
|
<option value="12">12
|
|
</option>
|
|
</select>
|
|
<div class="dot-button-container">
|
|
<input class="clearer dot" name="attr_party_momentum" checked="" value="0" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="1" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="2" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="3" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="4" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="5" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="6" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="7" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="8" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="9" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="10" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="11" type="radio" title="@{party_momentum}"/>
|
|
<input class="dot" name="attr_party_momentum" value="12" type="radio" title="@{party_momentum}"/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<section class="section-party-notes" id="notes">
|
|
<h2 class="subsystem-style scion-hero" data-i18n="notes"></h2>
|
|
<div class="adaptive adaptive--text"><span class="adaptive--text__span" name="attr_notes" title="@{notes}"></span>
|
|
<textarea class="underlined adaptive--text__textarea" name="attr_notes" title="@{notes}"></textarea>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
</div>
|
|
<rolltemplate class="sheet-rolltemplate-storypath">{{#^rollBetween() computed::finished 0 0}}<span class="finished"></span>{{/^rollBetween() computed::finished 0 0}}
|
|
<div class="template storypath{{#continuation}} continuation{{/continuation}}{{#first}} first{{/first}} finished">
|
|
<div class="upper-blur"></div>
|
|
<div class="lower-blur"></div>
|
|
<div class="content">
|
|
<div class="header">{{#title}}
|
|
<h3 class="title">{{title}}</h3>{{/title}}{{#attribute}}
|
|
<h4>{{attribute}}</h4>{{/attribute}}{{#skill}}{{#attribute}}
|
|
<h4 class="amp">&</h4>{{/attribute}}
|
|
<h4>{{skill}}</h4>{{/skill}}{{^continuation}}{{#character_name}}{{#character_id}}
|
|
<h4 class="character_name">[{{character_name}}](http://journal.roll20.net/character/{{character_id}})</h4>{{/character_id}}{{^character_id}}
|
|
<h4 class="character_name">{{character_name}}</h4>{{/character_id}}{{/character_name}}{{/continuation}}
|
|
</div>{{#vs}}
|
|
<div class="tempalte-row vs">
|
|
<h5 data-i18n="opposed by"></h5><span>{{vs}}</span>
|
|
</div>{{/vs}}{{#display_dice}}
|
|
<div class="template-row results">{{^continuation}}
|
|
<h5 data-i18n="results"></h5>{{/continuation}}<span>{{roll_1}}</span><span>{{roll_2}}</span><span>{{roll_3}}</span><span>{{roll_4}}</span><span>{{roll_5}}</span><span>{{roll_6}}</span><span>{{roll_7}}</span><span>{{roll_8}}</span><span>{{roll_9}}</span><span>{{roll_10}}</span>
|
|
</div>{{/display_dice}}{{#^rollTotal() computed::finished 0}}{{#enhancements}}
|
|
<div class="template-row">
|
|
<h5 data-i18n="enhancements"></h5><span>{{enhancements}}</span>
|
|
</div>{{/enhancements}}{{#successes}}
|
|
<div class="template-row">
|
|
<h5 data-i18n="successes"></h5><span>{{computed::successes}}</span>
|
|
</div>{{/successes}}{{#difficulty}}
|
|
<div class="template-row">
|
|
<h5 data-i18n="difficulty"></h5><span>{{computed::difficulty}}</span>
|
|
</div>{{/difficulty}}{{#extra_successes}}
|
|
<div class="template-row">
|
|
<h5 data-i18n="extra successes"></h5><span>{{computed::extra_successes}}</span>
|
|
</div>{{/extra_successes}}{{#description}}
|
|
<div class="template-block">
|
|
<h5 data-i18n="description"></h5><span class="description">{{description}}</span>
|
|
</div>{{/description}}{{/^rollTotal() computed::finished 0}}
|
|
</div>
|
|
</div>
|
|
</rolltemplate>
|
|
<input name="attr_template_start" value="@{whisper}&{template:storypath} {{character_name=@{character_name}}} {{character_id=@{character_id}}} {{system=@{system}}}" type="hidden" title="@{template_start}"/>
|
|
<script type="text/worker">//# sourceURL=tcfbts.js
|
|
|
|
const k = (function(){
|
|
const kFuncs = {};
|
|
|
|
const cascades = {"attr_character_name":{"name":"character_name","type":"text","defaultValue":"","affects":[],"triggeredFuncs":["setActionCalls"],"listenerFunc":"accessSheet","listener":"change:character_name"},"attr_sheet_state":{"name":"sheet_state","type":"hidden","defaultValue":"settings","triggeredFuncs":[],"affects":[]},"attr_collapsed":{"name":"collapsed","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_drop_name":{"triggeredFuncs":["handleCompendiumDrop"],"name":"drop_name","listener":"change:drop_name","listenerFunc":"accessSheet","type":"hidden","defaultValue":"","affects":[]},"attr_drop_data":{"name":"drop_data","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_nav-all":{"triggeredFuncs":["navigateSheet"],"name":"nav-all","listener":"clicked:nav-all","listenerFunc":"accessSheet","type":"action"},"act_nav-general":{"triggeredFuncs":["navigateSheet"],"name":"nav-general","listener":"clicked:nav-general","listenerFunc":"accessSheet","type":"action"},"act_nav-powers":{"triggeredFuncs":["navigateSheet"],"name":"nav-powers","listener":"clicked:nav-powers","listenerFunc":"accessSheet","type":"action"},"act_nav-antagonist":{"triggeredFuncs":["navigateSheet"],"name":"nav-antagonist","listener":"clicked:nav-antagonist","listenerFunc":"accessSheet","type":"action"},"act_nav-gm-screen":{"triggeredFuncs":["navigateSheet"],"name":"nav-gm-screen","listener":"clicked:nav-gm-screen","listenerFunc":"accessSheet","type":"action"},"act_nav-party-screen":{"triggeredFuncs":["navigateSheet"],"name":"nav-party-screen","listener":"clicked:nav-party-screen","listenerFunc":"accessSheet","type":"action"},"act_nav-settings":{"triggeredFuncs":["navigateSheet"],"name":"nav-settings","listener":"clicked:nav-settings","listenerFunc":"accessSheet","type":"action"},"attr_system":{"name":"system","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_sheet_type":{"triggeredFuncs":["styleCharacterType"],"name":"sheet_type","listener":"change:sheet_type","listenerFunc":"accessSheet","type":"select","defaultValue":"","affects":[]},"attr_target_number":{"name":"target_number","type":"number","defaultValue":"8","triggeredFuncs":[],"affects":[]},"attr_again":{"name":"again","type":"number","defaultValue":"10","triggeredFuncs":[],"affects":[]},"attr_difficulty_query":{"name":"difficulty_query","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_enhancement_query":{"name":"enhancement_query","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_whisper":{"name":"whisper","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_player":{"name":"player","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_concept":{"name":"concept","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_archetype":{"name":"archetype","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_skills":{"listenerFunc":"expandHeader","name":"skills","listener":"clicked:skills","type":"action"},"attr_repeating_skill_$X_name":{"name":"repeating_skill_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_skill_$X_path":{"name":"repeating_skill_$X_path","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_repeating_skill_$X_action":{"listenerFunc":"rollTask","name":"repeating_skill_$X_action","listener":"clicked:repeating_skill:action","type":"action"},"attr_repeating_skill_$X_action":{"name":"repeating_skill_$X_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_skill_$X_specialty":{"name":"repeating_skill_$X_specialty","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_skill_$X_rating":{"name":"repeating_skill_$X_rating","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_attributes":{"listenerFunc":"expandHeader","name":"attributes","listener":"clicked:attributes","type":"action"},"act_intellect-action":{"listenerFunc":"rollTask","name":"intellect-action","listener":"clicked:intellect-action","type":"action"},"attr_intellect_action":{"name":"intellect_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_intellect":{"name":"intellect","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_might-action":{"listenerFunc":"rollTask","name":"might-action","listener":"clicked:might-action","type":"action"},"attr_might_action":{"name":"might_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_might":{"name":"might","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_presence-action":{"listenerFunc":"rollTask","name":"presence-action","listener":"clicked:presence-action","type":"action"},"attr_presence_action":{"name":"presence_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_presence":{"name":"presence","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_cunning-action":{"listenerFunc":"rollTask","name":"cunning-action","listener":"clicked:cunning-action","type":"action"},"attr_cunning_action":{"name":"cunning_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_cunning":{"name":"cunning","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_dexterity-action":{"listenerFunc":"rollTask","name":"dexterity-action","listener":"clicked:dexterity-action","type":"action"},"attr_dexterity_action":{"name":"dexterity_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_dexterity":{"name":"dexterity","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_manipulation-action":{"listenerFunc":"rollTask","name":"manipulation-action","listener":"clicked:manipulation-action","type":"action"},"attr_manipulation_action":{"name":"manipulation_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_manipulation":{"name":"manipulation","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_resolve-action":{"listenerFunc":"rollTask","name":"resolve-action","listener":"clicked:resolve-action","type":"action"},"attr_resolve_action":{"name":"resolve_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_resolve":{"name":"resolve","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_stamina-action":{"listenerFunc":"rollTask","name":"stamina-action","listener":"clicked:stamina-action","type":"action"},"attr_stamina_action":{"name":"stamina_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_stamina":{"name":"stamina","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_composure-action":{"listenerFunc":"rollTask","name":"composure-action","listener":"clicked:composure-action","type":"action"},"attr_composure_action":{"name":"composure_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_composure":{"name":"composure","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_health":{"listenerFunc":"expandHeader","name":"health","listener":"clicked:health","type":"action"},"act_add-injury":{"listenerFunc":"sectionInteract","name":"add-injury","listener":"clicked:add-injury","type":"action"},"act_edit-injury":{"listenerFunc":"sectionInteract","name":"edit-injury","listener":"clicked:edit-injury","type":"action"},"act_pc-initiative-action":{"triggeredFuncs":["rollPCInitiative"],"name":"pc-initiative-action","listener":"clicked:pc-initiative-action","listenerFunc":"accessSheet","type":"action"},"attr_pc_initiative_action":{"name":"pc_initiative_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_initiative_skill":{"name":"initiative_skill","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_injury_$X_display_state":{"name":"repeating_injury_$X_display_state","type":"hidden","defaultValue":"bruised","triggeredFuncs":[],"affects":[]},"attr_repeating_injury_$X_collapse":{"name":"repeating_injury_$X_collapse","type":"hidden","defaultValue":1,"triggeredFuncs":["collapseSection"],"affects":[],"listener":"change:repeating_injury:collapse","listenerFunc":"accessSheet"},"attr_repeating_injury_$X_affected":{"name":"repeating_injury_$X_affected","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_injury_$X_type":{"name":"repeating_injury_$X_type","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_injury_$X_effect":{"name":"repeating_injury_$X_effect","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_aspirations":{"listenerFunc":"expandHeader","name":"aspirations","listener":"clicked:aspirations","type":"action"},"act_add-aspiration":{"listenerFunc":"sectionInteract","name":"add-aspiration","listener":"clicked:add-aspiration","type":"action"},"act_edit-aspiration":{"listenerFunc":"sectionInteract","name":"edit-aspiration","listener":"clicked:edit-aspiration","type":"action"},"attr_experience_used":{"affects":["experience_remaining"],"name":"experience_used","listener":"change:experience_used","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_experience_earned":{"affects":["experience_remaining"],"name":"experience_earned","listener":"change:experience_earned","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_experience_remaining":{"calculation":"calcXP","name":"experience_remaining","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"fieldset_repeating_aspiration":{"triggeredFuncs":["aspirationDisplay"],"name":"repeating_aspiration","listener":"remove:repeating_aspiration","listenerFunc":"accessSheet","type":"fieldset"},"attr_repeating_aspiration_$X_type":{"name":"repeating_aspiration_$X_type","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_aspiration_$X_name":{"name":"repeating_aspiration_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_aspiration_$X_completed":{"initialFunc":"orderaspirations","name":"repeating_aspiration_$X_completed","listener":"change:repeating_aspiration:completed","listenerFunc":"accessSheet","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_completed-aspiration":{"listenerFunc":"expandHeader","name":"completed-aspiration","listener":"clicked:completed-aspiration","type":"action"},"fieldset_repeating_completed-aspiration":{"triggeredFuncs":["aspirationDisplay"],"name":"repeating_completed-aspiration","listener":"remove:repeating_completed-aspiration","listenerFunc":"accessSheet","type":"fieldset"},"attr_repeating_completed-aspiration_$X_type":{"name":"repeating_completed-aspiration_$X_type","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_completed-aspiration_$X_name":{"name":"repeating_completed-aspiration_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_completed-aspiration_$X_completed":{"initialFunc":"orderaspirations","name":"repeating_completed-aspiration_$X_completed","listener":"change:repeating_completed-aspiration:completed","listenerFunc":"accessSheet","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_paths":{"listenerFunc":"expandHeader","name":"paths","listener":"clicked:paths","type":"action"},"act_add-path":{"listenerFunc":"sectionInteract","name":"add-path","listener":"clicked:add-path","type":"action"},"act_edit-path":{"listenerFunc":"sectionInteract","name":"edit-path","listener":"clicked:edit-path","type":"action"},"fieldset_repeating_path":{"listener":"remove:repeating_path","listenerFunc":"clearChildItems","name":"repeating_path","type":"fieldset"},"attr_repeating_path_$X_display_state":{"name":"repeating_path_$X_display_state","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_master_id":{"name":"repeating_path_$X_master_id","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_collapse":{"name":"repeating_path_$X_collapse","type":"hidden","defaultValue":"0","triggeredFuncs":["collapseSection"],"affects":[],"listener":"change:repeating_path:collapse","listenerFunc":"accessSheet"},"attr_repeating_path_$X_type":{"name":"repeating_path_$X_type","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_name":{"name":"repeating_path_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_status":{"name":"repeating_path_$X_status","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_condition":{"name":"repeating_path_$X_condition","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_skill":{"name":"repeating_path_$X_skill","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_description":{"name":"repeating_path_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_tags":{"name":"repeating_path_$X_tags","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_rating":{"name":"repeating_path_$X_rating","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_group":{"name":"repeating_path_$X_group","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_access":{"name":"repeating_path_$X_access","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_obligation":{"name":"repeating_path_$X_obligation","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_path_$X_obligation_status":{"name":"repeating_path_$X_obligation_status","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_repeating_path_$X_add-contact":{"listenerFunc":"addSubsection","name":"repeating_path_$X_add-contact","listener":"clicked:repeating_path:add-contact","type":"action"},"act_repeating_path_$X_add-group":{"listenerFunc":"addSubsection","name":"repeating_path_$X_add-group","listener":"clicked:repeating_path:add-group","type":"action"},"act_repeating_path_$X_add-access":{"listenerFunc":"addSubsection","name":"repeating_path_$X_add-access","listener":"clicked:repeating_path:add-access","type":"action"},"act_repeating_path_$X_add-obligation":{"listenerFunc":"addSubsection","name":"repeating_path_$X_add-obligation","listener":"clicked:repeating_path:add-obligation","type":"action"},"act_trademarks":{"listenerFunc":"expandHeader","name":"trademarks","listener":"clicked:trademarks","type":"action"},"act_add-trademark":{"listenerFunc":"sectionInteract","name":"add-trademark","listener":"clicked:add-trademark","type":"action"},"act_edit-trademark":{"listenerFunc":"sectionInteract","name":"edit-trademark","listener":"clicked:edit-trademark","type":"action"},"attr_repeating_trademark_$X_display_state":{"name":"repeating_trademark_$X_display_state","type":"hidden","defaultValue":"trademark","triggeredFuncs":[],"affects":[]},"attr_repeating_trademark_$X_collapse":{"name":"repeating_trademark_$X_collapse","type":"hidden","defaultValue":"0","triggeredFuncs":["collapseSection"],"affects":[],"listener":"change:repeating_trademark:collapse","listenerFunc":"accessSheet"},"attr_repeating_trademark_$X_name":{"name":"repeating_trademark_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_trademark_$X_used_session":{"name":"repeating_trademark_$X_used_session","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_trademark_$X_skill":{"name":"repeating_trademark_$X_skill","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_trademark_$X_description":{"name":"repeating_trademark_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_stunts":{"listenerFunc":"expandHeader","name":"stunts","listener":"clicked:stunts","type":"action"},"act_add-stunt":{"listenerFunc":"sectionInteract","name":"add-stunt","listener":"clicked:add-stunt","type":"action"},"act_edit-stunt":{"listenerFunc":"sectionInteract","name":"edit-stunt","listener":"clicked:edit-stunt","type":"action"},"attr_repeating_stunt_$X_display_state":{"name":"repeating_stunt_$X_display_state","type":"hidden","defaultValue":"stunt","triggeredFuncs":[],"affects":[]},"attr_repeating_stunt_$X_collapse":{"name":"repeating_stunt_$X_collapse","type":"hidden","defaultValue":"0","triggeredFuncs":["collapseSection"],"affects":[],"listener":"change:repeating_stunt:collapse","listenerFunc":"accessSheet"},"attr_repeating_stunt_$X_name":{"name":"repeating_stunt_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_stunt_$X_type":{"name":"repeating_stunt_$X_type","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_stunt_$X_description":{"name":"repeating_stunt_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_quips":{"listenerFunc":"expandHeader","name":"quips","listener":"clicked:quips","type":"action"},"act_add-quip":{"listenerFunc":"sectionInteract","name":"add-quip","listener":"clicked:add-quip","type":"action"},"act_edit-quip":{"listenerFunc":"sectionInteract","name":"edit-quip","listener":"clicked:edit-quip","type":"action"},"attr_repeating_quip_$X_display_state":{"name":"repeating_quip_$X_display_state","type":"hidden","defaultValue":"quip","triggeredFuncs":[],"affects":[]},"attr_repeating_quip_$X_collapse":{"name":"repeating_quip_$X_collapse","type":"hidden","defaultValue":"0","triggeredFuncs":["collapseSection"],"affects":[],"listener":"change:repeating_quip:collapse","listenerFunc":"accessSheet"},"attr_repeating_quip_$X_name":{"name":"repeating_quip_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_quip_$X_used_session":{"name":"repeating_quip_$X_used_session","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_quip_$X_description":{"name":"repeating_quip_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_tropes":{"listenerFunc":"expandHeader","name":"tropes","listener":"clicked:tropes","type":"action"},"act_add-trope":{"listenerFunc":"sectionInteract","name":"add-trope","listener":"clicked:add-trope","type":"action"},"act_edit-trope":{"listenerFunc":"sectionInteract","name":"edit-trope","listener":"clicked:edit-trope","type":"action"},"attr_repeating_trope_$X_display_state":{"name":"repeating_trope_$X_display_state","type":"hidden","defaultValue":"quip","triggeredFuncs":[],"affects":[]},"attr_repeating_trope_$X_collapse":{"name":"repeating_trope_$X_collapse","type":"hidden","defaultValue":"0","triggeredFuncs":["collapseSection"],"affects":[],"listener":"change:repeating_trope:collapse","listenerFunc":"accessSheet"},"attr_repeating_trope_$X_name":{"name":"repeating_trope_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_trope_$X_description":{"name":"repeating_trope_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_notes":{"listenerFunc":"expandHeader","name":"notes","listener":"clicked:notes","type":"action"},"attr_notes":{"name":"notes","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_goals":{"name":"goals","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_skill_collapse":{"name":"skill_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_health":{"name":"health","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_defense":{"name":"defense","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_initiative":{"name":"initiative","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_initiative-action":{"listenerFunc":"singleAttributeRoll","name":"initiative-action","listener":"clicked:initiative-action","type":"action"},"attr_initiative_action":{"name":"initiative_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_display_state":{"name":"repeating_special-rules_$X_display_state","type":"radio","defaultValue":"short-display","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_collapse":{"triggeredFuncs":["collapseSection"],"name":"repeating_special-rules_$X_collapse","listener":"change:repeating_special-rules:collapse","listenerFunc":"accessSheet","type":"checkbox","defaultValue":0,"affects":[]},"attr_repeating_special-rules_$X_name":{"name":"repeating_special-rules_$X_name","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_cost":{"name":"repeating_special-rules_$X_cost","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_duration":{"name":"repeating_special-rules_$X_duration","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_subject":{"name":"repeating_special-rules_$X_subject","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_range":{"name":"repeating_special-rules_$X_range","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_action":{"name":"repeating_special-rules_$X_action","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_cooldown":{"name":"repeating_special-rules_$X_cooldown","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_special-rules_$X_description":{"name":"repeating_special-rules_$X_description","type":"span","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-special-rules":{"listenerFunc":"sectionInteract","name":"add-special-rules","listener":"clicked:add-special-rules","type":"action"},"act_edit-special-rules":{"listenerFunc":"sectionInteract","name":"edit-special-rules","listener":"clicked:edit-special-rules","type":"action"},"attr_tension":{"name":"tension","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_party_momentum":{"name":"party_momentum","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_template_start":{"name":"template_start","type":"hidden","defaultValue":"@{whisper}&{template:storypath} {{character_name=@{character_name}}} {{character_id=@{character_id}}} {{system=@{system}}}","triggeredFuncs":[],"affects":[]}};
|
|
|
|
kFuncs.cascades = cascades;
|
|
|
|
const repeatingSectionDetails = [{"section":"repeating_skill","fields":["name","path","action","specialty","rating"]},{"section":"repeating_injury","fields":["display_state","collapse","affected","type","effect"]},{"section":"repeating_aspiration","fields":["type","name","completed"]},{"section":"repeating_completed-aspiration","fields":["type","name","completed"]},{"section":"repeating_path","fields":["display_state","master_id","collapse","type","name","status","condition","skill","description","tags","rating","group","access","obligation","obligation_status"]},{"section":"repeating_trademark","fields":["display_state","collapse","name","used_session","skill","description"]},{"section":"repeating_stunt","fields":["display_state","collapse","name","type","description"]},{"section":"repeating_quip","fields":["display_state","collapse","name","used_session","description"]},{"section":"repeating_trope","fields":["display_state","collapse","name","description"]},{"section":"repeating_special-rules","fields":["display_state","collapse","name","cost","duration","subject","range","action","cooldown","description"]}];
|
|
|
|
kFuncs.repeatingSectionDetails = repeatingSectionDetails;
|
|
|
|
const persistentTabs = [];
|
|
|
|
kFuncs.persistentTabs = persistentTabs;
|
|
/**
|
|
* The K-scaffold provides several variables to allow your scripts to tap into its information flow.
|
|
* @namespace Sheetworkers.Variables
|
|
*/
|
|
/**
|
|
* This stores the name of your sheet for use in the logging functions {@link log} and {@link debug}. Accessible by `k.sheetName`
|
|
* @memberof Variables
|
|
* @var
|
|
* @type {string}
|
|
*/
|
|
let sheetName = 'kScaffold Powered Sheet';
|
|
kFuncs.sheetName = sheetName;
|
|
/**
|
|
* This stores the version of your sheet for use in the logging functions{@link log} and {@link debug}. It is also stored in the sheet_version attribute on your character sheet. Accessible via `k.version`
|
|
* @memberof Variables
|
|
* @var
|
|
* @type {number}
|
|
*/
|
|
let version = 0;
|
|
kFuncs.version = version;
|
|
/**
|
|
* A boolean flag that tells the script whether to enable or disable {@link debug} calls. If the version of the sheet is `0`, or an attribute named `debug_mode` is found on opening this is set to true for your entire session. Otherwise, it remains false.
|
|
* @memberof Variables
|
|
* @var
|
|
* @type {boolean}
|
|
*/
|
|
let debugMode = false;
|
|
kFuncs.debugMode = debugMode;
|
|
const funcs = {};
|
|
kFuncs.funcs = funcs;
|
|
const updateHandlers = {};
|
|
const openHandlers = {};
|
|
const initialSetups = {};
|
|
const allHandlers = {};
|
|
const addFuncs = {};
|
|
|
|
const kscaffoldJSVersion = '1.0.0';
|
|
const kscaffoldPUGVersion = '1.0.0';
|
|
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
/**
|
|
* These are utility functions that are not directly related to Roll20 systems. They provide easy methods for everything from processing text and numbers to querying the user for input.
|
|
* @namespace Sheetworkers.Utilities
|
|
* @alias Utilities
|
|
*/
|
|
/**
|
|
* Replaces problem characters to use a string as a regex
|
|
* @memberof Utilities
|
|
* @param {string} text - The text to replace characters in
|
|
* @returns {string}
|
|
* @example
|
|
* const textForRegex = k.sanitizeForRegex('.some thing[with characters]');
|
|
* console.log(textForRegex);// => "\.some thing\[with characters\]"
|
|
*/
|
|
const sanitizeForRegex = function(text){
|
|
return text.replace(/\.|\||\(|\)|\[|\]|\-|\+|\?|\/|\{|\}|\^|\$|\*/g,'\\$&');
|
|
};
|
|
kFuncs.sanitizeForRegex = sanitizeForRegex;
|
|
|
|
/**
|
|
* Converts a value to a number, it\'s default value, or `0` if no default value passed.
|
|
* @memberof Utilities
|
|
* @param {string|number} val - Value to convert to a number
|
|
* @param {number} def - The default value, uses 0 if not passed
|
|
* @returns {number|undefined}
|
|
* @example
|
|
* const num = k.value('100');
|
|
* console.log(num);// => 100
|
|
*/
|
|
const value = function(val,def){
|
|
const convertVal = +val;
|
|
if(def !== undefined && isNaN(def)){
|
|
throw(`K-scaffold Error: invalid default for value(). Default: ${def}`);
|
|
}
|
|
return convertVal === 0 ?
|
|
convertVal :
|
|
(+val||def||0);
|
|
};
|
|
kFuncs.value = value;
|
|
|
|
/**
|
|
* Extracts the section (e.g. `repeating_equipment`), rowID (e.g `-;lkj098J:LKj`), and field name (e.g. `bulk`) from a repeating attribute name.
|
|
* @memberof Utilities
|
|
* @param {string} string - The string to parse
|
|
* @returns {array} - Array of matches. Index 0: the section name, e.g. repeating_equipment | Index 1:the row ID | index 2: The name of the attribute
|
|
* @returns {string[]}
|
|
* @example
|
|
* //Extract info from a full repeating name
|
|
* const [section,rowID,attrName] = k.parseRepeatName('repeating_equipment_-8908asdflkjZlkj23_name');
|
|
* console.log(section);// => "repeating_equipment"
|
|
* console.log(rowID);// => "-8908asdflkjZlkj23"
|
|
* console.log(attrName);// => "name"
|
|
*
|
|
* //Extract info from just a row name
|
|
* const [section,rowID,attrName] = k.parseRepeatName('repeating_equipment_-8908asdflkjZlkj23');
|
|
* console.log(section);// => "repeating_equipment"
|
|
* console.log(rowID);// => "-8908asdflkjZlkj23"
|
|
* console.log(attrName);// => undefined
|
|
*/
|
|
const parseRepeatName = function(string){
|
|
let match = string.match(/(repeating_[^_]+)_([^_]+)(?:_(.+))?/);
|
|
match.shift();
|
|
return match;
|
|
};
|
|
kFuncs.parseRepeatName = parseRepeatName;
|
|
|
|
/**
|
|
* Parses out the components of a trigger name similar to [parseRepeatName](#parserepeatname). Aliases: parseClickTrigger.
|
|
*
|
|
* Aliases: `k.parseClickTrigger`
|
|
* @memberof Utilities
|
|
* @param {string} string The triggerName property of the
|
|
* @returns {array} - For a repeating button named `repeating_equipment_-LKJhpoi98;lj_roll`, the array will be `['repeating_equipment','-LKJhpoi98;lj','roll']`. For a non repeating button named `roll`, the array will be `[undefined,undefined,'roll']`
|
|
* @returns {string[]}
|
|
* @example
|
|
* //Parse a non repeating trigger
|
|
* const [section,rowID,attrName] = k.parseTriggerName('clicked:some-button');
|
|
* console.log(section);// => undefined
|
|
* console.log(rowID);// => undefined
|
|
* console.log(attrName);// => "some-button"
|
|
*
|
|
* //Parse a repeating trigger
|
|
* const [section,rowID,attrName] = k.parseTriggerName('clicked:repeating_attack_-234lkjpd8fu8usadf_some-button');
|
|
* console.log(section);// => "repeating_attack"
|
|
* console.log(rowID);// => "-234lkjpd8fu8usadf"
|
|
* console.log(attrName);// => "some-button"
|
|
*
|
|
* //Parse a repeating name
|
|
* const [section,rowID,attrName] = k.parseTriggerName('repeating_attack_-234lkjpd8fu8usadf_some-button');
|
|
* console.log(section);// => "repeating_attack"
|
|
* console.log(rowID);// => "-234lkjpd8fu8usadf"
|
|
* console.log(attrName);// => "some-button"
|
|
*/
|
|
const parseTriggerName = function(string){
|
|
let match = string.replace(/^clicked:/,'').match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
|
|
match.shift();
|
|
return match;
|
|
};
|
|
kFuncs.parseTriggerName = parseTriggerName;
|
|
const parseClickTrigger = parseTriggerName;
|
|
kFuncs.parseClickTrigger = parseClickTrigger;
|
|
|
|
/**
|
|
* Parses out the attribute name from the htmlattribute name.
|
|
* @memberof Utilities
|
|
* @param {string} string - The triggerName property of the [event](https://wiki.roll20.net/Sheet_Worker_Scripts#eventInfo_Object).
|
|
* @returns {string}
|
|
* @example
|
|
* //Parse a name
|
|
* const attrName = k.parseHtmlName('attr_attribute_1');
|
|
* console.log(attrName);// => "attribute_1"
|
|
*/
|
|
const parseHTMLName = function(string){
|
|
let match = string.match(/(?:attr|act|roll)_(.+)/);
|
|
match.shift();
|
|
return match[0];
|
|
};
|
|
kFuncs.parseHTMLName = parseHTMLName;
|
|
|
|
/**
|
|
* Capitalize each word in a string
|
|
* @memberof Utilities
|
|
* @param {string} string - The string to capitalize
|
|
* @returns {string}
|
|
* @example
|
|
* const capitalized = k.capitalize('a word');
|
|
* console.log(capitalized);// => "A Word"
|
|
*/
|
|
const capitalize = function(string){
|
|
return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase());
|
|
};
|
|
kFuncs.capitalize = capitalize;
|
|
|
|
/**
|
|
* Extracts a roll query result for use in later functions. Must be awaited as per [startRoll documentation](https://wiki.roll20.net/Sheet_Worker_Scripts#Roll_Parsing.28NEW.29). Stolen from [Oosh\'s Adventures with Startroll thread](https://app.roll20.net/forum/post/10346883/adventures-with-startroll).
|
|
* @memberof Utilities
|
|
* @param {string} query - The query should be just the text as the `?{` and `}` at the start/end of the query are added by the function.
|
|
* @returns {Promise} - Resolves to the selected value from the roll query
|
|
* @example
|
|
* const rollFunction = async function(){
|
|
* //Get the result of a choose from list query
|
|
* const queryResult = await extractQueryResult('Prompt Text Here|Option 1|Option 2');
|
|
* console.log(queryResult);//=> "Option 1" or "Option 2" depending on what the user selects
|
|
*
|
|
* //Get free form input from the user
|
|
* const freeResult = await extractQueryResult('Prompt Text Here');
|
|
* consoel.log(freeResult);// => Whatever the user entered
|
|
* }
|
|
*/
|
|
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,'');
|
|
};
|
|
kFuncs.extractQueryResult = extractQueryResult;
|
|
|
|
/**
|
|
* Simulates a query for ensuring that async/await works correctly in the sheetworker environment when doing conditional startRolls. E.g. if you have an if/else and only one of the conditions results in `startRoll` being called (and thus an `await`), the sheetworker environment would normally crash. Awaiting this in the condition that does not actually need to call `startRoll` will keep the environment in sync.
|
|
* @memberof Utilities
|
|
* @param {string|number} [value] - The value to return. Optional.
|
|
* @returns {Promise} - Resolves to the value passed to the function
|
|
* @example
|
|
* const rollFunction = async function(){
|
|
* //Get the result of a choose from list query
|
|
* const queryResult = await pseudoQuery('a value');
|
|
* console.log(queryResult);//=> "a value"
|
|
* }
|
|
*/
|
|
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,'');
|
|
};
|
|
kFuncs.pseudoQuery = pseudoQuery;
|
|
|
|
/**
|
|
* An alias for console.log.
|
|
* @memberof Utilities
|
|
* @param {any} msg - The message can be a straight string, an object, or an array. If it is an object or array, the object will be broken down so that each key is used as a label to output followed by the value of that key. If the value of the key is an object or array, it will be output via `console.table`.
|
|
*/
|
|
const log = function(msg){
|
|
if(typeof msg === 'string'){
|
|
console.log(`%c${kFuncs.sheetName} log| ${msg}`,"background-color:#159ccf");
|
|
}else if(typeof msg === 'object'){
|
|
Object.keys(msg).forEach((m)=>{
|
|
if(typeof msg[m] === 'string'){
|
|
console.log(`%c${kFuncs.sheetName} log| ${m}: ${msg[m]}`,"background-color:#159ccf");
|
|
}else{
|
|
console.log(`%c${kFuncs.sheetName} log| ${typeof msg[m]} ${m}`,"background-color:#159ccf");
|
|
console.table(msg[m]);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
kFuncs.log = log;
|
|
|
|
/**
|
|
* Alias for console.log that only triggers when debug mode is enabled or when the sheet\'s version is `0`. Useful for entering test logs that will not pollute the console on the live sheet.
|
|
* @memberof Utilities
|
|
* @param {any} msg - 'See {@link k.log}
|
|
* @param {boolean} force - Pass as a truthy value to force the debug output to be output to the console regardless of debug mode.
|
|
* @returns {void}
|
|
*/
|
|
const debug = function(msg,force){
|
|
console.warn('kFuncs.version',kFuncs.version);
|
|
if(!kFuncs.debugMode && !force && kFuncs.version > 0) return;
|
|
if(typeof msg === 'string'){
|
|
console.log(`%c${kFuncs.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${kFuncs.sheetName} DEBUG| ${m}: ${msg[m]}`,"background-color:tan;color:red;");
|
|
}else{
|
|
console.log(`%c${kFuncs.sheetName} DEBUG| ${typeof msg[m]} ${m}`,"background-color:tan;color:red;font-weight:bold;");
|
|
console.table(msg[m]);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
kFuncs.debug = debug;
|
|
|
|
/**
|
|
* Orders the section id arrays for all sections in the `sections` object to match the repOrder attribute.
|
|
* @memberof Utilities
|
|
* @param {attributesProxy} attributes - The attributes object that must have a value for the reporder for each section.
|
|
* @param {object[]} sections - Object containing the IDs for the repeating sections, indexed by repeating section name.
|
|
*/
|
|
const orderSections = function(attributes,sections){
|
|
Object.keys(sections).forEach((section)=>{
|
|
attributes.attributes[`_reporder_${section}`] = commaArray(attributes[`_reporder_${section}`]);
|
|
sections[section] = orderSection(attributes.attributes[`_reporder_${section}`],sections[section]);
|
|
});
|
|
};
|
|
kFuncs.orderSections = orderSections;
|
|
|
|
/**
|
|
* Orders a single ID array.
|
|
* @memberof Utilities
|
|
* @param {string[]} repOrder - Array of IDs in the order they are in on the sheet.
|
|
* @param {string[]} IDs - Array of IDs to be ordered. Aka the default ID Array passed to the getSectionIDs callback
|
|
* @returns {string[]} - The ordered id array
|
|
*/
|
|
const orderSection = function(repOrder,IDs=[]){
|
|
const idArr = [...repOrder.filter(v => v),...IDs.filter(id => !repOrder.includes(id.toLowerCase()))];
|
|
return idArr;
|
|
};
|
|
kFuncs.orderSection = orderSection;
|
|
|
|
/**
|
|
* Splits a comma delimited string into an array
|
|
* @memberof Utilities
|
|
* @param {string} string - The string to split.
|
|
* @returns {array} - The string segments of the comma delimited list.
|
|
*/
|
|
const commaArray = function(string=''){
|
|
return string.toLowerCase().split(/\s*,\s*/);
|
|
};
|
|
kFuncs.commaArray = commaArray;
|
|
|
|
// Roll escape functions for passing data in action button calls. Base64 encodes/decodes the data.
|
|
const RE = {
|
|
chars: {
|
|
'"': '%quot;',
|
|
',': '%comma;',
|
|
':': '%colon;',
|
|
'}': '%rcub;',
|
|
'{': '%lcub;',
|
|
},
|
|
escape(data) {
|
|
return typeof data === 'object' ?
|
|
`KDATA${btoa(JSON.stringify(data))}` :
|
|
`KSTRING${btoa(data)}`;
|
|
},
|
|
unescape(string) {
|
|
const isData = typeof string === 'string' &&
|
|
(
|
|
string.startsWith('KDATA') ||
|
|
string.startsWith('KSTRING')
|
|
);
|
|
return isData ?
|
|
(
|
|
string.startsWith('KDATA') ?
|
|
JSON.parse(atob(string.replace(/^KDATA/,''))) :
|
|
atob(string.replace(/^KSTRING/,''))
|
|
) :
|
|
string;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Encodes data in Base64. This is useful for passing roll information to action buttons called from roll buttons.
|
|
* @function
|
|
* @param {string|object|any[]} data - The data that you want to Base64 encode
|
|
* @returns {string} - The encoded data
|
|
* @memberof! Utilities
|
|
*/
|
|
const escape = RE.escape;
|
|
/**
|
|
* Decodes Base64 encoded strings that were created by the K-scaffold
|
|
* @function
|
|
* @param {string|object|any[]} string - The string of encoded data to decode. If this is not a string, or is not a string that was encoded by the K-scaffold, it will be returned as is.
|
|
* @returns {string|object|any[]}
|
|
* @memberof! Utilities
|
|
*/
|
|
const unescape = RE.unescape;
|
|
|
|
Object.assign(kFuncs,{escape,unescape});/*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){
|
|
const eventName = event.triggerName || event.sourceAttribute;
|
|
let typePrefix = eventName.startsWith('clicked:') ?
|
|
'act_' :
|
|
event.removedInfo ?
|
|
'fieldset_' :
|
|
'attr_';
|
|
let cascName = `${typePrefix}${eventName.replace(/(?:removed?|clicked):/,'')}`;
|
|
let cascObj = casc[cascName];
|
|
k.debug({[cascName]:cascObj});
|
|
if(event && cascObj){
|
|
if(event.previousValue){
|
|
cascObj.previousValue = event.previousValue;
|
|
}else if(event.originalRollId){
|
|
cascObj.originalRollId = event.originalRollId;
|
|
cascObj.rollData = RE.unescape(event.originalRollId);
|
|
}
|
|
}
|
|
return cascObj || {};
|
|
};
|
|
|
|
const triggerFunctions = function(obj,attributes,sections,casc){
|
|
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,casc}) :
|
|
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 alwaysFunctions = function(trigger,attributes,sections,casc){
|
|
Object.values(allHandlers).forEach((handler)=>{
|
|
handler({trigger,attributes,sections,casc});
|
|
});
|
|
};
|
|
const processChange = function({event,trigger,attributes,sections,casc}){
|
|
if(event && !trigger){
|
|
debug(`${event.sourceAttribute} change detected. No trigger found`);
|
|
return;
|
|
}
|
|
if(!attributes || !sections || !casc){
|
|
debug(`!!! Insufficient arguments || attributes > ${!!attributes} | sections > ${!!sections} | casc > ${!!casc} !!!`);
|
|
return;
|
|
}
|
|
debug({trigger});
|
|
if(event){
|
|
debug('checking for initial & always functions');
|
|
alwaysFunctions(trigger,attributes,sections,casc);//Functions that should be run for all events.
|
|
initialFunction(trigger,attributes,sections,casc);//functions that should only be run if the attribute was the thing changed by the user
|
|
}
|
|
if(trigger){
|
|
debug(`processing ${trigger.name}`);
|
|
triggerFunctions(trigger,attributes,sections,casc);
|
|
if(!event && trigger.calculation && funcs[trigger.calculation]){
|
|
attributes[trigger.name] = funcs[trigger.calculation]({trigger,attributes,sections,casc});
|
|
}else if(trigger.calculation && !funcs[trigger.calculation]){
|
|
debug(`K-Scaffold Error: No function named ${trigger.calculation} found`);
|
|
}
|
|
if(Array.isArray(trigger.affects)){
|
|
attributes.queue.push(...trigger.affects);
|
|
}
|
|
}
|
|
attributes.set({attributes,sections,casc});
|
|
};
|
|
const attrTarget = {
|
|
updates:{},
|
|
attributes:{...attrs},
|
|
repOrders:{},
|
|
queue: [],
|
|
casc:{},
|
|
alwaysFunctions,
|
|
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({updates:obj.updates});
|
|
let trueCallback = Object.keys(obj.repOrders).length ?
|
|
function(){
|
|
Object.entries(obj.repOrders).forEach(([section,order])=>{
|
|
_setSectionOrder(section,order,)
|
|
});
|
|
callback && callback();
|
|
}:
|
|
callback;
|
|
Object.keys(obj.updates).forEach((key)=>obj.attributes[key] = obj.updates[key]);
|
|
const update = obj.updates;
|
|
obj.updates = {};
|
|
set(update,vocal,trueCallback);
|
|
}
|
|
}
|
|
}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 !== ''){
|
|
retValue = numRetVal;
|
|
}else if(cascades[cascRef] && (typeof cascades[cascRef].defaultValue === 'number' || cascades[cascRef].type === '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);
|
|
};
|
|
|
|
/**
|
|
* Function that registers a function for being called via the funcs object. Returns true if the function was successfully registered, and false if it could not be registered for any reason.
|
|
* @memberof Utilities
|
|
* @param {object} funcObj - Object with keys that are names to register functions under and values that are functions.
|
|
* @param {object} optionsObj - Object that contains options to use for this registration.
|
|
* @param {string[]} optionsObj.type - Array that contains the types of specialized functions that apply to the functions being registered. Valid types are `"opener"`, `"updater"`, and `"default"`. `"default"` is always used, and never needs to be passed.
|
|
* @returns {boolean} - True if the registration succeeded, false if it failed.
|
|
* @example
|
|
* //Basic Registration
|
|
* const myFunc = function({trigger,attributes,sections,casc}){};
|
|
* k.registerFuncs({myFunc});
|
|
*
|
|
* //Register a function to run on sheet open
|
|
* const openFunc = function({trigger,attributes,sections,casc}){};
|
|
* k.registerFuncs({openFunc},{type:['opener']})
|
|
*
|
|
* //Register a function to run on all events
|
|
* const allFunc = function({trigger,attributes,sections,casc}){};
|
|
* k.registerFuncs({allFunc},{type:['all']})
|
|
*/
|
|
const registerFuncs = function(funcObj,optionsObj = {}){
|
|
if(typeof funcObj !== 'object' || typeof optionsObj !== 'object'){
|
|
debug(`!!!! K-scaffold error: Improper arguments to register functions !!!!`);
|
|
return false;
|
|
}
|
|
const typeArr = optionsObj.type ? ['default',...optionsObj.type] : ['default'];
|
|
const typeSwitch = {
|
|
'opener':openHandlers,
|
|
'updater':updateHandlers,
|
|
'new':initialSetups,
|
|
'all':allHandlers,
|
|
'default':funcs
|
|
};
|
|
let setState;
|
|
Object.entries(funcObj).map(([prop,value])=>{
|
|
typeArr.forEach((type)=>{
|
|
if(typeSwitch[type][prop]){
|
|
debug(`!!! Duplicate function name for ${prop} as ${type}!!!`);
|
|
setState = false;
|
|
}else if(typeof value === 'function'){
|
|
typeSwitch[type][prop] = value;
|
|
setState = setState !== false ? true : false;
|
|
}else{
|
|
debug(`!!! K-scaffold error: Function registration requires a function. Invalid value to register as ${type} !!!`);
|
|
setState = false;
|
|
}
|
|
});
|
|
});
|
|
return setState;
|
|
};
|
|
kFuncs.registerFuncs = registerFuncs;
|
|
|
|
/**
|
|
* Function that sets up the action calls used in the roller mixin.
|
|
* @memberof Sheetworkers
|
|
* @param {object} attributes - The attribute values of the character
|
|
* @param {object[]} sections - All the repeating section IDs
|
|
*/
|
|
const setActionCalls = function({attributes,sections}){
|
|
actionAttributes.forEach((base)=>{
|
|
let [section,,field] = k.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;
|
|
|
|
/**
|
|
* Function to call a function previously registered to the funcs object. May not be used that much in actual sheets, but very useful when writing unit tests for your sheet. Either returns the function or null if no function exists.
|
|
* @memberof Sheetworkers
|
|
* @param {string} funcName - The name of the function to invoke.
|
|
* @param {...any} args - The arguments to call the function with.
|
|
* @returns {function|null}
|
|
* @example
|
|
* //Call myFunc with two arguments
|
|
* k.callFunc('myFunc','an argument','another argument');
|
|
*/
|
|
const callFunc = function(funcName,...args){
|
|
if(funcs[funcName]){
|
|
debug(`calling ${funcName}`);
|
|
return funcs[funcName](...args);
|
|
}else{
|
|
debug(`Invalid function name: ${funcName}`);
|
|
return null;
|
|
}
|
|
};
|
|
kFuncs.callFunc = callFunc;/**@namespace Sheetworkers */
|
|
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
//Sheet Updaters and styling functions
|
|
const updateSheet = function(){
|
|
log('updating sheet');
|
|
getAllAttrs({props:['debug_mode',...baseGet],callback:(attributes,sections,casc)=>{
|
|
kFuncs.debugMode = kFuncs.debugMode || !!attributes.debug_mode;
|
|
debug({sheet_version:attributes.sheet_version});
|
|
if(!attributes.sheet_version){
|
|
Object.entries(initialSetups).forEach(([funcName,handler])=>{
|
|
if(typeof funcs[funcName] === 'function'){
|
|
debug(`running ${funcName}`);
|
|
funcs[funcName]({attributes,sections,casc});
|
|
}else{
|
|
debug(`!!!Warning!!! no function named ${funcName} found. Initial sheet setup not performed.`);
|
|
}
|
|
});
|
|
}else{
|
|
Object.entries(updateHandlers).forEach(([ver,handler])=>{
|
|
if(attributes.sheet_version < +ver){
|
|
handler({attributes,sections,casc});
|
|
}
|
|
});
|
|
}
|
|
k.debug({openHandlers});
|
|
Object.entries(openHandlers).forEach(([funcName,func])=>{
|
|
if(typeof funcs[funcName] === 'function'){
|
|
debug(`running ${funcName}`);
|
|
funcs[funcName]({attributes,sections,casc});
|
|
}else{
|
|
debug(`!!!Warning!!! no function named ${funcName} found. Sheet open handling not performed.`);
|
|
}
|
|
});
|
|
setActionCalls({attributes,sections});
|
|
attributes.sheet_version = kFuncs.version;
|
|
log(`Sheet Update applied. Current Sheet Version ${kFuncs.version}`);
|
|
attributes.set();
|
|
log('Sheet ready for use');
|
|
}});
|
|
};
|
|
|
|
const initialSetup = function(attributes,sections){
|
|
debug('Initial sheet setup');
|
|
};
|
|
|
|
/**
|
|
* This is the default listener function for attributes that the K-Scaffold uses. It utilizes the `triggerFuncs`, `listenerFunc`, `calculation`, and `affects` properties of the K-scaffold trigger object (see the Pug section of the scaffold for more details).
|
|
* @memberof Sheetworkers
|
|
* @param {Roll20Event} event - The Roll20 event object
|
|
* @returns {void}
|
|
* @example
|
|
* //Call from an attribute change
|
|
* on('change:an_attribute',k.accessSheet);
|
|
*/
|
|
const accessSheet = function(event){
|
|
debug({funcs:Object.keys(funcs)});
|
|
debug({event});
|
|
getAllAttrs({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 || [];
|
|
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*/
|
|
/**
|
|
* These are functions that provide K-scaffold aliases for the basic Roll20 sheetworker functions. These functions also provide many additional features on top of the standard Roll20 sheetworkers.
|
|
* @namespace Sheetworkers.Sheetworker Aliases
|
|
*/
|
|
/**
|
|
* Alias for [setSectionOrder()](https://wiki.roll20.net/Sheet_Worker_Scripts#setSectionOrder.28.3CRepeating_Section_Name.3E.2C_.3CSection_Array.3E.2C_.3CCallback.3E.29) that allows you to use the section name in either `repeating_section` or `section` formats. Note that the Roll20 sheetworker [setSectionOrder](https://wiki.roll20.net/Sheet_Worker_Scripts#setSectionOrder.28.3CRepeating_Section_Name.3E.2C_.3CSection_Array.3E.2C_.3CCallback.3E.29) currently causes some display issues on sheets.
|
|
* @memberof Sheetworker Aliases
|
|
* @name setSectionOrder
|
|
* @param {string} section - The name of the section, with or without `repeating_`
|
|
* @param {string[]} order - Array of ids describing the desired order of the section.
|
|
* @returns {void}
|
|
* @example
|
|
* //Set the order of a repeating_weapon section
|
|
* k.setSectionOrder('repeating_equipment',['id1','id2','id3']);
|
|
* //Can also specify the section name without the repeating_ prefix
|
|
* k.setSectionOrder('equipment',['id1','id2','id3']);
|
|
*/
|
|
const _setSectionOrder = function(section,order){
|
|
let trueSection = section.replace(/repeating_/,'');
|
|
setSectionOrder(trueSection,order);
|
|
};
|
|
kFuncs.setSectionOrder = _setSectionOrder;
|
|
|
|
/**
|
|
* Alias for [removeRepeatingRow](https://wiki.roll20.net/Sheet_Worker_Scripts#removeRepeatingRow.28_RowID_.29) that also removes the row from the current object of attribute values and array of section IDs to ensure that erroneous updates are not issued.
|
|
* @memberof Sheetworker Aliases
|
|
* @name removeRepeatingRow
|
|
* @param {string} row - The row id to be removed
|
|
* @param {attributesProxy} attributes - The attribute values currently in memory
|
|
* @param {object} sections - Object that contains arrays of all the IDs in sections on the sheet indexed by repeating name.
|
|
* @returns {void}
|
|
* @example
|
|
* //Remove a repeating Row
|
|
* k.getAllAttrs({
|
|
* callback:(attributes,sections)=>{
|
|
* const rowID = sections.repeating_equipment[0];
|
|
* k.removeRepeatingRow(`repeating_equipment_${rowID}`,attributes,sections);
|
|
* console.log(sections.repeating_equipment); // => rowID no longer exists in the array.
|
|
* console.log(attributes[`repeating_equipment_${rowID}_name`]); // => undefined
|
|
* }
|
|
* })
|
|
*/
|
|
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);
|
|
};
|
|
kFuncs.removeRepeatingRow = _removeRepeatingRow;
|
|
|
|
/**
|
|
* Alias for [getAttrs()](https://wiki.roll20.net/Sheet_Worker_Scripts#getAttrs.28attributeNameArray.2C_callback.29) that converts the default object of attribute values into an {@link attributesProxy} and passes that back to the callback function.
|
|
* @memberof Sheetworker Aliases
|
|
* @name getAttrs
|
|
* @param {string[]} [props=baseGet] - Array of attribute names to get the value of. Defaults to {@link baseGet} if not passed.
|
|
* @param {function(attributesProxy)} callback - The function to call after the attribute values have been gotten. An {@link attributesProxy} is passed to the callback.
|
|
* @example
|
|
* //Gets the attributes named in props.
|
|
* k.getAttrs({
|
|
* props:['attribute_1','attribute_2'],
|
|
* callback:(attributes)=>{
|
|
* //Work with the attributes as you would in a normal getAttrs, or use the superpowers of the K-scaffold attributes object like so:
|
|
* attributes.attribute_1 = 'new value';
|
|
* attributes.set();
|
|
* }
|
|
* })
|
|
*/
|
|
const _getAttrs = function({props=baseGet,callback}){
|
|
getAttrs(props,(values)=>{
|
|
const attributes = createAttrProxy(values);
|
|
callback(attributes);
|
|
});
|
|
};
|
|
kFuncs.getAttrs = _getAttrs;
|
|
|
|
/**
|
|
* Alias for [getAttrs()](https://wiki.roll20.net/Sheet_Worker_Scripts#getAttrs.28attributeNameArray.2C_callback.29) and [getSectionIDs](https://wiki.roll20.net/Sheet_Worker_Scripts#getSectionIDs.28section_name.2Ccallback.29) that combines the actions of both sheetworker functions and converts the default object of attribute values into an {@link attributesProxy}. Also gets the details on how to handle all attributes from the master {@link cascades} object and.
|
|
* @memberof Sheetworker Aliases
|
|
* @param {Object} args
|
|
* @param {string[]} [args.props=baseGet] - Array of attribute names to get the value of. Defaults to {@link baseGet} if not passed.
|
|
* @param {repeatingSectionDetails} sectionDetails - Array of details about a section to get the IDs for and attributes that need to be gotten.
|
|
* @param {function(attributesProxy,sectionObj,expandedCascade):void} args.callback - The function to call after the attribute values have been gotten. An {@link attributesProxy} is passed to the callback along with a {@link sectionObj} and {@link expandedCascade}.
|
|
* @example
|
|
* //Get every K-scaffold linked attribute on the sheet
|
|
* k.getAllAttrs({
|
|
* callback:(attributes,sections,casc)=>{
|
|
* //Work with the attributes as you please.
|
|
* attributes.some_attribute = 'a value';
|
|
* attributes.set();//Apply our change
|
|
* }
|
|
* })
|
|
*/
|
|
const getAllAttrs = function({props=baseGet,sectionDetails=repeatingSectionDetails,callback}){
|
|
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);
|
|
})
|
|
});
|
|
};
|
|
kFuncs.getAllAttrs = getAllAttrs;
|
|
|
|
/**
|
|
* Alias for [getSectionIDs()](https://wiki.roll20.net/Sheet_Worker_Scripts#getSectionIDs.28section_name.2Ccallback.29) that allows you to iterate through several functions at once. Also assembles an array of repeating attributes to get.
|
|
* @memberof Sheetworker Aliases
|
|
* @param {object[]} sectionDetails - Array of details about a section to get the IDs for and attributes that need to be gotten.
|
|
* @param {string} sectionDetails.section - The full name of the repeating section including the `repeating_` prefix.
|
|
* @param {string[]} sectionDetails.fields - Array of field names that need to be gotten from the repeating section
|
|
* @param {function(string[],sectionObj)} callback - The function to call once all IDs have been gotten and the array of repating attributes to get has been assembled. The callback is passed the array of repating attributes to get and a {@link sectionObj}.
|
|
* @example
|
|
* // Get some section details
|
|
* const sectionDetails = {
|
|
* {section:'repeating_equipment',fields:['name','weight','cost']},
|
|
* {section:'repeating_weapon',fields:['name','attack','damage']}
|
|
* };
|
|
* k.getSections(sectionDetails,(attributeNames,sections)=>{
|
|
* console.log(attributeNames);// => Array containing all row specific attribute names
|
|
* console.log(sections);// => Object with arrays containing the row ids. Indexed by section name (e.g. repeating_eqiupment)
|
|
* })
|
|
*/
|
|
const getSections = function(sectionDetails,callback){
|
|
let queueClone = _.clone(sectionDetails);
|
|
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([],{});
|
|
}else{
|
|
worker(queueClone);
|
|
}
|
|
};
|
|
kFuncs.getSections = getSections;
|
|
|
|
// Sets the attributes while always calling with {silent:true}
|
|
// Can be awaited to get the values returned from _setAttrs
|
|
/**
|
|
* Alias for [setAttrs()](https://wiki.roll20.net/Sheet_Worker_Scripts#setAttrs.28values.2Coptions.2Ccallback.29) that sets silently by default.
|
|
* @memberof Sheetworker Aliases
|
|
* @param {object} obj - The object containting attributes to set
|
|
* @param {boolean} [vocal=false] - Whether to set silently (default value) or not.
|
|
* @param {function()} [callback] - The callback function to invoke after the setting has been completed. No arguments are passed to the callback function.
|
|
* @example
|
|
* //Set some attributes silently
|
|
* k.setAttrs({attribute_1:'new value'})
|
|
* //Set some attributes and triggers listeners
|
|
* k.setAttrs({attribute_1:'new value',true})
|
|
* //Set some attributes and call a callback function
|
|
* k.setAttrs({attribute_1:'new value'},null,()=>{
|
|
* //Do something after the attribute is set
|
|
* })
|
|
*/
|
|
const set = function(obj,vocal=false,callback){
|
|
setAttrs(obj,{silent:!vocal},callback);
|
|
};
|
|
kFuncs.setAttrs = set;
|
|
|
|
const generateCustomID = function(string){
|
|
if(!string.startsWith('-')){
|
|
string = `-${string}`;
|
|
}
|
|
rowID = generateRowID();
|
|
let re = new RegExp(`^.{${string.length}}`);
|
|
return `${string}${rowID.replace(re,'')}`;
|
|
};
|
|
|
|
|
|
/**
|
|
* Alias for generateRowID that adds the new id to the {@link sectionObj}. Also allows for creation of custom IDs that conform to the section ID requirements.
|
|
* @memberof Sheetworker Aliases
|
|
* @name generateRowID
|
|
* @param {sectionObj} sections
|
|
* @param {string} [customText] - Custom text to start the ID with. This text should not be longer than the standard repeating section ID format.
|
|
* @returns {string} - The created ID
|
|
* @example
|
|
* k.getAllAttrs({
|
|
* callback:(attributes,sections,casc)=>{
|
|
* //Create a new row ID
|
|
* const rowID = k.generateRowID('repeating_equipment',sections);
|
|
* console.log(rowID);// => -p8rg908ug0suzz
|
|
* //Create a custom row ID
|
|
* const customID = k.generateRowID('repeating_equipment',sections,'custom');
|
|
* console.log(customID);// => -custom98uadj89kj
|
|
* }
|
|
* });
|
|
*/
|
|
const _generateRowID = function(section,sections,customText){
|
|
let rowID = customText ?
|
|
generateCustomID(customText) :
|
|
generateRowID();
|
|
section = section.match(/^repeating_[^_]+$/) ?
|
|
section :
|
|
`repeating_${section}`;
|
|
sections[section] = sections[section] || [];
|
|
sections[section].push(rowID);
|
|
return `${section}_${rowID}`;
|
|
};
|
|
kFuncs.generateRowID = _generateRowID;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
const listeners = {};
|
|
|
|
/**
|
|
* The array of attribute names that the k-scaffold gets by default. Does not incude repeating attributes.
|
|
* @memberof Variables
|
|
* @var
|
|
* @type {array}
|
|
*/
|
|
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;
|
|
},[]);
|
|
kFuncs.baseGet = baseGet;
|
|
|
|
const registerEventHandlers = function(){
|
|
on('sheet:opened',updateSheet);
|
|
debug({funcKeys:Object.keys(funcs),funcs});
|
|
//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);
|
|
}
|
|
});
|
|
log(`kScaffold Loaded`);
|
|
};
|
|
setTimeout(registerEventHandlers,0);//Delay the execution of event registration to ensure all event properties are present.
|
|
|
|
/**
|
|
* Function to add a repeating section when the add button of a customControlFieldset or inlineFieldset is clicked.
|
|
* @memberof Sheetworkers
|
|
* @param {object} event - The R20 event object
|
|
*/
|
|
const addItem = function(event){
|
|
let [,,section] = parseClickTrigger(event.triggerName);
|
|
section = section.replace(/add-/,'');
|
|
getAllAttrs({
|
|
callback:(attributes,sections,casc) => {
|
|
let row = _generateRowID(section,sections);
|
|
debug({row});
|
|
attributes[`${row}_name`] = '';
|
|
setActionCalls({attributes,sections});
|
|
const trigger = cascades[`fieldset_repeating_${section}`];
|
|
if(trigger && trigger.addFuncs){
|
|
trigger.addFuncs.forEach((funcName) => {
|
|
if(funcs[funcName]){
|
|
funcs[funcName]({attributes,sections,casc,trigger});
|
|
}
|
|
});
|
|
}
|
|
attributes.set({attributes,sections,casc});
|
|
}
|
|
});
|
|
};
|
|
funcs.addItem = addItem;/**
|
|
* The default tab navigation function of the K-scaffold. Courtesy of Riernar. It will add `k-active-tab` to the active tab-container and `k-active-button` to the active button. You can either write your own CSS to control display of these, or use the default CSS included in `scaffold/_k.scss`. Note that `k-active-button` has no default CSS as it is assumed that you will want to style the active button to match your system.
|
|
* @memberof Sheetworkers
|
|
* @param {Object} trigger - The trigger object
|
|
* @param {object} attributes - The attribute values of the character
|
|
*/
|
|
const kSwitchTab = function ({ trigger, attributes }) {
|
|
const [container, tab] = (
|
|
trigger.name.match(/nav-tabs-(.+)--(.+)/) ||
|
|
[]
|
|
).slice(1);
|
|
$20(`[data-container-tab="${container}"]`).removeClass('k-active-tab');
|
|
$20(`[data-container-tab="${container}"][data-tab="${tab}"]`).addClass('k-active-tab');
|
|
$20(`[data-container-button="${container}"]`).removeClass('k-active-button');
|
|
$20(`[data-container-button="${container}"][data-button="${tab}"]`).addClass('k-active-button');
|
|
const tabInputName = `${container.replace(/\-/g,'_')}_tab`;
|
|
if(persistentTabs.indexOf(tabInputName) > -1){
|
|
attributes[tabInputName] = trigger.name;
|
|
}
|
|
}
|
|
|
|
registerFuncs({ kSwitchTab });
|
|
|
|
/**
|
|
* Sets persistent tabs to their last active state
|
|
* @memberof Sheetworkers
|
|
* @param {object} attributes - The attribute values of the character
|
|
*/
|
|
const kTabOnOpen = function({trigger,attributes,sections,casc}){
|
|
if(typeof persistentTabs === 'undefined') return;
|
|
persistentTabs.forEach((tabInput) => {
|
|
const pseudoTrigger = {name:attributes[tabInput]};
|
|
kSwitchTab({trigger:pseudoTrigger, attributes});
|
|
});
|
|
};
|
|
registerFuncs({ kTabOnOpen },{type:['opener']});
|
|
return kFuncs;
|
|
}());
|
|
const actionAttributes = ["repeating_skill_$X_action","intellect_action","might_action","presence_action","cunning_action","dexterity_action","manipulation_action","resolve_action","stamina_action","composure_action","pc_initiative_action","initiative_action"];const supplements = {};const skills = ["aim","athletics","close combat","command","culture","empathy","enigmas","humanities","integrity","larceny","medicine","persuasion","pilot","science","survival","technology"];const systems = ["beneath-the-sea"];let deedName = "aspiration";const sectionDisplays = {"general":["section-skills","section-attributes","section-equipment","section-notes","section-deeds"],"powers":["section-paths","section-calling","section-purview","section-birthrights"]};const navDisplays = {"antagonist":{"show":["antagonist"],"hide":["all","general","powers","gm_screen","party_screen"]},"character":{"show":["all","general","powers"],"hide":["antagonist","gm_screen","party_screen"]},"gm_screen":{"show":["gm_screen"],"hide":["all","general","powers","antagonist","party_screen"]},"party_screen":{"show":["party_screen"],"hide":["all","general","powers","antagonist","gm_screen"]}};const generalSections = ["skills","attributes","equipment","notes","deeds"];const powerSections = ["paths","calling","purview","birthrights"];const complexRepeatingSections = ["repeating_calling","repeating_path","repeating_purview"];const collapsibleMasters = ["repeating_path","repeating_purview"];const systemDefaults = {"beneath-the-sea":{"repeating_skill":{"field":"name","additional":false,"values":["aim","athletics","close combat","command","culture","empathy","enigmas","humanities","integrity","larceny","medicine","persuasion","pilot","science","survival","technology"]},"repeating_path":{"field":"display_state","values":["master","add-detail"],"additional":true},"repeating_relationship":{"number":0,"field":"name","values":[""],"additional":true},"repeating_trademark":{"field":"display_state","number":0,"values":["trademark"],"additional":true},"repeating_aspiration":{"field":"type","values":["short","long"],"additional":true},"repeating_completed-aspiration":{"field":"type","values":[],"additional":true},"repeating_injury":{"field":"type","values":["Just a Flesh Wound","Just a Flesh Wound","Just a Flesh Wound","That'll Leave a Scar","That'll Leave a Scar","That'll Leave a Scar","Last-Ditch Effort","Last-Ditch Effort","Last-Ditch Effort","Don't Forget Me","Death Scene"],"additional":true},"repeating_equipment":{"number":0,"field":"display_state","values":["equipment"],"additional":true},"repeating_trope":{"number":0,"field":"display_state","values":["trope"],"additional":true},"repeating_quip":{"number":0,"field":"display_state","values":["quip"],"additional":true},"repeating_stunt":{"number":0,"field":"display_state","values":["stunt"],"additional":true},"repeating_special-rules":{"number":0,"field":"name","values":[""],"additional":true},"attributes":["intellect","cunning","resolve","might","dexterity","stamina","presence","manipulation","composure"],"clashStats":["intellect","cunning","resolve","might","dexterity","stamina","presence","manipulation","composure"]}};const repeatMonitor = [];const characterAttributes = ["intellect","cunning","resolve","might","dexterity","stamina","presence","manipulation","composure"];const singular = {"Paths":"Path","Trademarks":"Trademark","Stunts":"Stunt","Quips":"Quip","Tropes":"Trope"};const sectionDisplayNames = {"path":"short-master","trademark":"short-trademark","stunt":"short-stunt","quip":"short-quip","contact":"short-contact","group":"short-group","access":"short-access","obligation":"short-obligation","trope":"short-trope","special-rules":"short-display"};const antagonistSpecialSections = {};const specialCategories = {};const itemCategories = ["Trademarks","Stunts","Quips","Paths","Tropes","special-rules"];/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
//Sheet Variables
|
|
k.sheetName = 'They Came From ...';
|
|
k.version = 1.00;
|
|
|
|
sectionDisplays.all = Object.entries(sectionDisplays).reduce((arr,[prop,sections])=>[...arr,...sections],[]);
|
|
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
const resetOrderStyling = function(attributes,reset=false){//Temporary fix for display issue with reordering section`s via setSectionOrder
|
|
const clearObj = {};
|
|
let updateKeys = Object.keys(attributes.updates);
|
|
let unusedSections = ['repeating_injury','repeating_path','repeating_calling'].filter((sec)=>updateKeys.every((attr)=>!attr.startsWith(sec)));
|
|
|
|
unusedSections.some((section)=>{
|
|
let id = attributes.attributes[`_reporder_${section}`] && attributes.attributes[`_reporder_${section}`].find(i=>Object.keys(attributes.attributes).some((attr)=>attr.startsWith(`${section}_${i}`)));
|
|
if(id){
|
|
attributes[`${section}_${id}_display_state`] = attributes[`${section}_${id}_display_state`];
|
|
clearObj[`${section}_${id}_display_state`] = 'loading';
|
|
return true;
|
|
}
|
|
});
|
|
k.setAttr(clearObj,null,()=>{
|
|
let sections = Object.keys(attributes.repOrders);
|
|
sections.forEach((section)=>{
|
|
k.setSectionOrder(section,attributes.repOrders[section]);
|
|
});
|
|
if(reset){
|
|
k.setAttrs(attributes.attributes);
|
|
}
|
|
});
|
|
};
|
|
|
|
const orderComplex = function({section,attributes,sections}){
|
|
section = section.startsWith('repeating_') ? section : `repeating_${section}`;
|
|
|
|
|
|
const masterIDs = sections[section].filter((id)=>!attributes[`${section}_${id}_master_id`]);
|
|
|
|
sections[section] = sections[section].reduce((memo,id)=>{
|
|
if(attributes[`${section}_${id}_master_id`]){
|
|
|
|
const isDetail = attributes[`${section}_${id}_display_state`] === 'add-detail';
|
|
const lastChildIndex = getChildIndex(memo,section,id,attributes,isDetail);
|
|
if(lastChildIndex < 0){
|
|
k.removeRepeatingRow(`${section}_${id}`,attributes,sections)
|
|
}else{
|
|
memo.splice(lastChildIndex,0,id);
|
|
}
|
|
}
|
|
return memo;
|
|
},masterIDs);
|
|
attributes[section] = sections[section];
|
|
};
|
|
|
|
const getChildIndex = function(memo,section,id,attributes,detailAdd){
|
|
|
|
const lastChild = [...memo].reverse().find((fid)=>{
|
|
|
|
if(attributes[`${section}_${fid}_master_id`] === attributes[`${section}_${id}_master_id`]){
|
|
return detailAdd ?
|
|
true :
|
|
attributes[`${section}_${fid}_display_state`] !== 'add-detail';
|
|
}else{
|
|
return fid.toLowerCase() === attributes[`${section}_${id}_master_id`];
|
|
}
|
|
});
|
|
|
|
let lastIndex = memo.indexOf(lastChild);
|
|
|
|
return lastIndex >= 0 ? lastIndex + 1 : 0;
|
|
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
/*
|
|
Sheet Styling
|
|
*/
|
|
const navigateSheet = function({trigger,attributes}){
|
|
k.debug('navigating sheet');
|
|
let triggerName = trigger ?
|
|
trigger.name :
|
|
attributes.sheet_state;
|
|
let target = triggerName.replace(/clicked:|nav-/g,'');
|
|
k.debug({target});
|
|
$20('.navigable-section,.sheet-nav__tab').removeClass('active');
|
|
$20(`.sheet-nav__tab.${target},.navigable-section--${target}`).addClass('active');
|
|
attributes.sheet_state = target;
|
|
};
|
|
k.registerFuncs({navigateSheet});
|
|
|
|
const setupSystem = function({attributes,sections}){
|
|
let system = attributes.system;
|
|
createSections(system,attributes,sections);
|
|
systems.forEach((sys)=>{
|
|
let action = sys === system ? 'addClass' : 'removeClass';
|
|
$20(`.subsystem-style`)[action](`system-${sys}`);
|
|
$20(`.${sys}${sys===system ? '' : `:not(.${system})`}`)[action]('active');
|
|
});
|
|
setupAttributes(system,attributes);
|
|
};
|
|
k.registerFuncs({setupSystem});
|
|
|
|
const setupAttributes = function(system,attributes){
|
|
let attrArr = systemDefaults[system].attributes;
|
|
attrArr.forEach((attr)=>{
|
|
attributes[attr] = attributes[attr] || 1;
|
|
});
|
|
};
|
|
|
|
const createSections = function(system,attributes,sections){
|
|
let details = systemDefaults[system];
|
|
Object.keys(sections).forEach((section)=>{
|
|
let sectionDetails = details[section];
|
|
if(sectionDetails){
|
|
checkSectionContents(sectionDetails,section,sections,attributes);
|
|
}else{
|
|
sections[section].forEach((id)=>{//clean up unneeded sections
|
|
sections[section].forEach((id)=>{
|
|
k.removeRepeatingRow(`${section}_${id}`,attributes,sections);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
const checkSectionContents = function(sectionDetails,section,sections,attributes){
|
|
const IDs = [...sections[section]];
|
|
if(sectionDetails.hasOwnProperty('number')){
|
|
let missing = Math.max(sectionDetails.number - sections[section].length,0);
|
|
|
|
_.range(missing).forEach(()=>{
|
|
let row = k.generateRowID(section,sections);
|
|
attributes[`${row}_${sectionDetails.field || 'name'}`] = sectionDetails.values[0];
|
|
});
|
|
}else if(sectionDetails.values){
|
|
let missing = [];//Array to store missing values
|
|
let unusedIDs = {};//Object to store whether IDs are needed (true) or not (false).
|
|
sectionDetails.values.forEach((val)=>{
|
|
let found = IDs.some((id)=>{
|
|
if(attributes[`${section}_${id}_${sectionDetails.field}`].endsWith(val)){
|
|
unusedIDs[id] = true;
|
|
return true;
|
|
}else{
|
|
unusedIDs[id] = unusedIDs[id] || false;
|
|
return false;
|
|
}
|
|
},false);
|
|
if(!found){
|
|
missing.push(val);
|
|
}
|
|
return found;
|
|
});
|
|
|
|
if(missing.length){
|
|
const parentChild = {
|
|
parentID:null,
|
|
children:[]
|
|
};
|
|
missing.forEach((val)=>{
|
|
|
|
let newRow = k.generateRowID(section,sections,val === 'add-detail' ? 'ADD' : '');
|
|
|
|
let newID = newRow.replace(/repeating_[^_]+_/,'');
|
|
|
|
if(complexRepeatingSections.indexOf(section) >= 0){
|
|
if(val === 'master'){
|
|
parentChild.parentID = newID.toLowerCase();
|
|
}else{
|
|
parentChild.children.push(newID);
|
|
}
|
|
}
|
|
attributes[`${newRow}_${sectionDetails.field}`] = val;
|
|
if(sectionDetails.additionalFields){
|
|
sectionDetails.additionalFields
|
|
}
|
|
attributes[`${newRow}_display_state`] = section === 'repeating_injury' ?
|
|
`short-${val}` :
|
|
val;
|
|
if(/calling/.test(section)){
|
|
attributes[`${newRow}_rating_max`] = 1;
|
|
}
|
|
});
|
|
if(parentChild.parentID){
|
|
parentChild.children.forEach((id)=>{
|
|
attributes[`${section}_${id}_master_id`] = parentChild.parentID;
|
|
});
|
|
|
|
orderComplex({section,attributes,sections});
|
|
}
|
|
}
|
|
if(!sectionDetails.additional){
|
|
Object.keys(unusedIDs).forEach((id)=>{
|
|
if(!unusedIDs[id]){
|
|
k.removeRepeatingRow(`${section}_${id}`,attributes,sections);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
const populateInitiativeSkills = (attributes,sections) => {
|
|
const optionsArray = sections['repeating_skill']
|
|
.filter(id => id)
|
|
.reduce((arr,id) => {
|
|
arr.push({value:id.toLowerCase(),label:getTranslationByKey(attributes[`repeating_skill_${id}_name`])});
|
|
return arr;
|
|
},[{value:'',label:getTranslationByKey('select one')}]);
|
|
populateListOptions({elemSelector:'#initiative-skill-select',optionsArray});
|
|
attributes.initiative_skill = optionsArray.some(obj => obj.value === attributes.initiative_skill) ?
|
|
attributes.initiative_skill :
|
|
'';
|
|
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
const styleOnOpen = function({attributes,sections}){
|
|
navigateSheet({attributes});
|
|
setupSystem({attributes,sections});
|
|
styleCharacterType({attributes});
|
|
retrieveHeaders(attributes);
|
|
populateInitiativeSkills(attributes,sections);
|
|
};
|
|
k.registerFuncs({styleOnOpen},{type:['opener']})
|
|
|
|
const retrieveHeaders = function(attributes){
|
|
|
|
let collapsed = attributes.collapsed.split(/\s*,\s*/).filter(c=>!/^\s*$/.test(c) && c !== 'completed deeds');
|
|
collapsed.forEach((c)=>{
|
|
$20(`.${c}.expandable-header`).addClass('collapse');
|
|
});
|
|
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
/*
|
|
Calculation Functions
|
|
These functions must return a value to be applied to an attribute
|
|
*/
|
|
const defenseCalc = function({attributes}){
|
|
return Math.max(attributes.resolve,attributes.stamina,attributes.composure);
|
|
};
|
|
k.registerFuncs({defenseCalc});
|
|
|
|
const calcXP = function({attributes}){
|
|
return attributes.experience_earned - attributes.experience_used;
|
|
};
|
|
k.registerFuncs({calcXP});
|
|
|
|
const movementCalc = function({attributes,sections}){
|
|
const athleticsID = sections.repeating_skill.find((id)=>attributes[`repeating_skill_${id}_name`] === 'athletics');
|
|
return attributes[`repeating_skill_${athleticsID}_rating`] +
|
|
Math.max(
|
|
1,
|
|
attributes.might,
|
|
attributes.dexterity
|
|
);
|
|
};
|
|
k.registerFuncs({movementCalc});/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
//Expands/collapses the sections
|
|
const expandHeader = function(event){
|
|
let [section,rowID,name] = k.parseClickTrigger(event.triggerName);
|
|
const target = `.${name}.expandable-header`;
|
|
k.getAllAttrs({props:['collapsed'],callback:(attributes,sections)=>{
|
|
let collapsed = attributes.collapsed.split(/\s*,\s*/);
|
|
|
|
if(section){
|
|
name = 'completed deeds';
|
|
let hide = 0
|
|
if(collapsed.indexOf(name) < 0){
|
|
collapsed.push(name);
|
|
hide = 1;
|
|
}else{
|
|
collapsed = collapsed.filter(b => b!==name && !/^\s*$/.test(b) );
|
|
}
|
|
sections[section].forEach((id)=>{
|
|
attributes[`${section}_${id}_hide`] = attributes[`${section}_${id}_completed`] ? hide : 0;
|
|
});
|
|
}else{
|
|
if(collapsed.indexOf(name) < 0){
|
|
collapsed.push(name);
|
|
}else{
|
|
collapsed = collapsed.filter(b => b!==name && !/^\s*$/.test(b) );
|
|
}
|
|
$20(target).toggleClass('collapse');
|
|
}
|
|
attributes.collapsed = collapsed.join(',');
|
|
attributes.set();
|
|
}})
|
|
};
|
|
k.registerFuncs({expandHeader});
|
|
|
|
//adds a subsection (e.g. a knack) to the over section (e.g. repeating_calling)
|
|
const addSubsection = async function(event){
|
|
let [section,rowID,button] = k.parseClickTrigger(event.triggerName);
|
|
let type = button.replace(/add-/,'');
|
|
let newID = generateRowID();
|
|
k.getAllAttrs({props:[],callback:(attributes,sections,casc)=>{
|
|
addSystemSubSection({type,newID,rowID,section,attributes,sections,casc});
|
|
}})
|
|
};
|
|
k.registerFuncs({addSubsection});
|
|
|
|
const addChildSection = function(attributes,sections,section,rowID,type,newID){
|
|
attributes[`${section}_${newID}_type`]=type;
|
|
attributes[`${section}_${newID}_display_state`]=type;
|
|
attributes[`${section}_${newID}_master_id`]=attributes[`${section}_${rowID}_master_id`];
|
|
if(/path/.test(section)){
|
|
attributes[`${section}_${newID}_rating`] = 1;
|
|
}
|
|
let triggerPos = sections[section].indexOf(rowID);
|
|
sections[section].splice(triggerPos,0,newID);
|
|
attributes[section] = sections[section];
|
|
attributes.set();
|
|
};
|
|
|
|
const sectionInteract = function(event){
|
|
|
|
let [action,targetSection] = extractRepcontrolDetails(event.htmlAttributes.name);
|
|
const interactSwitch = {
|
|
add:addRow,
|
|
edit:editSection
|
|
};
|
|
interactSwitch[action](targetSection);
|
|
};
|
|
k.registerFuncs({sectionInteract});
|
|
|
|
const extractRepcontrolDetails = function(name){
|
|
|
|
let match = name ? name.match(/^act_(.+?)-(.+)/) : [null,null,null];
|
|
match.shift();
|
|
return match;
|
|
};
|
|
|
|
const addRow = function(section){
|
|
k.getAllAttrs({callback:(attributes,sections)=>{
|
|
let IDs = sections[`repeating_${section}`];
|
|
system = attributes.system;
|
|
const defaults = systemDefaults[system][`repeating_${section}`];
|
|
if(!defaults) return;
|
|
let rowID;
|
|
let row;
|
|
const childParent = {
|
|
parent:'',
|
|
children:[]
|
|
}
|
|
if(!defaults.values){
|
|
row = k.generateRowID(section,sections);
|
|
attributes[`${row}_${defaults.field}`]='';
|
|
}else if(section==='deed'){
|
|
addDeed(system,attributes,sections);
|
|
}else if(section === 'injury'){
|
|
addInjury(system,attributes,sections);
|
|
}else{
|
|
defaults.values.forEach((value)=>{
|
|
row = k.generateRowID(section,sections,value === 'add-detail' ? 'ADD' : '');
|
|
rowID = row.replace(/repeating_[^_]+_/,'');
|
|
if(value === 'add-detail'){
|
|
childParent.children.push(rowID);
|
|
}else{
|
|
childParent.parent = rowID.toLowerCase();
|
|
}
|
|
attributes[`${row}_${defaults.field}`] = value;
|
|
attributes[`${row}_display_state`] = value;
|
|
if(/calling/.test(section)){
|
|
attributes[`${row}_rating_max`] = 1;
|
|
}
|
|
});
|
|
childParent.children.forEach((id)=>{
|
|
attributes[`repeating_${section}_${id}_master_id`] = childParent.parent;
|
|
});
|
|
orderComplex({section,attributes,sections});
|
|
}
|
|
attributes.set();
|
|
}});
|
|
};
|
|
|
|
const editSection = function(section){
|
|
let target = `fieldset.repeating_${section} + .repcontainer`;
|
|
$20(target).toggleClass('editmode');
|
|
if(/aspiration|deed/.test(section)){
|
|
$20(target.replace(/(deed|aspiration)/,'completed-$1')).toggleClass('editmode');
|
|
}
|
|
};
|
|
|
|
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
/*
|
|
Rolls
|
|
*/
|
|
const singleAttributeRoll = function(event){
|
|
debugger;
|
|
let [,,button] = k.parseClickTrigger(event.triggerName);
|
|
let field = button.replace(/-action$/,'').replace(/-/g,'_');
|
|
k.getAllAttrs({
|
|
props:[...k.baseGet,'difficulty_query','target_number'],sectionDetails:[],
|
|
callback:async (attributes)=>{
|
|
let [pool,addDice] = await assemblePool(attributes[field],0);
|
|
let enhancements = 0;
|
|
if(attributes.enhancement_query === 'on'){
|
|
enhancements = await k.extractQueryResult(getTranslationByKey('Do you have any enhancements'));
|
|
enhancements = k.value(enhancements);
|
|
}else{
|
|
await k.pseudoQuery('');
|
|
}
|
|
let [message,reusableMessage] = await assembleMessage(attributes,pool,field.replace(/_/g,' '),null,null,enhancements);
|
|
|
|
let confirm;
|
|
if(attributes.enhancement_query === 'on' || attributes.difficulty_query === 'on'){
|
|
confirm = await confirmRoll(attributes,{skillName:`^{${field.replace(/_/g,' ')}}`,skillDice:attributes[field],enhancements,addDice});
|
|
}else{
|
|
confirm = await k.pseudoQuery(1);
|
|
}
|
|
if(k.value(confirm)){
|
|
executeRoll(attributes,reusableMessage,message);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
k.registerFuncs({singleAttributeRoll});
|
|
|
|
const rollTask = function(event){
|
|
k.getAllAttrs({
|
|
props:[...k.baseGet,'difficulty_query','target_number'],
|
|
callback:async (attributes,sections)=>{
|
|
let [pool,skillName,attributeName,enhancements] = await queryPool(event.triggerName,attributes,sections);
|
|
if(!pool && !skillName && !attributeName){
|
|
return;
|
|
}
|
|
let [message,reusableMessage] = await assembleMessage(attributes,pool,skillName,attributeName,null,enhancements);
|
|
executeRoll(attributes,reusableMessage,message);
|
|
}
|
|
})
|
|
};
|
|
k.registerFuncs({rollTask});
|
|
|
|
const queryPool = async function(clicked,attributes,sections){
|
|
|
|
let [section,rowID,field] = k.parseClickTrigger(clicked);
|
|
field = field.replace(/-action$/,'');
|
|
|
|
let query;
|
|
let queryOptions;
|
|
let queryPrompt;
|
|
let skillDice;
|
|
let attributeDice;
|
|
let attributeName;
|
|
let skillName;
|
|
if(section === 'repeating_skill'){
|
|
let systemDetails = systemDefaults[attributes.system];
|
|
queryOptions = systemDetails.attributes.reduce((m,attr)=>{
|
|
m.push(`${k.capitalize(getTranslationByKey(attr))},${attr}`);
|
|
return m;
|
|
},[]).join('|');
|
|
queryPrompt = getTranslationByKey('attribute query');
|
|
skillDice = attributes[`${section}_${rowID}_rating`];
|
|
skillName = `^{@{${section}_${rowID}_name}}`;
|
|
}else{
|
|
let fieldRef = section ? parseSectionRoll(section,rowID,attributes,sections) : field;
|
|
section = 'repeating_skill';
|
|
const baseOptions = /resolve|stamina|composure/.test(fieldRef) ?
|
|
[k.capitalize(getTranslationByKey('attribute only'))] :
|
|
[];
|
|
queryOptions = sections[section].reduce((m,id)=>{
|
|
m.push(`${k.capitalize(getTranslationByKey(attributes[`${section}_${id}_name`]))},${section}_${id}`);
|
|
return m;
|
|
},baseOptions).join('|')
|
|
queryPrompt = getTranslationByKey('skill query');
|
|
attributeDice = attributes[fieldRef];
|
|
attributeName = `^{${field}}`;
|
|
}
|
|
query = `${queryPrompt}|${queryOptions}`;
|
|
|
|
let queryResult = await k.extractQueryResult(query);
|
|
if(!attributeDice){
|
|
attributeDice = attributes[queryResult];
|
|
attributeName = `^{${queryResult}}`;
|
|
}else{
|
|
skillDice = queryResult === 'Attribute Only' ?
|
|
0 :
|
|
attributes[`${queryResult}_rating`];
|
|
skillName = queryResult === 'Attribute Only' ?
|
|
'' :
|
|
`^{@{${queryResult}_name}}`;
|
|
}
|
|
let [pool,addDice] = await assemblePool(skillDice,attributeDice)
|
|
let enhancements = 0;
|
|
if(attributes.enhancement_query === 'on'){
|
|
enhancements = await k.extractQueryResult(getTranslationByKey('Do you have any enhancements'));
|
|
enhancements = k.value(enhancements);
|
|
}else{
|
|
await k.pseudoQuery('');
|
|
}
|
|
let confirm = await confirmRoll(attributes,{skillName,skillDice,attributeName,attributeDice,enhancements,addDice});
|
|
if(k.value(confirm)){
|
|
return [pool,skillName,attributeName,enhancements];
|
|
}else{
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const assembleMessage = async function(attributes,pool,skillName,attributeName,rollTitle,enhancements){
|
|
let reusableMessage = `{{successes=[[0['computed value']]]}} {{display_dice=true}} {{system=${attributes.system}}} {{finished=[[0]]}} {{enhancements=[[${enhancements}]]}}`;
|
|
if(attributes.difficulty_query === 'on'){
|
|
let difficulty = await k.extractQueryResult(`${getTranslationByKey('difficulty query')}|0`);
|
|
reusableMessage = `{{difficulty=[[0${difficulty}]]}} {{extra_successes=[[0[computed value]]]}} ${reusableMessage}`;
|
|
}else{
|
|
await k.pseudoQuery('');
|
|
}
|
|
return [`@{template_start} ${reusableMessage} ${rollTitle ? `{{title=${rollTitle}}}` : ''} {{skill=${skillName || ''}}} {{attribute=${attributeName || ''}}} ${pool} {{first=true}}`,reusableMessage];
|
|
};
|
|
|
|
const buildQuery = async function(prompt,options,attributes,sections){
|
|
|
|
if(typeof options === 'string'){
|
|
options = options.startsWith('repeating') ? sections[options].map(id=>`${k.capitalize(getTranslationByKey(attributes[`${options}_${id}_name`]))},${options}_${id}_rating`) : '';
|
|
}
|
|
let query = `${getTranslationByKey(prompt)}|${options.join('|')}`;
|
|
return await k.extractQueryResult(query);
|
|
};
|
|
|
|
const assemblePool = async function(skillDice,attributeDice){
|
|
const baseDice = k.value(skillDice)+k.value(attributeDice);
|
|
const additionalDice = await k.extractQueryResult(`${getTranslationByKey('additional dice query')}|${[...Array(Math.max(0,10 - baseDice) + 1).keys()].map(k=>k).join('|')}|`);
|
|
return [[...Array(baseDice + (+additionalDice)).keys()].map((n)=>{
|
|
return `{{roll_${n+1}=[[1d10cs>@{target_number}cf<1cf>@{again}]]}}`;
|
|
}).join(' ').trim() || '{{roll_1=^{no dice available}}}',+additionalDice];
|
|
};
|
|
|
|
const confirmRoll = function(attributes,{skillName,skillDice,attributeName,attributeDice,enhancements,addDice}){
|
|
let promptLookup = 'Roll {{skill}} and {{attribute}} with {{enhancement number}} enhancements and {{addDice}} additional dice?';
|
|
k.debug({addDice});
|
|
if(!skillName && !attributeName){
|
|
return k.pseudoQuery(1);
|
|
}else if(!skillName){
|
|
promptLookup = promptLookup.replace(/\{\{skill\}\} and /,'');
|
|
}else if(!attributeName){
|
|
promptLookup = promptLookup.replace(/ and \{\{attribute\}\}/,'');
|
|
}
|
|
if(!enhancements){
|
|
promptLookup = promptLookup.replace(/ \{\{enhancement number\}\} enhancements and/,'');
|
|
}
|
|
if(!addDice){
|
|
promptLookup = promptLookup.replace(/\s+(?:and|with) \{\{addDice\}\} additional dice/,'');
|
|
}
|
|
k.debug({promptLookup});
|
|
let prompt = getTranslationByKey(promptLookup);
|
|
|
|
const rollDetails = {[skillName]:skillDice,[attributeName]:attributeDice,enhancements,addDice};
|
|
k.debug({rollDetails});
|
|
k.debug({prompt});
|
|
Object.entries(rollDetails).forEach(([name,val],index)=>{
|
|
k.debug({name,val});
|
|
name = /^\^\{/.test(name) ?
|
|
(
|
|
/@{/.test(name) ?
|
|
getTranslationByKey(attributes[name.replace(/[\^@]{|}/g,'')]) :
|
|
getTranslationByKey(name.replace(/^\^{|}$/g,''))
|
|
) :
|
|
name;
|
|
let replaceText = name !== 'enhancements' && name !== 'addDice' ?
|
|
`${k.capitalize(name)} (${val})` :
|
|
val;
|
|
let replaceTarget = `\\{\\{${index}\\}\\}`;
|
|
|
|
|
|
prompt = prompt.replace(new RegExp(replaceTarget,'g'),replaceText);
|
|
});
|
|
k.debug({prompt});
|
|
return k.extractQueryResult(`${prompt}|${getTranslationByKey('Yes')},1|${getTranslationByKey('No')},0`);
|
|
};
|
|
|
|
const executeRoll = async function(attributes,reusableMessage,message,accumulator = {successes:0}){
|
|
const rollNums = [1,2,3,4,5,6,7,8,9,10];
|
|
let roll = await startRoll(message);
|
|
const computeObj = {};
|
|
const tn = attributes.target_number;
|
|
accumulator.successes = Object.keys(roll.results).reduce((total,key)=>{
|
|
return total += (/roll_/.test(key) && roll.results[key].result >= tn) ? 1 : 0;
|
|
},accumulator.successes);
|
|
if(rollNums.some((n)=>roll.results[`roll_${n}`] && roll.results[`roll_${n}`].result >= attributes.again)){
|
|
let pool = rollNums.reduce((m,n)=>{
|
|
if(roll.results[`roll_${n}`] && roll.results[`roll_${n}`].result >= Math.max(attributes.again,5)){
|
|
m+=`{{roll_${n}=[[1d10cs>@{target_number}cf<1cf>@{again}]]}}`;
|
|
}
|
|
return m;
|
|
},'');
|
|
let newMessage = `@{template_start} ${reusableMessage} ${pool} {{continuation=true}}`;
|
|
executeRoll(attributes,reusableMessage,newMessage,accumulator);
|
|
}else{
|
|
computeObj.finished = 1;
|
|
computeObj.successes = accumulator.successes;
|
|
if(accumulator.successes){
|
|
computeObj.successes += (roll.results.enhancements.result || 0);
|
|
}
|
|
if(roll.results.difficulty){
|
|
computeObj.extra_successes = Math.max(computeObj.successes - roll.results.difficulty.result,0);
|
|
}
|
|
}
|
|
finishRoll(roll.rollId,computeObj);
|
|
if(/initiative/.test(message)){
|
|
startRoll(`![[${computeObj.successes}&{tracker}]]`,(r) => finishRoll(r.rollId));
|
|
}
|
|
};
|
|
|
|
const parseSectionRoll = function(section,rowID,attributes){
|
|
let masterID = section === 'repeating_path' ? rowID : (attributes[`${section}_${rowID}_master_id`] || rowID);
|
|
return `${section}_${masterID}_rating`;
|
|
};
|
|
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
// Triggered Functions
|
|
|
|
const collapseSection = function({trigger,attributes,sections}){
|
|
const [section,rowID,field] = k.parseTriggerName(trigger.name);
|
|
if(attributes[`${section}_${rowID}_type`] === 'add-detail') return;
|
|
let collapse = attributes[trigger.name];
|
|
let rows;
|
|
if(/master/.test(attributes[`${section}_${rowID}_display_state`])){
|
|
|
|
rows = sections[section].filter((id)=>{
|
|
return attributes[`${section}_${id}_master_id`]===rowID && attributes[`${section}_${id}_display_state`] !== 'add-detail';
|
|
});
|
|
if(collapsibleMasters.indexOf(section)>=0){
|
|
|
|
rows.push(rowID);
|
|
}
|
|
}else{
|
|
rows = [rowID];
|
|
}
|
|
rows.forEach((id)=>{
|
|
if(collapse){
|
|
if(!attributes[`${section}_${id}_display_state`].startsWith('short-')){
|
|
attributes[`${section}_${id}_display_state`] = `short-${attributes[`${section}_${id}_display_state`]}`;
|
|
attributes[`${section}_${id}_collapse`] = 1;
|
|
}
|
|
}else{
|
|
attributes[`${section}_${id}_display_state`] = attributes[`${section}_${id}_display_state`].replace(/short-/,'');
|
|
attributes[`${section}_${id}_collapse`] = 0;
|
|
}
|
|
});
|
|
};
|
|
k.registerFuncs({collapseSection});
|
|
|
|
const orderDeeds = function({trigger,attributes,sections}){
|
|
const [section,rowID,field] = k.parseTriggerName(trigger.name);
|
|
const newSection = /completed/.test(section) ?
|
|
deedName :
|
|
`completed-${deedName}`;
|
|
const newRow = k.generateRowID(newSection,sections);
|
|
['type','name','completed'].forEach((attr)=>{
|
|
attributes[`${newRow}_${attr}`] = attributes[`${section}_${rowID}_${attr}`];
|
|
});
|
|
k.removeRepeatingRow(`${section}_${rowID}`,attributes,sections);
|
|
deedDisplay({sections});
|
|
};
|
|
k.registerFuncs({orderDeeds});
|
|
|
|
const deedDisplay = function({sections}){
|
|
k.debug({sections});
|
|
const action = sections[`repeating_completed-${deedName}`].length ?
|
|
'remove' :
|
|
'add';
|
|
$20(`.completed-${deedName}`)[`${action}Class`]('disable');
|
|
$20(`.completed-${deedName} ~ .repcontainer`)[`${action}Class`]('disable');
|
|
};
|
|
k.registerFuncs({deedDisplay},{type:['opener']});
|
|
|
|
const styleCharacterType = function({attributes}){
|
|
let type = attributes.sheet_type.replace(/\s+/g,'_');
|
|
k.debug({type});
|
|
$20('.sheet-nav__tab--possible').addClass('disable');
|
|
$20(`.sheet-nav__tab--possible--${type}`).removeClass('disable');
|
|
};
|
|
k.registerFuncs({styleCharacterType});/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
const clearChildItems = function(event){
|
|
const [section,masterID] = k.parseRepeatName(event.sourceAttribute);
|
|
k.getAllAttrs({
|
|
callback:(attributes,sections,)=>{
|
|
if(/master/.test(event.removedInfo[`${section}_${masterID}_display_state`])){
|
|
|
|
sections[section].forEach((id)=>{
|
|
if(attributes[`${section}_${id}_master_id`]===masterID.toLowerCase()){
|
|
k.removeRepeatingRow(`${section}_${id}`,attributes,sections);
|
|
}
|
|
});
|
|
}else if(/injury|calling|deed/.test(section)){
|
|
//cleanUpSection(attributes,sections,section,masterID);
|
|
}
|
|
//The below is a temporary fix for display issues associated with setSectionOrder
|
|
let clearID = attributes.attributes[`_reporder_${section}`].find(id=>Object.keys(attributes.attributes).some((attr)=>attr.startsWith(`${section}_${id}`)));
|
|
if(clearID){
|
|
|
|
attributes[`${section}_${clearID}_display_state`] = attributes[`${section}_${clearID}_display_state`];
|
|
attributes.set();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
k.registerFuncs({clearChildItems});
|
|
|
|
const cleanUpSection = function(attributes,sections,section,masterID){
|
|
let trigger;
|
|
switch(section){
|
|
case 'repeating_injury':
|
|
//Health handling
|
|
break;
|
|
case 'repeating_calling':
|
|
trigger = {name:`${section}_${masterID}_rating_max`};
|
|
sumActiveCallings(trigger,attributes,sections);
|
|
break;
|
|
case 'repeating_deed':
|
|
//remove completed deeds header if no completed deeds
|
|
//orderDeeds(trigger,attributes,sections);
|
|
break;
|
|
}
|
|
};/**
|
|
* Rolls initiative for pcs.
|
|
* @param {object} trigger - The trigger that caused the function to be called
|
|
* @param {object} attributes - The attribute values of the character
|
|
* @param {object[]} sections - All the repeating section IDs
|
|
* @param {object} casc - Expanded cascade object
|
|
*/
|
|
const rollPCInitiative = async function ({ trigger, attributes, sections, casc }) {
|
|
const skillRow = attributes.initiative_skill ?
|
|
`repeating_skill_${attributes.initiative_skill}` :
|
|
'';
|
|
const skillDice = skillRow ?
|
|
attributes[`${skillRow}_rating`] :
|
|
0;
|
|
const [pool, addDice] = await assemblePool(skillDice, attributes.cunning);
|
|
let enhancements = 0;
|
|
if (attributes.enhancement_query === 'on') {
|
|
enhancements = await k.extractQueryResult(getTranslationByKey('Do you have any enhancements'));
|
|
enhancements = k.value(enhancements);
|
|
} else {
|
|
await k.pseudoQuery('');
|
|
}
|
|
let [message, reusableMessage] = await assembleMessage(attributes, pool, attributes[`${skillRow}_name`], 'cunning', 'initiative', enhancements);
|
|
|
|
let confirm;
|
|
if (attributes.enhancement_query === 'on' || attributes.difficulty_query === 'on') {
|
|
confirm = await confirmRoll(attributes, { skillName: `^{${field.replace(/_/g, ' ')}}`, skillDice: attributes[field], enhancements, addDice });
|
|
} else {
|
|
confirm = await k.pseudoQuery(1);
|
|
}
|
|
if (k.value(confirm)) {
|
|
executeRoll(attributes, reusableMessage, message);
|
|
}
|
|
};
|
|
k.registerFuncs({ rollPCInitiative });/**
|
|
* Does the system specific handling for specialized drop categories.
|
|
* @param {object} data - the data received from the drop
|
|
* @param {AttributesProxy} attributes - The attributes object from the K-scaffold
|
|
* @param {object} sections - The sections object form the k-scaffold
|
|
* @param {object} casc - The cascade object form the k-scaffold
|
|
* @returns {object|[]} - The manipulated data object
|
|
*/
|
|
const processSpecialCategories = (data,attributes,sections,casc) => {
|
|
|
|
if(data.Category !== 'Threats'){
|
|
let category = data.Category.replace(/\s.+/,'');
|
|
data = {[`data-${category}`]:[data]};
|
|
if(data[`data-${category}`]['data-features']){
|
|
data[category]['data-features'].forEach((feature)=>{
|
|
let cat = k.capitalize(feature.category);
|
|
data[`data-${cat}`] = data[`data-${cat}`] || [];
|
|
data[`data-${cat}`].push({name:feature.name});
|
|
});
|
|
}
|
|
}else if(data.Category === 'Threats'){
|
|
npcHandler(data,attributes,sections,casc);
|
|
}
|
|
return data;
|
|
}
|
|
/**
|
|
* Does all the generic handling for NPC type dropped items (e.g. Antagonists in Scion and Threats in TCF)
|
|
* @param {object} data - the data received from the drop
|
|
* @param {AttributesProxy} attributes - The attributes object from the K-scaffold
|
|
* @param {object} sections - The sections object form the k-scaffold
|
|
* @param {object} casc - The cascade object form the k-scaffold
|
|
*/
|
|
const npcHandler = (data,attributes,sections,casc) => {
|
|
clearSheet(attributes,sections,casc);
|
|
attributes.character_name = data['data-cs-name'] || data.Name;
|
|
attributes.sheet_state = 'antagonist';
|
|
attributes.queue.push('sheet_type');
|
|
attributes.queue.push('character_name');
|
|
const tokenObj = {
|
|
width:70,
|
|
height:70,
|
|
};
|
|
setDefaultToken(tokenObj);
|
|
|
|
navigateSheet({attributes});
|
|
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
//Initiate a compendium lookup
|
|
|
|
//Start of the actual compendium lookup functions
|
|
//Function to initiate the lookup. Parses the statblock to figure out what needs to be looked up in the compendium, and what attribute should be filtered for based on the category.
|
|
//ARGUMENTS
|
|
//statblock (object): base object dropped from the compendium
|
|
//callback (function): A function that will use the parsed compendium lookup data to finish the drag and drop of the item
|
|
//The callback function receives the parsed statblock as an argument
|
|
const compendiumLookup = function({queue, callback, origBlock, attributes, sections, casc, pass = 0}) {
|
|
pass++;
|
|
origBlock = origBlock || queue[0];
|
|
queue = queue || [origBlock];
|
|
let searchArray = [];
|
|
queue.forEach((statblock)=>{
|
|
searchArray = itemCategories.reduce((m, type) => {//Construct an array of items to search for.
|
|
let searchType = specialCategories[type] || type;
|
|
if (statblock[`data-${type}`]){
|
|
let searchNames = statblock[`data-${type}`].map((obj) => obj.name || obj.display_name || '')
|
|
.join('|')
|
|
.replace(/\|{2,}/g,'')
|
|
.replace(/\|$/,'');
|
|
if(searchNames.length > 0){
|
|
let searchString = `Category:${searchType} display_name:${searchNames}`;
|
|
m.push(searchString);
|
|
}
|
|
}
|
|
return m;
|
|
}, searchArray);
|
|
k.debug({searchArray});
|
|
if(searchArray.length){
|
|
lookupBurndown(statblock, searchArray, callback, origBlock, attributes, sections, casc,pass);
|
|
}else{
|
|
callback(origBlock);
|
|
}
|
|
});
|
|
};
|
|
|
|
//Actually queries the compendium to get details on the items in each category using the searchArray assembled in compendiumLookup
|
|
//ARGUMENTS
|
|
//statblock (object): the original object dropped from the compendium
|
|
//callback (function): A function that will use the parsed compendium lookup data to finish the drag and drop of the item
|
|
//The callback function receives the parsed statblock as an argument
|
|
const lookupBurndown = function(statblock, searchArray, callback, origBlock, attributes, sections, casc,pass) {
|
|
getCompendiumQuery(searchArray, (dataArr) => {
|
|
k.debug({dataArr});
|
|
const expansionIDs = dataArr
|
|
.reduce( (arr,obj)=>{
|
|
arr.push(k.value(obj.data.Expansion));
|
|
return arr;
|
|
},[])
|
|
.sort((a,b) => b - a);
|
|
itemCategories.forEach((category) => {
|
|
if (statblock[`data-${category}`]) {
|
|
origBlock[`data-${category}`] = statblock[`data-${category}`].map((obj) => {
|
|
const fullItem = getItemDetails(obj, dataArr, expansionIDs);
|
|
fullItem['data-cs-name'] = fullItem['data-cs-name'] || fullItem.name;
|
|
return fullItem;
|
|
});
|
|
}
|
|
});
|
|
const queue = additionalLookups(statblock,origBlock);
|
|
if(queue.length /*&& pass < 2*/){
|
|
compendiumLookup({queue,callback,origBlock, attributes, sections, casc,pass});
|
|
}else{
|
|
callback(origBlock);
|
|
}
|
|
});
|
|
};
|
|
|
|
const additionalLookups = function(statblock,origBlock){
|
|
return Object.entries(statblock).reduce((memo,[prop,arr])=>{
|
|
if(prop.startsWith('data-') && Array.isArray(arr)){
|
|
let nestedData = arr.reduce((m,obj)=>{
|
|
Object.entries(obj).forEach(([nProp,nVal])=>{
|
|
/*commented code shouldn't be needed
|
|
if(
|
|
nProp.startsWith('data-') &&
|
|
typeof nVal === 'string' &&
|
|
nVal.startsWith('[{')
|
|
){
|
|
try{
|
|
let jArr = JSON.parse(nVal);
|
|
if(Array.isArray(jArr)){
|
|
jArr = jArr.map((o)=>{
|
|
o.datasource = obj['cs-name'] || obj.Category;
|
|
if(!o.datasource){
|
|
delete o.datasource;
|
|
}
|
|
return o;
|
|
});
|
|
origBlock[nProp] = origBlock[nProp] ?
|
|
[...origBlock[nProp],...jArr] :
|
|
[...jArr];
|
|
m.data[nProp] = jArr;
|
|
m.nested = true;
|
|
}
|
|
}catch(err){
|
|
//if the parse failed, do nothing
|
|
}
|
|
}else */
|
|
if(nProp === 'data-features'){
|
|
try{
|
|
if(typeof nVal === 'string'){
|
|
k.debug('string data-feature detected');
|
|
nVal = JSON.parse(nVal);
|
|
}
|
|
nVal.forEach((feature)=>{
|
|
if(!origBlock[`data-${feature.category}`] || origBlock[`data-${feature.category}`].every((o)=>o.display_name.toLowerCase() !== feature.name.toLowerCase())){
|
|
m.nested = true;
|
|
m.data[`data-${feature.category}`] = m.data[`data-${feature.category}`] || [];
|
|
m.data[`data-${feature.category}`].push({display_name:feature.name});
|
|
}
|
|
});
|
|
}catch(err){
|
|
k.debug({[`Invalid data-feature on ${obj.display_name}`]:err,'data-feature':nVal},true);
|
|
}
|
|
}
|
|
});
|
|
return m;
|
|
},{data:{},nested:false});
|
|
if(nestedData && nestedData.nested){
|
|
memo.push(nestedData.data);
|
|
}
|
|
}
|
|
return memo;
|
|
},[]);
|
|
};
|
|
|
|
//Finds the compendium item that most precisely matches the needs of the compendium item based on expansion ID and name property.
|
|
//If nothing matches based on expansion ID, simply returns the first item with appropriate name property.
|
|
//ARGUMENTS
|
|
//itemObj (object): the data on the Item to return that is included in the base statblock
|
|
//dataArr (array): The compendium items that were returned from getCompendiumQuery
|
|
const getItemDetails = function(itemObj, dataArr, expansionIDs) {
|
|
let objName = itemObj.display_name || itemObj.name;
|
|
let compendiumObj;
|
|
expansionIDs.find((id) => {
|
|
compendiumObj = dataArr.find((dataObj) => k.value(dataObj.data.Expansion) === id &&
|
|
dataObj.data.display_name === objName);
|
|
if (compendiumObj) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
if (!compendiumObj) { //If we didn't find an object matching the expansion pathways and the object name, just find the first item with a matching name
|
|
compendiumObj = dataArr.find((dataObj) =>
|
|
dataObj.data.display_name === objName);
|
|
if (!compendiumObj) { //If there still isn't a matching compendium object, return the original Item
|
|
return itemObj;
|
|
}
|
|
}
|
|
Object.keys(compendiumObj.data).forEach((key) => {
|
|
itemObj[key] = itemObj[key] || compendiumObj.data[key];
|
|
});
|
|
return itemObj;
|
|
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
|
|
/*jshint -W014,-W084,-W030,-W033*/
|
|
const handleCompendiumDrop = function(event){
|
|
k.debug({event});
|
|
k.getAllAttrs({
|
|
callback:(attributes,sections,casc)=>{
|
|
clearDropAttributes();
|
|
let data = parseDropData(attributes.drop_data,attributes,sections,casc);
|
|
if(!data) return;
|
|
k.debug({data});
|
|
compendiumLookup({
|
|
origBlock:data,
|
|
attributes,sections,casc,
|
|
callback:async (lookupObj)=>{
|
|
debugger;
|
|
k.debug({'threat data':data});
|
|
await processDropData({data:lookupObj,attributes,sections,casc});
|
|
k.debug('Drop processing complete');
|
|
attributes.set({attributes,sections,casc});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
k.registerFuncs({handleCompendiumDrop});
|
|
|
|
const parseDropData = function(drop_data,attributes,sections,casc){
|
|
k.debug('parsing drop data');
|
|
k.debug({drop_data:`"${drop_data}"`});
|
|
try{
|
|
let data = JSON.parse(drop_data);
|
|
data = Object.entries(data)
|
|
.reduce((memo,[key,val])=>{
|
|
if(val){
|
|
if(typeof val === 'string' && val.startsWith('[{')){
|
|
val = JSON.parse(val);
|
|
}
|
|
memo[key] = val;
|
|
}
|
|
return memo;
|
|
},{});
|
|
return processSpecialCategories(data,attributes,sections,casc);
|
|
}catch(err){
|
|
k.debug({'Invalid Drop Data! Drop aborted. Error:':err,drop_data},true);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const clearSheet = function(attributes,sections,casc){
|
|
Object.entries(sections).forEach(([section,idArray])=>{
|
|
idArray.forEach((id)=>{
|
|
k.removeRepeatingRow(`${section}_${id}`,attributes,sections);
|
|
});
|
|
});
|
|
Object.keys(attributes.attributes).forEach((attr)=>{
|
|
if(casc[`attr_${attr}`]){
|
|
attributes[attr] = casc[`attr_${attr}`].hasOwnProperty('defaultValue') ?
|
|
casc[`attr_${attr}`].defaultValue :
|
|
'';
|
|
}
|
|
})
|
|
};
|
|
|
|
const clearDropAttributes = function(){
|
|
k.setAttrs({drop_name:'',drop_data:''});
|
|
};
|
|
|
|
const processDropData = function({data, attributes, sections, casc,section=''}){
|
|
section = section ? `${section}_` : '';
|
|
const worker = async (queue) => {
|
|
let [key,val] = queue.shift();
|
|
try{
|
|
let setKey = key.replace(/^(?:data-)?cs(?:list)?-/,'');
|
|
if(typeof val === 'string' && val.startsWith('[{')){
|
|
val = JSON.parse(val);
|
|
}
|
|
if(!Array.isArray(val) && (key.startsWith('cs-') || key.startsWith('data-cs-'))){
|
|
await k.pseudoQuery();
|
|
attributes[`${section}${setKey}`] = val;
|
|
}else if(Array.isArray(val) && key.startsWith('data-')){
|
|
let lookupKey = key.replace(/data-(?:cs(?:list)?-)?/,'');
|
|
|
|
if(repeatProcessors[lookupKey]){
|
|
k.debug(`processing ${lookupKey}`);
|
|
await repeatProcessors[lookupKey](lookupKey,val,attributes,sections,casc);
|
|
}else if(key.startsWith('data-cslist-')){
|
|
await k.pseudoQuery();
|
|
attributes[`${section}${setKey}`] = val.map((o)=>o.name).join(', ');
|
|
}else if(key === 'data-content'){
|
|
await k.pseudoQuery();
|
|
attributes[`${section}description`] = val.reduce((textArr,content)=>{
|
|
if(content.heading){
|
|
textArr.push(content.heading);
|
|
}
|
|
if(content.content){
|
|
let text = /list/.test(content.type) ? `• ${content.content}` : content.content;
|
|
textArr.push(text);
|
|
}
|
|
return textArr;
|
|
},[]).join('\n')
|
|
.replace(/\n{2,}/,'\n')
|
|
.replace(/\n+$/,'');
|
|
}
|
|
}
|
|
}catch(err){
|
|
k.debug({[`Invalid JSON entered for ${key} on ${data.display_name}`]:err,JSON:val});
|
|
}
|
|
if(queue.length){
|
|
return worker(queue);
|
|
}else{
|
|
return true;
|
|
}
|
|
};
|
|
return worker(Object.entries(data));
|
|
};
|
|
|
|
const dropGenericSection = async function(lookupKey,arr,attributes,sections,casc){
|
|
await k.pseudoQuery();
|
|
k.debug(`dropping generic section for ${lookupKey}`);
|
|
if(!Array.isArray(arr)){
|
|
k.debug({'Invalid section details passed. Exiting. Object passed was:':arr},true);
|
|
return;
|
|
}
|
|
let type = (singular[lookupKey] || lookupKey).toLowerCase();
|
|
type = antagonistSpecialSections[type] || type;
|
|
k.debug({arr});
|
|
const worker = async (queue)=>{
|
|
let itemObj = queue.shift();
|
|
let section = k.generateRowID(type,sections);
|
|
itemObj[`cs-display_state`] = sectionDisplayNames[type];
|
|
if(itemObj[`cs-display_state`]){
|
|
if(itemObj[`cs-display_state`].startsWith('short-')){
|
|
itemObj['cs-collapse'] = 1;
|
|
}
|
|
if(/master/.test(itemObj[`cs-display_state`])){
|
|
let controlSection = k.generateRowID(type,sections,'ADD');
|
|
attributes[`${controlSection}_master_id`] = section.toLowerCase().replace(/repeating_[^_]+_/,'');
|
|
attributes[`${controlSection}_display_state`] = 'add-detail';
|
|
}
|
|
itemObj[`cs-rating_max`] = 1;
|
|
orderComplex({section:type,sections,attributes});
|
|
}
|
|
if(itemObj['data-cs-condition']){
|
|
itemObj['data-cs-condition'] = ['data-cs-condition','data-cs-condition_effect','data-cs-condition_momentum']
|
|
.map((attr)=>{
|
|
let c = itemObj[attr];
|
|
delete itemObj[attr];
|
|
return c;
|
|
})
|
|
.filter( c => c)
|
|
.join('\n');
|
|
}
|
|
await processDropData({data:itemObj,attributes,sections,casc,section});
|
|
if(queue.length){
|
|
return worker(queue);
|
|
}else{
|
|
return true;
|
|
}
|
|
};
|
|
return worker([...arr]);
|
|
};
|
|
|
|
const dropChildItem = async function(lookupKey,arr,attributes,sections,casc){
|
|
k.debug(`dropping childItem for ${lookupKey}`);
|
|
await k.pseudoQuery();
|
|
if(!Array.isArray(arr)){
|
|
k.debug({'Invalid section details passed. Exiting. Object passed was:':arr},true);
|
|
return;
|
|
}
|
|
const parentSections = {
|
|
Knacks:'calling',
|
|
Boons:'purview'
|
|
};
|
|
let type = singular[lookupKey].toLowerCase();
|
|
let section = `repeating_${parentSections[lookupKey]}`;
|
|
let queryText = `${getTranslationByKey(`${lookupKey} drop query`)}|`;
|
|
let queryOptions = sections[section]
|
|
.filter((id)=>attributes[`${section}_${id}_display_state`].endsWith('master'))
|
|
.map((id)=>`${attributes[`${section}_${id}_name`]
|
|
|| getTranslationByKey('unnamed')},${id}`);
|
|
let target = queryOptions.length > 1 ? undefined : queryOptions[0].replace(/^.+,([^_]+)$/,'$1');
|
|
let query = target ? undefined : `${queryText}|${queryOptions.join('|')}`;
|
|
const worker = async (queue,t) =>{
|
|
let itemObj = queue.shift();
|
|
target = await (target ? k.pseudoQuery(target) : k.extractQueryResult(query));
|
|
let row = k.generateRowID(parentSections[lookupKey],sections);
|
|
itemObj['cs-master_id'] = target;
|
|
itemObj['cs-collapse'] = 1;
|
|
itemObj[`cs-display_state`] = `short-${type}`;
|
|
await processDropData({data:itemObj,attributes,sections,casc,section:row});
|
|
orderComplex({section,sections,attributes});
|
|
if(queue.length){
|
|
return worker(queue);
|
|
}else{
|
|
return true;
|
|
}
|
|
};
|
|
await worker([...arr],target);
|
|
};
|
|
|
|
const dropEquipment = async function(lookupKey,arr,attributes,sections,casc){
|
|
k.debug(`dropping equipment for ${lookupKey}`);
|
|
await k.pseudoQuery();
|
|
if(!Array.isArray(arr)){
|
|
k.debug({'Invalid section details passed. Exiting. Object passed was:':arr},true);
|
|
return;
|
|
}
|
|
const worker = async (queue) =>{
|
|
let itemObj = queue.shift();
|
|
let sectionName = /weapon|armor/i.test(itemObj['data-cs-type']) ?
|
|
'equipment' :
|
|
'birthright';
|
|
let type = sectionName === 'birthright' ?
|
|
itemObj['data-cs-type'] :
|
|
sectionName;
|
|
itemObj['cs-collapse'] = 1;
|
|
itemObj[`cs-display_state`] = `short-${type}`;
|
|
let section = `repeating_${sectionName}_${generateRowID()}`;
|
|
await processDropData({data:itemObj,attributes,sections,casc,section});
|
|
if(queue.length){
|
|
return worker(queue);
|
|
}else{
|
|
return true;
|
|
}
|
|
};
|
|
return await worker([...arr]);
|
|
};
|
|
|
|
const dropSkill = async function(lookupKey,arr,attributes,sections,casc){
|
|
k.debug(`dropping skill for ${lookupKey}`);
|
|
await k.pseudoQuery();
|
|
if(!Array.isArray(arr)){
|
|
k.debug({'Invalid section details passed. Exiting. Object passed was:':arr},true);
|
|
return;
|
|
}
|
|
const sectionDetails = systemDefaults['beneath-the-sea'].repeating_skill;
|
|
// Set up the base skill attributes
|
|
checkSectionContents(sectionDetails,'repeating_skill',sections,attributes);
|
|
const worker = async (queue) =>{
|
|
let itemObj = queue.shift();
|
|
itemObj['data-cs-name'] = itemObj['data-cs-name'].toLowerCase();
|
|
const rowID = sections.repeating_skill.find(id => attributes[`repeating_skill_${id}_name`] === itemObj['data-cs-name']) ||
|
|
generateRowID();
|
|
let section = `repeating_skill_${rowID}`;
|
|
await processDropData({data:itemObj,attributes,sections,casc,section});
|
|
if(queue.length){
|
|
return worker(queue);
|
|
}else{
|
|
return true;
|
|
}
|
|
};
|
|
return await worker([...arr]);
|
|
};/**
|
|
* Object containing the categories and which drop functions to call for each of them.
|
|
*/
|
|
const repeatProcessors = {
|
|
Paths:dropGenericSection,
|
|
Trademarks:dropGenericSection,
|
|
Stunts:dropGenericSection,
|
|
Tropes:dropGenericSection,
|
|
'special-rules':dropGenericSection,
|
|
skill:dropSkill
|
|
};/**
|
|
* Logic for adding the system specific subsections
|
|
*/
|
|
const addSystemSubSection = ({type,rowID,newID,section,attributes,sections}) => {
|
|
addChildSection(attributes,sections,section,rowID,type,newID)
|
|
}
|
|
</script>
|
|
|
|
|
|
|
|
|