mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-31 04:02:29 +00:00
1979 lines
75 KiB
HTML
1979 lines
75 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<input type='hidden' class='sheet-tabstoggle' name='attr_sheetTab' value='character'/>
|
|
<div class='sheet-character'>
|
|
<button type="action" class=sheet-stub-tab>CharacterJournal</button>
|
|
<div class=sheet-tabrow>
|
|
<button type="action" class=sheet-selected-tab name="act_character">Character</button>
|
|
<button type="action" class=sheet-unselected-tab name="act_journal">Journal</button>
|
|
</div>
|
|
<div class="sheet-grid-section">
|
|
<div class="sheet-namelvl sheet-block">
|
|
<div class=sheet-label>
|
|
<div> <!-- Always break -->
|
|
<span class=sheet-first-word>Name</span>
|
|
<input type=text style='width: 22em;' name="attr_cname"/>
|
|
<span class=sheet-nowrap>
|
|
Age <input type=text style='width: 3em;'name="attr_age"/>
|
|
</span>
|
|
</div>
|
|
<span class=sheet-nowrap>
|
|
Title/Rank <input type=text style='width: 7em;' name="attr_title"/>
|
|
</span>
|
|
<span class=sheet-nowrap>
|
|
Race <input type=text style='width: 8em;'name="attr_race"/>
|
|
</span>
|
|
<span class=sheet-nowrap title="or pronouns.">
|
|
Gender <input type=text style='width: 5em;' name="attr_gender"/>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="sheet-stats sheet-block">
|
|
<div> <!-- To prevent them from vertically spreading out -->
|
|
<!-- ST -->
|
|
<div class=sheet-flex-centered>
|
|
<span>
|
|
<span title="Base Strength">
|
|
<span class=sheet-stat-label >ST</span>
|
|
<input class=sheet-tiny-input type=text name="attr_ST_max" value="8"/>
|
|
</span>
|
|
<span class=sheet-stat-plus>+</span>
|
|
<input class=sheet-small-input type=number title="Temporary adjustment to Strength, not counting Wounds or Fatigue." name="attr_ST_adj" value="0"/>
|
|
<span class=sheet-stat-equals>=</span>
|
|
<span class=sheet-computed-value title="Final Strength. Base plus Adjustments, not counting Wounds or Fatigue." name="attr_ST"/>
|
|
</span>
|
|
<span>
|
|
<button class=sheet-dice-button type="action" title="Strength Check" name="act_st_check"></button>
|
|
</span>
|
|
</div>
|
|
<!-- DX -->
|
|
<div class=sheet-flex-centered>
|
|
<span>
|
|
<span title="Base Dexterity">
|
|
<span class=sheet-stat-label >DX</span>
|
|
<input class=sheet-tiny-input type=text name="attr_DX_max" value="8"/>
|
|
</span>
|
|
<span class=sheet-stat-plus>+</span>
|
|
<input class=sheet-small-input type=number title="Temporary adjustment to Dexterity." name="attr_DX_adj" value="0"/>
|
|
<span class=sheet-stat-equals>=</span>
|
|
<span class=sheet-computed-value title="Final Dexterity. Base plus Adjustments minus Armor Penalties." name="attr_DX"/>
|
|
</span>
|
|
<span>
|
|
<button class=sheet-dice-button type="action" title="Dexterity Check" name="act_dx_check"></button>
|
|
</span>
|
|
</div>
|
|
<!-- IQ -->
|
|
<div class=sheet-flex-centered>
|
|
<span>
|
|
<span title="Base IQ">
|
|
<span class=sheet-stat-label >IQ</span>
|
|
<input class=sheet-tiny-input type=text name="attr_IQ_max" value="8"/>
|
|
</span>
|
|
<span class=sheet-stat-plus>+</span>
|
|
<input class=sheet-small-input type=number title="Temporary adjustment to IQ." name="attr_IQ_adj" value="0"/>
|
|
<span class=sheet-stat-equals>=</span>
|
|
<span class=sheet-computed-value title="Final IQ. Base plus Adjustments." name="attr_IQ"/>
|
|
</span>
|
|
<span>
|
|
<button class=sheet-dice-button type="action" title="IQ Check" name="act_iq_check"></button>
|
|
</span>
|
|
</div>
|
|
<!-- SAVES -->
|
|
<div class=sheet-flex-centered>
|
|
<span>
|
|
<input type=checkbox name=attr_save2d class=sheet-caption-checkbox hidden>
|
|
<button class=sheet-caption-button title="Roll 2d6 for next Stat or Health Check." type="action" name="act_save2d">2d</button>
|
|
<input type=checkbox name=attr_save3d class=sheet-caption-checkbox value='on' hidden>
|
|
<button class=sheet-caption-button title="Roll 3d6 for next Stat or Health Check." type="action" name="act_save3d">3d</button>
|
|
<input type=checkbox name=attr_save4d class=sheet-caption-checkbox hidden>
|
|
<button class=sheet-caption-button title="Roll 4d6 for next Stat or Health Check." type="action" name="act_save4d">4d</button>
|
|
<input type=checkbox name=attr_save5d class=sheet-caption-checkbox hidden>
|
|
<button class=sheet-caption-button title="Roll 5d6 for next Stat or Health Check." type="action" name="act_save5d">5d</button>
|
|
<input type=checkbox name=attr_save6d class=sheet-caption-checkbox value='on'hidden>
|
|
<button class=sheet-caption-button title="Roll 6d6 for next Stat or Health Check." type="action" name="act_save6d">6d</button>
|
|
<input type=checkbox name=attr_save7d class=sheet-caption-checkbox hidden>
|
|
<button class=sheet-caption-button title="Roll 7d6 for next Stat or Health Check." type="action" name="act_save7d">7d</button>
|
|
<input type=checkbox name=attr_save8d class=sheet-caption-checkbox hidden>
|
|
<button class=sheet-caption-button title="Roll 8d6 for next Stat or Health Check." type="action" name="act_save8d">8d</button>
|
|
</span>
|
|
<span title="Blank check, provided for the criticals. Success on roll ≤ the number.">
|
|
<button class=sheet-dice-button type="action" title="Blank Stat Check" name="act_blank_check"></button>
|
|
</span>
|
|
</div>
|
|
<!-- MA -->
|
|
<div class=sheet-flex-centered>
|
|
<span>
|
|
<span title="Movement Allowance.">
|
|
<span class=sheet-stat-label>MA</span>
|
|
<input class=sheet-tiny-input type=text name="attr_MA_max" value="10"/>
|
|
</span>
|
|
<span class=sheet-stat-plus>+</span>
|
|
<input class=sheet-small-input type=number title="Temporary adjustment to Movement Allowance, excluding caused by weight." name="attr_MA_adj" value="0"/>
|
|
<span class=sheet-stat-equals>=</span>
|
|
<span class=sheet-computed-value title="Final Movement Allowance. Base plus Adjustments." name="attr_MA"/>
|
|
</span>
|
|
<span>
|
|
<span title="Total weight carried.">
|
|
<span class=sheet-label>Carry</span>
|
|
<span class=sheet-computed-value name="attr_weightcarried"></span>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div class=sheet-flex-centered>
|
|
<span> </span>
|
|
<span class='sheet-label sheet-nowrap' title="Weight penalties, automatically applied." name="attr_mapenalty"></span>
|
|
</div>
|
|
<!-- MANA -->
|
|
<div class=sheet-flex-centered>
|
|
<span>
|
|
<span title="Max Staff Mana.">
|
|
<span class=sheet-stat-label>Mana</span>
|
|
<input class=sheet-tiny-input type=text name="attr_mana_max" value="0"/>
|
|
</span>
|
|
<span class=sheet-stat-plus title="How much Mana spent.">-</span>
|
|
<input class=sheet-small-input title="How much Mana spent." type=number name="attr_mana_adj" value="0"/>
|
|
<span class=sheet-stat-equals>=</span>
|
|
<span class=sheet-computed-value title="Remaining Mana." type=number name="attr_mana"></span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sheet-combat sheet-block">
|
|
<div> <!-- stops from stretching -->
|
|
<!-- Turn Order -->
|
|
<div class=sheet-flex-centered style='margin-bottom: .5rem'>
|
|
<button class=sheet-dice-button type="action" title="Add yourself to Turn Order, select your Token first." name="act_turnorder">Turn Order</button>
|
|
<span>
|
|
<button class=sheet-dice-button type="action" title="Roll team Initiative." name="act_initiative">Initiative</button>
|
|
<input class=sheet-onechar-input type=text title="Bonus to Initiative, as for Tactics or Strategist." name="attr_initiative_adj"/>
|
|
</span>
|
|
</div>
|
|
<!-- Health -->
|
|
<div class=sheet-flex-centered>
|
|
<span title="Wounded ST, current Strength reduced by Wounds and Fatigue.">
|
|
<span class='sheet-label sheet-first-word' style='width: 5rem'>Health </span>
|
|
<span class=sheet-computed-value name="attr_health"/>
|
|
</span>
|
|
<span>
|
|
<button class=sheet-dice-button type="action" title="Health Check" name="act_health_check"></button>
|
|
</span>
|
|
</div>
|
|
<!-- Wounds -->
|
|
<div class=sheet-flex-centered>
|
|
<span title="Damage done to your Health, must be healed.">
|
|
<span class=sheet-label style='width: 5rem;'>Wounds </span>
|
|
<input type=number class=sheet-small-input name="attr_Hits" value="0"/>
|
|
</span>
|
|
<button class=sheet-dice-button type="action" title="Reaction to attacker initiating Hand to Hand Combat." style='width: 4.6rem' name="act_hthreaction">HTH</button>
|
|
</div>
|
|
<!-- Fatigue -->
|
|
<div class=sheet-flex-centered>
|
|
<span title="Temporary damage done to your Health as for spellcasting.">
|
|
<span class=sheet-label style='width: 5rem'>Fatigue </span>
|
|
<input type=number class=sheet-small-input name="attr_Fatigue" value="0"/>
|
|
</span>
|
|
<span>
|
|
<button class=sheet-dice-button type="action" title="Shield Rush Save vs weaker (unwounded strength) rusher." style='width: 4.6rem' name="act_shieldrushweaker">SRW</button>
|
|
<button class=sheet-dice-button type="action" title="Shield Rush Save vs equal or stronger (unwounded strength) rusher." style='width: 4.6rem' name="act_shieldrushstronger">SRS</button>
|
|
</span>
|
|
</div>
|
|
<!-- Minus to Hit -->
|
|
<div class=sheet-flex-centered>
|
|
<span title="Subtract from enemy DX when attacking you.">
|
|
<span class=sheet-label style='width: 8rem' >Minus to Hit </span>
|
|
<span class=sheet-computed-value name="attr_minustohit_max"/>
|
|
</span>
|
|
<span title="Subtract from enemy DX when being attacked from flank or rear.">
|
|
<span class=sheet-label>MtH Flank </span>
|
|
<span class=sheet-computed-value name="attr_minustohit"/>
|
|
</span>
|
|
</div>
|
|
<!-- Hits Blocked -->
|
|
<div class=sheet-flex-centered>
|
|
<span title="Hits blocked per attack by your armor/shields/abilities.">
|
|
<span class=sheet-label>Hits Blocked</span>
|
|
<span class=sheet-computed-value name="attr_hitsblocked_max"/>
|
|
</span>
|
|
<span title="Hits blocked per flank or rear attack by your armor and abilities.">
|
|
<span class=sheet-label>HB Flank</span>
|
|
<span class=sheet-computed-value name="attr_hitsblocked"/>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sheet-talents sheet-block">
|
|
<div> <!-- to keep it all together, not spread out vertically -->
|
|
<div class='sheet-fieldset-header sheet-flex-section' style='line-height: 3rem;'>
|
|
<span class='sheet-first-word sheet-flex'>Talent</span>
|
|
<span title="IQ available to buy new Talents or Spells." class=sheet-nowrap>
|
|
<span>Available IQ</span>
|
|
<span class=sheet-computed-value name="attr_iqunspent"/>
|
|
</span>
|
|
</div>
|
|
<fieldset class=repeating_talents>
|
|
<div class=sheet-flex-section>
|
|
<input type=text name=attr_talentname class=sheet-flex placeholder="Talent Name">
|
|
|
|
<button class="sheet-describe-button" title="Describe the talent." type="action" name="act_describe"/>
|
|
<button class="sheet-expand-button" title="Expand and edit more fields." type="action" name="act_expand"/>
|
|
</div>
|
|
<input type="checkbox" name="attr_options-flag" hidden class="sheet-toggle">
|
|
<div class="sheet-toggle-checked" style='margin-left: 1em; width: 90%'>
|
|
<div>
|
|
<span class=sheet-nowrap>
|
|
<span title="IQ points you spent, if any, for this talent. Subtracts from Available IQ.">IQ Spent: </span>
|
|
<input type=text class=sheet-tiny-input name="attr_talentcost">
|
|
</span>
|
|
|
|
<span class=sheet-nowrap>
|
|
<span title="XP you spent, if any, for this talent. Subtracts from XP Available.">XP Spent: </span>
|
|
<input type=text class=sheet-small-input name="attr_expspent">
|
|
</span>
|
|
</div>
|
|
<div class=sheet-nowrap>
|
|
<span title="IQ required to use this talent.">IQ Required: </span>
|
|
<input type=text class=sheet-tiny-input name=attr_talentiq>
|
|
</div>
|
|
<span title="Description that shows when you click the Describe button.">Description:</span>
|
|
<textarea class=sheet-text-area spellcheck="false" name="attr_description"></textarea>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
<div class="sheet-attack sheet-block">
|
|
<div class=sheet-flex-centered style='flex-flow: row wrap;'>
|
|
<span class="sheet-label sheet-first-word" title="Roll to attack with selected Weapon or cast selected Spell at different to hit DX adjustments.">Attack</span>
|
|
<span class=sheet-nowrap>
|
|
<button class=sheet-attack-button title="Vs. Invisible or in total darkness/blind" type="action" name="act_attack-6">-6</button>
|
|
<button class=sheet-attack-button type="action" name="act_attack-5">-5</button>
|
|
<button class=sheet-attack-button title="Missile/Thrown against Prone behind cover or only head and shoulders visible, or vs. Flying" type="action" name="act_attack-4">-4</button>
|
|
<button class=sheet-attack-button title="Health at 3 or less" type="action" name="act_attack-3">-3</button>
|
|
<button class=sheet-attack-button title="Missile at 5+6 MH, Severely Wounded (-5 or more) last turn, standing on a body, vertical cover, melee from > half yard below" type="action" name="act_attack-2">-2</button>
|
|
<button class=sheet-attack-button title="Missile at 3+4 MH, Thrown per hex, Molotail per MH, or Projectile per 10 vertical feet" type="action" name="act_attack-1">-1</button>
|
|
<button class=sheet-attack-button type="action" name="act_attack0">0</button>
|
|
<button class=sheet-attack-button title="Missile per attackable extra hex of a multi hex creature" type="action" name="act_attack1">+1</button>
|
|
<button class=sheet-attack-button title="Melee/Thrown against Flank, or pole set against charger, Melee from > half yard above" type="action" name="act_attack2">+2</button>
|
|
<button class=sheet-attack-button type="action" name="act_attack3">+3</button>
|
|
<button class=sheet-attack-button title="Melee/Thrown against Rear, Prone, or in HTH" type="action" name="act_attack4">+4</button>
|
|
<button class=sheet-attack-button type="action" name="act_attack5">+5</button>
|
|
<button class=sheet-attack-button type="action" name="act_attack6">+6</button>
|
|
</span>
|
|
<button class=sheet-attack-button title="Automatic attack/cast success, use for HTH Reaction=6, bound victims, etc." type="action" name="act_attacka">A</button>
|
|
<input type=checkbox name=attr_defending class=sheet-caption-checkbox hidden>
|
|
<button class=sheet-caption-button title="Roll 4d6, use vs. Defending/Dodging or Unskilled Attack, deselects after each attack" type="action" name="act_attack4d">4d</button>
|
|
<input type=checkbox name=attr_defending2 class=sheet-caption-checkbox hidden>
|
|
<button class=sheet-caption-button title="Roll 5d6, use vs. Weapon Mastery Defending/Dodging, deselects after each attack" type="action" name="act_attack5d">5d</button>
|
|
</div>
|
|
</div>
|
|
<div class="sheet-weapons sheet-block">
|
|
<div class='sheet-fieldset-header sheet-flex-section'>
|
|
<span class=sheet-first-word style='position:absolute;'>Weapon</span>
|
|
<button class=sheet-select-button style='visibility:hidden' type="action"/>
|
|
<span class=sheet-first-word style='width: 13rem; visibility:hidden'>Weapon</span>
|
|
<span style='width: 6rem;' title="Damage per attack, e.g. 1d6+1" >Damage</span>
|
|
<span style='width: 4rem;' title="DX adjustment to hit an enemy, automatically applied.">To Hit</span>
|
|
<span class=sheet-flex>Notes</span>
|
|
<span style='width: 5rem;' title="Current projectile or weapon count.">Count</span>
|
|
<span style='width: 4rem;' title="Weapon or projectile weight per item.">Lbs</span>
|
|
<span style='position: relative;' title="Not carried, won't count as weight.">
|
|
<span style='position: absolute;'>NC</span>
|
|
<button type="action" class=sheet-checkbox-button name="act_none" style='visibility:hidden;'>
|
|
</span>
|
|
|
|
<button class=sheet-describe-button style='visibility:hidden' type="action"/>
|
|
<button class=sheet-expand-button style='visibility:hidden' type="action"/>
|
|
</div>
|
|
<fieldset class=repeating_weapons>
|
|
<div class=sheet-flex-section>
|
|
<input type="checkbox" class="sheet-select-radio" name="attr_selection-flag" hidden>
|
|
<button type="action" class=sheet-select-button title="Select this Weapon for Attack!" name="act_select"/>
|
|
<input type=text name=attr_weaponname style='width: 13rem;' placeholder="Weapon Name">
|
|
<input type=text name=attr_weapondamage style='width: 6rem;'>
|
|
<input type=text name=attr_tohit style='width: 4rem;'>
|
|
<input type=text name=attr_weaponnotes class=sheet-flex>
|
|
<input type=number name=attr_count value="1" style='width: 5rem;'>
|
|
<input type=text name=attr_weight style='width: 4rem;'>
|
|
<input type="checkbox" name="attr_notcarried" class=sheet-checkbox-checkbox hidden>
|
|
<button type="action" class=sheet-checkbox-button name="act_check_notcarried" />
|
|
|
|
<button class=sheet-describe-button title="Describe the weapon." type="action" name="act_describe"/>
|
|
<button class=sheet-expand-button title="Expand and edit more fields in the selected weapon." type="action" name="act_expand"/>
|
|
</div>
|
|
<input type="checkbox" name="attr_options-flag" class="sheet-toggle" hidden>
|
|
<div class="sheet-toggle-checked sheet-expanded">
|
|
<span title="Text shown following the character name when attacking, optional.">Attack Flavor Text:</span>
|
|
<input type=text name="attr_attackflavor" style='display: block; width:100%' placeholder="swings his mighty war axe.">
|
|
<span title="Text shown when the attack hits, optional.">Success Flavor Text:</span>
|
|
<input type=text name="attr_successflavor" style='display: block; width:100%' placeholder="The axe hits with a sickening crunch!">
|
|
<span title="Text shown when the attack misses, optional.">Failure Flavor Text:</span>
|
|
<input type=text name="attr_failureflavor" style='display: block; width:100%' placeholder="The axe swooshes air!">
|
|
<span title="Details about the weapon, quoted with the Describe button.">Description:</span>
|
|
<textarea class=sheet-text-area spellcheck="false" name="attr_description"></textarea>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
<div class="sheet-spells sheet-block">
|
|
<div class='sheet-fieldset-header sheet-flex-section'>
|
|
<span class=sheet-first-word style='position:absolute;'>Spell</span>
|
|
<button class=sheet-select-button style='visibility:hidden' type="action"/>
|
|
<span class=sheet-first-word style='width: 13rem; visibility:hidden'>Spell</span>
|
|
<span style='width: 6rem;' title="Damage done per spell, if any, e.g. 1d6+1">Damage</span>
|
|
<span style='width: 4rem;' title="DX adjustment to hit an enemy, automatic on Cast.">To Hit</span>
|
|
<span style='width: 7rem;' title="Strength cost when casting and after, e.g. 1 + 1/turn.">ST Cost</span>
|
|
<span style='width: 7rem;' title="Duration in turns, minutes, e.g. 10 turns.">Duration</span>
|
|
<span class=sheet-flex>Notes</span>
|
|
|
|
<button class=sheet-describe-button style='visibility:hidden' type="action"/>
|
|
<button class=sheet-expand-button style='visibility:hidden' type="action"/>
|
|
</div>
|
|
<fieldset class=repeating_spells>
|
|
<div class=sheet-flex-section>
|
|
<input type="checkbox" class="sheet-select-radio" name="attr_selection-flag" hidden>
|
|
<button title="Select this Spell for casting." class=sheet-select-button type="action" name="act_select"/>
|
|
<input type=text name=attr_spellname style='width: 13rem;' placeholder="Spell Name">
|
|
<input type=text name=attr_spelldamage style='width: 6rem;'>
|
|
<input type=text name=attr_tohit style='width: 4rem;'>
|
|
<input type=text name=attr_stcost style='width: 7rem;'>
|
|
<input type=text name=attr_duration style='width: 7rem;'>
|
|
<input type=text name=attr_spellnotes class=sheet-flex>
|
|
|
|
<button class=sheet-describe-button title="Describe the spell." type="action" name="act_describe"/>
|
|
<button class=sheet-expand-button title="Expand and edit more fields." type="action" name="act_expand"/>
|
|
</div>
|
|
<input type="checkbox" name="attr_options-flag" hidden class="sheet-toggle">
|
|
<div class="sheet-toggle-checked sheet-expanded">
|
|
<div>
|
|
<span>
|
|
<span title="IQ points you spent, if any, for this spell. Subtracts from Available IQ.">IQ Spent: </span>
|
|
<input type=text class=sheet-tiny-input name="attr_spellcost">
|
|
</span>
|
|
|
|
<span>
|
|
<span title="XP you spent, if any, for this spell. Subtracts from XP Available.">XP Spent: </span>
|
|
<input type=text class=sheet-small-input name="attr_expspent">
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<span title="IQ required to get and cast this Spell.">IQ Required: </span>
|
|
<input type=text class=sheet-tiny-input name=attr_iqrequired>
|
|
</div>
|
|
<div>
|
|
<span title="Text that displays when the spell succeeds, optional.">Success Flavor Text:</span>
|
|
<input type=text name="attr_successflavor" style='display: block; width:100%' placeholder="A giant wolf appears.">
|
|
</div>
|
|
<div>
|
|
<span title="Text that displays when the spell fails, optional.">Failure Flavor Text:</span>
|
|
<input type=text name="attr_failureflavor" style='display: block; width:100%' placeholder="A whisker falls to the ground.">
|
|
</div>
|
|
<div title="Description that shows when you click the Describe button.">Description:</div>
|
|
<textarea class=sheet-text-area spellcheck="false" name="attr_description"></textarea>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
<div class="sheet-armor sheet-block">
|
|
<div class='sheet-fieldset-header sheet-flex-section'>
|
|
<span class=sheet-first-word style='width: 13rem;' title="Put armor, shields, and Toughness here.">Armor/Shield</span>
|
|
<span style='width: 5rem;' title="Damage absorbed per attack.">Hits Blocked</span>
|
|
<span style='width: 5rem;' title="Subtracted from opponent's DX when they attack.">Minus to Hit</span>
|
|
<span style='width: 5rem;' title="Subtracted from your Dexterity.">DX Penalty</span>
|
|
<span class=sheet-flex>Notes</span>
|
|
<span style='width: 4rem;' title="Armor/shield weight.">Lbs</span>
|
|
<span style='position: relative;' title="Non-Flank, won't block hits or give minus to hit on flanking attacks. Should be set on shields and main gauche.">
|
|
<span style='position: absolute;'>NF</span>
|
|
<button type="action" class=sheet-checkbox-button name="act_none" style='visibility:hidden;'>
|
|
</span>
|
|
<span style='position: relative;' title="Not carried, won't count towards Weight, Hits Blocked, Minus to Hit, or DX penalty.">
|
|
<span style='position: absolute;'>NC</span>
|
|
<button type="action" class=sheet-checkbox-button name="act_none" style='visibility:hidden;'>
|
|
</span>
|
|
|
|
<button class=sheet-describe-button style='visibility:hidden' type="action"/>
|
|
<button class=sheet-expand-button style='visibility:hidden' type="action"/>
|
|
</div>
|
|
<fieldset class=repeating_armor>
|
|
<div class=sheet-flex-section>
|
|
<input type=text name=attr_defensename value="" style='width: 13rem;' placeholder="Armor/Shield Type">
|
|
<input type=text name=attr_hitsblocked value="0" style='width: 5rem;'>
|
|
<input type=text name=attr_minustohit value="0" style='width: 5rem;'>
|
|
<input type=text name=attr_dexpenalty value="0" style='width: 5rem;'>
|
|
<input type=text name=attr_defensenotes class=sheet-flex>
|
|
<input type=text name=attr_weight value="" style='width: 4rem;'>
|
|
<input type="checkbox" name="attr_nonflanking" class=sheet-checkbox-checkbox hidden>
|
|
<button type="action" class=sheet-checkbox-button name="act_check_nonflanking" />
|
|
<input type="checkbox" name="attr_notcarried" class=sheet-checkbox-checkbox hidden>
|
|
<button type="action" class=sheet-checkbox-button name="act_check_notcarried" />
|
|
|
|
<button class=sheet-describe-button title="Describe the Armor/Shield." type="action" name="act_describe"/>
|
|
<button class=sheet-expand-button title="Expand and edit more fields." type="action" name="act_expand"/>
|
|
</div>
|
|
<input type="checkbox" name="attr_options-flag" hidden class="sheet-toggle">
|
|
<div class="sheet-toggle-checked sheet-expanded">
|
|
<div title="Description that shows when you click the Describe button.">Description:</div>
|
|
<textarea class=sheet-text-area spellcheck="false" name="attr_description"></textarea>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
<div class="sheet-equip sheet-block">
|
|
<div class='sheet-fieldset-header sheet-flex-section'>
|
|
<span class=sheet-first-word style='width: 13rem'>Equipment</span>
|
|
<span style='width: 8rem;'>Count</span>
|
|
<span class=sheet-flex>Notes</span>
|
|
<span style='width: 4rem;' title="Weight per item.">Lbs</span>
|
|
<span style='position: relative;' title="Not carried, won't count as weight.">
|
|
<span style='position: absolute;'>NC</span>
|
|
<button type="action" class=sheet-checkbox-button name="act_none" style='visibility:hidden;'>
|
|
</span>
|
|
|
|
<button class=sheet-describe-button style='visibility:hidden' type="action"/>
|
|
<button class=sheet-expand-button style='visibility:hidden' type="action"/>
|
|
</div>
|
|
<fieldset class=repeating_equipment>
|
|
<div class=sheet-flex-section>
|
|
<input type=text name=attr_name style='width: 13rem;' placeholder="Equipment Name">
|
|
<input type=number name=attr_count value="1" style='width: 8rem;'>
|
|
<input type=text name=attr_notes class=sheet-flex>
|
|
<input type=text name=attr_weight value="" style='width: 4rem;'>
|
|
<input type="checkbox" name="attr_notcarried" class=sheet-checkbox-checkbox hidden>
|
|
<button type="action" class=sheet-checkbox-button name="act_check_notcarried" />
|
|
|
|
<button class=sheet-describe-button title="Describe the Equipment." type="action" name="act_describe"/>
|
|
<button class=sheet-expand-button title="Expand and edit more fields." type="action" name="act_expand"/>
|
|
</div>
|
|
<input type="checkbox" name="attr_options-flag" hidden class="sheet-toggle">
|
|
<div class="sheet-toggle-checked sheet-expanded">
|
|
<div title="Description that shows when you click the Describe button.">Description:</div>
|
|
<textarea class=sheet-text-area spellcheck="false" name="attr_description"></textarea>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class='sheet-journal'>
|
|
<button type="action" class=sheet-stub-tab>CharacterJournal</button>
|
|
<div class=sheet-tabrow>
|
|
<button type="action" class=sheet-unselected-tab name="act_character">Character</button>
|
|
<button type="action" class=sheet-selected-tab name="act_journal">Journal</button>
|
|
</div>
|
|
<div class="sheet-grid-section-journal">
|
|
<div class="sheet-pointhistory sheet-block">
|
|
<label>Experience Points</label>
|
|
<span class=sheet-label>
|
|
<div style='margin-bottom: .5em'>
|
|
<span title="Total amount of Experience Points the character has ever gained.">
|
|
<span>Total XP</span>
|
|
<input type=number style='width: 6em;' name="attr_experiencetotal" value="0"/>
|
|
</span>
|
|
 
