mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 19:52:29 +00:00
1437 lines
71 KiB
HTML
1437 lines
71 KiB
HTML
<input type="hidden" name="attr_sheet_type" class="sheet-type-select" value="pc">
|
|
<div class="container">
|
|
<div class="eem">
|
|
<input type="hidden" name="attr_sheet_type" class="sheet-type-select" value="pc">
|
|
<!--********* PC HEADER **********-->
|
|
<div class="header sheet-type-pc">
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content two-columns">
|
|
<label for="attr_character_name" data-i18n="name-and-pronouns">Name & Pronouns</label>
|
|
<input type="text" id="attr_character_name" name="attr_character_name" />
|
|
<label for="attr_class" data-i18n="class">Class</label>
|
|
<input type="text" id="attr_class" name="attr_class" list="class-list" data-i18n-dynamic />
|
|
<datalist id="class-list">
|
|
<option data-i18n="bard">Bard</option>
|
|
<option data-i18n="dungeoneer">Dungeoneer</option>
|
|
<option data-i18n="gnome">Gnome</option>
|
|
<option data-i18n="knight-errant">Knight-Errant</option>
|
|
<option data-i18n="loyal-chum">Loyal Chum</option>
|
|
<option data-i18n="rascal">Rascal</option>
|
|
</datalist>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content two-columns"><label for="attr_folk" data-i18n="folk">Folk</label><input type="text" id="attr_folk" name="attr_folk" /><label for="attr_homeland" data-i18n="homeland">Homeland</label><input type="text" id="attr_homeland" name="attr_homeland" /></div>
|
|
</div>
|
|
</div>
|
|
<div class="xp-rows">
|
|
<div class="xp-background"><img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/xp_bw.png" /></div>
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content two-columns"><label for="attr_lv" data-i18n="lv">LV</label><input type="number" name="attr_lv" /></div>
|
|
</div>
|
|
</div>
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content two-columns"><label for="attr_xp" data-i18n="xp">XP</label><input type="number" name="attr_xp" /></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="logo"><img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/logo-flat.png" alt="Land of Eem Logo" /></div>
|
|
<img class="top-avatar" name="attr_character_avatar" />
|
|
</div>
|
|
<!--********* NPC HEADER **********-->
|
|
<div class="header sheet-type-npc">
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content two-columns">
|
|
<label for="attr_character_name" data-i18n="name">Name</label>
|
|
<input type="text" id="attr_character_name" name="attr_character_name" />
|
|
<label for="attr_folk" data-i18n="folk">Folk</label>
|
|
<input type="text" id="attr_folk" name="attr_folk" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content two-columns">
|
|
<label for="attr_job" data-i18n="job">Job</label>
|
|
<input type="text" id="attr_job" name="attr_job" />
|
|
<label for="attr_detail" data-i18n="detail">Detail</label>
|
|
<input type="text" id="attr_detail" name="attr_detail" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="logo"><img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/logo-flat.png" alt="Land of Eem Logo" /></div>
|
|
<img class="top-avatar" name="attr_character_avatar" />
|
|
</div>
|
|
<!--********* ADVERSARY HEADER **********-->
|
|
<div class="header sheet-type-adversary">
|
|
<input class="adversary-name" name="attr_character_name" type="text" />
|
|
<input type="hidden" name="attr_adversary_type" class="adversary-type" value="creature">
|
|
<div class="adversary-type-symbol"></div>
|
|
<div class="logo"><img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/logo-flat.png" alt="Land of Eem Logo" /></div>
|
|
<img class="top-avatar" name="attr_character_avatar" />
|
|
</div>
|
|
<!--********* NAV BAR **********-->
|
|
<input type="hidden" class="sheet-tabstoggle" name="attr_sheetTab" value="attributes" />
|
|
<div class="nav-bar">
|
|
<input type="hidden" name="attr_sheet_type" class="sheet-type-select" value="pc">
|
|
<button type="action" name="act_npc" class="sheet-type-npc sheet-button-npc" data-i18n="npc">NPC</button>
|
|
<button type="action" name="act_adversary-preview" class="sheet-type-adversary sheet-button-preview" data-i18n="preview">Preview</button>
|
|
<button type="action" name="act_adversary-definition" class="sheet-type-adversary sheet-button-adversary" data-i18n="definition">Definition</button>
|
|
<button type="action" name="act_attributes" class="sheet-type-pc sheet-button0" data-i18n="attributes-and-abilities">Attributes</button>
|
|
<button type="action" name="act_inventory" class="sheet-type-pc sheet-button1" data-i18n="inventory-and-crafting">Inventory</button>
|
|
<button type="action" name="act_background" class="sheet-type-pc sheet-button2" data-i18n="personality-and-backstory">Background</button>
|
|
<button type="action" name="act_notes" class="sheet-type-pc sheet-button3" data-i18n="notes-and-portrait">Journal</button>
|
|
<button type="action" name="act_portrait" class="sheet-button4" data-i18n="portrait">Portrait</button>
|
|
<button type="action" name="act_settings" class="sheet-button5"><span data-i18n="settings">Settings</span><span class="gear-icon" style="font-family:Pictos;">x</span></button>
|
|
</div>
|
|
<!--********* SHEET - NPC **********-->
|
|
<div class="sheet-npc">
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content">
|
|
<label class="hint" data-i18n="traits">Traits</label>
|
|
<textarea rows="3" name="attr_traits"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content">
|
|
<label class="hint" data-i18n="motivation">Motivation</label>
|
|
<textarea rows="3" name="attr_motivation"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="grid-column-end: span 2;"></div>
|
|
<div class="note-box" style="grid-column-end: span 2;">
|
|
<div>
|
|
<h2 data-i18n="location" class="hint">Current Location</h2>
|
|
<textarea rows="2" name="attr_location"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="note-box flexible" style="grid-column-end: span 2;">
|
|
<div>
|
|
<h2 data-i18n="description" class="hint">Description</h2>
|
|
<textarea name="attr_description"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--********* SHEET - ADVERSARY PREVIEW **********-->
|
|
<div class="sheet-adversary-preview">
|
|
<input type="hidden" name="attr_adversary_type" class="adversary-type" value="creature" />
|
|
<div class="adversary-basic-info">
|
|
<div class="non-creeper">
|
|
<div class="two-columns">
|
|
<h2 data-i18n="preview-level">Level</h2>
|
|
<input type="hidden" name="attr_max_level" class="max-level" />
|
|
<input type="hidden" name="attr_min_level" class="min-level" />
|
|
<select name="attr_adversary_level">
|
|
<option value="1" class="lt-2 lt-3 lt-4 lt-5 lt-6 lt-7 lt-8 lt-9 lt-10">1</option>
|
|
<option value="2" class="lt-3 lt-4 lt-5 lt-6 lt-7 lt-8 lt-9 lt-10 gt-1">2</option>
|
|
<option value="3" class="lt-4 lt-5 lt-6 lt-7 lt-8 lt-9 lt-10 gt-1 gt-2">3</option>
|
|
<option value="4" class="lt-5 lt-6 lt-7 lt-8 lt-9 lt-10 gt-1 gt-2 gt-3">4</option>
|
|
<option value="5" class="lt-6 lt-7 lt-8 lt-9 lt-10 gt-1 gt-2 gt-3 gt-4">5</option>
|
|
<option value="6" class="lt-7 lt-8 lt-9 lt-10 gt-1 gt-2 gt-3 gt-4 gt-5">6</option>
|
|
<option value="7" class="lt-8 lt-9 lt-10 gt-1 gt-2 gt-3 gt-4 gt-5 gt-6">7</option>
|
|
<option value="8" class="lt-9 lt-10 gt-1 gt-2 gt-3 gt-4 gt-5 gt-6 gt-7">8</option>
|
|
<option value="9" class="lt-10 gt-1 gt-2 gt-3 gt-4 gt-5 gt-6 gt-7 gt-8">9</option>
|
|
<option value="10" class="gt-1 gt-2 gt-3 gt-4 gt-5 gt-6 gt-7 gt-8 gt-9">10</option>
|
|
</select>
|
|
<h2 data-i18n="preview-size">Size</h2>
|
|
<input type="hidden" name="attr_keyword_tiny" class="keyword-tiny" />
|
|
<input type="hidden" name="attr_keyword_small" class="keyword-small" />
|
|
<input type="hidden" name="attr_keyword_medium" class="keyword-medium" />
|
|
<input type="hidden" name="attr_keyword_folk" class="keyword-folk" />
|
|
<input type="hidden" name="attr_keyword_large" class="keyword-large" />
|
|
<input type="hidden" name="attr_keyword_huge" class="keyword-huge" />
|
|
<select name="attr_adversary_size">
|
|
<option value="tiny" class="tiny" data-i18n="tiny"></option>
|
|
<option value="small" class="small" data-i18n="small"></option>
|
|
<option value="medium" class="medium" data-i18n="medium"></option>
|
|
<option value="folk" class="folk" data-i18n="folk"></option>
|
|
<option value="large" class="large" data-i18n="large"></option>
|
|
<option value="huge" class="huge" data-i18n="huge"></option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="non-creeper">
|
|
<div class="two-columns right-border">
|
|
<h2 data-i18n="preview-class">Class</h2>
|
|
<input type="hidden" name="attr_keyword_goon" class="keyword-goon" />
|
|
<input type="hidden" name="attr_keyword_bruiser" class="keyword-bruiser" />
|
|
<input type="hidden" name="attr_keyword_champion" class="keyword-champion" />
|
|
<select name="attr_adversary_class">
|
|
<option value="goon" class="goon" data-i18n="goon_short"></option>
|
|
<option value="bruiser" class="bruiser" data-i18n="bruiser_short"></option>
|
|
<option value="champion" class="champion" data-i18n="champion_short"></option>
|
|
</select>
|
|
<h2 style="grid-column-end: span 2;">(<span name="attr_abbreviation">L1-G</span>)</h2>
|
|
</div>
|
|
</div>
|
|
<div class="non-creeper">
|
|
<h2 data-i18n="adversary-levels">Levels</h2>
|
|
<span><span name="attr_min_level">1</span>-<span name="attr_max_level">10</span></span>
|
|
</div>
|
|
<div class="non-creeper">
|
|
<h2 data-i18n="adversary-classes">Classes</h2>
|
|
<span name="attr_classes">G</span>
|
|
</div>
|
|
<div class="non-creeper">
|
|
<h2 data-i18n="adversary-actions">Actions</h2>
|
|
<span name="attr_adversary_actions">1</span>
|
|
</div>
|
|
<div class="keywords"><span name="attr_keywords">-</span></div>
|
|
</div>
|
|
<div class="stat-box stat-courage">
|
|
<h2 data-i18n="courage">Courage</h2>
|
|
<input type="number" name="attr_courage_max" class="big" />
|
|
<input type="number" name="attr_courage" class="small" />
|
|
<label data-i18n="current" class="small">Current</label>
|
|
</div>
|
|
<input type="hidden" name="attr_adversary_disadvantage" class="adversary-disadvantage" value="false" />
|
|
<div class="stat-box stat-wrangle">
|
|
<h2><span data-i18n="wrangle">Wrangle</span> <span class="disadvantage-note" data-i18n="disadvantage_short">(D)</span></h2>
|
|
<input type="number" name="attr_wrangle_parley_max" class="big" value="1" min="0" max="2" />
|
|
<input type="number" name="attr_wrangle_parley" class="small" value="1" min="0" max="2" />
|
|
<label data-i18n="current" class="small">Current</label>
|
|
</div>
|
|
<div class="stat-box stat-parley">
|
|
<h2><span data-i18n="parley">Parley</span> <span class="disadvantage-note" data-i18n="disadvantage_short">(D)</span></h2>
|
|
<input type="number" name="attr_wrangle_parley_max" class="big" value="1" min="0" max="2" />
|
|
<input type="number" name="attr_wrangle_parley" class="small" value="1" min="0" max="2" />
|
|
<label data-i18n="current" class="small">Current</label>
|
|
</div>
|
|
<div class="stat-box stat-attack">
|
|
<button type="action" name="act_attack">
|
|
<h2 data-i18n="attack">Attack</h2>
|
|
</button>
|
|
<input type="text" name="attr_attack" class="big" />
|
|
<input type="text" name="attr_dread" class="small" />
|
|
<label data-i18n="dread" for="attr_dread" class="small">Dread</label>
|
|
<div style="display:block; width: 0; height: 0; overflow: visible;"><img style="opacity: 0.2; min-width: 97px ; min-height: 97px;" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/stats_2.png" /></div>
|
|
</div>
|
|
<div class="stat-box stat-defense">
|
|
<h2 data-i18n="defense">Defense</h2>
|
|
<input type="text" name="attr_defense" class="big" />
|
|
<input type="number" name="attr_block" class="small" />
|
|
<label data-i18n="block" for="attr_block" class="small">Block</label>
|
|
</div>
|
|
<div class="stat-box stat-mettle">
|
|
<button type="action" name="act_mettle">
|
|
<h2 data-i18n="mettle">Mettle</h2>
|
|
</button>
|
|
<input type="text" name="attr_mettle" class="big" value="-2" />
|
|
</div>
|
|
<div style="grid-row-start: 2; grid-column-start: 3; grid-row-end: span 4" class="note-box">
|
|
<div class="repeating_skills_container">
|
|
<h2 data-i18n="abilities">Abilities</h2>
|
|
<fieldset class="repeating_skills">
|
|
<input type="hidden" class="skilledit" name="attr_skilledit" value="0" />
|
|
<input type="checkbox" name="attr_skilledit" />
|
|
<input type="text" name="attr_skillname" value="">
|
|
<button type="action" name="act_share" class="btn-share">
|
|
<div></div>
|
|
</button>
|
|
<div class="sheet-auto-expand">
|
|
<span name="attr_skilldescription"></span>
|
|
<textarea class="skill-description" name="attr_skilldescription" rows="4"></textarea>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--********* SHEET - ADVERSARY DEFINITION **********-->
|
|
<div class="sheet-adversary-definition">
|
|
<input type="hidden" name="attr_adversary_type" class="adversary-type" value="creature">
|
|
<h2 style="grid-column-end: span 6;" class="hint" data-i18n="adversary-type">Type</h2>
|
|
<h2 style="grid-column-end: span 6;" class="hint non-creeper" data-i18n="adversary-classes">Classes</h2>
|
|
<input type="radio" name="attr_adversary_type" value="creature" checked /><span data-i18n="creature">Creature</span>
|
|
<input type="radio" name="attr_adversary_type" value="critter" /><span data-i18n="critter">Critter</span>
|
|
<input type="radio" name="attr_adversary_type" value="creeper" /><span data-i18n="creeper">Creeper</span>
|
|
<input class="non-creeper" type="checkbox" name="attr_keyword_goon" checked /><span class="non-creeper" data-i18n="goon">Goon</span>
|
|
<input class="non-creeper" type="checkbox" name="attr_keyword_bruiser" /><span class="non-creeper" data-i18n="bruiser">Bruiser</span>
|
|
<input class="non-creeper" type="checkbox" name="attr_keyword_champion" /><span class="non-creeper" data-i18n="champion">Champion</span>
|
|
<div class="separator non-creeper"></div>
|
|
<h2 style="grid-column-end: span 6;" class="hint non-creeper" data-i18n="adversary-levels">Levels</h2>
|
|
<h2 style="grid-column-end: span 6;" class="hint non-creeper" data-i18n="adversary-actions">Actions</h2>
|
|
<div class="levels non-creeper">
|
|
<span data-i18n="min-level">Min Level:</span><input type="number" max="10" min="1" id="attr_min_level" name="attr_min_level" value="1" />
|
|
<span data-i18n="max-level">Max Level:</span><input type="number" max="10" min="1" id="attr_max_level" name="attr_max_level" value="10" />
|
|
</div>
|
|
<div class="levels non-creeper">
|
|
<span class="non-creeper" data-i18n="number">Number:</span>
|
|
<input class="non-creeper" type="number" id="attr_adversary_actions" name="attr_adversary_actions" max="10" min="1" value="1" />
|
|
</div>
|
|
<div class="separator non-creeper"></div>
|
|
<h2 style="grid-column-end: span 6;" class="hint non-creeper" data-i18n="wrangle-parley-disadvantage">Wrangle/Parley</h2>
|
|
<h2 style="grid-column-end: span 6;" class="hint creeper-swap" data-i18n="number-appearing">Number Appearing</h2>
|
|
<input class="non-creeper" type="radio" name="attr_adversary_disadvantage" value="false" checked /><span class="non-creeper" data-i18n="check_standard">Standard</span>
|
|
<input class="non-creeper" type="radio" name="attr_adversary_disadvantage" value="true" /><span class="non-creeper" data-i18n="check_disadvantage">Disadvantage</span>
|
|
<div class="non-creeper" style="grid-column-end: span 2;"></div>
|
|
<input type="checkbox" name="attr_keyword_solo" /><span data-i18n="solo">Solo</span>
|
|
<input type="checkbox" name="attr_keyword_group" /><span data-i18n="group">Group</span>
|
|
<input type="checkbox" name="attr_keyword_horde" /><span data-i18n="horde">Horde</span>
|
|
<div class="separator"></div>
|
|
<h2 style="grid-column-end: span 12;" class="hint" data-i18n="sizes">Sizes</h2>
|
|
<input type="checkbox" name="attr_keyword_tiny" /><span data-i18n="tiny">Tiny</span>
|
|
<input type="checkbox" name="attr_keyword_small" /><span data-i18n="small">Small</span>
|
|
<input type="checkbox" name="attr_keyword_medium" /><span data-i18n="medium">Medium</span>
|
|
<input type="checkbox" name="attr_keyword_folk" /><span data-i18n="folk">Folk</span>
|
|
<input type="checkbox" name="attr_keyword_large" /><span data-i18n="large">Large</span>
|
|
<input type="checkbox" name="attr_keyword_huge" /><span data-i18n="huge">Huge</span>
|
|
<div class="separator"></div>
|
|
<h2 style="grid-column-end: span 12;" class="hint" data-i18n="special">Special</h2>
|
|
<input type="checkbox" name="attr_keyword_minions" /><span data-i18n="minions">Minions</span>
|
|
<input type="checkbox" name="attr_keyword_aquatic" /><span data-i18n="aquatic">Aquatic</span>
|
|
<input type="checkbox" name="attr_keyword_flying" /><span data-i18n="flying">Flying</span>
|
|
<input type="checkbox" name="attr_keyword_fast" /><span data-i18n="fast">Fast</span>
|
|
<input type="checkbox" name="attr_keyword_undead" /><span data-i18n="undead">Undead</span>
|
|
<input type="checkbox" name="attr_keyword_dwimmercrafty" /><span data-i18n="dwimmercrafty">Dwimmercrafty</span>
|
|
</div>
|
|
<!--********* SHEET - ATTRIBUTES & ABILITIES **********-->
|
|
<div class="sheet-attributes">
|
|
<div class="attribute-box">
|
|
<div>
|
|
<button type="action" name="act_vim">
|
|
<h2 data-i18n="vim">Vim</h2>
|
|
</button>
|
|
<div class="dice-field"><input type="text" name="attr_vim" /></div>
|
|
</div>
|
|
<ul>
|
|
<li><input type="text" name="attr_charm" /></li>
|
|
<li><input type="text" name="attr_inspire" /></li>
|
|
<li><input type="text" name="attr_mettle" /></li>
|
|
<li><input type="text" name="attr_perception" /></li>
|
|
</ul>
|
|
<ul>
|
|
<li><button type="action" name="act_charm" data-i18n="charm">Charm</button></li>
|
|
<li><button type="action" name="act_inspire" data-i18n="inspire">Inspire</button></li>
|
|
<li><button type="action" name="act_mettle" data-i18n="mettle">Mettle</button></li>
|
|
<li><button type="action" name="act_perception" data-i18n="perception">Perception</button></li>
|
|
</ul>
|
|
</div>
|
|
<div class="stat-box stat-courage">
|
|
<h2 data-i18n="courage">Courage</h2>
|
|
<input type="number" name="attr_courage_max" class="big" />
|
|
<input type="number" name="attr_courage" class="small" />
|
|
<label data-i18n="current" class="small">Current</label>
|
|
</div>
|
|
<div class="corner-box-wrapper side" style="grid-row-end: span 3;">
|
|
<label class="corner-box-header" data-i18n="proficiencies">Proficiencies</label>
|
|
<div class="corner-box">
|
|
<div class=content><textarea name="attr_proficiencies" rows="3"></textarea></div>
|
|
</div>
|
|
<label class="corner-box-header" data-i18n="conditions">Conditions</label>
|
|
<div class="corner-box">
|
|
<div class=content><textarea name="attr_conditions" rows="3"></textarea></div>
|
|
</div>
|
|
</div>
|
|
<div class="corner-box-wrapper side" style="grid-row-end: span 3;">
|
|
<label class="corner-box-header" data-i18n="deficiencies">Deficiencies</label>
|
|
<div class="corner-box">
|
|
<div class=content><textarea name="attr_deficiencies" rows="3"></textarea></div>
|
|
</div>
|
|
<label class="corner-box-header" data-i18n="heroic-titles">Heroic Titles</label>
|
|
<div class="corner-box">
|
|
<div class=content><textarea name="attr_heroic_titles" rows="3"></textarea></div>
|
|
</div>
|
|
</div>
|
|
<div class="separator"></div>
|
|
<div></div>
|
|
<div class="attribute-box">
|
|
<div>
|
|
<button type="action" name="act_vigor">
|
|
<h2 data-i18n="vigor">Vigor</h2>
|
|
</button>
|
|
<div class="dice-field"><input type="text" name="attr_vigor" /></div>
|
|
</div>
|
|
<ul>
|
|
<li><input type="text" name="attr_athletics" /></li>
|
|
<li><input type="text" name="attr_intimidate" /></li>
|
|
<li><input type="text" name="attr_might" /></li>
|
|
<li><input type="text" name="attr_vitality" /></li>
|
|
</ul>
|
|
<ul>
|
|
<li><button type="action" name="act_athletics" data-i18n="athletics">Athletics</button></li>
|
|
<li><button type="action" name="act_intimidate" data-i18n="intimidate">Intimidate</button></li>
|
|
<li><button type="action" name="act_might" data-i18n="might">Might</button></li>
|
|
<li><button type="action" name="act_vitality" data-i18n="vitality">Vitality</button></li>
|
|
</ul>
|
|
</div>
|
|
<div class="stat-box stat-attack">
|
|
<button type="action" name="act_attack">
|
|
<h2 data-i18n="attack">Attack</h2>
|
|
</button>
|
|
<input type="text" name="attr_attack" class="big" />
|
|
<input type="text" name="attr_dread" class="small" />
|
|
<label data-i18n="dread" for="attr_dread" class="small">Dread</label>
|
|
<div style="display:block; width: 0; height: 0; overflow: visible;"><img style="opacity: 0.2; min-width: 97px ; min-height: 97px;" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/stats_2.png" /></div>
|
|
</div>
|
|
<div class="separator"></div>
|
|
<div></div>
|
|
<div style="grid-column-end: span 2; grid-row-end: span 5;" class="note-box">
|
|
<div class="repeating_skills_container">
|
|
<h2 data-i18n="abilities">Abilities</h2>
|
|
<fieldset class="repeating_skills">
|
|
<input type="hidden" class="skilledit" name="attr_skilledit" value="0" />
|
|
<input type="checkbox" name="attr_skilledit" />
|
|
<input type="text" name="attr_skillname" value="">
|
|
<button type="action" name="act_share" class="btn-share">
|
|
<div></div>
|
|
</button>
|
|
<div class="sheet-auto-expand">
|
|
<span name="attr_skilldescription"></span>
|
|
<textarea class="skill-description" name="attr_skilldescription" rows="4"></textarea>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
<div class="attribute-box">
|
|
<div>
|
|
<button type="action" name="act_knack">
|
|
<h2 data-i18n="knack">Knack</h2>
|
|
</button>
|
|
<div class="dice-field"><input type="text" name="attr_knack" /></div>
|
|
</div>
|
|
<ul>
|
|
<li><input type="text" name="attr_nimbleness" /></li>
|
|
<li><input type="text" name="attr_search" /></li>
|
|
<li><input type="text" name="attr_sneak" /></li>
|
|
<li><input type="text" name="attr_trickery" /></li>
|
|
</ul>
|
|
<ul>
|
|
<li><button type="action" name="act_nimbleness" data-i18n="nimbleness">Nimbleness</button></li>
|
|
<li><button type="action" name="act_search" data-i18n="search">Search</button></li>
|
|
<li><button type="action" name="act_sneak" data-i18n="sneak">Sneak</button></li>
|
|
<li><button type="action" name="act_trickery" data-i18n="trickery">Trickery</button></li>
|
|
</ul>
|
|
</div>
|
|
<div class="stat-box stat-defense">
|
|
<h2 data-i18n="defense">Defense</h2>
|
|
<input type="text" name="attr_defense" class="big" />
|
|
<input type="number" name="attr_block" class="small" />
|
|
<label data-i18n="block" for="attr_block" class="small">Block</label>
|
|
</div>
|
|
<div class="separator"></div>
|
|
<div></div>
|
|
<div class="attribute-box">
|
|
<div>
|
|
<button type="action" name="act_knowhow">
|
|
<h2 data-i18n="knowhow">Knowhow</h2>
|
|
</button>
|
|
<div class="dice-field"><input type="text" name="attr_knowhow" /></div>
|
|
</div>
|
|
<ul>
|
|
<li><input type="text" name="attr_lore" /></li>
|
|
<li><input type="text" name="attr_realms" /></li>
|
|
<li><input type="text" name="attr_tinker" /></li>
|
|
<li><input type="text" name="attr_wilderness" /></li>
|
|
</ul>
|
|
<ul>
|
|
<li><button type="action" name="act_lore" data-i18n="lore">Lore</button></li>
|
|
<li><button type="action" name="act_realms" data-i18n="realms">Realms</button></li>
|
|
<li><button type="action" name="act_tinker" data-i18n="tinker">Tinker</button></li>
|
|
<li><button type="action" name="act_wilderness" data-i18n="wilderness">Wilderness</button></li>
|
|
</ul>
|
|
</div>
|
|
<div class="stat-box stat-quest">
|
|
<h2 data-i18n="quest">Quest Pts.</h2>
|
|
<input type="number" name="attr_quest_pts" class="big" />
|
|
</div>
|
|
</div>
|
|
<!--********* SHEET - INVENTORY AND CRAFTING **********-->
|
|
<div class="sheet-inventory">
|
|
<div class="stripe upper">
|
|
<section>
|
|
<h2 class="inventory" data-i18n="inventory">Inventory</h2>
|
|
<h2 data-i18n="slots">Slots:</h2>
|
|
<input class="small" type="number" name="attr_inventory_slots" />
|
|
<h2 class="small">/</h2><input class="small" type="number" name="attr_inventory_slots_max" />
|
|
<div class="checkbox-group">
|
|
<input type="checkbox" name="attr_overburdened" />
|
|
<label data-i18n="overburdened" for="attr_overburdened">Overburdened</label>
|
|
<input type="checkbox" name="attr_tired" />
|
|
<label data-i18n="tired" for="attr_tired">Tired</label>
|
|
</div>
|
|
</section>
|
|
<section>
|
|
<div class="treasure"><input type="text" name="attr_treasure_hunting" /></div>
|
|
<button class="treasure" type="action" name="act_treasure_hunting">
|
|
<h2 class="treasure" data-i18n="treasure-hunting">Treasure Hunting:</h2>
|
|
</button>
|
|
</section>
|
|
</div>
|
|
<div class="inventory-upper">
|
|
<div class="note-box">
|
|
<div>
|
|
<h2 class="worn" data-i18n="worn">Worn</h2>
|
|
<textarea rows="6" name="attr_worn"></textarea>
|
|
<div class="watermark worn"><img class="worn" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/worn.png" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="vertical-line"></div>
|
|
<div class="note-box">
|
|
<div>
|
|
<h2 class="carried" data-i18n="carried">Carried</h2>
|
|
<textarea rows="6" name="attr_carried"></textarea>
|
|
<div class="watermark carried"><img class="carried" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/carried.png" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="stripe lower">
|
|
<section>
|
|
<img class="crafting" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/crafting.png" />
|
|
<h2 data-i18n="crafting">Crafting</h2>
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content two-columns"><label data-i18n="num-materials">No. of
|
|
materials</label><input type="number" name="attr_material_number" /></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<section>
|
|
<img class="coins" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/coins.png" />
|
|
<h2 data-i18n="coins">Coins</h2>
|
|
<section class="hint" data-i18n="tradeup">3:1 tradeup</section>
|
|
</section>
|
|
<section>
|
|
<img class="rations" src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/rations.png" />
|
|
<h2 data-i18n="rations">Rations</h2>
|
|
</section>
|
|
</div>
|
|
<div class="inventory-lower">
|
|
<div class="note-box">
|
|
<div>
|
|
<div class="crafting fake-textarea">
|
|
<textarea rows="2" name="attr_material_elemental"></textarea>
|
|
<textarea rows="2" name="attr_material_fish"></textarea>
|
|
<textarea rows="2" name="attr_material_beast"></textarea>
|
|
<textarea rows="2" name="attr_material_herb"></textarea>
|
|
</div>
|
|
<div class="crafting-icons">
|
|
<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/material_elemental.png" />
|
|
<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/material_fish.png" />
|
|
<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/material_beast.png" />
|
|
<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/LandOfEem/images/material_herb.png" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="vertical-line"></div>
|
|
<div class="note-box">
|
|
<div>
|
|
<div class="coins fake-textarea">
|
|
<section class="small" data-i18n="copper">Copper</section>
|
|
<section class="hint">d6</section><input type="number" name="attr_coins_copper" />
|
|
<section class="small" data-i18n="silver">Silver</section>
|
|
<section class="hint">d8</section><input type="number" name="attr_coins_silver" />
|
|
<section class="small" data-i18n="gold">Gold</section>
|
|
<section class="hint">d10</section><input type="number" name="attr_coins_gold" />
|
|
<section class="small" data-i18n="ancient">Ancient</section>
|
|
<section class="hint">d12</section><input type="number" name="attr_coins_ancient" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="vertical-line"></div>
|
|
<div class="note-box">
|
|
<div>
|
|
<div class="rations fake-textarea">
|
|
<section class="hint">d6</section><input type="number" name="attr_rations_d6" />
|
|
<section class="hint">d8</section><input type="number" name="attr_rations_d8" />
|
|
<section class="hint">d10</section><input type="number" name="attr_rations_d10" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--********* SHEET - BACKGROUND AND NOTES **********-->
|
|
<div class="sheet-background">
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content">
|
|
<label class="hint" data-i18n="ideals">Ideals</label>
|
|
<section class="sparkle"></section>
|
|
<section data-i18n="ideals-hint" class="hint">Gain individual XP if you demonstrate your ideals</section>
|
|
<textarea rows="3" name="attr_ideals"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="corner-box-wrapper">
|
|
<div class="corner-box">
|
|
<div class="content">
|
|
<label class="hint" data-i18n="flaws">Flaws</label>
|
|
<section class="sparkle"></section>
|
|
<section data-i18n="flaws-hint" class="hint">Gain individual XP if you demonstrate your flaws</section>
|
|
<textarea rows="3" name="attr_flaws"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="grid-column-end: span 2;"></div>
|
|
<div class="note-box">
|
|
<div>
|
|
<h2 data-i18n="personal-quest" class="hint">Personal Quest</h2>
|
|
<section class="sparkle"></section>
|
|
<section data-i18n="personal-quest-hint" class="hint">Gain party XP if you pursue your personal quest</section>
|
|
<textarea rows="5" name="attr_personal_quest"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="note-box" style="grid-row-end: span 3;">
|
|
<div>
|
|
<h2 data-i18n="backstory" class="hint">Backstory</h2>
|
|
<section class="sparkle"></section>
|
|
<section data-i18n="backstory-hint" class="hint">Gain individual XP if you tell a story about your past</section>
|
|
<textarea rows="13" name="attr_backstory"></textarea>
|
|
</div>
|
|
</div>
|
|
<div></div>
|
|
<div class="note-box">
|
|
<div>
|
|
<h2 data-i18n="relationships" class="hint">Relationships</h2>
|
|
<section class="sparkle"></section>
|
|
<section data-i18n="relationships-hint" class="hint">Gain party XP if you build on a relationship</section>
|
|
<textarea rows="6" name="attr_relationships"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--********* NOTES **********-->
|
|
<div class="sheet-notes">
|
|
<div class="note-box">
|
|
<div>
|
|
<h2 data-i18n="other-notes" class="hint">Notes</h2>
|
|
<textarea rows="18" name="attr_other_notes"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--********* PORTRAIT **********-->
|
|
<div class="sheet-portrait">
|
|
<div></div>
|
|
<img name="attr_character_avatar">
|
|
<div></div>
|
|
</div>
|
|
<!--********* SETTINGS **********-->
|
|
<div class="sheet-settings">
|
|
<div class="settings-dropdown">
|
|
<label for="attr_sheet_type" data-i18n="sheet-type">Sheet Type:</label>
|
|
<select name="attr_sheet_type" id="attr_sheet_type" value="pc">
|
|
<option type="action" value="pc" data-i18n="pc">PC</option>
|
|
<option type="action" value="npc" data-i18n="npc">NPC</option>
|
|
<option type="action" value="adversary" data-i18n="adversary">Adversary</option>
|
|
</select>
|
|
<section class="sparkle"> </section> <span class="hint" data-i18n="hint-sheet-type">Changes the sheet type.
|
|
</span>
|
|
</div>
|
|
<div class="separator"></div>
|
|
<div class="settings-radio">
|
|
<label data-i18n="clamp-modifiers">Clamp Attribute/Skills:</label>
|
|
<input type="radio" name="attr_clamp_modifiers" value="true" checked /><span data-i18n="on">On</span>
|
|
<input type="radio" name="attr_clamp_modifiers" value="false" /><span data-i18n="off">Off</span>
|
|
<section class="sparkle"> </section> <span class="hint" data-i18n="hint-clamp-modifiers">On: keeps Attribute and Skill values between -3 and +3.</span>
|
|
</div>
|
|
<div class="separator"></div>
|
|
<div class="settings-radio">
|
|
<label data-i18n="clamp-roll-modifier">Clamp Final Roll Modifier:</label>
|
|
<input type="radio" name="attr_clamp_roll_modifier" value="true" checked /><span data-i18n="on">On</span>
|
|
<input type="radio" name="attr_clamp_roll_modifier" value="false" /><span data-i18n="off">Off</span>
|
|
<section class="sparkle"> </section> <span class="hint" data-i18n="hint-clamp-roll-modifier">On: keeps the final sum of roll modifiers between -3 and +3.</span>
|
|
</div>
|
|
<div class="separator"></div>
|
|
</div>
|
|
</div>
|
|
<!--********* FOOTER **********-->
|
|
<div class="footer">
|
|
<span>Character Sheet (v.<span name="attr_version"></span>) by </span><a href="https://ko-fi.com/blazesanecki/">Blaze Sanecki</a>, made with a blessing from <span>by Ben Costa & James Parks</span> - creators of the <a href="https://landofeem.com/"><b>Land of Eem</b></a>.
|
|
</div>
|
|
</div>
|
|
<!--********* HIDDEN ATTRIBUTES **********-->
|
|
<input type="hidden" name="attr_version" value="2.1.0" />
|
|
<!--********* ROLL TEMPLATES **********-->
|
|
<rolltemplate class="sheet-rolltemplate-attack">
|
|
<div class="template-container {{sheet_type}}">
|
|
<h2><span data-i18n="{{name}}">Name</span><i> ({{character}})</i></h2>
|
|
<div class="results attack">
|
|
<ul>
|
|
<li>
|
|
{{#rollGreater() computed::roll 11}}
|
|
<span class="critical-hit">
|
|
{{/rollGreater() computed::roll 11}}
|
|
<h3><span data-i18n="attack">Attack</span>: {{computed::roll}} <span data-i18n="damage">Damage</span>: {{computed::damage}}</h3>
|
|
{{#rollGreater() computed::roll 11}}
|
|
</span>
|
|
{{/rollGreater() computed::roll 11}}
|
|
</li>
|
|
{{#rollTotal() computed::roll roll}}
|
|
<div style="display:none">
|
|
{{/rollTotal() computed::roll roll}}
|
|
<li>
|
|
<h5 data-i18n="modifier-clamped">Modifier clamped to [-3..3] range.</h5>
|
|
</li>
|
|
{{#rollTotal() computed::roll roll}}
|
|
</div>
|
|
{{/rollTotal() computed::roll roll}}
|
|
{{#rollLess() computed::roll 3}}
|
|
<li>
|
|
<h4><b>[1-2]: </b></span> <span data-i18n="critical-miss">Critical Miss.</span></h4>
|
|
</li>
|
|
<li>
|
|
<h4><b><span data-i18n="fumble-example">Fumble Example</span>: </b><span data-i18n="fumble_example_{{random_index}}">EXAMPLE</span></h4>
|
|
</li>
|
|
{{/rollLess() computed::roll 3}}
|
|
{{#rollBetween() computed::roll 3 5}}
|
|
<li>
|
|
<h4><b>[3-5]: </b></span><span data-i18n="miss-with-a-plus">Miss with a Plus.</span></h4>
|
|
</li>
|
|
<li>
|
|
<h4><b><span data-i18n="plus-example">Plus Example</span>: </b><span data-i18n="plus_example_{{random_index}}">EXAMPLE</span></h4>
|
|
</li>
|
|
{{/rollBetween() computed::roll 3 5}}
|
|
{{#rollBetween() computed::roll 6 8}}
|
|
<li>
|
|
<h4><b>[6-8] (<span data-i18n="melee">Melee</span>): </b></span><span data-i18n="hit-with-a-counterattack">Hit with a Counterattack.</span></h4>
|
|
<h4><b>[6-8] (<span data-i18n="ranged">Ranged</span>): </b></span><span data-i18n="grazing-shot">Grazing Shot.</span></h4>
|
|
</li>
|
|
{{/rollBetween() computed::roll 6 8}}
|
|
{{#rollBetween() computed::roll 9 11}}
|
|
<li>
|
|
<h4><b>[9-11]: </b></span><span data-i18n="hit">Hit.</span></h4>
|
|
</li>
|
|
{{/rollBetween() computed::roll 9 11}}
|
|
{{#rollGreater() computed::roll 11}}
|
|
<li>
|
|
<h4><b>[12+]: </b></span><span data-i18n="critical-hit">Critical Hit.</span></h4>
|
|
</li>
|
|
{{/rollGreater() computed::roll 11}}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<rolltemplate class="sheet-rolltemplate-check">
|
|
<div class="template-container {{sheet_type}}">
|
|
<h2><span data-i18n="{{name}}">Name</span><i> ({{character}})</i></h2>
|
|
<div class="results check">
|
|
<ul>
|
|
<li>
|
|
{{#rollGreater() computed::roll 11}}
|
|
<span class="critical-hit">
|
|
{{/rollGreater() computed::roll 11}}
|
|
<h3><span data-i18n="result">Result</span>: {{computed::roll}}</h3>
|
|
{{#rollGreater() computed::roll 11}}
|
|
</span>
|
|
{{/rollGreater() computed::roll 11}}
|
|
</li>
|
|
{{#rollTotal() computed::roll roll}}
|
|
<div style="display:none">
|
|
{{/rollTotal() computed::roll roll}}
|
|
<li>
|
|
<h5><span data-i18n="modifier-clamped">Modifier clamped to [-3..3] range.</span></h5>
|
|
</li>
|
|
{{#rollTotal() computed::roll roll}}
|
|
</div>
|
|
{{/rollTotal() computed::roll roll}}
|
|
{{#rollLess() computed::roll 3}}
|
|
<li>
|
|
<h4><b>[1-2]: </b></span> <span data-i18n="complete-failure">Complete Failure.</span></h4>
|
|
</li>
|
|
{{/rollLess() computed::roll 3}}
|
|
{{#rollBetween() computed::roll 3 5}}
|
|
<li>
|
|
<h4><b>[3-5]: </b></span><span data-i18n="failure-with-a-plus">Failure with a Plus.</span></h4>
|
|
</li>
|
|
{{/rollBetween() computed::roll 3 5}}
|
|
{{#rollBetween() computed::roll 6 8}}
|
|
<li>
|
|
<h4><b>[6-8]: </b></span><span data-i18n="success-with-a-twist">Success with a Twist.</span></h4>
|
|
</li>
|
|
{{/rollBetween() computed::roll 6 8}}
|
|
{{#rollBetween() computed::roll 9 11}}
|
|
<li>
|
|
<h4><b>[9-11]: </b></span><span data-i18n="success">Success.</span></h4>
|
|
</li>
|
|
{{/rollBetween() computed::roll 9 11}}
|
|
{{#rollGreater() computed::roll 11}}
|
|
<li>
|
|
<h4><b>[12+]: </b></span><span data-i18n="complete-success">Complete Success.</span></h4>
|
|
</li>
|
|
{{/rollGreater() computed::roll 11}}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<rolltemplate class="sheet-rolltemplate-description">
|
|
<div class="template-container {{sheet_type}}">
|
|
<h2><span>{{name}}</span><i> ({{character}})</i></h2>
|
|
<div class="results check">
|
|
<ul>
|
|
<li>
|
|
<h4>{{description}}</h4>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</rolltemplate>
|
|
|
|
<rolltemplate class="sheet-rolltemplate-hidden">
|
|
<div class="sheet-template-container" style="display: none;"></div>
|
|
</rolltemplate>
|
|
|
|
<!--********* SCRIPTS **********-->
|
|
<script type="text/worker">
|
|
//********* TAB BUTTONS **********/
|
|
const buttonlist = ["npc","adversary-definition","adversary-preview", "attributes", "inventory", "background", "notes", "portrait", "settings"]
|
|
buttonlist.forEach(button => {
|
|
on(`clicked:${button}`, function() {
|
|
setAttrs({
|
|
sheetTab: button
|
|
})
|
|
})
|
|
})
|
|
|
|
//********* SKILL SHARE **********/
|
|
on("clicked:repeating_skills:share", function(eventinfo) {
|
|
const rowId = eventinfo.sourceAttribute.split("_")[2]
|
|
getAttrs([`repeating_skills_${rowId}_skillname`, `repeating_skills_${rowId}_skilldescription`, "character_name"], async (values) => {
|
|
const skillName = values[`repeating_skills_${rowId}_skillname`] || "Unnamed Skill"
|
|
const skillDescription = values[`repeating_skills_${rowId}_skilldescription`] || "No description."
|
|
const characterName = values["character_name"] || "Unknown Character"
|
|
|
|
const roll = await startRoll(`&{template:description} {{sheet_type=@{sheet_type}}} {{name=${skillName}}} {{character=${characterName}}} {{description=${skillDescription}}} {{ignore=[[0]]}}`);
|
|
finishRoll(roll.rollId);
|
|
})
|
|
})
|
|
|
|
//********* DEFINE SIGNED AND UNSIGNED ATTRIBUTES **********/
|
|
const signed = ["attack", "defense", "vim", "charm", "inspire", "mettle", "perception", "vigor", "athletics", "intimidate", "might", "vitality", "knack", "nimbleness", "search", "sneak", "trickery", "knowhow", "lore", "realms", "tinker", "wilderness", "treasure_hunting"];
|
|
const unsigned = ["courage", "courage_max", "quest_pts", "block", "inventory_slots", "inventory_slots_max", "material_number", "coins_copper", "coins_silver", "coins_gold", "coins_ancient", "rations_d6", "rations_d8", "rations_d10", "lv", "xp"];
|
|
const regular = ["vim", "charm", "inspire", "mettle", "perception", "vigor", "athletics", "intimidate", "might", "vitality", "knack", "nimbleness", "search", "sneak", "trickery", "knowhow", "lore", "realms", "tinker", "wilderness", "treasure_hunting"];
|
|
const keywords = ["keyword_tiny", "keyword_small", "keyword_medium", "keyword_large", "keyword_huge", "keyword_folk", "keyword_solo", "keyword_group", "keyword_horde", "keyword_minions", "keyword_aquatic", "keyword_flying", "keyword_fast", "keyword_undead", "keyword_dwimmercrafty"];
|
|
const keywordsSize = ["tiny", "small", "medium", "large", "huge", "folk"]
|
|
const keywordClasses = ["goon", "bruiser", "champion"]
|
|
const keywordNumbers = ["solo", "group", "horde"]
|
|
const keywordSpecial = ["minions", "aquatic", "flying", "fast", "undead", "dwimmercrafty"]
|
|
const courageBruiser = [4, 7, 11, 14, 18, 21, 25, 28, 32, 35]
|
|
const courageChampion = [7, 13, 20, 26, 33, 39, 46, 52, 59, 65]
|
|
|
|
//********* DEFINE CUSTOM FUNCTIONS **********/
|
|
const updateStats = (statNames, difference) => {
|
|
const result = {}
|
|
getAttrs(statNames.concat("clamp_modifiers"), (vals) => {
|
|
statNames.forEach(stat => {
|
|
let statVal = parseInt(vals[stat]) + difference
|
|
const clamp = vals["clamp_modifiers"] === "true"
|
|
if(clamp) statVal = (statVal >= 3 ? 3 : (statVal <= -3 ? -3 : statVal))
|
|
result[stat] = statVal>=0 ? "+" + statVal : statVal
|
|
setAttrs({
|
|
[stat]: result[stat]
|
|
})
|
|
})
|
|
})
|
|
return result
|
|
}
|
|
|
|
const getCourage = (classType, level, size) => {
|
|
let result = level
|
|
if(classType === "bruiser") {
|
|
result = courageBruiser[level - 1] || 4
|
|
}
|
|
else if(classType === "champion") {
|
|
result = courageChampion[level - 1] || 7
|
|
}
|
|
if(size === "large")
|
|
{
|
|
result += level
|
|
}
|
|
else if(size === "huge")
|
|
{
|
|
result += 2*level
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
//********* HANDLE LVL AND CLASS CHANGE FOR ADVERSARIES **********/
|
|
on("change:adversary_level change:adversary_class", (eventinfo) => {
|
|
// Ignore sheetworker events
|
|
if(eventinfo.sourceType === "sheetworker") {
|
|
return
|
|
}
|
|
getAttrs(["adversary_level", "adversary_class"], (values) => {
|
|
let selected_level = parseInt(values["adversary_level"])
|
|
if(isNaN(selected_level) || selected_level < 1) {
|
|
selected_level = 1
|
|
}
|
|
else if(selected_level > 10) {
|
|
selected_level = 10
|
|
}
|
|
let selected_class = values["adversary_class"] || "goon"
|
|
//TODO: update courage etc.
|
|
})
|
|
})
|
|
|
|
//********* VALIDATE MIN MAX LEVEL **********/
|
|
on("change:min_level change:max_level", (eventinfo) => {
|
|
// Ignore sheetworker events
|
|
if(eventinfo.sourceType === "sheetworker") {
|
|
return
|
|
}
|
|
getAttrs(["min_level", "max_level"], (values) => {
|
|
let min = parseInt(values["min_level"])
|
|
if(isNaN(min) || min < 1) {
|
|
min = 1
|
|
}
|
|
else if(min > 10) {
|
|
min = 10
|
|
}
|
|
let max = parseInt(values["max_level"])
|
|
if(isNaN(max) || max < 1) {
|
|
max = 1
|
|
}
|
|
else if(max > 10) {
|
|
max = 10
|
|
}
|
|
if(min>max) {
|
|
// Swap values
|
|
const temp = min
|
|
min = max
|
|
max = temp
|
|
}
|
|
|
|
setAttrs({
|
|
min_level: min,
|
|
max_level: max
|
|
})
|
|
})
|
|
})
|
|
|
|
//********* VALIDATE WRANGLE PARLEY **********/
|
|
on("change:wrangle_parley change:wrangle_parley_max", (eventinfo) => {
|
|
// Ignore sheetworker events
|
|
if(eventinfo.sourceType === "sheetworker") {
|
|
return
|
|
}
|
|
getAttrs(["wrangle_parley", "wrangle_parley_max"], (values) => {
|
|
let wp = parseInt(values["wrangle_parley"])
|
|
let wp_max = parseInt(values["wrangle_parley_max"])
|
|
if(isNaN(wp)) {
|
|
wp = 1
|
|
}
|
|
if(isNaN(wp_max)) {
|
|
wp_max = 1
|
|
}
|
|
if(wp > 2) {
|
|
wp = 2
|
|
}
|
|
else if(wp<0){
|
|
wp = 0
|
|
}
|
|
if(wp_max > 2) {
|
|
wp_max = 2
|
|
}
|
|
else if(wp_max<0){
|
|
wp_max = 0
|
|
}
|
|
if(wp > wp_max) {
|
|
wp = wp_max
|
|
}
|
|
setAttrs({
|
|
wrangle_parley: wp,
|
|
wrangle_parley_max: wp_max
|
|
})
|
|
})
|
|
})
|
|
|
|
on("change:adversary_level change:adversary_class change:adversary_size", (eventinfo) => {
|
|
// Ignore sheetworker events
|
|
if(eventinfo.sourceType === "sheetworker") {
|
|
return
|
|
}
|
|
getAttrs(["adversary_level", "adversary_class", "adversary_size"], (values) => {
|
|
let selected_level = parseInt(values["adversary_level"])
|
|
if(isNaN(selected_level) || selected_level < 1) {
|
|
selected_level = 1
|
|
}
|
|
else if(selected_level > 10) {
|
|
selected_level = 10
|
|
}
|
|
let selected_class = values["adversary_class"] || "goon"
|
|
let selected_size = values["adversary_size"] || "medium"
|
|
let c = getCourage(selected_class, selected_level, selected_size)
|
|
let m = "-2"
|
|
if(selected_class === "bruiser") {
|
|
m = "+0"
|
|
}
|
|
else if(selected_class === "champion") {
|
|
m = "+2"
|
|
}
|
|
let a = ""
|
|
a += getTranslationByKey("level_short")
|
|
a += selected_level
|
|
a += "-"
|
|
a += getTranslationByKey(selected_class+"_short")
|
|
setAttrs({
|
|
courage_max: c,
|
|
courage: c,
|
|
mettle: m,
|
|
abbreviation: a
|
|
})
|
|
})
|
|
})
|
|
|
|
|
|
//********* UPDATE KEYWORD STRING ON CHANGE **********/
|
|
on(keywordClasses.map(field => "change:keyword_" + field).join(" "), (eventinfo) => {
|
|
// Ignore sheetworker events
|
|
if(eventinfo.sourceType === "sheetworker") {
|
|
return
|
|
}
|
|
// Rebuild the keyword string
|
|
getAttrs(keywordClasses.map(field => "keyword_" + field), (values) => {
|
|
let classesString = ""
|
|
keywordClasses.forEach(s => {
|
|
if(values["keyword_" + s] === "on") {
|
|
if(classesString !== "") {
|
|
classesString += ", "
|
|
}
|
|
classesString += getTranslationByKey(s+"_short")
|
|
}
|
|
})
|
|
|
|
if(classesString == "") {
|
|
classesString = "-"
|
|
}
|
|
|
|
setAttrs({
|
|
classes: classesString,
|
|
})
|
|
})
|
|
})
|
|
|
|
on(keywords.map(field => "change:" + field).join(" "), (eventinfo) => {
|
|
// Ignore sheetworker events
|
|
if(eventinfo.sourceType === "sheetworker") {
|
|
return
|
|
}
|
|
// Fix incompatible keyword selections
|
|
if(eventinfo.sourceAttribute==='keyword_folk' && eventinfo.newValue==='on') {
|
|
// Automatically uncheck overlaping keywords
|
|
setAttrs({
|
|
keyword_small: "false",
|
|
keyword_medium: "false",
|
|
})
|
|
}
|
|
else if((eventinfo.sourceAttribute==='keyword_small' || eventinfo.sourceAttribute==='keyword_medium') && eventinfo.newValue==='on') {
|
|
setAttrs({
|
|
keyword_folk: "false",
|
|
})
|
|
}
|
|
// Rebuild the keyword string
|
|
getAttrs(keywords, (values) => {
|
|
let keywordString = ""
|
|
let sizeString = ""
|
|
let numberString = ""
|
|
let specialString = ""
|
|
|
|
keywordsSize.forEach(s => {
|
|
|
|
if(values["keyword_" + s] === "on") {
|
|
if(sizeString !== "") {
|
|
sizeString += "/"
|
|
}
|
|
sizeString += getTranslationByKey(s)
|
|
}
|
|
})
|
|
|
|
keywordNumbers.forEach(s => {
|
|
if(values["keyword_" + s] === "on") {
|
|
if(numberString !== "") {
|
|
numberString += "/"
|
|
}
|
|
numberString += getTranslationByKey(s)
|
|
}
|
|
})
|
|
|
|
keywordSpecial.forEach(s => {
|
|
if(values["keyword_" + s] === "on") {
|
|
if(specialString !== "") {
|
|
specialString += "/"
|
|
}
|
|
specialString += getTranslationByKey(s)
|
|
}
|
|
})
|
|
|
|
if(sizeString !== "") {
|
|
keywordString += sizeString
|
|
}
|
|
if(numberString !== "") {
|
|
if(keywordString !== "") {
|
|
keywordString += ", "
|
|
}
|
|
keywordString += numberString
|
|
}
|
|
if(specialString !== "") {
|
|
if(keywordString !== "") {
|
|
keywordString += ", "
|
|
}
|
|
keywordString += specialString
|
|
}
|
|
if(keywordString === "") {
|
|
keywordString = "-"
|
|
}
|
|
|
|
setAttrs({
|
|
keywords: keywordString,
|
|
})
|
|
})
|
|
})
|
|
|
|
//********* VALIDATE SIGNED NUMBER VALUES ON CHANGE **********/
|
|
on(signed.map(field => "change:" + field).join(" "), (eventinfo) => {
|
|
const fieldName = eventinfo.sourceAttribute
|
|
getAttrs([fieldName, "clamp_modifiers"], (values) => {
|
|
//********* IGNORE SHEETWORKER EVENTS **********/
|
|
if(eventinfo.sourceType === "sheetworker") {
|
|
return
|
|
}
|
|
const clamp = values["clamp_modifiers"] === "true"
|
|
//********* CLAMP TO -3 +3 AND SET THE VALUE **********/
|
|
let oldValue = parseInt(eventinfo.previousValue)||0
|
|
let val = parseInt(values[fieldName])
|
|
if(isNaN(val)) {
|
|
val = "+0"
|
|
}
|
|
else if(clamp && val < -3) {
|
|
val = "-3"
|
|
}
|
|
else if(clamp && val > 3) {
|
|
val = "+3"
|
|
}
|
|
else if(val >= 0) {
|
|
val = "+" + val
|
|
}
|
|
setAttrs({
|
|
[fieldName]: val
|
|
})
|
|
const difference = (parseInt(val) - oldValue)
|
|
//********* SYNC ABILITIES AND STATS WITH THEIR ATTRIBUTES **********/
|
|
if(fieldName === "vim"){
|
|
updateStats(["charm", "inspire", "mettle", "perception"], difference)
|
|
getAttrs(["courage", "courage_max"], (vals) => {
|
|
setAttrs({
|
|
courage_max: parseInt(vals["courage_max"]) + difference,
|
|
courage: parseInt(vals["courage"]) + difference
|
|
})
|
|
})
|
|
|
|
}
|
|
else if(fieldName === "vigor"){
|
|
const newVals = updateStats(["athletics", "intimidate", "might", "vitality"], difference)
|
|
getAttrs(["might", "vitality"], (vals) => {
|
|
setAttrs({
|
|
attack: val,
|
|
inventory_slots_max: 20 + parseInt(newVals["might"])+parseInt(newVals["vitality"])
|
|
})
|
|
})
|
|
|
|
}
|
|
else if(fieldName === "knack"){
|
|
updateStats(["nimbleness", "search", "sneak", "trickery"], difference)
|
|
setAttrs({
|
|
defense: parseInt(val)>0 ? -parseInt(val) : "+" +(-parseInt(val)),
|
|
})
|
|
}
|
|
else if(fieldName === "knowhow"){
|
|
updateStats(["lore", "realms", "tinker", "wilderness"], difference)
|
|
setAttrs({
|
|
quest_pts: parseInt(val)+3
|
|
})
|
|
}
|
|
else if(fieldName === "might"){
|
|
getAttrs(["vitality"], (vals) => {
|
|
setAttrs({
|
|
inventory_slots_max: 20 + parseInt(val)+parseInt(vals["vitality"])
|
|
})
|
|
})
|
|
}
|
|
else if(fieldName === "vitality"){
|
|
getAttrs(["might"], (vals) => {
|
|
setAttrs({
|
|
inventory_slots_max: 20 + parseInt(val)+parseInt(vals["might"])
|
|
})
|
|
})
|
|
}
|
|
})
|
|
})
|
|
//********* VALIDATE UNSIGNED NUMBER VALUES ON CHANGE (FOR FIREFOX BROWSER) **********/
|
|
on(unsigned.map(field => "change:" + field).join(" "), (eventinfo) => {
|
|
const fieldName = eventinfo.sourceAttribute
|
|
getAttrs([fieldName], (values) => {
|
|
let val = parseInt(values[fieldName])
|
|
if(isNaN(val)) {
|
|
setAttrs({
|
|
[fieldName]: 0
|
|
})
|
|
}
|
|
})
|
|
})
|
|
//********* VALIDATE ALL NUMBER VALUES ON CHARACTER SHEET OPEN **********/
|
|
on("sheet:opened", (eventinfo) => {
|
|
|
|
signed.forEach((fieldName) => {
|
|
getAttrs([fieldName, "clamp_modifiers"], (values) => {
|
|
let val = parseInt(values[fieldName])
|
|
const clamp = values["clamp_modifiers"] === "true"
|
|
if(isNaN(val)) {
|
|
setAttrs({
|
|
[fieldName]: "+0"
|
|
})
|
|
}
|
|
else if(clamp && val < -3) {
|
|
setAttrs({
|
|
[fieldName]: "-3"
|
|
})
|
|
}
|
|
else if(clamp && val > 3) {
|
|
setAttrs({
|
|
[fieldName]: "+3"
|
|
})
|
|
}
|
|
else if(val >= 0) {
|
|
setAttrs({
|
|
[fieldName]: "+" + val
|
|
})
|
|
}
|
|
else {
|
|
setAttrs({
|
|
[fieldName]: val
|
|
})
|
|
}
|
|
})
|
|
})
|
|
unsigned.forEach((fieldName) => {
|
|
getAttrs([fieldName], (values) => {
|
|
if(values[fieldName] === "") {
|
|
if(fieldName === "inventory_slots_max") {
|
|
setAttrs({
|
|
[fieldName]: 20
|
|
})
|
|
}
|
|
else if (fieldName === "courage_max" || fieldName === "courage") {
|
|
setAttrs({
|
|
[fieldName]: 12
|
|
})
|
|
}
|
|
else if (fieldName === "quest_pts") {
|
|
setAttrs({
|
|
[fieldName]: 3
|
|
})
|
|
}
|
|
else {
|
|
setAttrs({
|
|
[fieldName]: 0
|
|
})
|
|
}
|
|
}
|
|
})
|
|
})
|
|
//********* SET DEFAULT VALUE FOR DREAD AND SHEET TYPE **********/
|
|
getAttrs(["dread","sheet_type"], (values) => {
|
|
let val = values["dread"]
|
|
if(val === undefined || val === "") {
|
|
setAttrs({
|
|
dread: "1d4"
|
|
})
|
|
}
|
|
val = values["sheet_type"]
|
|
if(val === undefined || val === "") {
|
|
setAttrs({
|
|
sheet_type: "pc"
|
|
})
|
|
}
|
|
})
|
|
//********* FIX OLD ABILITIES FIELD **********/
|
|
getAttrs(["abilities"], (values) => {
|
|
let val = values["abilities"]
|
|
if(val !== undefined && val !== "") {
|
|
let attrs = {}
|
|
let newid = generateRowID()
|
|
var skillid = "repeating_skills_" + newid
|
|
attrs[skillid+"_skilledit"] = "false"
|
|
attrs[skillid+"_skillname"] = getTranslationByKey('backup_ability')
|
|
attrs[skillid+"_skilldescription"] = val
|
|
attrs["abilities"]=""
|
|
setAttrs(attrs)
|
|
}
|
|
})
|
|
})
|
|
//********* CLASS CHANGE HANDLER **********/
|
|
on("change:class", (eventinfo) => {
|
|
let key = eventinfo.newValue
|
|
if(key === getTranslationByKey('bard')) {
|
|
key = "bard";
|
|
}
|
|
else if(key === getTranslationByKey('dungeoneer')) {
|
|
key = "dungeoneer";
|
|
}
|
|
else if(key === getTranslationByKey('gnome')) {
|
|
key = "gnome";
|
|
}
|
|
else if(key === getTranslationByKey('knight-errant')) {
|
|
key = "knight-errant";
|
|
}
|
|
else if(key === getTranslationByKey('loyal-chum')) {
|
|
key = "loyal-chum";
|
|
}
|
|
else if(key === getTranslationByKey('rascal')) {
|
|
key = "rascal";
|
|
}
|
|
|
|
getAttrs(["hidden_class", "vim"], (values) => {
|
|
// Same class - no change needed
|
|
if(values["hidden_class"] === key) return
|
|
|
|
const v = parseInt(values["vim"])
|
|
let dread = "1d4"
|
|
let courage_max = 12+v
|
|
|
|
if(key === "bard") {dread = "1d4"; courage_max = v+12; }
|
|
else if(key === "dungeoneer") { dread = "1d6"; courage_max = v+13; }
|
|
else if(key === "gnome") { dread = "1d8"; courage_max = v+14; }
|
|
else if(key === "knight-errant") { dread = "1d10"; courage_max = v+15; }
|
|
else if(key === "loyal-chum") { dread = "1d6"; courage_max = v+13; }
|
|
else if(key === "rascal") { dread = "1d8"; courage_max = v+14; }
|
|
else return
|
|
|
|
setAttrs({
|
|
dread: dread,
|
|
courage_max: courage_max,
|
|
courage: courage_max,
|
|
hidden_class: key
|
|
})
|
|
})
|
|
})
|
|
|
|
//********* SET ROLL EVENT LISTENERS *********/
|
|
|
|
// Helper function to grab player input
|
|
const getRegularQuery = async (v) => {
|
|
const strCheckType = getTranslationByKey('check_type')
|
|
const strCheckStandard = getTranslationByKey('check_standard')
|
|
const strCheckAdvantage = getTranslationByKey('check_advantage')
|
|
const strCheckDisadvantage = getTranslationByKey('check_disadvantage')
|
|
const strModifier = getTranslationByKey('modifier')
|
|
|
|
let rollBase = `! {{query1=[[?{${strCheckType}|${strCheckStandard},1d12|${strCheckAdvantage},2d12kh1|${strCheckDisadvantage},2d12kl1};?{${strModifier}|${v}}]]}}`;
|
|
queryRoll = await startRoll(rollBase);
|
|
finishRoll(queryRoll.rollId); // you can just let this time out if you want - we're done with it
|
|
let queryResponse = queryRoll.results.query1.expression.split(";");
|
|
let tmp = parseInt(queryResponse[1]);
|
|
queryResponse[1] = tmp>=0 ? "+" + tmp : tmp;
|
|
return queryResponse;
|
|
};
|
|
|
|
regular.forEach((s) => {
|
|
on(`clicked:${s}`, async () => {
|
|
getAttrs([s, "clamp_roll_modifier"], async (values) => {
|
|
let v = parseInt(values[s])
|
|
const clamp = values["clamp_roll_modifier"] === "true"
|
|
if(v >= 0) v = "+" + v
|
|
|
|
const rollQuery = await getRegularQuery(0)
|
|
const rollStr = rollQuery[0] + v + rollQuery[1]
|
|
startRoll(`&{template:check} {{sheet_type=@{sheet_type}}} {{character=@{character_name}}} {{name=${s}}} {{roll=[[${rollStr}]]}}`, (checkResult) => {
|
|
let modifier = checkResult.results.roll.dice[0]
|
|
let expression = checkResult.results.roll.expression
|
|
if(expression.startsWith("2d12kl1")){
|
|
if(modifier > checkResult.results.roll.dice[1]) modifier = checkResult.results.roll.dice[1]
|
|
}
|
|
else if (expression.startsWith("2d12kh1")){
|
|
if(modifier < checkResult.results.roll.dice[1]) modifier = checkResult.results.roll.dice[1]
|
|
}
|
|
modifier = checkResult.results.roll.result - modifier
|
|
let computedResult = checkResult.results.roll.result
|
|
if(clamp && modifier > 3)
|
|
computedResult = computedResult - modifier + 3
|
|
else if(clamp && modifier < -3)
|
|
computedResult = computedResult - modifier - 3
|
|
finishRoll(checkResult.rollId, {
|
|
roll: computedResult
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
//********* ATTACK **********/
|
|
|
|
// Helper function to grab player input
|
|
const getAttackQuery = async (v, lastDefense) => {
|
|
const strCheckType = getTranslationByKey('check_type')
|
|
const strCheckStandard = getTranslationByKey('check_standard')
|
|
const strCheckAdvantage = getTranslationByKey('check_advantage')
|
|
const strCheckDisadvantage = getTranslationByKey('check_disadvantage')
|
|
const strModifier = getTranslationByKey('modifier')
|
|
const strDefense = getTranslationByKey("opponents-defense")
|
|
|
|
let rollBase = `! {{query1=[[?{${strCheckType}|${strCheckStandard},1d12|${strCheckAdvantage},2d12kh1|${strCheckDisadvantage},2d12kl1};?{${strModifier}|${v}};?{${strDefense}|${lastDefense}}]]}}`;
|
|
queryRoll = await startRoll(rollBase);
|
|
finishRoll(queryRoll.rollId); // you can just let this time out if you want - we're done with it
|
|
let queryResponse = queryRoll.results.query1.expression.split(";");
|
|
let tmp = parseInt(queryResponse[1]);
|
|
queryResponse[1] = tmp>=0 ? "+" + tmp : tmp;
|
|
tmp = parseInt(queryResponse[2]);
|
|
queryResponse[2] = tmp>=0 ? "+" + tmp : tmp;
|
|
|
|
return queryResponse;
|
|
};
|
|
|
|
on("clicked:attack", async () => {
|
|
getAttrs(["attack", "last_opponent_defense", "clamp_roll_modifier"], async values => {
|
|
let v = parseInt(values["attack"])
|
|
const clamp = values["clamp_roll_modifier"] === "true"
|
|
if(v >= 0) v = "+" + v
|
|
|
|
let lastDefense = values["last_opponent_defense"] || "+0"
|
|
const rollQuery = await getAttackQuery(0, lastDefense)
|
|
const rollStr = rollQuery[0] + v + rollQuery[1] + rollQuery[2]
|
|
setAttrs({
|
|
last_opponent_defense: rollQuery[2]
|
|
})
|
|
|
|
startRoll(`&{template:attack} {{sheet_type=@{sheet_type}}} {{character=@{character_name}}} {{random_index=[[[{1d6}]]]}} {{name=attack}} {{damage=[[@{dread}]]}} {{roll=[[${rollStr}]]}} {{hint=[[0]]}}`, (checkResult) => {
|
|
let modifier = checkResult.results.roll.dice[0]
|
|
let expression = checkResult.results.roll.expression
|
|
let expressionParts = expression.split(/([+-])/)
|
|
|
|
if(expression.startsWith("2d12kl1")){
|
|
if(modifier > checkResult.results.roll.dice[1]) modifier = checkResult.results.roll.dice[1]
|
|
}
|
|
else if (expression.startsWith("2d12kh1")){
|
|
if(modifier < checkResult.results.roll.dice[1]) modifier = checkResult.results.roll.dice[1]
|
|
}
|
|
modifier = checkResult.results.roll.result - modifier
|
|
let computedResult = checkResult.results.roll.result
|
|
if(clamp && modifier > 3)
|
|
computedResult = computedResult - modifier + 3
|
|
else if(clamp && modifier < -3)
|
|
computedResult = computedResult - modifier - 3
|
|
let computedDamage = checkResult.results.damage.result
|
|
if(computedResult > 11) {
|
|
computedDamage *= 2
|
|
}
|
|
finishRoll(checkResult.rollId, {
|
|
roll: computedResult,
|
|
damage: computedDamage
|
|
})
|
|
})
|
|
})
|
|
})
|
|
</script> |