|
|
<span title="XP available to spend on new Attributes, Talents, or Spells.">Available XP <span class=sheet-computed-value name="attr_experienceunspent"/></span>
|
|
 
|
|
<span title="Total XP spent on Attributes, Talents, and Spells.">Spent XP <span class=sheet-computed-value name="attr_experiencespent"/></span>
|
|
</div>
|
|
<div style='margin-bottom: .5em'>
|
|
<span title="Sum of all Attributes.">
|
|
Attribute Point Total <span class=sheet-computed-value name="attr_totalattrpoints"/>
|
|
</span>
|
|
 
|
|
<span title="XP cost to get your next attribute point.">
|
|
Next Attribute Point Cost <span class=sheet-computed-value name="attr_nextattrcost"/> XP
|
|
</span>
|
|
</div>
|
|
</span>
|
|
<br>
|
|
<div class='sheet-fieldset-header sheet-flex-section'>
|
|
<span style='width: 12rem;' title="Attribute points you bought with XP, to track available XP.">Attribute Acquired</span>
|
|
<span title='Experience Points you spent on this attribute' style='width: 8rem;'>XP Spent</span>
|
|
<span class=sheet-flex>Notes</span>
|
|
</div>
|
|
<fieldset class=repeating_exphistory>
|
|
<div class=sheet-flex-section>
|
|
<input type=text name=attr_expability style='width: 12rem;' placeholder='<Attribute name>'>
|
|
<input type=text name=attr_expspent style='width: 8rem;'>
|
|
<input type=text name=attr_expnotes class=sheet-flex>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
<div class="sheet-notes sheet-block">
|
|
<label>Notes</label>
|
|
<textarea class=sheet-text-area spellcheck="false" name="attr_inventory" title="Notes"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="sheet-otherspells sheet-block">
|
|
<label>Other Spells and Talents</label>
|
|
<textarea class=sheet-text-area spellcheck="false" name="attr_spells" title="Spells"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<rolltemplate class="sheet-rolltemplate-stat-check">
|
|
<div class=container>
|
|
<div><h1>{{character_name}} makes {{stat_a}}
|
|
<span class=capitalize>{{stat_name}}</span>
|
|
check.
|
|
</h1></div>
|
|
<div class=arrow-container><div class=arrow-right></div></div>
|
|
<div>
|
|
<span class=template-dice>{{computed::roll1}}</span>
|
|
= {{roll1}} vs {{stat}}
|
|
</div>
|
|
<!-- Success! -->
|
|
{{#rollGreater() computed::success 0}}
|
|
{{#rollTotal() computed::success 3}}
|
|
<div class=success>Critical Success!</div>
|
|
{{/rollTotal() computed::success 3}}
|
|
{{#rollTotal() computed::success 2}}
|
|
<div class=success>Automatic Success!</div>
|
|
{{/rollTotal() computed::success 2}}
|
|
{{#rollTotal() computed::success 1}}
|
|
<div class=success>Success!</div>
|
|
{{/rollTotal() computed::success 1}}
|
|
{{/rollGreater() computed::success 0}}
|
|
<!-- Failure -->
|
|
{{#rollLess() computed::success 0}}
|
|
{{#rollTotal() computed::success -3}}
|
|
<div class=failure>Critical Failure!</div>
|
|
{{/rollTotal() computed::success -3}}
|
|
{{#rollTotal() computed::success -2}}
|
|
<div class=failure>Automatic Failure!</div>
|
|
{{/rollTotal() computed::success -2}}
|
|
{{#rollTotal() computed::success -1}}
|
|
<div class=failure>Failure!</div>
|
|
{{/rollTotal() computed::success -1}}
|
|
{{/rollLess() computed::success 0}}
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<!-- For an attack -->
|
|
<rolltemplate class="sheet-rolltemplate-attack">
|
|
<div class=container>
|
|
<div><h1>
|
|
{{#attackflavor}}
|
|
{{character_name}} {{attackflavor}}
|
|
{{/attackflavor}}
|
|
{{^attackflavor}}
|
|
{{#weaponname}}
|
|
{{character_name}} attacks with {{pronoun}} <span class=capitalize>{{weaponname}}</span>!
|
|
{{/weaponname}}
|
|
{{^weaponname}}
|
|
{{character_name}} Attacks!</span>
|
|
{{/weaponname}}
|
|
{{/attackflavor}}
|
|
</h1></div>
|
|
{{#defending}}
|
|
<div><h2>Opponent is defending/dodging</h2></div>
|
|
{{/defending}}
|
|
{{^roll1}}
|
|
<div><h2>Automatic Hit</h2></div>
|
|
{{/roll1}}
|
|
<div class=arrow-container><div class="arrow-right"></div></div>
|
|
{{#roll1}}
|
|
<div>
|
|
<div>
|
|
<span class=template-dice>{{computed::roll1}}</span>
|
|
<span>= {{roll1}} vs </span>
|
|
{{#simpletohit}}
|
|
{{tohit}}
|
|
{{/simpletohit}}
|
|
</div>
|
|
{{^simpletohit}}
|
|
<div>
|
|
{{dx}}
|
|
{{#weaponadj}}
|
|
<span> + <span class=template-weaponadj>{{weaponadj}}</span></span>
|
|
{{/weaponadj}}
|
|
{{#tohitadj}}
|
|
<span> + {{tohitadj}}</span>
|
|
{{/tohitadj}}
|
|
<span> = {{tohit}}</span>
|
|
</div>
|
|
{{/simpletohit}}
|
|
</div>
|
|
{{/roll1}}
|
|
<!-- Success! -->
|
|
{{#rollGreater() computed::success 0}}
|
|
{{#successflavor}}
|
|
<div class=flavor-success>{{successflavor}}</div>
|
|
{{/successflavor}}
|
|
{{^successflavor}}
|
|
<div class=success>Hit!</div>
|
|
{{/successflavor}}
|
|
{{#rollTotal() computed::success 4}}
|
|
{{#damage}}
|
|
<div class=keyvalue><span>Damage: </span>{{damage}} × 3 = {{computed::damage}}</div>
|
|
{{/damage}}
|
|
<div class=success>Critical: Triple Damage!</div>
|
|
{{/rollTotal() computed::success 4}}
|
|
{{#rollTotal() computed::success 3}}
|
|
{{#damage}}
|
|
<div class=keyvalue><span>Damage: </span>{{damage}} × 2 = {{computed::damage}}</div>
|
|
{{/damage}}
|
|
<div class=success>Critical: Double Damage!</div>
|
|
{{/rollTotal() computed::success 3}}
|
|
{{#rollLess() computed::success 3}}
|
|
{{#damage}}
|
|
<div class=keyvalue><span>Damage: </span>{{damage}}</div>
|
|
{{/damage}}
|
|
{{#rollTotal() computed::success 2}}
|
|
<div class=success>Automatic hit.</div>
|
|
{{/rollTotal() computed::success 2}}
|
|
{{/rollLess() computed::success 3}}
|
|
{{/rollGreater() computed::success 0}}
|
|
<!-- Failure -->
|
|
{{#rollLess() computed::success 0}}
|
|
{{#failureflavor}}
|
|
<div class=flavor-failure>{{failureflavor}}</div>
|
|
{{/failureflavor}}
|
|
{{^failureflavor}}
|
|
<div class=failure>Miss!</div>
|
|
{{/failureflavor}}
|
|
{{#rollTotal() computed::success -4}}
|
|
<div class=failure>Critical: Weapon breaks or 1d6 damage!</div>
|
|
{{/rollTotal() computed::success -4}}
|
|
{{#rollTotal() computed::success -3}}
|
|
<div class=failure>Critical: Drops weapon or 1d6 damage!</div>
|
|
{{/rollTotal() computed::success -3}}
|
|
{{#rollTotal() computed::success -2}}
|
|
<div class=failure>Automatic miss.</div>
|
|
{{/rollTotal() computed::success -2}}
|
|
{{/rollLess() computed::success 0}}
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<!-- For casting a spell -->
|
|
<rolltemplate class="sheet-rolltemplate-cast-spell">
|
|
<div class=container>
|
|
<div><h1>{{character_name}} casts <span class=capitalize>{{spellname}}</span>.</h1></div>
|
|
{{#defending}}
|
|
<div><h2>Opponent is defending/dodging</h2></div>
|
|
{{/defending}}
|
|
{{^roll1}}
|
|
<div><h2>Automatic Success</h2></div>
|
|
{{/roll1}}
|
|
<div class=arrow-container><div class=arrow-right></div></div>
|
|
{{#roll1}}
|
|
<div>
|
|
<div>
|
|
<span class=template-dice>{{computed::roll1}}</span>
|
|
<span>= {{roll1}} vs </span>
|
|
{{#simpletohit}}
|
|
{{tohit}}
|
|
{{/simpletohit}}
|
|
</div>
|
|
{{^simpletohit}}
|
|
<div>
|
|
{{dx}}
|
|
{{#spelladj}}
|
|
<span> + <span class=template-weaponadj>{{spelladj}}</span></span>
|
|
{{/spelladj}}
|
|
{{#tohitadj}}
|
|
<span> + {{tohitadj}}</span>
|
|
{{/tohitadj}}
|
|
<span> = {{tohit}}</span>
|
|
</div>
|
|
{{/simpletohit}}
|
|
</div>
|
|
{{/roll1}}
|
|
<!-- Success -->
|
|
{{#rollGreater() computed::success 0}}
|
|
{{#successflavor}}
|
|
<div class=flavor-success>{{successflavor}}</div>
|
|
{{/successflavor}}
|
|
{{^successflavor}}
|
|
<div class=success>Success!</div>
|
|
{{/successflavor}}
|
|
{{#rollTotal() computed::success 4}}
|
|
{{#damage}}
|
|
<div class=keyvalue><span>Damage: </span>{{damage}} × 3 = {{computed::damage}}</div>
|
|
{{/damage}}
|
|
<div class=success>Critical: Triple Damage or Effect!</div>
|
|
{{/rollTotal() computed::success 4}}
|
|
{{#rollTotal() computed::success 3}}
|
|
{{#damage}}
|
|
<div class=keyvalue><span>Damage: </span>{{damage}} × 2 = {{computed::damage}}</div>
|
|
{{/damage}}
|
|
<div class=success>Critical: Double Damage or Effect!</div>
|
|
{{/rollTotal() computed::success 3}}
|
|
{{#rollLess() computed::success 3}}
|
|
{{#damage}}
|
|
<div class=keyvalue><span>Damage: </span>{{damage}}</div>
|
|
{{/damage}}
|
|
{{#rollTotal() computed::success 2}}
|
|
<div class=success>Automatic success.</div>
|
|
{{/rollTotal() computed::success 2}}
|
|
{{/rollLess() computed::success 3}}
|
|
{{/rollGreater() computed::success 0}}
|
|
<!-- Failure -->
|
|
{{#rollLess() computed::success 0}}
|
|
{{#failureflavor}}
|
|
<div class=flavor-failure>{{failureflavor}}</div>
|
|
{{/failureflavor}}
|
|
{{^failureflavor}}
|
|
<div class=failure>Failure!</div>
|
|
{{/failureflavor}}
|
|
{{#rollTotal() computed::success -4}}
|
|
<div class=failure>Critical: Staff breaks!</div>
|
|
{{/rollTotal() computed::success -4}}
|
|
{{#rollTotal() computed::success -3}}
|
|
<div class=failure>Critical: Lose ST, drop staff!</div>
|
|
{{/rollTotal() computed::success -3}}
|
|
{{#rollTotal() computed::success -2}}
|
|
<div class=failure>Automatic fail.</div>
|
|
{{/rollTotal() computed::success -2}}
|
|
{{/rollLess() computed::success 0}}
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<!-- For Initiative order -->
|
|
<rolltemplate class="sheet-rolltemplate-initiative">
|
|
<div class=container>
|
|
<div><h1>
|
|
{{character_name}} takes initiative!
|
|
</h1></div>
|
|
<div class=arrow-container><div class=arrow-right></div></div>
|
|
<div>
|
|
<div>
|
|
{{#initiativeadj}}
|
|
<span class=template-dice>{{computed::roll1}}</span> + {{initiativeadj}} = {{roll1}}
|
|
{{/initiativeadj}}
|
|
{{^initiativeadj}}
|
|
<span class=template-dice>{{computed::roll1}}</span> = {{roll1}}
|
|
{{/initiativeadj}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<!-- For turn order -->
|
|
<rolltemplate class="sheet-rolltemplate-turnorder">
|
|
<div class=container>
|
|
<div><h1>
|
|
{{character_name}}'s Turn Order
|
|
</h1></div>
|
|
<div class=arrow-container><div class=arrow-right></div></div>
|
|
<div class=keyvalue>
|
|
<span>Dexterity: </span><span>{{turnorder}}</span>
|
|
</div>
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<!-- Shield Rush -->
|
|
<rolltemplate class="sheet-rolltemplate-shield-rush">
|
|
<div class=container>
|
|
<div><h1>{{character_name}} makes a Shield Rush save.</h1></div>
|
|
{{#weaker}}
|
|
<div><h2>Rusher is weaker</h2></div>
|
|
{{/weaker}}
|
|
{{^weaker}}
|
|
<div><h2>Rusher is equal or stronger</h2></div>
|
|
{{/weaker}}
|
|
<div class=arrow-container><div class=arrow-right></div></div>
|
|
<div>
|
|
<span class=template-dice>{{computed::roll1}}</span>
|
|
= {{roll1}} vs {{stat}}
|
|
</div>
|
|
<!-- Success! -->
|
|
{{#rollGreater() computed::success 0}}
|
|
<div class=success>Remains standing!<div>
|
|
{{/rollGreater() computed::success 0}}
|
|
<!-- Failure -->
|
|
{{#rollLess() computed::success 0}}
|
|
<div class=failure>Falls prone!<div>
|
|
{{#rollTotal() computed::success -2}}
|
|
<div class=failure>Automatic Failure.</div>
|
|
{{/rollTotal() computed::success -2}}
|
|
{{/rollLess() computed::success 0}}
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<!-- HTH Reaction -->
|
|
<rolltemplate class="sheet-rolltemplate-hth-reaction">
|
|
<div class=container>
|
|
<div><h1>{{character_name}} reacts to Hand to Hand Combat.</h1></div>
|
|
<div class=arrow-container><div class=arrow-right></div></div>
|
|
<div>
|
|
<span class=template-dice>{{computed::roll1}}</span>
|
|
</div>
|
|
{{#rollBetween() roll1 1 2}}
|
|
<div class=description>
|
|
<div>Drops weapon and shield unless main-gouche or dagger.</div>
|
|
<div>Must fight bare handed this turn.</div>
|
|
<div>Drops prone with attacker.</div>
|
|
<div>
|
|
{{/rollBetween() roll1 1 2}}
|
|
{{#rollBetween() roll1 3 4}}
|
|
<div class=description>
|
|
<div>Drops weapon and shield unless main-gouche or dagger.</div>
|
|
<div>Readies dagger and can use it this turn.</div>
|
|
<div>Drops prone with attacker.</div>
|
|
<div>
|
|
{{/rollBetween() roll1 3 4}}
|
|
{{#rollTotal() roll1 5}}
|
|
<div class=description>
|
|
<div>Keeps weapon and shield!</div>
|
|
<div>Attacker backs up to adjacent hex in the direction they came from.</div>
|
|
<div>No Hand to Hand combat.</div>
|
|
{{/rollTotal() roll1 5}}
|
|
{{#rollTotal() roll1 6}}
|
|
<div class=description>
|
|
<div>Keeps weapon and shield!</div>
|
|
<div>Automatically hits the attacker RIGHT NOW.</div>
|
|
<div>Attacker backs up to adjacent hex in the direction they came from.</div>
|
|
<div>No Hand to Hand combat.</div>
|
|
<div class=keyvalue>
|
|
<span>Note: </span>
|
|
<span>
|
|
If attacked from behind or has no weapon and no unarmed-combat skill, re-roll this.
|
|
</span>
|
|
</div>
|
|
<div>
|
|
{{/rollTotal() roll1 6}}
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<!-- For Descriptions -->
|
|
<rolltemplate class="sheet-rolltemplate-description">
|
|
<div class=container>
|
|
<div><h1>
|
|
<span class=capitalize>{{item_name}}</span>
|
|
</h1></div>
|
|
<div class=arrow-container><div class=arrow-right></div></div>
|
|
{{#stat0}}
|
|
<div class=keyvalue><span>{{name0}}: </span><span>{{stat0}}</span></div>
|
|
{{/stat0}}
|
|
{{#stat1}}
|
|
<div class=keyvalue><span>{{name1}}: </span><span>{{stat1}}</span></div>
|
|
{{/stat1}}
|
|
{{#stat2}}
|
|
<div class=keyvalue><span>{{name2}}: </span><span>{{stat2}}</span></div>
|
|
{{/stat2}}
|
|
{{#stat3}}
|
|
<div class=keyvalue><span>{{name3}}: </span><span>{{stat3}}</span></div>
|
|
{{/stat3}}
|
|
{{#stat4}}
|
|
<div class=keyvalue><span>{{name4}}: </span><span>{{stat4}}</span></div>
|
|
{{/stat4}}
|
|
{{#stat5}}
|
|
<div class=keyvalue><span>{{name5}}: </span>{{stat5}}</div>
|
|
{{/stat5}}
|
|
<div class=description><span>{{description}}</span></div>
|
|
</div>
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<script type="text/worker">
|
|
|
|
// just concats to make a property name.
|
|
const repeatingPropertyName = function(repName, id, itemName) {
|
|
return `repeating_${repName}_${id}_${itemName}`;
|
|
}
|
|
|
|
// Safe method to get a repeating property.
|
|
const getFloatProperty = function(v, repName, id, itemName) {
|
|
let property = repeatingPropertyName(repName, id, itemName);
|
|
if (v[property] == 'on')
|
|
{
|
|
return 1
|
|
}
|
|
let val = parseFloat(v[property]);
|
|
return isNaN(val) ? 0 : val;
|
|
}
|
|
|
|
// to compute the total we need the object,
|
|
// the ids, the repName, and various itemnames.
|
|
const computeRepeatingTotal = function(v, idarray, repName, weightName, countName, notCarriedName, nonFlankingName) {
|
|
let total = 0
|
|
for(var i=0; i < idarray.length; i++) {
|
|
let id = idarray[i]
|
|
let value = getFloatProperty(v, repName, id, weightName);
|
|
if (countName) {
|
|
value *= getFloatProperty(v, repName, id, countName);
|
|
}
|
|
if (notCarriedName) {
|
|
let notCarried = getFloatProperty(v, repName, id, notCarriedName);
|
|
value *= (1.0 - notCarried);
|
|
}
|
|
if (nonFlankingName) {
|
|
let nonFlanking = getFloatProperty(v, repName, id, nonFlankingName);
|
|
value *= (1.0 - nonFlanking);
|
|
}
|
|
total += value;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
//e.g: "weapons" and "weaponwt"
|
|
// postcallback takes attrs, idarray, repName, weightName, countName, notCarriedName, nonFlankingName
|
|
const grabIds = function(idarray, repName, weightName, countName, notCarriedName, nonFlankingName, postcallback) {
|
|
let attrs = [];
|
|
for(var i=0; i < idarray.length; i++) {
|
|
let id = idarray[i];
|
|
attrs.push(repeatingPropertyName(repName, id, weightName));
|
|
if (countName) {
|
|
attrs.push(repeatingPropertyName(repName, id, countName));
|
|
}
|
|
if (notCarriedName) {
|
|
let propertyName = repeatingPropertyName(repName, id, notCarriedName);
|
|
attrs.push(propertyName);
|
|
}
|
|
if (nonFlankingName) {
|
|
let propertyName = repeatingPropertyName(repName, id, nonFlankingName);
|
|
attrs.push(propertyName);
|
|
}
|
|
}
|
|
getAttrs(attrs, function(v) {
|
|
postcallback(v, idarray, repName, weightName, countName, notCarriedName, nonFlankingName);
|
|
});
|
|
}
|
|
|
|
const diceArrayToString = function(rolls) {
|
|
let diceString = '';
|
|
if (rolls) {
|
|
for (let i = 0; i < rolls.length; ++i) {
|
|
let roll = rolls[i];
|
|
if (roll == 1) diceString += 'a';
|
|
else if (roll == 2) diceString += 'b';
|
|
else if (roll == 3) diceString += 'c';
|
|
else if (roll == 4) diceString += 'd';
|
|
else if (roll == 5) diceString += 'e';
|
|
else diceString += 'f';
|
|
if (i < rolls.length -1)
|
|
diceString += ' ' // ' '
|
|
}
|
|
}
|
|
return diceString;
|
|
}
|
|
|
|
// do the roll check and compute the dice string.
|
|
const doRollWithDiceString = function(macro) {
|
|
const finishRollWithDiceString = function(rollId, rolls) {
|
|
let diceString = diceArrayToString(rolls);
|
|
finishRoll(
|
|
rollId,
|
|
{
|
|
roll1: diceString
|
|
}
|
|
);
|
|
}
|
|
macro += ' {{character_name=@{cname}}}';
|
|
startRoll(macro, (results) => {
|
|
if ('roll1' in results.results) {
|
|
finishRollWithDiceString(results.rollId, results.results.roll1.dice);
|
|
}
|
|
else {
|
|
finishRollWithDiceString(results.rollId);
|
|
}
|
|
});
|
|
}
|
|
|
|
// calls back on this selected weapon or fails.
|
|
// callback takes the id of the selected one, and all the ids.
|
|
// calls with all empty and null if nothing found.
|
|
const onSelectedWeaponOrSpell = function(callback) {
|
|
// find selected weapon, if any.
|
|
getSectionIDs("weapons", function(sectionIds) {
|
|
let selectedIds = [];
|
|
for (var i = 0; i < sectionIds.length; ++i) {
|
|
selectedIds.push(repeatingPropertyName('weapons', sectionIds[i], 'selection-flag'));
|
|
}
|
|
getAttrs(selectedIds, function(values) {
|
|
for (var i = 0; i < sectionIds.length; ++i) {
|
|
let id = sectionIds[i];
|
|
let selectedName = repeatingPropertyName('weapons', id, 'selection-flag');
|
|
if (values[selectedName] == 'on') {
|
|
callback(id, sectionIds, 'weapon');
|
|
return;
|
|
}
|
|
}
|
|
// must be a spell, not a weapon.
|
|
getSectionIDs("spells", function(sectionIds) {
|
|
let selectedIds = [];
|
|
for (var i = 0; i < sectionIds.length; ++i) {
|
|
selectedIds.push(repeatingPropertyName('spells', sectionIds[i], 'selection-flag'));
|
|
}
|
|
getAttrs(selectedIds, function(values) {
|
|
for (var i = 0; i < sectionIds.length; ++i) {
|
|
let id = sectionIds[i];
|
|
let selectedName = repeatingPropertyName('spells', id, 'selection-flag');
|
|
if (values[selectedName] == 'on') {
|
|
callback(id, sectionIds, 'spell');
|
|
return;
|
|
}
|
|
}
|
|
// Nothing was found, pass in empty for the type.
|
|
callback('', null, '');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Describes an item, give it name, description attributes.
|
|
// and a linear array of [name, attribute, ..] pairs.
|
|
const describeItem = function(nameAttr, descriptionAttr, statArray) {
|
|
let macro = '&{template:description}'
|
|
+ ` {{item_name=@{${nameAttr}}}}`
|
|
+ ` {{description=@{${descriptionAttr}}}}`;
|
|
|
|
if (statArray) {
|
|
for (var i = 0; i*2 < statArray.length; ++i) {
|
|
let name = statArray[i*2];
|
|
let attr = statArray[i*2 + 1];
|
|
macro += ` {{name${i}=${name}}} {{stat${i}=@{${attr}}}}`;
|
|
}
|
|
}
|
|
startRoll(macro, (results) => {
|
|
finishRoll( results.rollId, {} );
|
|
});
|
|
}
|
|
|
|
// toggle this not carried. id starts with repeating_[weapon][spell] or any of them, really.
|
|
// attrName is "notcarried" or "nonflanking"
|
|
const toggleCheckbox = function(id, attrName) {
|
|
|
|
let selected = id ? (id + '_' + attrName).toLowerCase() : attrName;
|
|
|
|
getAttrs([selected], function(values) {
|
|
let oldValue = values[selected];
|
|
let newValue = (oldValue == 'on' ? 0 : 'on');
|
|
let valueMap = {};
|
|
// toggle this one, turn off the rest.
|
|
valueMap[selected] = newValue;
|
|
// Can't be silent, has to tell them.
|
|
setAttrs(valueMap);
|
|
});
|
|
}
|
|
|
|
const clearCheckbox = function(attrName) {
|
|
let valueMap = {};
|
|
// toggle this one, turn off the rest.
|
|
valueMap[attrName] = 0;
|
|
setAttrs(valueMap);
|
|
};
|
|
|
|
|
|
// select this weapon or spell, id starts with repeating_[weapon][spell]
|
|
// it will set attributes ready_name, damage, to_hit, attack, success, failure
|
|
// so macros can get at them.
|
|
const selectWeaponOrSpell = function(id) {
|
|
// get them all, and de check any not me...
|
|
let selected = (id + "_selection-flag").toLowerCase();
|
|
getSectionIDs("weapons", function(weaponIds) {
|
|
getSectionIDs("spells", function(spellIds) {
|
|
getAttrs([selected], function(values) {
|
|
let oldValue = values[selected];
|
|
let newValue = (oldValue == 'on' ? 0 : 'on');
|
|
let valueMap = {};
|
|
let readyAttributes = [];
|
|
// toggle this one, turn off the rest
|
|
valueMap[selected] = newValue;
|
|
|
|
// Radio button the rest.
|
|
if (newValue == 'on') {
|
|
for (var i = 0; i < weaponIds.length; ++i) {
|
|
let propertyName = repeatingPropertyName('weapons', weaponIds[i], 'selection-flag');
|
|
if (propertyName != selected) {
|
|
valueMap[propertyName] = 0;
|
|
} else {
|
|
let id = weaponIds[i];
|
|
readyAttributes = [repeatingPropertyName('weapons', id, 'weaponname'),
|
|
repeatingPropertyName('weapons', id, 'weapondamage'),
|
|
repeatingPropertyName('weapons', id, 'tohit'),
|
|
repeatingPropertyName('weapons', id, 'attackflavor'),
|
|
repeatingPropertyName('weapons', id, 'successflavor'),
|
|
repeatingPropertyName('weapons', id, 'failureflavor')];
|
|
}
|
|
}
|
|
for (var i = 0; i < spellIds.length; ++i) {
|
|
let propertyName = repeatingPropertyName('spells', spellIds[i], 'selection-flag');
|
|
if (propertyName != selected) {
|
|
valueMap[propertyName] = 0;
|
|
} else {
|
|
let id = spellIds[i];
|
|
readyAttributes = [repeatingPropertyName('spells', id, 'spellname'),
|
|
repeatingPropertyName('spells', id, 'spelldamage'),
|
|
repeatingPropertyName('spells', id, 'tohit'),
|
|
repeatingPropertyName('spells', id, 'attackflavor'),
|
|
repeatingPropertyName('spells', id, 'successflavor'),
|
|
repeatingPropertyName('spells', id, 'failureflavor')];
|
|
}
|
|
}
|
|
}
|
|
getAttrs(readyAttributes, function(readyValues) {
|
|
// convert into ready_ attributes.
|
|
for (const [key, value] of Object.entries(readyValues)) {
|
|
if (key.endsWith('name')) {
|
|
valueMap['ready_name'] = value
|
|
} else if (key.endsWith('damage')) {
|
|
valueMap['ready_damage'] = value
|
|
} else if (key.endsWith('tohit')) {
|
|
valueMap['ready_tohit'] = value
|
|
} else if (key.endsWith('attackflavor')) {
|
|
valueMap['ready_attackflavor'] = value
|
|
} else if (key.endsWith('successflavor')) {
|
|
valueMap['ready_successflavor'] = value
|
|
} else if (key.endsWith('failureflavor')) {
|
|
valueMap['ready_failureflavor'] = value
|
|
}
|
|
}
|
|
setAttrs(valueMap, {silent: true});
|
|
})
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Toggles the expansion of an item. you need to give it repeating_X-Y for id.
|
|
const toggleItem = function(id) {
|
|
let optionsName = id + '_options-flag';
|
|
getAttrs([optionsName], function(v) {
|
|
let attrMap = {};
|
|
attrMap[optionsName] = v[optionsName] == 0 ? 'on' : 0;
|
|
setAttrs(attrMap, {silent: true});
|
|
});
|
|
}
|
|
|
|
// a const value, not an attribute, no @ in front. Optionally as roll.
|
|
const rollTemplateValue = function(templateName, attrName, asRoll) {
|
|
return asRoll ? ` {{${templateName}=[[${attrName}]]}}` : ` {{${templateName}=${attrName}}}`;
|
|
}
|
|
|
|
// Return a template string if attrs contains attrName
|
|
const rollTemplateProperty = function(templateName, attrName, attrs, asRoll) {
|
|
if (asRoll) {
|
|
if ( !attrs || (attrs[attrName] != '' && attrs[attrName] != '0')) {
|
|
return rollTemplateValue(templateName, `@{${attrName}}`, true);
|
|
}
|
|
} else {
|
|
if (!attrs || (attrs[attrName] != '')) {
|
|
return rollTemplateValue(templateName, `@{${attrName}}`);
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
// Update Str
|
|
on('sheet:opened change:st_max change:st_adj', function() {
|
|
getAttrs(['st_max', 'st_adj'], function(v) {
|
|
let finalStr = (parseInt(v.st_max)||0) + (parseInt(v.st_adj)||0);
|
|
setAttrs({
|
|
st: finalStr
|
|
});
|
|
});
|
|
});
|
|
|
|
// Update IQ
|
|
on('sheet:opened change:iq_max change:iq_adj', function() {
|
|
getAttrs(['iq_max', 'iq_adj'], function(v) {
|
|
let finalIQ = (parseInt(v.iq_max)||0) + (parseInt(v.iq_adj)||0);
|
|
setAttrs({
|
|
iq: finalIQ
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
// Do a roll with stats. Just a stat check.
|
|
// No combat.
|
|
const doSaveRoll = function(macro) {
|
|
// returns success level. positive is success.
|
|
// negative is failure.
|
|
const successLevel = function(results) {
|
|
let roll = results.roll1.result;
|
|
|
|
// Check all the crits first.
|
|
if (results.roll1.dice.length == 2) {
|
|
// 2d6 I know, looks weird, but thems the rules.
|
|
if (roll == 2) return 2; // auto success
|
|
if (roll == 12) return -3; // crit fail
|
|
}
|
|
else if (results.roll1.dice.length == 3) {
|
|
// 3d6
|
|
if (roll < 5) return 3; // crit success
|
|
if (roll == 5) return 2; // auto success
|
|
if (roll > 16) return -3; // crit fail
|
|
if (roll == 16) return -2; // auto fail
|
|
}
|
|
else if (results.roll1.dice.length == 4) {
|
|
// 4d6
|
|
if (roll < 8) return 3;
|
|
if (roll == 8) return 2;
|
|
if (roll > 20) return -3;
|
|
if (roll == 20) return -2;
|
|
}
|
|
else if (results.roll1.dice.length == 5) {
|
|
// 5d6
|
|
if (roll < 11) return 3;
|
|
if (roll == 11) return 2;
|
|
if (roll > 22) return -3;
|
|
if (roll == 22) return -2;
|
|
}
|
|
else if (results.roll1.dice.length == 6) {
|
|
// 6d6
|
|
if (roll < 14) return 3;
|
|
if (roll == 14) return 2;
|
|
if (roll > 24) return -3;
|
|
if (roll == 24) return -2;
|
|
}
|
|
else if (results.roll1.dice.length == 7) {
|
|
// 7d6
|
|
if (roll < 17) return 3;
|
|
if (roll == 17) return 2;
|
|
if (roll > 26) return -3;
|
|
if (roll == 26) return -2;
|
|
}
|
|
else {
|
|
// 8d6
|
|
if (roll < 20) return 3;
|
|
if (roll == 20) return 2;
|
|
if (roll > 28) return -3;
|
|
if (roll == 28) return -2;
|
|
}
|
|
// Normal to-hit check.
|
|
if (results.stat)
|
|
{
|
|
return (roll <= results.stat.result) ? 1 : -1;
|
|
}
|
|
// unstatted version.
|
|
return 0;
|
|
}
|
|
|
|
macro += ' {{character_name=@{cname}}}';
|
|
macro += rollTemplateValue('success', 0, true); // level of success, crits, etc.
|
|
startRoll(macro, (results) => {
|
|
let level = successLevel(results.results);
|
|
let diceString = diceArrayToString(results.results.roll1.dice);
|
|
finishRoll( results.rollId, {
|
|
'roll1': diceString,
|
|
'success': level
|
|
});
|
|
});
|
|
}
|
|
|
|
// attr is actual attribute st
|
|
// name is the friendly name like strength
|
|
// label is to show in the template, so like STR
|
|
// a is whether to save a <name> roll or an <name> roll.
|
|
const doStatRoll = function(attr, name, label, an = 'a') {
|
|
getAttrs(saveDiceAttrs(), function(values) {
|
|
let dice = getSaveDiceValue(values)
|
|
s = "&{template:stat-check} {{stat_a=" + an + "}} {{stat_name=" + name + "}} {{stat=[[@{" + attr + "}[" + label + "]]]}} {{roll1=[[" + dice + "d6]]}}";
|
|
doSaveRoll(s);
|
|
// since we've baked dice into the string we are save.
|
|
// We do this because that's the most common value.
|
|
setSaveDice(3);
|
|
});
|
|
}
|
|
|
|
on('clicked:st_check', (info) => {
|
|
doStatRoll('st', 'strength', 'STR');
|
|
});
|
|
on('clicked:dx_check', (info) => {
|
|
doStatRoll('dx', 'dexterity', 'DEX');
|
|
});
|
|
on('clicked:iq_check', (info) => {
|
|
doStatRoll('iq', 'IQ', 'IQ', 'an');
|
|
});
|
|
on('clicked:blank_check', (info) => {
|
|
getAttrs(saveDiceAttrs(), function(values) {
|
|
let dice = getSaveDiceValue(values)
|
|
s = "&{template:stat-check} {{stat_a=a}} {{stat_name=}} {{stat=...}} {{roll1=[[" + dice + "d6]]}}";
|
|
doSaveRoll(s);
|
|
setSaveDice(3);
|
|
});
|
|
});
|
|
on('clicked:health_check', (info) => {
|
|
doStatRoll('health', 'health', 'HEALTH');
|
|
// doSaveRoll("&{template:stat-check} {{stat_a=a}} {{stat_name=health}} {{stat=[[@{health}[HEALTH]]]}} {{roll1=[[3d6]]}}");
|
|
});
|
|
|
|
on('clicked:initiative', (info) => {
|
|
let initiativeAdjName = 'initiative_adj';
|
|
let attrNames = [initiativeAdjName];
|
|
getAttrs(attrNames, function(v) {
|
|
let mstring = "&{template:initiative}"
|
|
let initiativeRoll = '1d6';
|
|
// provisionally add initiative adjustment
|
|
if (parseInt(v[initiativeAdjName])||0) {
|
|
let initiativeAdj = `@{${initiativeAdjName}}[INITIATIVE_ADJ]`;
|
|
initiativeRoll += ` + ${initiativeAdj}`;
|
|
mstring += rollTemplateValue('initiativeadj', initiativeAdj, true);
|
|
}
|
|
mstring += rollTemplateValue('roll1', initiativeRoll, true);
|
|
doRollWithDiceString(mstring);
|
|
});
|
|
});
|
|
|
|
on('clicked:turnorder', (info) => {
|
|
doRollWithDiceString("&{template:turnorder} {{turnorder=[[@{DX}[DEX] &{tracker}]]}}");
|
|
});
|
|
|
|
on('clicked:hthreaction', (info) => {
|
|
doRollWithDiceString("&{template:hth-reaction} {{roll1=[[1d6]]}}");
|
|
});
|
|
|
|
// Do a roll with stats.
|
|
const doShieldRush = function(macro) {
|
|
// returns success level. positive is success.
|
|
// negative is failure.
|
|
const successLevel = function(results) {
|
|
let roll = results.roll1.result;
|
|
let stat = results.stat.result;
|
|
|
|
// Check all the crits first.
|
|
if (results.roll1.dice.length == 3) {
|
|
// 3d6
|
|
if (roll >= 16) return -2; // auto fail
|
|
}
|
|
else {
|
|
// 2d6
|
|
if (roll == 12) return -2;
|
|
}
|
|
// Normal to-hit check.
|
|
return (roll <= stat) ? 1 : -1;
|
|
}
|
|
|
|
macro += ' {{character_name=@{cname}}}';
|
|
macro += rollTemplateValue('success', 0, true); // level of success, crits, etc.
|
|
startRoll(macro, (results) => {
|
|
let level = successLevel(results.results);
|
|
let diceString = diceArrayToString(results.results.roll1.dice);
|
|
finishRoll( results.rollId, {
|
|
'roll1': diceString,
|
|
'success': level
|
|
});
|
|
});
|
|
}
|
|
|
|
on('clicked:shieldrushweaker', (info) => {
|
|
doShieldRush("&{template:shield-rush} {{weaker=1}} {{stat=[[@{DX}[DEX]]]}} {{roll1=[[2d6]]}}");
|
|
});
|
|
on('clicked:shieldrushstronger', (info) => {
|
|
doShieldRush("&{template:shield-rush} {{stat=[[@{DX}[DEX]]]}} {{roll1=[[3d6]]}}");
|
|
});
|
|
|
|
/* TALENT HANDLERS */
|
|
|
|
// Expand or describe a talent.
|
|
on('clicked:repeating_talents', (info) => {
|
|
let id = info.sourceAttribute;
|
|
let name = info.htmlAttributes.name;
|
|
if (name == 'act_describe') {
|
|
describeItem(id + '_talentname', id + '_description', ['IQ Required', id + '_talentiq']);
|
|
}
|
|
else if (name == 'act_expand') {
|
|
toggleItem(id);
|
|
}
|
|
});
|
|
|
|
/* WEAPON HANDLERS */
|
|
|
|
// Weapon Attack Buttons.
|
|
on('clicked:attack-6'
|
|
+ ' clicked:attack-5'
|
|
+ ' clicked:attack-4'
|
|
+ ' clicked:attack-3'
|
|
+ ' clicked:attack-2'
|
|
+ ' clicked:attack-1'
|
|
+ ' clicked:attack0'
|
|
+ ' clicked:attack1'
|
|
+ ' clicked:attack2'
|
|
+ ' clicked:attack3'
|
|
+ ' clicked:attack4'
|
|
+ ' clicked:attack5'
|
|
+ ' clicked:attack6'
|
|
+ ' clicked:attacka', (info) => {
|
|
let toHitCharacter = info.triggerName.substring(14);
|
|
let toHit = parseInt(toHitCharacter)||0;
|
|
|
|
onSelectedWeaponOrSpell(function(id, selectedIds, type) {
|
|
|
|
// return the string depending on whether we are defending or not.
|
|
const diceProperties = function (attrs) {
|
|
// we are weapon mastery defending, so need 5d6
|
|
if (attrs.defending2 == 'on') {
|
|
return rollTemplateValue('defending', '1') + rollTemplateValue('roll1', '5d6', true);
|
|
// we are defending, so need 4d6
|
|
} else if (attrs.defending == 'on') {
|
|
return rollTemplateValue('defending', '1') + rollTemplateValue('roll1', '4d6', true);
|
|
} else {
|
|
return rollTemplateValue('roll1', '3d6', true);
|
|
}
|
|
}
|
|
|
|
// returns success level. positive is success.
|
|
// negative is failure.
|
|
const successLevel = function(results) {
|
|
// if no roll, normal success.
|
|
if (!('roll1' in results)) {
|
|
return 1;
|
|
}
|
|
let roll = results.roll1.result;
|
|
let tohit = results.tohit.result;
|
|
// Check all the crits first.
|
|
if (results.roll1.dice.length == 3) {
|
|
// 3d6
|
|
if (roll == 3) return 4;
|
|
if (roll == 4) return 3;
|
|
if (roll == 5) return 2;
|
|
if (roll == 16) return -2;
|
|
if (roll == 17) return -3;
|
|
if (roll == 18) return -4;
|
|
}
|
|
/* I took some liberties with the 4d6 and 5d6 dodging crits.
|
|
5d6 crits didn't even exist, and the 4d6 ones were overly punitive.
|
|
See this doc:
|
|
https://drive.google.com/file/d/1XHh8qUuFnZo0qG89klxAw7i4XNNGYiPd/view?usp=sharing
|
|
*/
|
|
else if (results.roll1.dice.length == 4) {
|
|
if (roll <= 5) return 4;
|
|
if (roll == 6) return 3;
|
|
if (roll == 7) return 2;
|
|
if (roll == 20) return -2;
|
|
if (roll == 21 || roll == 22) return -3;
|
|
if (roll >= 23) return -4;
|
|
}
|
|
else { /* 5d6 */
|
|
if (roll <= 7) return 4;
|
|
if (roll == 8) return 3;
|
|
if (roll == 9) return 2;
|
|
if (roll == 24) return -2;
|
|
if (roll == 25 || roll == 26) return -3;
|
|
if (roll >= 27) return -4;
|
|
}
|
|
// Normal to-hit check.
|
|
return (roll <= tohit) ? 1 : -1;
|
|
}
|
|
|
|
// Do the Attack roll with success level.
|
|
const doAttackRoll = function(macro) {
|
|
macro += ' {{character_name=@{cname}}}';
|
|
macro += rollTemplateValue('success', 0, true); // level of success, crits, etc.
|
|
startRoll(macro, (results) => {
|
|
let level = successLevel(results.results)
|
|
let computed = {'success': level};
|
|
if (('damage' in results.results) && level > 2) {
|
|
computed['damage'] = results.results.damage.result * (level - 1);
|
|
}
|
|
if ('roll1' in results.results) {
|
|
computed['roll1'] = diceArrayToString(results.results.roll1.dice);
|
|
}
|
|
finishRoll( results.rollId, computed );
|
|
});
|
|
}
|
|
|
|
// Nothing selected, do a generic roll.
|
|
if (type == '') {
|
|
getAttrs(['defending', 'defending2'], function(v) {
|
|
let mstring = `&{template:attack}`;
|
|
// append total and separate to hits, if present.
|
|
if (toHitCharacter != 'a') {
|
|
mstring += diceProperties(v);
|
|
let dxAdj = `@{DX}[DEX]`;
|
|
let toHitString = ` {{tohit=[[${dxAdj}`;
|
|
let oldHitLength = toHitString.length;
|
|
mstring += rollTemplateValue('dx', dxAdj, true);
|
|
|
|
// and to hit adjustment
|
|
if (toHit != 0) {
|
|
let toHitAdj = `${toHit}[TO_HIT]`
|
|
toHitString += ` + ${toHitAdj}`;
|
|
mstring += rollTemplateValue('tohitadj', toHitAdj, true);
|
|
}
|
|
if (oldHitLength == toHitString.length) {
|
|
mstring += rollTemplateValue('simpletohit', '1');
|
|
}
|
|
// and finish.
|
|
toHitString += ']]}}';
|
|
mstring += toHitString;
|
|
}
|
|
doAttackRoll(mstring);
|
|
});
|
|
} else if (type == 'weapon') {
|
|
let attackFlavorName = repeatingPropertyName('weapons', id, 'attackflavor');
|
|
let successFlavorName = repeatingPropertyName('weapons', id, 'successflavor');
|
|
let failureFlavorName = repeatingPropertyName('weapons', id, 'failureflavor');
|
|
let weaponDamageName = repeatingPropertyName('weapons', id, 'weapondamage');
|
|
let attrNames = ['gender', attackFlavorName, successFlavorName, failureFlavorName, weaponDamageName, 'defending', 'defending2'];
|
|
let weaponToHitName = repeatingPropertyName('weapons', id, 'tohit');
|
|
if (toHitCharacter != 'a') {
|
|
attrNames.push(weaponToHitName);
|
|
}
|
|
getAttrs(attrNames, function(v) {
|
|
let mstring = `&{template:attack}`
|
|
+ rollTemplateProperty('weaponname', repeatingPropertyName('weapons', id, 'weaponname'));
|
|
|
|
// append total and separate to hits, if present.
|
|
if (weaponToHitName in v) {
|
|
mstring += diceProperties(v);
|
|
let dxAdj = `@{DX}[DEX]`;
|
|
let toHitString = ` {{tohit=[[${dxAdj}`;
|
|
let oldHitLength = toHitString.length;
|
|
mstring += rollTemplateValue('dx', dxAdj, true);
|
|
|
|
// provisionally add weapon toHit.
|
|
if (parseInt(v[weaponToHitName])||0) {
|
|
let weaponAdj = `@{${weaponToHitName}}[WPN_ADJ]`;
|
|
toHitString += ` + (${weaponAdj})`;
|
|
mstring += rollTemplateValue('weaponadj', weaponAdj, true);
|
|
}
|
|
// and to hit adjustment
|
|
if (toHit != 0) {
|
|
let toHitAdj = `${toHit}[TO_HIT]`
|
|
toHitString += ` + ${toHitAdj}`;
|
|
mstring += rollTemplateValue('tohitadj', toHitAdj, true);
|
|
}
|
|
if (oldHitLength == toHitString.length) {
|
|
mstring += rollTemplateValue('simpletohit', '1');
|
|
}
|
|
// and finish.
|
|
toHitString += ']]}}';
|
|
mstring += toHitString;
|
|
}
|
|
|
|
// conditionally add damage as roll.
|
|
mstring += rollTemplateProperty('damage', weaponDamageName, v, true);
|
|
|
|
{ // add pronoun.
|
|
let gender = v.gender.toLowerCase();
|
|
let pronoun = 'their';
|
|
if (gender == 'male') pronoun = 'his';
|
|
else if (gender == 'female') pronoun = 'her'
|
|
mstring += rollTemplateValue('pronoun', pronoun);
|
|
}
|
|
mstring += rollTemplateProperty('attackflavor', attackFlavorName, v);
|
|
mstring += rollTemplateProperty('successflavor', successFlavorName, v);
|
|
mstring += rollTemplateProperty('failureflavor', failureFlavorName, v);
|
|
doAttackRoll(mstring);
|
|
});
|
|
}
|
|
else if (type == 'spell') {
|
|
let damageName = repeatingPropertyName('spells', id, 'spelldamage');
|
|
let successFlavorName = repeatingPropertyName('spells', id, 'successflavor');
|
|
let failureFlavorName = repeatingPropertyName('spells', id, 'failureflavor');
|
|
let attrNames = [successFlavorName, failureFlavorName, damageName, 'defending', 'defending2'];
|
|
let spellToHitName = repeatingPropertyName('spells', id, 'tohit');
|
|
if (toHitCharacter != 'a') {
|
|
attrNames.push(spellToHitName);
|
|
}
|
|
getAttrs(attrNames, function(v) {
|
|
let mstring = `&{template:cast-spell}`
|
|
+ rollTemplateProperty('spellname', repeatingPropertyName('spells', id, 'spellname'));
|
|
|
|
// append to hit values if present.
|
|
if (spellToHitName in v) {
|
|
mstring += diceProperties(v);
|
|
let dxAdj = `@{DX}[DEX]`;
|
|
let toHitString = ` {{tohit=[[${dxAdj}`;
|
|
let oldHitLength = toHitString.length;
|
|
mstring += rollTemplateValue('dx', dxAdj, true);
|
|
|
|
// provisionally add spell toHit.
|
|
if (parseInt(v[spellToHitName])||0) {
|
|
let spellAdj = `@{${spellToHitName}}[SPELL_ADJ]`;
|
|
toHitString += ` + (${spellAdj})`;
|
|
mstring += rollTemplateValue('spelladj', spellAdj, true);
|
|
}
|
|
// and rolled to hit.
|
|
if (toHit != 0) {
|
|
let toHitAdj = `${toHit}[TO_HIT]`;
|
|
toHitString += ` + ${toHitAdj}`;
|
|
mstring += rollTemplateValue('tohitadj', toHitAdj, true);
|
|
}
|
|
|
|
// just dex.
|
|
if (toHitString.length == oldHitLength) {
|
|
mstring += rollTemplateValue('simpletohit', '1');
|
|
}
|
|
// and finish.
|
|
toHitString += ']]}}';
|
|
mstring += toHitString;
|
|
}
|
|
mstring += rollTemplateProperty('damage', damageName, v, true);
|
|
mstring += rollTemplateProperty('successflavor', successFlavorName, v);
|
|
mstring += rollTemplateProperty('failureflavor', failureFlavorName, v);
|
|
doAttackRoll(mstring);
|
|
});
|
|
}
|
|
// Deselect defending checkboxes.
|
|
// since it can easily just stay on by mistake.
|
|
setAttrs({
|
|
defending: 0,
|
|
defending2: 0
|
|
});
|
|
});
|
|
});
|
|
|
|
// Radio/Toggle attack buttons.
|
|
on(' clicked:attack5d', (info) => {
|
|
toggleCheckbox('', 'defending2');
|
|
clearCheckbox('defending');
|
|
});
|
|
on(' clicked:attack4d', (info) => {
|
|
toggleCheckbox('', 'defending');
|
|
clearCheckbox('defending2');
|
|
});
|
|
|
|
// Returns the save dice attribute name
|
|
// give the dice.
|
|
const saveDiceAttr = function(dice) {
|
|
return 'save' + dice + 'd';
|
|
}
|
|
|
|
// returns an array of save dice attrs.
|
|
const saveDiceAttrs = function() {
|
|
let attrs = []
|
|
for (let i = 2; i < 9; ++i) {
|
|
attrs.push(saveDiceAttr(i));
|
|
}
|
|
return attrs;
|
|
}
|
|
|
|
// Given a value map, returns the number of dice.
|
|
const getSaveDiceValue = function(values) {
|
|
for (let i = 2; i < 9; ++i) {
|
|
if (values[saveDiceAttr(i)] != 0) {
|
|
return i;
|
|
}
|
|
}
|
|
// default.
|
|
return 3;
|
|
}
|
|
|
|
// Tell it how many dice to use, it sets the radio
|
|
// button.
|
|
const setSaveDice = function(dice) {
|
|
// Seems like you have to set attrs from a get attrs.
|
|
getAttrs(saveDiceAttrs(), function(values) {
|
|
for (let i = 2; i < 9; ++i) {
|
|
values[saveDiceAttr(i)] = (i == dice) ? 'on' : 0;
|
|
}
|
|
setAttrs(values);
|
|
});
|
|
}
|
|
|
|
// Radio save buttons.
|
|
on('clicked:save2d'
|
|
+ ' clicked:save3d'
|
|
+ ' clicked:save4d'
|
|
+ ' clicked:save5d'
|
|
+ ' clicked:save6d'
|
|
+ ' clicked:save7d'
|
|
+' clicked:save8d', (info) => {
|
|
|
|
setSaveDice(parseInt(info.triggerName.substring(12)) || 3);
|
|
});
|
|
|
|
// Weapons Select/Expand/Describe/NC
|
|
on('clicked:repeating_weapons', (info) => {
|
|
let name = info.htmlAttributes.name;
|
|
let id = info.sourceAttribute;
|
|
if (name == 'act_select') {
|
|
selectWeaponOrSpell(id);
|
|
}
|
|
else if (name == 'act_check_notcarried') {
|
|
toggleCheckbox(id, 'notcarried');
|
|
}
|
|
else if (name == 'act_describe') {
|
|
describeItem(id + '_weaponname', id + '_description',
|
|
['To Hit', id + '_tohit'
|
|
, 'Damage', id + '_weapondamage'
|
|
, 'Weight', id + '_weight'
|
|
, 'Note', id + '_weaponnotes']);
|
|
}
|
|
else if (name == 'act_expand') {
|
|
toggleItem(id);
|
|
}
|
|
});
|
|
|
|
|
|
/* Spell Handlers */
|
|
|
|
// Update Mana
|
|
on('sheet:opened change:mana_max change:mana_adj', function() {
|
|
getAttrs(['mana_max', 'mana_adj'], function(v) {
|
|
let finalMana = (parseInt(v.mana_max)||0) - (parseInt(v.mana_adj)||0);
|
|
setAttrs({
|
|
mana: finalMana
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
// Spell Cast Select/Expand/Describe
|
|
on('clicked:repeating_spells', (info) => {
|
|
let name = info.htmlAttributes.name;
|
|
let id = info.sourceAttribute;
|
|
if (name == 'act_select') {
|
|
selectWeaponOrSpell(id);
|
|
}
|
|
if (name == 'act_describe') {
|
|
describeItem(id + '_spellname', id + '_description',
|
|
['To Hit', id + '_tohit',
|
|
'Damage', id + '_spelldamage',
|
|
'ST Cost', id + '_stcost',
|
|
'Duration', id + '_duration',
|
|
'Note', id + '_spellnotes'
|
|
]);
|
|
}
|
|
else if (name == 'act_expand') {
|
|
toggleItem(id);
|
|
}
|
|
});
|
|
|
|
/* Armor Handlers */
|
|
// Armor expand/describe/notcarried
|
|
on('clicked:repeating_armor', (info) => {
|
|
let id = info.sourceAttribute;
|
|
let name = info.htmlAttributes.name;
|
|
if (name == 'act_check_nonflanking') {
|
|
toggleCheckbox(id, 'nonflanking');
|
|
} else if (name == 'act_check_notcarried') {
|
|
toggleCheckbox(id, 'notcarried');
|
|
} else if (name == 'act_describe') {
|
|
describeItem(id + '_defensename', id + '_description',
|
|
['Hits Blocked', id + '_hitsblocked',
|
|
'Minus to Hit', id + '_minustohit',
|
|
'DX Penalty', id + '_dexpenalty',
|
|
'Weight', id + '_weight',
|
|
'Non-flanking', id + '_nonflanking',
|
|
'Note', id + '_defensenotes']);
|
|
}
|
|
else if (name == 'act_expand') {
|
|
toggleItem(id);
|
|
}
|
|
});
|
|
|
|
/* Equipment Handlers */
|
|
// Expand/Describe/NotCarried a talent.
|
|
on('clicked:repeating_equipment', (info) => {
|
|
let id = info.sourceAttribute;
|
|
let name = info.htmlAttributes.name;
|
|
if (name == 'act_check_notcarried') {
|
|
toggleCheckbox(id, 'notcarried');
|
|
} else if (name == 'act_describe') {
|
|
describeItem(id + '_name', id + '_description',
|
|
['Count', id + '_count',
|
|
'Weight', id + '_weight',
|
|
'Note', id + '_notes'
|
|
]);
|
|
}
|
|
else if (name == 'act_expand') {
|
|
toggleItem(id);
|
|
}
|
|
});
|
|
|
|
|
|
/* Experience Handlers */
|
|
|
|
// Update experience spent and unspent.
|
|
// need to sum from three sources.
|
|
on('sheet:opened change:experiencetotal'
|
|
+ ' change:repeating_exphistory remove:repeating_exphistory'
|
|
+ ' change:repeating_spells remove:repeating_spells'
|
|
+ ' change:repeating_talents remove:repeating_talents', function() {
|
|
let totalSpent = 0;
|
|
let totalCallbacks = 0;
|
|
|
|
let idCallback = function(attrs, idarray, repName, spentName) {
|
|
totalSpent += computeRepeatingTotal(attrs, idarray, repName, spentName);
|
|
if (++totalCallbacks == 3) {
|
|
getAttrs(["experiencetotal"], function(v) {
|
|
let total = parseInt(v.experiencetotal)||0;
|
|
setAttrs({
|
|
experienceunspent: (total - totalSpent),
|
|
experiencespent: totalSpent
|
|
})
|
|
});
|
|
}
|
|
}
|
|
getSectionIDs("exphistory", function(v) {grabIds(v, 'exphistory', 'expspent', '', '', '', idCallback);});
|
|
getSectionIDs("talents", function(v) {grabIds(v, 'talents', 'expspent', '', '', '', idCallback);});
|
|
getSectionIDs("spells", function(v) {grabIds(v, 'spells', 'expspent', '', '', '', idCallback);});
|
|
});
|
|
|
|
/* Armor Handlers */
|
|
|
|
// Update Hits Blocked
|
|
on('sheet:opened change:repeating_armor remove:repeating_armor', function() {
|
|
let idCallback = function(attrs, idarray, repName, blockedName, countName, notCarriedName, nonFlankingName) {
|
|
let totalBlocked = computeRepeatingTotal(attrs, idarray, repName, blockedName, countName, notCarriedName);
|
|
let totalBlockedFlanking = computeRepeatingTotal(attrs, idarray, repName, blockedName, countName, notCarriedName, nonFlankingName);
|
|
setAttrs({
|
|
hitsblocked_max: totalBlocked,
|
|
hitsblocked: totalBlockedFlanking
|
|
});
|
|
}
|
|
getSectionIDs("armor", function(v) {grabIds(v, 'armor', 'hitsblocked', '', 'notcarried', 'nonflanking', idCallback);});
|
|
});
|
|
|
|
// Update Minus to Hit
|
|
on('sheet:opened change:repeating_armor remove:repeating_armor', function() {
|
|
let idCallback = function(attrs, idarray, repName, minusToHitName, countName, notCarriedName, nonFlankingName) {
|
|
let totalMinus = computeRepeatingTotal(attrs, idarray, repName, minusToHitName, countName, notCarriedName);
|
|
let totalMinusFlanking = computeRepeatingTotal(attrs, idarray, repName, minusToHitName, countName, notCarriedName, nonFlankingName);
|
|
setAttrs({
|
|
minustohit_max: totalMinus,
|
|
minustohit: totalMinusFlanking
|
|
});
|
|
}
|
|
getSectionIDs("armor", function(v) {grabIds(v, 'armor', 'minustohit', '', 'notcarried', 'nonflanking', idCallback);});
|
|
});
|
|
|
|
on('sheet:opened change:st change:hits change:fatigue', function() {
|
|
getAttrs(['st', 'hits', 'fatigue'], function(v) {
|
|
let hitpoints = v.st - v.hits - v.fatigue;
|
|
setAttrs({
|
|
health: hitpoints,
|
|
health_max: v.st
|
|
});
|
|
});
|
|
});
|
|
|
|
// Update weight carried and penalty, and MA
|
|
on('sheet:opened'
|
|
+ ' change:race '
|
|
+ ' change:ma_max change:ma_adj'
|
|
+ ' change:repeating_weapons remove:repeating_weapons'
|
|
+ ' change:repeating_armor remove:repeating_armor'
|
|
+ ' change:repeating_equipment remove:repeating_equipment'
|
|
+ ' change:dx_max change:dx_adj', function() {
|
|
let totalWeight = 0;
|
|
let totalCallbacks = 0; // how many times called back.
|
|
let dexPenalty = 0;
|
|
|
|
let computeAll = function() {
|
|
getAttrs(['st', 'race', 'ma_max', 'ma_adj', 'dx_max', 'dx_adj'], function(v) {
|
|
let strength = parseInt(v.st)||0;
|
|
if (v.race) {
|
|
let race = v.race.toLowerCase();
|
|
if (race.includes('dwarf') || race.includes('dwarven')) {
|
|
strength *= 2;
|
|
}
|
|
}
|
|
|
|
// Round to the nearest tenth.
|
|
totalWeight = Math.floor(totalWeight * 10 + .5) / 10;
|
|
|
|
let finalDex = (parseInt(v.dx_max)||0) + (parseInt(v.dx_adj)||0) - dexPenalty;
|
|
let penalty = '';
|
|
let finalMA = (parseInt(v.ma_max)||0) + (parseInt(v.ma_adj)||0);
|
|
if (totalWeight <= 2 * strength) {
|
|
penalty += 'No movement penalty';
|
|
} else if (totalWeight <= 3 * strength) {
|
|
penalty += 'Swim at DX -2';
|
|
} else if (totalWeight <= 4 * strength) {
|
|
penalty += 'Swim at DX -5';
|
|
} else if (totalWeight <= 6 * strength) {
|
|
penalty += "No swim, MA = 8";
|
|
finalMA = Math.min(8, finalMA);
|
|
} else if (totalWeight < 8 * strength) {
|
|
penalty += 'MA = 6, DX -1';
|
|
finalMA = Math.min(6, finalMA);
|
|
finalDex -= 1;
|
|
} else if (totalWeight < 10 * strength) {
|
|
penalty += 'MA = 4, DX -2';
|
|
finalMA = Math.min(4, finalMA);
|
|
finalDex -= 2;
|
|
} else {
|
|
penalty += "MA = 4, DX -2, can't move. See manual."
|
|
finalMA = Math.min(4, finalMA);
|
|
finalDex -= 2;
|
|
}
|
|
setAttrs({
|
|
weightcarried: totalWeight,
|
|
ma: finalMA,
|
|
mapenalty: penalty,
|
|
dx: finalDex
|
|
});
|
|
});
|
|
};
|
|
|
|
let dexCallback = function(attrs, idarray, repName, dexPenaltyName, countName, notCarriedName) {
|
|
dexPenalty = computeRepeatingTotal(attrs, idarray, repName, dexPenaltyName, countName, notCarriedName);
|
|
if (++totalCallbacks == 4) {
|
|
computeAll();
|
|
}
|
|
}
|
|
|
|
let idCallback = function(attrs, idarray, repName, weightName, countName, notCarriedName) {
|
|
totalWeight += computeRepeatingTotal(attrs, idarray, repName, weightName, countName, notCarriedName);
|
|
|
|
if (++totalCallbacks == 4) {
|
|
computeAll();
|
|
}
|
|
}
|
|
|
|
getSectionIDs("weapons", function(v) {grabIds(v, 'weapons', 'weight', 'count', 'notcarried', '', idCallback);});
|
|
getSectionIDs("armor", function(v) {grabIds(v, 'armor', 'weight', '', 'notcarried', '', idCallback);});
|
|
getSectionIDs("equipment", function(v) {grabIds(v, 'equipment', 'weight', 'count', 'notcarried', '', idCallback);});
|
|
|
|
getSectionIDs("armor", function(v) {grabIds(v, 'armor', 'dexpenalty', '', 'notcarried', '', dexCallback);});
|
|
});
|
|
|
|
// Attribute points and next attribute cost.
|
|
on('sheet:opened '
|
|
+ 'change:st_max '
|
|
+ 'change:dx_max '
|
|
+ 'change:iq_max ', function() {
|
|
getAttrs(['st_max', 'dx_max', 'iq_max'], function(v) {
|
|
let total = (parseInt(v.st_max)||0) + (parseInt(v.dx_max)||0) + (parseInt(v.iq_max)||0);
|
|
let nextCost = 0; // just a table based on total
|
|
let added = total + 1; // since next..
|
|
if (added <= 34)
|
|
nextCost = 100;
|
|
else if (added == 35)
|
|
nextCost = 200;
|
|
else if (added == 36)
|
|
nextCost = 300;
|
|
else if (added == 37)
|
|
nextCost = 600;
|
|
else
|
|
nextCost = 1000 << (added - 38);
|
|
|
|
setAttrs({
|
|
totalattrpoints: total,
|
|
nextattrcost: nextCost
|
|
});
|
|
});
|
|
});
|
|
|
|
// Update unspent iq points.
|
|
on('sheet:opened '
|
|
+ 'change:repeating_talents remove:repeating_talents '
|
|
+ 'change:repeating_spells remove:repeating_spells '
|
|
+ 'change:iq_max ', function() {
|
|
let totalSpent = 0;
|
|
let totalCallbacks = 0; // number of times called back.
|
|
|
|
let idCallback = function(attrs, idarray, repName, costName) {
|
|
totalSpent += computeRepeatingTotal(attrs, idarray, repName, costName);
|
|
if (++totalCallbacks == 2) {
|
|
getAttrs(['iq_max'], function(v) {
|
|
let unspent = (parseInt(v.iq_max)||0) - totalSpent;
|
|
setAttrs({
|
|
iqunspent: unspent
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
getSectionIDs("talents", function(v) {grabIds(v, 'talents', 'talentcost', '', '', '', idCallback);});
|
|
getSectionIDs("spells", function(v) {grabIds(v, 'spells', 'spellcost', '', '', '', idCallback);});
|
|
});
|
|
|
|
|
|
/* Funny stuff with journals */
|
|
const buttonlist = ["character","journal"];
|
|
buttonlist.forEach(button => {
|
|
on(`clicked:${button}`, function() {
|
|
setAttrs({
|
|
sheetTab: button
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
|
|
|
|
|
|
|