Files
roll20-character-sheets/aetherial_expanse/aetherial_expanse.html
T

34259 lines
1.8 MiB
Plaintext

<web>
<input class="sheet-type-flag" name="attr_charactersheet_type" type="hidden"/>
<input name="attr_version" type="hidden" value="4.2"/>
<input name="attr_appliedUpdates" type="hidden"/>
<input class="monster_confirm_flag" name="attr_monster_confirm_flag" type="hidden"/>
<input class="mancer_confirm_flag" name="attr_mancer_confirm_flag" type="hidden"/>
<div class="licensecontainer compendium-drop-target monsters vehicles">
<input class="npc_toggle" name="attr_npc" type="hidden" value="0"/>
<input class="npcspellcastingflag" name="attr_npcspellcastingflag" type="hidden"/>
<input class="toggleflag" name="attr_rtype" type="hidden"/>
<input class="kingdom_toggle" name="attr_kingdom" type="hidden" value="0"/>
<div class="advantagetoggle">
<input type="radio" class="toggle-left" name="attr_advantagetoggle" value="{{query=1}} {{advantage=1}} {{r2=[[@{d20}"><span data-i18n="adv-u">ADVANTAGE</span>
<input type="radio" class="toggle-center" name="attr_advantagetoggle" value="{{query=1}} {{normal=1}} {{r2=[[0d20" checked="checked"><span data-i18n="norm-u">NORMAL</span>
<input type="radio" class="toggle-right" name="attr_advantagetoggle" value="{{query=1}} {{disadvantage=1}} {{r2=[[@{d20}"><span data-i18n="disadv-u">DISADVANTAGE</span>
</div>
<input class="toggleflag" name="attr_wtype" type="hidden"/>
<div class="advantagetoggle whispertoggle">
<input class="toggle-left" name="attr_whispertoggle" type="radio" value="" checked="checked"/><span data-i18n="public:-u">PUBLIC</span>
<input class="toggle-right" name="attr_whispertoggle" type="radio" value="/w gm "/><span data-i18n="to-gm:-u">TO<span class="togm">GM</span></span>
</div>
<div class="container npc">
<input class="vehicle-flag" type="hidden" name="attr_is_vehicle" value="0"/>
<div class="is-vehicle">
<input class="npc_options-flag" type="checkbox" name="attr_npc_options-flag" checked="checked"/><span>y</span>
<div class="npc_options">
<div class="row title"><span class="label--uppercase" data-i18n="vehicle-options"></span></div>
<div class="vehicle__cols">
<div class="vehicle__col--left">
<div class="npc-options__stats">
<div class="row"><span class="label--uppercase" data-i18n="ship-type"></span>
<input type="text" name="attr_vehicle_type" placeholder=""/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="size"></span>
<select class="vehicle__capitalized-field" name="attr_vehicle_size" style="margin-bottom:0;height:23px;outline:none;">
<option class="vehicle__capitalized-field" value="tiny" data-i18n="tiny"></option>
<option class="vehicle__capitalized-field" value="small" data-i18n="small"></option>
<option class="vehicle__capitalized-field" value="medium" data-i18n="medium"></option>
<option class="vehicle__capitalized-field" value="large" data-i18n="large"></option>
<option .vehicle__capitalized-fieldvalue="huge" data-i18n="huge"></option>
<option class="vehicle__capitalized-field" value="gargantuan" data-i18n="gargantuan"></option>
</select>
</div>
<div class="row"><span class="label--uppercase" data-i18n="base-speed"></span>
<input class="num" type="number" name="attr_vehicle_base_speed" placeholder="250"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="mobility"></span>
<select class="vehicle__capitalized-field" name="attr_vehicle_mobility" style="margin-bottom:0;height:23px;outline:none;">
<option class="vehicle__capitalized-field" value="low" data-i18n="low"></option>
<option class="vehicle__capitalized-field" value="balanced" data-i18n="balanced"></option>
<option class="vehicle__capitalized-field" value="high" data-i18n="high"></option>
</select>
</div>
<div class="row"><span class="label--uppercase" data-i18n="fuel-items"></span>
<input type="text" name="attr_vehicle_fuel_items" placeholder=""/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="hit-points"></span>
<input type="number" name="attr_vehicle_hp_max" placeholder="10"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="explosion-cap"></span>
<input type="number" name="attr_vehicle_explosion_cap" placeholder="10"/>
</div>
</div>
<div class="npc-options__weapons" style="margin-top: 20px">
<div class="row"><span class="label--uppercase" data-i18n="bow-weapon-slots"></span>
<input class="num" type="number" name="attr_vehicle_bow_slots" placeholder="0"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="port-weapon-slots"></span>
<input class="num" type="number" name="attr_vehicle_port_slots" placeholder="0"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="starboard-weapon-slots"></span>
<input class="num" type="number" name="attr_vehicle_starboard_slots" placeholder="0"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="stern-weapon-slots"></span>
<input class="num" type="number" name="attr_vehicle_stern_slots" placeholder="0"/>
</div>
</div>
<div class="npc-options__extras" style="margin-top: 20px">
<div class="row">
<input class="num" type="checkbox" name="attr_vehicle_show_ammunition" value="1" style="margin-right: 4px;"/><span class="label--uppercase" data-i18n="ammunition"></span>
</div>
<div class="row">
<input class="num" type="checkbox" name="attr_vehicle_show_officers" value="1" style="margin-right: 4px;"/><span class="label--uppercase" data-i18n="officers-and-specialists"></span>
</div>
</div>
</div>
<div class="vehicle__col--right">
<div class="row"><span class="label--uppercase" data-i18n="unranked-crew"></span>
<input class="num" type="number" name="attr_vehicle_unranked_crew_max" placeholder="32"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="maximum-crew"></span>
<input class="num" type="number" name="attr_vehicle_maximum_crew" placeholder="32"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="maximum-crew-bonus"></span>
<input type="text" name="attr_vehicle_maximum_crew_bonus" placeholder=""/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="skeleton-crew"></span>
<input class="num" type="number" name="attr_vehicle_skeleton_crew" placeholder="12"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="mettle-pool"></span>
<input class="num" type="text" name="attr_vehicle_mettle_pool" placeholder="7d4"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="relevant-crew-boons"></span>
<textarea name="attr_vehicle_crew_boons"></textarea>
</div>
</div>
</div>
<div class="row title"><span class="label--uppercase" data-i18n="gen-opts-u"></span></div>
<div class="row"><span data-i18n="character-sheet-type-u"></span>
<select name="attr_charactersheet_type">
<option value="pc">PC</option>
<option value="npc">NPC</option>
<option value="vehicle">VEHICLE</option>
<option value="kingdom">KINGDOM</option>
</select>
<input type="hidden" name="attr_npc" value="0"/>
<input type="hidden" name="attr_is_vehicle" value="0"/>
</div>
<div class="row"><span class="label--uppercase" data-i18n="token_size-u"></span>
<input class="text" name="attr_token_size" value="1" placeholder="1"/>
</div>
<div class="row"><span data-i18n="roll-queries:-u"></span>
<select name="attr_rtype">
<option value="{{always=1}} {{r2=[[@{d20}" data-i18n="always-adv">Always Roll Advantage</option>
<option value="@{advantagetoggle}" data-i18n="adv-toggle">Advantage Toggle</option>
<option value="@{queryadvantage}" data-i18n="query-adv">Query Advantage</option>
<option value="{{normal=1}} {{r2=[[0d20" data-i18n="never-adv">Never Roll Advantage</option>
</select>
</div>
<div class="row"><span data-i18n="whisp-rolls-gm:-u"></span>
<select name="attr_wtype">
<option value="" data-i18n="never-whisper-roll">Never Whisper Rolls</option>
<option value="@{whispertoggle}" data-i18n="toggle-roll-whisper">Whisper Toggle</option>
<option value="?{Whisper?|Public Roll,|Whisper Roll,/w gm }" data-i18n="query-whisper-roll">Query Whisper</option>
<option value="/w gm " data-i18n="always-whisper-roll">Always Whisper Rolls</option>
</select>
</div>
<div class="row"><span data-i18n="auto-dmg-roll:-u"></span>
<select class="dtype" name="attr_dtype">
<option value="pick" data-i18n="never-dmg">Don&apos;t Auto Roll Damage</option>
<option value="full" data-i18n="always-dmg">Auto Roll Damage &amp; Crit</option>
</select>
</div>
<div class="row"><span data-i18n="npc-name-in-rolls:-u"></span>
<select name="attr_npc_name_flag">
<option value="{{name=@{npc_name}}}" data-i18n="show">Show</option>
<option value="0" data-i18n="hide">Hide</option>
</select>
</div>
</div>
<div class="display">
<div class="row title red"><span name="attr_character_name"></span></div>
<div class="row subtitle"> <span name="attr_vehicle_type"></span></div>
<div class="vehicle__cols">
<div class="vehicle__col--left">
<div class="vehicle__stats">
<div class="vehicle__size"><span class="stat--title" data-i18n="size"> </span><span class="stat--value" name="attr_vehicle_size" style="text-transform: capitalize;"></span></div>
<div class="vehicle__base-speed"><span class="stat--title" data-i18n="base-speed"> </span><span class="stat--value" name="attr_vehicle_base_speed"></span></div>
<div class="vehicle__mobility"><span class="stat--title" data-i18n="mobility"> </span><span class="stat--value" name="attr_vehicle_mobility" style="text-transform: capitalize;"></span></div>
<div class="vehicle__fuel-items"><span class="stat--title" data-i18n="fuel-items"> </span><span class="stat--value" name="attr_vehicle_fuel_items"></span></div>
</div>
<div class="vehicle__stats" style="margin-top: 20px">
<div class="vehicle__hp"><span class="stat--title" data-i18n="hit-points"></span>
<input class="current-hp" type="text" name="attr_vehicle_hp" placeholder="0"/><span class="stat--value" name="attr_vehicle_hp_max"></span>
</div>
<div class="vehicle__explosion-cap"><span class="stat--title" data-i18n="explosion-cap"> </span><span class="stat--value" name="attr_vehicle_explosion_cap"></span></div>
</div>
<!--WEAPONS-->
<input class="npc_ui_flags" type="hidden" name="attr_ui_flags" value=""/>
<!--.row.traits-->
<div class="row actions vehicleweapon vehicleweapon--bow">
<input class="vehicle__weapon-slots" type="hidden" name="attr_vehicle_bow_slots"/>
<div class="row actiontitle" style="position: relative;"><span><span class="title red" data-i18n="bow-weapons"></span><span class="title red">&nbsp;(</span><span class="title red" name="attr_vehicle_bow_slots"></span><span class="title red">&nbsp;</span><span class="title red" data-i18n="slots"></span><span class="title red">)</span></span></div>
<fieldset class="repeating_vehicleweapon-bow">
<input class="set_bg_color" type="hidden" name="attr_color" value=""/>
<div class="color-link"></div>
<div class="link-margin"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_vehicleweapon-bow_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="quantity"></span>
<input type="number" name="attr_quantity" placeholder="1"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="name"></span>
<input type="text" name="attr_name" placeholder="Canon"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="target-die"></span>
<select name="attr_target_die">
<option value="" data-i18n="choose"> </option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="range"></span>
<select name="attr_range">
<option value="" data-i18n="choose"> </option>
<option value="short" data-i18n="short"></option>
<option value="standard" data-i18n="standard"></option>
<option value="long" data-i18n="long"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="weight"></span>
<select name="attr_weight">
<option value="" data-i18n="choose"> </option>
<option value="light" data-i18n="light"></option>
<option value="heavy" data-i18n="heavy"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="additional-effects"></span>
<textarea name="attr_additional_effects"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<button class="pick btn ui-draggable" type="roll" name="roll_npc_roll_output" value="@{wtype}&{template:simple} {{rname=@{name}}} {{r1=[[@{target_die}]]}} {{normal=1}} {{desctitle=@{vehicle_ammo_name}}} {{desc=@{vehicle_ammo_desc}}} @{charname_output}"><span name="attr_quantity"></span><span class="vehicleweapon__weapon-quantity-signal">x&nbsp;</span><span class="vehicle__capitalized-field--first" name="attr_name"></span><span class="vehicleweapon__weapon-parenthesis">&nbsp;(</span><span name="attr_target_die"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_range" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_weight" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-parenthesis">)</span></button>
<div class="vehicle__text-field-display"><span class="additional-effects" name="attr_additional_effects"></span>
<input type="checkbox"/>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="row actions vehicleweapon vehicleweapon--port">
<input class="vehicle__weapon-slots" type="hidden" name="attr_vehicle_port_slots"/>
<div class="row actiontitle" style="position: relative;"><span><span class="title red" data-i18n="port-weapons"></span><span class="title red">&nbsp;(</span><span class="title red" name="attr_vehicle_port_slots"></span><span class="title red">&nbsp;</span><span class="title red" data-i18n="slots"></span><span class="title red">)</span></span></div>
<fieldset class="repeating_vehicleweapon-port">
<input class="set_bg_color" type="hidden" name="attr_color" value=""/>
<div class="color-link"></div>
<div class="link-margin"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_vehicleweapon-port_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="quantity"></span>
<input type="number" name="attr_quantity" placeholder="1"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="name"></span>
<input type="text" name="attr_name" placeholder="Canon"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="target-die"></span>
<select name="attr_target_die">
<option value="" data-i18n="choose"> </option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="range"></span>
<select name="attr_range">
<option value="" data-i18n="choose"> </option>
<option value="short" data-i18n="short"></option>
<option value="standard" data-i18n="standard"></option>
<option value="long" data-i18n="long"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="weight"></span>
<select name="attr_weight">
<option value="" data-i18n="choose"> </option>
<option value="light" data-i18n="light"></option>
<option value="heavy" data-i18n="heavy"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="additional-effects"></span>
<textarea name="attr_additional_effects"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<button class="pick btn ui-draggable" type="roll" name="roll_npc_roll_output" value="@{wtype}&{template:simple} {{rname=@{name}}} {{r1=[[@{target_die}]]}} {{normal=1}} {{desctitle=@{vehicle_ammo_name}}} {{desc=@{vehicle_ammo_desc}}} @{charname_output}"><span name="attr_quantity"></span><span class="vehicleweapon__weapon-quantity-signal">x&nbsp;</span><span class="vehicle__capitalized-field--first" name="attr_name"></span><span class="vehicleweapon__weapon-parenthesis">&nbsp;(</span><span name="attr_target_die"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_range" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_weight" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-parenthesis">)</span></button>
<div class="vehicle__text-field-display"><span class="additional-effects" name="attr_additional_effects"></span>
<input type="checkbox"/>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="row actions vehicleweapon vehicleweapon--starboard">
<input class="vehicle__weapon-slots" type="hidden" name="attr_vehicle_starboard_slots"/>
<div class="row actiontitle" style="position: relative;"><span><span class="title red" data-i18n="starboard-weapons"></span><span class="title red">&nbsp;(</span><span class="title red" name="attr_vehicle_starboard_slots"></span><span class="title red">&nbsp;</span><span class="title red" data-i18n="slots"></span><span class="title red">)</span></span></div>
<fieldset class="repeating_vehicleweapon-starboard">
<input class="set_bg_color" type="hidden" name="attr_color" value=""/>
<div class="color-link"></div>
<div class="link-margin"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_vehicleweapon-starboard_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="quantity"></span>
<input type="number" name="attr_quantity" placeholder="1"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="name"></span>
<input type="text" name="attr_name" placeholder="Canon"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="target-die"></span>
<select name="attr_target_die">
<option value="" data-i18n="choose"> </option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="range"></span>
<select name="attr_range">
<option value="" data-i18n="choose"> </option>
<option value="short" data-i18n="short"></option>
<option value="standard" data-i18n="standard"></option>
<option value="long" data-i18n="long"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="weight"></span>
<select name="attr_weight">
<option value="" data-i18n="choose"> </option>
<option value="light" data-i18n="light"></option>
<option value="heavy" data-i18n="heavy"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="additional-effects"></span>
<textarea name="attr_additional_effects"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<button class="pick btn ui-draggable" type="roll" name="roll_npc_roll_output" value="@{wtype}&{template:simple} {{rname=@{name}}} {{r1=[[@{target_die}]]}} {{normal=1}} {{desctitle=@{vehicle_ammo_name}}} {{desc=@{vehicle_ammo_desc}}} @{charname_output}"><span name="attr_quantity"></span><span class="vehicleweapon__weapon-quantity-signal">x&nbsp;</span><span class="vehicle__capitalized-field--first" name="attr_name"></span><span class="vehicleweapon__weapon-parenthesis">&nbsp;(</span><span name="attr_target_die"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_range" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_weight" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-parenthesis">)</span></button>
<div class="vehicle__text-field-display"><span class="additional-effects" name="attr_additional_effects"></span>
<input type="checkbox"/>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="row actions vehicleweapon vehicleweapon--stern">
<input class="vehicle__weapon-slots" type="hidden" name="attr_vehicle_stern_slots"/>
<div class="row actiontitle" style="position: relative;"><span><span class="title red" data-i18n="stern-weapons"></span><span class="title red">&nbsp;(</span><span class="title red" name="attr_vehicle_stern_slots"></span><span class="title red">&nbsp;</span><span class="title red" data-i18n="slots"></span><span class="title red">)</span></span></div>
<fieldset class="repeating_vehicleweapon-stern">
<input class="set_bg_color" type="hidden" name="attr_color" value=""/>
<div class="color-link"></div>
<div class="link-margin"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_vehicleweapon-stern_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="quantity"></span>
<input type="number" name="attr_quantity" placeholder="1"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="name"></span>
<input type="text" name="attr_name" placeholder="Canon"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="target-die"></span>
<select name="attr_target_die">
<option value="" data-i18n="choose"> </option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="range"></span>
<select name="attr_range">
<option value="" data-i18n="choose"> </option>
<option value="short" data-i18n="short"></option>
<option value="standard" data-i18n="standard"></option>
<option value="long" data-i18n="long"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="weight"></span>
<select name="attr_weight">
<option value="" data-i18n="choose"> </option>
<option value="light" data-i18n="light"></option>
<option value="heavy" data-i18n="heavy"></option>
</select>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="additional-effects"></span>
<textarea name="attr_additional_effects"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<button class="pick btn ui-draggable" type="roll" name="roll_npc_roll_output" value="@{wtype}&{template:simple} {{rname=@{name}}} {{r1=[[@{target_die}]]}} {{normal=1}} {{desctitle=@{vehicle_ammo_name}}} {{desc=@{vehicle_ammo_desc}}} @{charname_output}"><span name="attr_quantity"></span><span class="vehicleweapon__weapon-quantity-signal">x&nbsp;</span><span class="vehicle__capitalized-field--first" name="attr_name"></span><span class="vehicleweapon__weapon-parenthesis">&nbsp;(</span><span name="attr_target_die"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_range" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span class="vehicle__capitalized-field" name="attr_weight" style="text-transform: capitalize;"></span><span class="vehicleweapon__weapon-parenthesis">)</span></button>
<div class="vehicle__text-field-display"><span class="additional-effects" name="attr_additional_effects"></span>
<input type="checkbox"/>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="row actions vehicleammo vehicle-extra">
<input class="vehicle-extra__toggle" type="hidden" name="attr_vehicle_show_ammunition"/>
<input type="hidden" name="vehicle_ammo_name"/>
<input type="hidden" name="vehicle_ammo_desc"/>
<div class="row actiontitle" style="position: relative;"><span class="title red" data-i18n="ammunition"></span></div>
<fieldset class="repeating_vehicleammo">
<div class="link-margin"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_vehicleammo_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="name"></span>
<input type="text" name="attr_name" placeholder="Cannister Shot"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="description"></span>
<textarea name="attr_description"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<div class="row">
<input type="checkbox" name="attr_loaded" value="1"/>
<div class="vehicle__autoresize-field"><span name="attr_quantity"></span><span class="vehicleweapon__weapon-quantity-signal">x&nbsp;</span>
<input type="number" name="attr_quantity" placeholder="1"/>
</div><span class="vehicle__capitalized-field--first" name="attr_name"></span>
<div class="vehicle__text-field-display"><span class="ammo-description" name="attr_description"></span>
<input type="checkbox"/>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
<div class="vehicle__col--right">
<input class="npc_ui_flags" type="hidden" name="attr_ui_flags" value=""/>
<div class="vehicle__stats vehiclecrew">
<div class="vehicle__hp"><span class="stat--title" data-i18n="unranked-crew"></span>
<input class="current-hp" type="text" name="attr_vehicle_unranked_crew" placeholder="0"/><span class="stat--value" name="attr_vehicle_unranked_crew_max"></span>
</div>
<div class="vehicle__max-crew"><span class="stat--title" data-i18n="maximum-crew"> </span><span class="stat--value" name="attr_vehicle_maximum_crew"></span><span class="stat--value max-crew-bonus" name="attr_vehicle_maximum_crew_bonus"></span></div>
<div class="vehicle__skeleton-crew"><span class="stat--title" data-i18n="skeleton-crew"> </span><span class="stat--value" name="attr_vehicle_skeleton_crew"></span></div>
<div class="vehicle__mettle-pool">
<button type="roll" name="roll_vehicle_mettle" value="@{wtype}&{template:simple} {{rname=^{mettle-pool}}} {{r1=[[@{vehicle_mettle_pool}]]}} {{normal=1}} @{charname_output}" style="font-size: 13px !important;font-weight: 700;color: rgb(51, 51, 51);outline: none;"><span class="stat--title" data-i18n="mettle-pool"> </span><span class="stat--value" name="attr_vehicle_mettle_pool" style="font-weight: normal;"></span></button>
</div>
<div class="vehicle__crew-boons"><span class="stat--title" data-i18n="relevant-crew-boons"> </span><span class="stat--value" name="attr_vehicle_crew_boons"></span></div>
</div>
<!--.vehicle__shortcuts
.vehicle__init
button(type='roll' name='roll_vehicle_init' value!='@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{init}}} {{r1=[[@{d20} &{tracker}]]}} {{normal=1}} {{type=Initiative}}')
span.npc-init-die t
span(style='display: block;') INIT
.vehicle__crashdamage
button(type='roll' name='roll_vehicle_crash' value!='@{wtype}&{template:simple} {{rname=^{crash-damage}}} {{r1=[[?{Object size|Large, 4d10|Huge, 8d10|Gargantuan, 16d10}]]}} {{normal=1}} @{charname_output}')
span.npc-init-die t
span(style='display: block;') CRASH
-->
<div class="row actions vehicleofficer vehicle-extra">
<input class="vehicle-extra__toggle" type="hidden" name="attr_vehicle_show_ammunition"/>
<div class="row actiontitle" style="position: relative;"><span class="title red" data-i18n="officers-and-specialists"></span></div>
<fieldset class="repeating_vehicleofficer">
<div class="link-margin"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_vehicleofficer_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="name"></span>
<input type="text" name="attr_name" placeholder="Jack Sparrow"/>
</div>
<div class="row">
<datalist id="vehicle-roles">
<option data-i18n="arcanist"></option>
<option data-i18n="artist"></option>
<option data-i18n="boatswain"></option>
<option data-i18n="captain"></option>
<option data-i18n="cook"></option>
<option data-i18n="first-mate"></option>
<option data-i18n="helmsperson"></option>
<option data-i18n="master-gunner"></option>
<option data-i18n="navigators"></option>
<option data-i18n="quartermaster"></option>
<option data-i18n="shipwright"></option>
<option data-i18n="spymaster"></option>
<option data-i18n="surgeon"></option>
</datalist><span class="label--semicolon label--uppercase" data-i18n="role"></span>
<input type="text" name="attr_role" placeholder="Captain" list="vehicle-roles"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="rank"></span>
<input type="number" name="attr_rank" placeholder="1"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="description"></span>
<textarea name="attr_description"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<div class="row"><span class="vehicle__capitalized-field--first" name="attr_name"></span><span class="vehicleweapon__weapon-parenthesis">&nbsp;(</span><span name="attr_role"></span><span class="vehicleweapon__weapon-range-separator">,&nbsp;</span><span data-i18n="rank"></span><span class="vehicleweapon__weapon-range-separator">&nbsp;</span><span name="attr_rank"></span><span class="vehicleweapon__weapon-parenthesis">)</span>
<div class="vehicle__text-field-display"><span class="officer-description" name="attr_description"></span>
<input type="checkbox"/>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="row actions vehicleupgrade">
<div class="row actiontitle" style="position: relative;"><span class="title red" data-i18n="upgrades"></span></div>
<fieldset class="repeating_vehicleupgrade">
<div class="link-margin"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_vehicleupgrade_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="name"></span>
<input type="text" name="attr_name" placeholder="Multiversal Orrey"/>
</div>
<div class="row"><span class="label--semicolon label--uppercase" data-i18n="description"></span>
<textarea name="attr_description"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<div class="row"><span class="vehicle__capitalized-field--first" name="attr_name"></span>
<div class="vehicle__text-field-display"><span class="upgrade-description" name="attr_description"></span>
<input type="checkbox"/>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="vehicle__cargo actions vehicleactions">
<div class="row actiontitle" style="position: relative;"><span class="title red label--capitalize" data-i18n="cargo"></span>
<input class="overweight" type="hidden" name="attr_vehicle_is_overweight" value="0"/>
<input type="hidden" name="attr_vehicle_total_weight" value="0"/><span class="vehicle-total-weight"><span class="label--semicolon" data-i18n="total-weight-u"></span><span name="attr_vehicle_total_weight"></span></span>
</div>
<div class="vehicle__cargo__labels"><span>Qnt.</span><span>Item Name</span><span>Lbs</span></div>
<fieldset class="repeating_vehicle-cargo-items">
<div class="vehicle__cols">
<input type="number" name="attr_quantity" placeholder="1"/>
<input type="text" name="attr_name" placeholder="Item name..."/>
<input type="number" name="attr_weight" placeholder="10"/>
</div>
</fieldset>
</div>
</div>
</div>
</div>
<div class="vehicle-weapon-confirmation">
<input class="vehicle_weapon-confirmation__toggle" type="hidden" name="attr_vehicle_weapon_confirmation_toggle"/>
<div class="vehicle-weapon-confirmation__dialog"><span data-i18n="select-weapon-slot"></span>
<select name="attr_vehicle_dropped_weapon_slot">
<option data-i18n="choose" value=""></option>
<option data-i18n="bow" value="bow"></option>
<option data-i18n="port" value="port"></option>
<option data-i18n="starboard" value="starboard"></option>
<option data-i18n="stern" value="stern"></option>
</select>
<div class="actions">
<button type="action" name="act_cancelweaponslot" data-i18n="cancel"></button>
<button type="action" name="act_acceptweaponslot" data-i18n="accept"> </button>
</div>
</div>
<div class="vehicle-weapon-confirmation__background"></div>
</div>
</div>
<div class="is-npc">
<input class="npc_options-flag" type="checkbox" name="attr_npc_options-flag" checked="checked"/><span>y</span>
<input class="npc_ui_flags" type="hidden" name="attr_ui_flags" value=""/>
<div class="npc_options">
<div class="row title"><span data-i18n="npc-options-u">NPC OPTIONS</span></div>
<div class="row"><span data-i18n="name-u">NAME</span>
<input type="text" name="attr_npc_name"/>
</div>
<div class="row"><span data-i18n="npc-type-u">NPC TYPE</span>
<input type="text" name="attr_npc_type" placeholder="Medium fiend, any evil alignment" data-i18n-placeholder="npc-type-place"/>
</div>
<div class="row"><span data-i18n="armor-class-u">ARMOR CLASS</span>
<input class="num" type="text" name="attr_npc_ac" placeholder="10"/><span data-i18n="type-u">TYPE</span>
<input type="text" name="attr_npc_actype" placeholder="scale mail" data-i18n-placeholder="npc-armor-place" value=""/>
</div>
<div class="row"><span data-i18n="hit-points-u">HIT POINTS</span>
<input class="num" type="text" name="attr_hp_max" placeholder="82"/><span data-i18n="formula-u">FORMULA</span>
<input type="text" name="attr_npc_hpformula" placeholder="11d8 + 33"/>
</div>
<div class="row"><span data-i18n="speed-u">SPEED</span>
<input type="text" name="attr_npc_speed" placeholder="30 ft., fly 60ft." data-i18n-placeholder="npc-speed-place"/>
</div>
<div class="spacer"></div>
<div class="row"><span data-i18n="attributes-u">ATTRIBUTES</span></div>
<div class="row"><span data-i18n="str-u">STR</span>
<input class="num" type="text" name="attr_strength_base" value="10"/><span data-i18n="dex-u">DEX</span>
<input class="num" type="text" name="attr_dexterity_base" value="10"/><span data-i18n="con-u">CON</span>
<input class="num" type="text" name="attr_constitution_base" value="10"/><span data-i18n="int-u">INT</span>
<input class="num" type="text" name="attr_intelligence_base" value="10"/><span data-i18n="wis-u">WIS</span>
<input class="num" type="text" name="attr_wisdom_base" value="10"/><span data-i18n="cha-u">CHA</span>
<input class="num" type="text" name="attr_charisma_base" value="10"/>
</div>
<div class="spacer"></div>
<div class="row"><span data-i18n="saves-u">SAVES</span></div>
<div class="row"><span data-i18n="str-u">STR</span>
<input class="num" type="text" name="attr_npc_str_save_base" placeholder="0"/>
<input type="hidden" name="attr_npc_str_save" value="@{strength_mod}"/><span data-i18n="dex-u">DEX</span>
<input class="num" type="text" name="attr_npc_dex_save_base" placeholder="0"/>
<input type="hidden" name="attr_npc_dex_save" value="@{dexterity_mod}"/><span data-i18n="con-u">CON</span>
<input class="num" type="text" name="attr_npc_con_save_base" placeholder="0"/>
<input type="hidden" name="attr_npc_con_save" value="@{constitution_mod}"/><span data-i18n="int-u">INT</span>
<input class="num" type="text" name="attr_npc_int_save_base" placeholder="0"/>
<input type="hidden" name="attr_npc_int_save" value="@{intelligence_mod}"/><span data-i18n="wis-u">WIS</span>
<input class="num" type="text" name="attr_npc_wis_save_base" placeholder="0"/>
<input type="hidden" name="attr_npc_wis_save" value="@{wisdom_mod}"/><span data-i18n="cha-u">CHA</span>
<input class="num" type="text" name="attr_npc_cha_save_base" placeholder="0"/>
<input type="hidden" name="attr_npc_cha_save" value="@{charisma_mod}"/>
</div>
<div class="spacer"></div>
<div class="row"><span data-i18n="skills-u">SKILLS</span></div>
<div class="row skills-list" data-i18n-list="skills-list">
<div class="col">
<div class="row" data-i18n-list-item="acrobatics"><span data-i18n="acrobatics-u">ACROBATICS</span>
<input class="num" type="text" name="attr_npc_acrobatics_base" placeholder="0"/>
<input type="hidden" name="attr_npc_acrobatics" value="@{dexterity_mod}"/>
</div>
<div class="row" data-i18n-list-item="animal_handling"><span data-i18n="animal_handling-u">ANIMAL HANDLING</span>
<input class="num" type="text" name="attr_npc_animal_handling_base" placeholder="0"/>
<input type="hidden" name="attr_npc_animal_handling" value="@{wisdom_mod}"/>
</div>
<div class="row" data-i18n-list-item="arcana"><span data-i18n="arcana-u">ARCANA</span>
<input class="num" type="text" name="attr_npc_arcana_base" placeholder="0"/>
<input type="hidden" name="attr_npc_arcana" value="@{intelligence_mod}"/>
</div>
<div class="row" data-i18n-list-item="athletics"><span data-i18n="athletics-u">ATHLETICS</span>
<input class="num" type="text" name="attr_npc_athletics_base" placeholder="0"/>
<input type="hidden" name="attr_npc_athletics" value="@{strength_mod}"/>
</div>
<div class="row" data-i18n-list-item="deception"><span data-i18n="deception-u">DECEPTION</span>
<input class="num" type="text" name="attr_npc_deception_base" placeholder="0"/>
<input type="hidden" name="attr_npc_deception" value="@{charisma_mod}"/>
</div>
<div class="row" data-i18n-list-item="history"><span data-i18n="history-u">HISTORY</span>
<input class="num" type="text" name="attr_npc_history_base" placeholder="0"/>
<input type="hidden" name="attr_npc_history" value="@{intelligence_mod}"/>
</div>
</div>
<div class="col" data-i18n-list-sublist="">
<div class="row" data-i18n-list-item="insight"><span data-i18n="insight-u">INSIGHT</span>
<input class="num" type="text" name="attr_npc_insight_base" placeholder="0"/>
<input type="hidden" name="attr_npc_insight" value="@{wisdom_mod}"/>
</div>
<div class="row" data-i18n-list-item="intimidation"><span data-i18n="intimidation-u">INTIMIDATION</span>
<input class="num" type="text" name="attr_npc_intimidation_base" placeholder="0"/>
<input type="hidden" name="attr_npc_intimidation" value="@{charisma_mod}"/>
</div>
<div class="row" data-i18n-list-item="investigation"><span data-i18n="investigation-u">INVESTIGATION</span>
<input class="num" type="text" name="attr_npc_investigation_base" placeholder="0"/>
<input type="hidden" name="attr_npc_investigation" value="@{intelligence_mod}"/>
</div>
<div class="row" data-i18n-list-item="medicine"><span data-i18n="medicine-u">MEDICINE</span>
<input class="num" type="text" name="attr_npc_medicine_base" placeholder="0"/>
<input type="hidden" name="attr_npc_medicine" value="@{wisdom_mod}"/>
</div>
<div class="row" data-i18n-list-item="nature"><span data-i18n="nature-u">NATURE</span>
<input class="num" type="text" name="attr_npc_nature_base" placeholder="0"/>
<input type="hidden" name="attr_npc_nature" value="@{intelligence_mod}"/>
</div>
<div class="row" data-i18n-list-item="navigation"><span data-i18n="navigation-u">NAVIGATION</span>
<input class="num" type="text" name="attr_npc_navigation_base" placeholder="0"/>
<input type="hidden" name="attr_npc_navigation" value="@{intelligence_mod}"/>
</div>
<div class="row" data-i18n-list-item="perception"><span data-i18n="perception-u">PERCEPTION</span>
<input class="num" type="text" name="attr_npc_perception_base" placeholder="0"/>
<input type="hidden" name="attr_npc_perception" value="@{wisdom_mod}"/>
</div>
</div>
<div class="col" data-i18n-list-sublist="">
<div class="row" data-i18n-list-item="performance"><span data-i18n="performance-u">PERFORMANCE</span>
<input class="num" type="text" name="attr_npc_performance_base" placeholder="0"/>
<input type="hidden" name="attr_npc_performance" value="@{charisma_mod}"/>
</div>
<div class="row" data-i18n-list-item="persuasion"><span data-i18n="persuasion-u">PERSUASION</span>
<input class="num" type="text" name="attr_npc_persuasion_base" placeholder="0"/>
<input type="hidden" name="attr_npc_persuasion" value="@{charisma_mod}"/>
</div>
<div class="row" data-i18n-list-item="religion"><span data-i18n="religion-u">RELIGION</span>
<input class="num" type="text" name="attr_npc_religion_base" placeholder="0"/>
<input type="hidden" name="attr_npc_religion" value="@{intelligence_mod}"/>
</div>
<div class="row" data-i18n-list-item="sleight_of_hand"><span data-i18n="sleight_of_hand-u">SLEIGHT_OF_HAND</span>
<input class="num" type="text" name="attr_npc_sleight_of_hand_base" placeholder="0"/>
<input type="hidden" name="attr_npc_sleight_of_hand" value="@{dexterity_mod}"/>
</div>
<div class="row" data-i18n-list-item="stealth"><span data-i18n="stealth-u">STEALTH</span>
<input class="num" type="text" name="attr_npc_stealth_base" placeholder="0"/>
<input type="hidden" name="attr_npc_stealth" value="@{dexterity_mod}"/>
</div>
<div class="row" data-i18n-list-item="survival"><span data-i18n="survival-u">SURVIVAL</span>
<input class="num" type="text" name="attr_npc_survival_base" placeholder="0"/>
<input type="hidden" name="attr_npc_survival" value="@{wisdom_mod}"/>
</div>
</div>
</div>
<div class="row"><span data-i18n="damage-vuln-u">DAMAGE VULNERABILITIES</span>
<input type="text" name="attr_npc_vulnerabilities" placeholder="fire" data-i18n-placeholder="npc-dmg-vuln-place"/>
</div>
<div class="row"><span data-i18n="damage-res-u">DAMAGE RESISTANCES</span>
<input type="text" name="attr_npc_resistances" placeholder="cold" data-i18n-placeholder="npc-dmg-res-place"/>
</div>
<div class="row"><span data-i18n="damage-imm-u">DAMAGE IMMUNITIES</span>
<input type="text" name="attr_npc_immunities" placeholder="lighting, thunder" data-i18n-placeholder="npc-dmg-imm-place"/>
</div>
<div class="row"><span data-i18n="cond-imm-u">CONDITION IMMUNITIES</span>
<input type="text" name="attr_npc_condition_immunities" placeholder="charmed" data-i18n-placeholder="npc-cond-imm-place"/>
</div>
<div class="row"><span data-i18n="senses-u">SENSES</span>
<input type="text" name="attr_npc_senses" placeholder="darkvision 120ft., passive Perception 16" data-i18n-placeholder="npc-senses-place"/>
</div>
<div class="row"><span data-i18n="langs-u">LANGUAGES</span>
<input type="text" name="attr_npc_languages" placeholder="Abyssal, Common, Infernal" value="—" data-i18n-placeholder="npc-langs-place"/>
</div>
<div class="row"><span data-i18n="challenge-u">CHALLENGE</span>
<input class="num" type="text" name="attr_npc_challenge" placeholder="1"/><span data-i18n="xp-u">XP</span>
<input type="text" name="attr_npc_xp" placeholder="250"/>
</div>
<div class="row"><span data-i18n="prof-bonus-u">PROFICIENCY BONUS</span>
<input type="text" name="attr_npc_pb" placeholder="0" value="0"/>
</div>
<div class="row"><span data-i18n="token_size-u">TOKEN SIZE</span>
<input class="text" name="attr_token_size" value="1" placeholder="1"/>
</div>
<div class="row">
<input class="npcspellcastingflag" type="checkbox" name="attr_npcspellcastingflag" value="1"/><span data-i18n="spellcast-npc-u">SPELLCASTING NPC</span>
<div class="npcspelloptions"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spellcasting_ability" style="margin: 0px; width: auto; padding: 0px; height: 15px;">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="npcspelloptions">
<div class="row"><span data-i18n="glob-atk-mod:-u">GLOBAL MAGIC ATTACK MODIFIER:</span>
<input class="num" type="text" name="attr_globalmagicmod" placeholder="0" value="0"/>
</div>
<div class="row"><span data-i18n="magic-caster-lvl:-u">MAGIC CASTER LEVEL:</span>
<input class="num" type="text" name="attr_caster_level"/>
</div>
<div class="row"><span data-i18n="spell_dc_mod:-u">SPELL SAVE DC MOD:</span>
<input class="num" type="text" name="attr_spell_dc_mod" placeholder="0" value="0"/>
</div>
</div>
</div>
<div class="row">
<input type="checkbox" name="attr_npcbonusactionsflag" value="1"/><span data-i18n="has-bonusaction-u">HAS BONUS ACTIONS </span>
</div>
<div class="row">
<input type="checkbox" name="attr_npcreactionsflag" value="1"/><span data-i18n="has-reaction-u">HAS REACTIONS</span>
</div>
<div class="row"><span data-i18n="leg-actions:-u">LEGENDARY ACTIONS:</span>
<input class="num" type="text" name="attr_npc_legendary_actions" value="0" placeholder="0"/>
</div>
<div class="row">
<input type="checkbox" name="attr_npc_mythic_actions" value="1"/><span data-i18n="myt-actions:-u">MYTHIC ACTIONS:</span>
<textarea class="mythic-rules" name="attr_npc_mythic_actions_desc" placeholder="Mythic Action Rules. This creature can use the options below..."></textarea>
</div>
<div class="row title"><span data-i18n="gen-opts-u">GENERAL OPTIONS</span></div>
<div class="row"><span data-i18n="character-sheet-type-u"></span>
<select name="attr_charactersheet_type">
<option value="pc">PC</option>
<option value="npc">NPC</option>
<option value="vehicle">VEHICLE</option>
<option value="kingdom">KINGDOM</option>
</select>
<input type="hidden" name="attr_npc" value="0"/>
<input type="hidden" name="attr_is_vehicle" value="0"/>
</div>
<div class="row"><span data-i18n="roll-queries:-u">ROLL QUERIES:</span>
<select name="attr_rtype">
<option value="{{always=1}} {{r2=[[@{d20}" data-i18n="always-adv">Always Roll Advantage</option>
<option value="@{advantagetoggle}" data-i18n="adv-toggle">Advantage Toggle</option>
<option value="@{queryadvantage}" data-i18n="query-adv">Query Advantage</option>
<option value="{{normal=1}} {{r2=[[0d20" data-i18n="never-adv">Never Roll Advantage</option>
</select>
</div>
<div class="row"><span data-i18n="whisp-rolls-gm:-u">WHISPER ROLLS TO GM:</span>
<select name="attr_wtype">
<option value="" data-i18n="never-whisper-roll">Never Whisper Rolls</option>
<option value="@{whispertoggle}" data-i18n="toggle-roll-whisper">Whisper Toggle</option>
<option value="?{Whisper?|Public Roll,|Whisper Roll,/w gm }" data-i18n="query-whisper-roll">Query Whisper</option>
<option value="/w gm " data-i18n="always-whisper-roll">Always Whisper Rolls</option>
</select>
</div>
<div class="row"><span data-i18n="auto-dmg-roll:-u">AUTO DAMAGE ROLL:</span>
<select class="dtype" name="attr_dtype">
<option value="pick" data-i18n="never-dmg">Don&apos;t Auto Roll Damage</option>
<option value="full" data-i18n="always-dmg">Auto Roll Damage &amp; Crit</option>
</select>
</div>
<div class="row"><span data-i18n="npc-name-in-rolls:-u">NPC NAME IN ROLLS:</span>
<select name="attr_npc_name_flag">
<option value="{{name=@{npc_name}}}" data-i18n="show">Show</option>
<option value="0" data-i18n="hide">Hide</option>
</select>
</div>
<div class="row">
<input type="checkbox" name="attr_init_tiebreaker" value="@{dexterity}/100"/><span data-i18n="add-dex-tiebreaker-u">ADD DEX TIEBREAKER TO INITIATIVE</span>
</div>
</div>
<div class="display stat-block">
<div class="row title red">
<button type="roll" name="roll_npc_init" style="width: 100%;" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{init}}} {{mod=[[[[@{initiative_bonus}]][DEX]]]}} {{r1=[[@{d20}+[[@{initiative_bonus}]][DEX] &{tracker}]]}} {{normal=1}} {{type=Initiative}}"><span name="attr_npc_name"></span></button>
</div>
<div class="row subtitle" style="margin-top: -5px;"><span class="italics" name="attr_npc_type"></span></div>
<div class="triangle"></div>
<div class="row red" style="position:relative;"><span class="bold" data-i18n="ac">Armor Class</span><span class="num" name="attr_npc_ac"></span>
<input class="actype" type="hidden" name="attr_npc_actype" value=""/><span class="parens" name="attr_npc_actype"></span>
<button type="roll" name="roll_npc_init" style="position: absolute; right: 5px; height: 30px; width: 20px;" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{init}}} {{mod=[[[[@{initiative_bonus}]][DEX]]]}} {{r1=[[@{d20}+[[@{initiative_bonus}]][DEX] &{tracker}]]}} {{normal=1}} {{type=Initiative}}"><span class="npc-init-die">t</span><span style="display: block;">INIT</span></button>
</div>
<div class="row red"><span class="bold" data-i18n="hp">Hit Points</span><span class="num3 info" name="attr_hp_max"></span>
<button type="roll" name="roll_npc_hpformula" value="@{wtype}&{template:npc} {{type=Hit Points}} {{normal=1}} @{npc_name_flag} {{rname=^{hp}}} {{mod=@{npc_hpformula}}} {{r1=[[@{npc_hpformula}]]}} charname=@{character_name}"><span class="parens" name="attr_npc_hpformula" style="font-size: 12px; font-weight: normal;"></span></button>
</div>
<div class="row red"><span class="bold" data-i18n="speed">Speed</span><span class="info" name="attr_npc_speed"></span></div>
<div class="triangle"></div>
<div class="row red">
<div class="attr-container">
<button type="roll" name="roll_npc_str" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{strength}}} {{mod=[[[[@{strength_mod}]][STR]]]}} {{r1=[[@{d20}+[[@{strength_mod}]][STR]]]}} @{rtype}+[[@{strength_mod}]][STR]]]}} {{type=Ability}}"><span class="bold" data-i18n="str-u">STR</span><span name="attr_strength"></span></button>
<input class="negativeflag" type="hidden" name="attr_npc_str_negative" value="0"/><span class="npcattr" name="attr_strength_mod"></span>
</div>
<div class="attr-container">
<button type="roll" name="roll_npc_dex" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{dexterity}}} {{mod=[[[[@{dexterity_mod}]][DEX]]]}} {{r1=[[@{d20}+[[@{dexterity_mod}]][DEX]]]}} @{rtype}+[[@{dexterity_mod}]][DEX]]]}} {{type=Ability}}"><span class="bold" data-i18n="dex-u">DEX</span><span name="attr_dexterity"></span></button>
<input class="negativeflag" type="hidden" name="attr_npc_dex_negative" value="0"/><span class="npcattr" name="attr_dexterity_mod"></span>
</div>
<div class="attr-container">
<button type="roll" name="roll_npc_con" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{constitution}}} {{mod=[[[[@{constitution_mod}]][CON]]]}} {{r1=[[@{d20}+[[@{constitution_mod}]][CON]]]}} @{rtype}+[[@{constitution_mod}]][CON]]]}} {{type=Ability}}"><span class="bold" data-i18n="con-u">CON</span><span name="attr_constitution"></span></button>
<input class="negativeflag" type="hidden" name="attr_npc_con_negative" value="0"/><span class="npcattr" name="attr_constitution_mod"></span>
</div>
<div class="attr-container">
<button type="roll" name="roll_npc_int" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{intelligence}}} {{mod=[[[[@{intelligence_mod}]][INT]]]}} {{r1=[[@{d20}+[[@{intelligence_mod}]][INT]]]}} @{rtype}+[[@{intelligence_mod}]][INT]]]}} {{type=Ability}}"><span class="bold" data-i18n="int-u">INT</span><span name="attr_intelligence"></span></button>
<input class="negativeflag" type="hidden" name="attr_npc_int_negative" value="0"/><span class="npcattr" name="attr_intelligence_mod"></span>
</div>
<div class="attr-container">
<button type="roll" name="roll_npc_wis" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{wisdom}}} {{mod=[[[[@{wisdom_mod}]][WIS]]]}} {{r1=[[@{d20}+[[@{wisdom_mod}]][WIS]]]}} @{rtype}+[[@{wisdom_mod}]][WIS]]]}} {{type=Ability}}"><span class="bold" data-i18n="wis-u">WIS</span><span name="attr_wisdom"></span></button>
<input class="negativeflag" type="hidden" name="attr_npc_wis_negative" value="0"/><span class="npcattr" name="attr_wisdom_mod"></span>
</div>
<div class="attr-container">
<button type="roll" name="roll_npc_cha" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{charisma}}} {{mod=[[[[@{charisma_mod}]][CHA]]]}} {{r1=[[@{d20}+[[@{charisma_mod}]][CHA]]]}} @{rtype}+[[@{charisma_mod}]][CHA]]]}} {{type=Ability}}"><span class="bold" data-i18n="cha-u">CHA</span><span name="attr_charisma"></span></button>
<input class="negativeflag" type="hidden" name="attr_npc_cha_negative" value="0"/><span class="npcattr" name="attr_charisma_mod"></span>
</div>
</div>
<div class="triangle"></div>
<input class="flag" type="hidden" name="attr_npc_saving_flag" value=""/>
<div class="row saving red"><span class="bold" data-i18n="saving-throw">Saving Throws</span>
<input class="flag" type="hidden" name="attr_npc_str_save_flag" value="0"/>
<button type="roll" name="roll_npc_str_save" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{strength-save}}} {{mod=[[[[@{npc_str_save}]][STR SAVE]]]}} {{r1=[[@{d20}+[[@{npc_str_save}]][STR SAVE]]]}} @{rtype}+[[@{npc_str_save}]][STR SAVE]]]}} {{type=Save}}"><span data-i18n="str">Str</span><span class="stat" name="attr_npc_str_save"></span></button>
<input class="flag" type="hidden" name="attr_npc_dex_save_flag" value="0"/>
<button type="roll" name="roll_npc_dex_save" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{dexterity-save}}} {{mod=[[[[@{npc_dex_save}]][DEX SAVE]]]}} {{r1=[[@{d20}+[[@{npc_dex_save}]][DEX SAVE]]]}} @{rtype}+[[@{npc_dex_save}]][DEX SAVE]]]}} {{type=Save}}"><span data-i18n="dex">Dex</span><span class="stat" name="attr_npc_dex_save"></span></button>
<input class="flag" type="hidden" name="attr_npc_con_save_flag" value="0"/>
<button type="roll" name="roll_npc_con_save" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{constitution-save}}} {{mod=[[[[@{npc_con_save}]][CON SAVE]]]}} {{r1=[[@{d20}+[[@{npc_con_save}]][CON SAVE]]]}} @{rtype}+[[@{npc_con_save}]][CON SAVE]]]}} {{type=Save}}"><span data-i18n="con">Con</span><span class="stat" name="attr_npc_con_save"></span></button>
<input class="flag" type="hidden" name="attr_npc_int_save_flag" value="0"/>
<button type="roll" name="roll_npc_int_save" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{intelligence-save}}} {{mod=[[[[@{npc_int_save}]][INT SAVE]]]}} {{r1=[[@{d20}+[[@{npc_int_save}]][INT SAVE]]]}} @{rtype}+[[@{npc_int_save}]][INT SAVE]]]}} {{type=Save}}"><span data-i18n="int">Int</span><span class="stat" name="attr_npc_int_save"></span></button>
<input class="flag" type="hidden" name="attr_npc_wis_save_flag" value="0"/>
<button type="roll" name="roll_npc_wis_save" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{wisdom-save}}} {{mod=[[[[@{npc_wis_save}]][WIS SAVE]]]}} {{r1=[[@{d20}+[[@{npc_wis_save}]][WIS SAVE]]]}} @{rtype}+[[@{npc_wis_save}]][WIS SAVE]]]}} {{type=Save}}"><span data-i18n="wis">Wis</span><span class="stat" name="attr_npc_wis_save"></span></button>
<input class="flag" type="hidden" name="attr_npc_cha_save_flag" value="0"/>
<button type="roll" name="roll_npc_cha_save" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{charisma-save}}} {{mod=[[[[@{npc_cha_save}]][CHA SAVE]]]}} {{r1=[[@{d20}+[[@{npc_cha_save}]][CHA SAVE]]]}} @{rtype}+[[@{npc_cha_save}]][CHA SAVE]]]}} {{type=Save}}"><span data-i18n="cha">Cha</span><span class="stat" name="attr_npc_cha_save"></span></button>
</div>
<input class="flag" type="hidden" name="attr_npc_skills_flag" value=""/>
<div class="row skills skills-list red" data-i18n-list="skills-list"><span class="bold" data-i18n="skills">Skills</span><span data-i18n-list-item="acrobatics">
<input class="flag" type="hidden" name="attr_npc_acrobatics_flag" value="0"/>
<button type="roll" name="roll_npc_acrobatics" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{acrobatics}}} {{mod=@{npc_acrobatics}}} {{r1=[[@{d20}+@{npc_acrobatics}]]}} @{rtype}+@{npc_acrobatics}]]}} {{type=Skill}}"><span data-i18n="acrobatics">Acrobatics</span><span class="stat" name="attr_npc_acrobatics"></span></button></span><span data-i18n-list-item="animal_handling">
<input class="flag" type="hidden" name="attr_npc_animal_handling_flag" value="0"/>
<button type="roll" name="roll_npc_animal_handling" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{animal_handling}}} {{mod=@{npc_animal_handling}}} {{r1=[[@{d20}+@{npc_animal_handling}]]}} @{rtype}+@{npc_animal_handling}]]}} {{type=Skill}}"><span data-i18n="animal_handling">Animal handling</span><span class="stat" name="attr_npc_animal_handling"></span></button></span><span data-i18n-list-item="arcana">
<input class="flag" type="hidden" name="attr_npc_arcana_flag" value="0"/>
<button type="roll" name="roll_npc_arcana" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{arcana}}} {{mod=@{npc_arcana}}} {{r1=[[@{d20}+@{npc_arcana}]]}} @{rtype}+@{npc_arcana}]]}} {{type=Skill}}"><span data-i18n="arcana">Arcana</span><span class="stat" name="attr_npc_arcana"></span></button></span><span data-i18n-list-item="athletics">
<input class="flag" type="hidden" name="attr_npc_athletics_flag" value="0"/>
<button type="roll" name="roll_npc_athletics" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{athletics}}} {{mod=@{npc_athletics}}} {{r1=[[@{d20}+@{npc_athletics}]]}} @{rtype}+@{npc_athletics}]]}} {{type=Skill}}"><span data-i18n="athletics">Athletics</span><span class="stat" name="attr_npc_athletics"></span></button></span><span data-i18n-list-item="deception">
<input class="flag" type="hidden" name="attr_npc_deception_flag" value="0"/>
<button type="roll" name="roll_npc_deception" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{deception}}} {{mod=@{npc_deception}}} {{r1=[[@{d20}+@{npc_deception}]]}} @{rtype}+@{npc_deception}]]}} {{type=Skill}}"><span data-i18n="deception">Deception</span><span class="stat" name="attr_npc_deception"></span></button></span><span data-i18n-list-item="history">
<input class="flag" type="hidden" name="attr_npc_history_flag" value="0"/>
<button type="roll" name="roll_npc_history" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{history}}} {{mod=@{npc_history}}} {{r1=[[@{d20}+@{npc_history}]]}} @{rtype}+@{npc_history}]]}} {{type=Skill}}"><span data-i18n="history">History</span><span class="stat" name="attr_npc_history"></span></button></span><span data-i18n-list-item="insight">
<input class="flag" type="hidden" name="attr_npc_insight_flag" value="0"/>
<button type="roll" name="roll_npc_insight" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{insight}}} {{mod=@{npc_insight}}} {{r1=[[@{d20}+@{npc_insight}]]}} @{rtype}+@{npc_insight}]]}} {{type=Skill}}"><span data-i18n="insight">Insight</span><span class="stat" name="attr_npc_insight"></span></button></span><span data-i18n-list-item="intimidation">
<input class="flag" type="hidden" name="attr_npc_intimidation_flag" value="0"/>
<button type="roll" name="roll_npc_intimidation" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{intimidation}}} {{mod=@{npc_intimidation}}} {{r1=[[@{d20}+@{npc_intimidation}]]}} @{rtype}+@{npc_intimidation}]]}} {{type=Skill}}"><span data-i18n="intimidation">Intimidation</span><span class="stat" name="attr_npc_intimidation"></span></button></span><span data-i18n-list-item="investigation">
<input class="flag" type="hidden" name="attr_npc_investigation_flag" value="0"/>
<button type="roll" name="roll_npc_investigation" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{investigation}}} {{mod=@{npc_investigation}}} {{r1=[[@{d20}+@{npc_investigation}]]}} @{rtype}+@{npc_investigation}]]}} {{type=Skill}}"><span data-i18n="investigation">Investigation</span><span class="stat" name="attr_npc_investigation"></span></button></span><span data-i18n-list-item="medicine">
<input class="flag" type="hidden" name="attr_npc_medicine_flag" value="0"/>
<button type="roll" name="roll_npc_medicine" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{medicine}}} {{mod=@{npc_medicine}}} {{r1=[[@{d20}+@{npc_medicine}]]}} @{rtype}+@{npc_medicine}]]}} {{type=Skill}}"><span data-i18n="medicine">Medicine</span><span class="stat" name="attr_npc_medicine"></span></button></span><span data-i18n-list-item="nature">
<input class="flag" type="hidden" name="attr_npc_nature_flag" value="0"/>
<button type="roll" name="roll_npc_nature" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{nature}}} {{mod=@{npc_nature}}} {{r1=[[@{d20}+@{npc_nature}]]}} @{rtype}+@{npc_nature}]]}} {{type=Skill}}"><span data-i18n="nature">Nature</span><span class="stat" name="attr_npc_nature"></span></button></span><span data-i18n-list-item="navigation">
<input class="flag" type="hidden" name="attr_npc_navigation_flag" value="0"/>
<button type="roll" name="roll_npc_navigation" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{navigation}}} {{mod=@{npc_navigation}}} {{r1=[[@{d20}+@{npc_navigation}]]}} @{rtype}+@{npc_navigation}]]}} {{type=Skill}}"><span data-i18n="navigation">Navigation</span><span class="stat" name="attr_npc_navigation"></span></button></span><span data-i18n-list-item="perception">
<input class="flag" type="hidden" name="attr_npc_perception_flag" value="0"/>
<button type="roll" name="roll_npc_perception" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{perception}}} {{mod=@{npc_perception}}} {{r1=[[@{d20}+@{npc_perception}]]}} @{rtype}+@{npc_perception}]]}} {{type=Skill}}"><span data-i18n="perception">Perception</span><span class="stat" name="attr_npc_perception"></span></button></span><span data-i18n-list-item="performance">
<input class="flag" type="hidden" name="attr_npc_performance_flag" value="0"/>
<button type="roll" name="roll_npc_performance" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{performance}}} {{mod=@{npc_performance}}} {{r1=[[@{d20}+@{npc_performance}]]}} @{rtype}+@{npc_performance}]]}} {{type=Skill}}"><span data-i18n="performance">Performance</span><span class="stat" name="attr_npc_performance"></span></button></span><span data-i18n-list-item="persuasion">
<input class="flag" type="hidden" name="attr_npc_persuasion_flag" value="0"/>
<button type="roll" name="roll_npc_persuasion" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{persuasion}}} {{mod=@{npc_persuasion}}} {{r1=[[@{d20}+@{npc_persuasion}]]}} @{rtype}+@{npc_persuasion}]]}} {{type=Skill}}"><span data-i18n="persuasion">Persuasion</span><span class="stat" name="attr_npc_persuasion"></span></button></span><span data-i18n-list-item="religion">
<input class="flag" type="hidden" name="attr_npc_religion_flag" value="0"/>
<button type="roll" name="roll_npc_religion" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{religion}}} {{mod=@{npc_religion}}} {{r1=[[@{d20}+@{npc_religion}]]}} @{rtype}+@{npc_religion}]]}} {{type=Skill}}"><span data-i18n="religion">Religion</span><span class="stat" name="attr_npc_religion"></span></button></span><span data-i18n-list-item="sleight_of_hand">
<input class="flag" type="hidden" name="attr_npc_sleight_of_hand_flag" value="0"/>
<button type="roll" name="roll_npc_sleight_of_hand" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{sleight_of_hand}}} {{mod=@{npc_sleight_of_hand}}} {{r1=[[@{d20}+@{npc_sleight_of_hand}]]}} @{rtype}+@{npc_sleight_of_hand}]]}} {{type=Skill}}"><span data-i18n="sleight_of_hand">Sleight_of_hand</span><span class="stat" name="attr_npc_sleight_of_hand"></span></button></span><span data-i18n-list-item="stealth">
<input class="flag" type="hidden" name="attr_npc_stealth_flag" value="0"/>
<button type="roll" name="roll_npc_stealth" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{stealth}}} {{mod=@{npc_stealth}}} {{r1=[[@{d20}+@{npc_stealth}]]}} @{rtype}+@{npc_stealth}]]}} {{type=Skill}}"><span data-i18n="stealth">Stealth</span><span class="stat" name="attr_npc_stealth"></span></button></span><span data-i18n-list-item="survival">
<input class="flag" type="hidden" name="attr_npc_survival_flag" value="0"/>
<button type="roll" name="roll_npc_survival" value="@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{survival}}} {{mod=@{npc_survival}}} {{r1=[[@{d20}+@{npc_survival}]]}} @{rtype}+@{npc_survival}]]}} {{type=Skill}}"><span data-i18n="survival">Survival</span><span class="stat" name="attr_npc_survival"></span></button></span>
</div>
<input class="flag" type="hidden" name="attr_npc_vulnerabilities"/>
<div class="row status vulnerabilities red"><span class="bold" data-i18n="dmg-vuln">Damage Vulnerabilities</span><span class="info" name="attr_npc_vulnerabilities"></span></div>
<input class="flag" type="hidden" name="attr_npc_resistances"/>
<div class="row status resistances red"><span class="bold" data-i18n="dmg-res">Damage Resistances</span><span class="info" name="attr_npc_resistances"></span></div>
<input class="flag" type="hidden" name="attr_npc_immunities"/>
<div class="row status immunities red"><span class="bold" data-i18n="dmg-imm">Damage Immunities</span><span class="info" name="attr_npc_immunities"></span></div>
<input class="flag" type="hidden" name="attr_npc_condition_immunities"/>
<div class="row status condition_immunities red"><span class="bold" data-i18n="cond-imm">Condition Immunities</span><span class="info" name="attr_npc_condition_immunities"></span></div>
<div class="row senses red"><span class="bold" data-i18n="senses">Senses</span><span class="info" name="attr_npc_senses"></span></div>
<div class="row languages red"><span class="bold" data-i18n="langs">Languages</span><span class="info languages" name="attr_npc_languages" value="—"></span></div>
<div class="row red"><span class="bold" data-i18n="challenge">Challenge</span><span class="num" name="attr_npc_challenge"></span><span class="parens xp info" name="attr_npc_xp"></span></div>
<input class="toggle-npc-pb" type="hidden" name="attr_npc_pb"/>
<div class="row red"><span class="bold" data-i18n="prof-bonus">Proficiency Bonus</span><span class="info" name="attr_npc_pb"></span></div>
<div class="triangle"></div>
<div class="row traits">
<fieldset class="repeating_npctrait"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_npctrait_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="trait">
<div class="npc_options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_name" placeholder="False Appearance." data-i18n-placeholder="npc-trait-name-place"/>
</div>
<div class="row">
<textarea name="attr_description" placeholder="If the dragon fails..." data-i18n-placeholder="npc-trait-desc-place"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<button class="pick btn ui-draggable" type="roll" name="roll_npc_roll_output" value="@{wtype}&amp;{template:traits} @{charname_output} {{name=@{name}}} {{description=@{description}}}">
<div class="row wide"><span class="bold wide" name="attr_name"></span></div>
</button><span class="description" name="attr_description"></span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="actions_container"></div>
<input class="dflag" type="hidden" name="attr_dflag" value="pick"/>
<div class="row actions">
<div class="row actiontitle" style="position: relative;"><span class="title red" data-i18n="actions">Actions</span></div>
<fieldset class="repeating_npcaction"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_npcaction_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_name"/>
</div>
<div class="row">
<input type="checkbox" name="attr_attack_flag" value="on"/><span data-i18n="attack-u">ATTACK</span>
</div>
<input class="attack_options" type="hidden" name="attr_attack_flag" value="0"/>
<div class="row attack_option"><span data-i18n="type:-u">TYPE:</span>
<select name="attr_attack_type">
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select><span style="margin-left: 15px;" data-i18n="range-reach:-u">RANGE/REACH:</span>
<input type="text" name="attr_attack_range" placeholder="5 ft." style="width: 100px;" data-i18n-placeholder="range-reach-place"/>
</div>
<div class="row attack_option"><span data-i18n="to-hit:-u">TO HIT:</span>
<input type="text" name="attr_attack_tohit" value="0" placeholder="5" style="width: 30px;"/><span style="margin-left: 15px;" data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_attack_target" placeholder="One target" style="width: 100px;" data-i18n-placeholder="to-hit-place"/>
</div>
<input type="hidden" name="attr_damage_flag"/>
<div class="row attack_option"><span data-i18n="on-hit:-u">ON HIT:</span>
<input type="text" name="attr_attack_damage" placeholder="1d10" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype" placeholder="slashing" style="width: 100px;" data-i18n-placeholder="on-hit-place"/>
<input type="hidden" name="attr_attack_crit"/>
</div>
<div class="row attack_option"><span data-i18n="on-hit2:-u">ON HIT 2:</span>
<input type="text" name="attr_attack_damage2" placeholder="2d6+5" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype2" placeholder="fire" style="width: 100px;" data-i18n-placeholder="on-hit-2-place"/>
<input type="hidden" name="attr_attack_crit2"/>
</div>
<div class="row attack_option"><span data-i18n="desc:-u">DESCRIPTION:</span>
<select name="attr_show_desc">
<option value="@{description}" data-i18n="show">Show</option>
<option value=" " data-i18n="hide">Hide</option>
</select>
</div>
<div class="row">
<textarea name="attr_description"></textarea>
</div>
<div class="row attack_option actiondamage"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_attack_damage" placeholder="1d10" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype" placeholder="slashing" style="width: 100px;" data-i18n-placeholder="on-hit-place"/>
<input type="hidden" name="attr_attack_crit"/>
</div>
</div>
<div class="display">
<button class="pick" type="roll" name="roll_npc_action" value="@{rollbase}">
<div class="row wide"><span class="bold wide" name="attr_name"></span></div>
</button>
<input class="attack_options" type="hidden" name="attr_attack_flag"/>
<div class="row attack attack_option"><span class="italics" name="attr_attack_type"></span><span class="italics"> Weapon Attack:</span><span name="attr_attack_tohitrange"></span></div>
<div class="row attack attack_option"><span class="italics" style="font-size: 13px; font-weight: normal; vertical-align: middle;" data-i18n="hit:">Hit:</span><span name="attr_attack_onhit" style="vertical-align: middle;"></span></div><span class="description" name="attr_description"></span>
</div>
<input type="hidden" name="attr_rollbase"/>
<button type="roll" style="display: none;" name="roll_npc_dmg" value="@{wtype}&{template:npcdmg} @{damage_flag} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{description=@{show_desc}}}"></button>
<button type="roll" style="display: none;" name="roll_npc_crit" value="@{wtype}&{template:npcdmg} @{damage_flag} {{crit=1}} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}} {{description=@{show_desc}}}"></button>
</div>
</fieldset>
</div>
<!--BONUS ACTIONS-->
<input class="bonusaction_flag" type="hidden" name="attr_npcbonusactionsflag"/>
<div class="row actions bonusactions">
<div class="row actiontitle" style="position: relative;"><span class="title red" data-i18n="bonus-actions">Bonus Actions</span></div>
<fieldset class="repeating_npcbonusaction"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_npcbonusaction_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="action">
<div class="npc_options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_name"/>
</div>
<div class="row">
<input type="checkbox" name="attr_attack_flag" value="on"/><span data-i18n="attack-u">ATTACK</span>
</div>
<input class="attack_options" type="hidden" name="attr_attack_flag" value="0"/>
<div class="row attack_option"><span data-i18n="type:-u">TYPE:</span>
<select name="attr_attack_type">
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select><span style="margin-left: 15px;" data-i18n="range-reach:-u">RANGE/REACH:</span>
<input type="text" name="attr_attack_range" placeholder="5 ft." style="width: 100px;" data-i18n-placeholder="range-reach-place"/>
</div>
<div class="row attack_option"><span data-i18n="to-hit:-u">TO HIT:</span>
<input type="text" name="attr_attack_tohit" value="0" placeholder="5" style="width: 30px;"/><span style="margin-left: 15px;" data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_attack_target" placeholder="One target" style="width: 100px;" data-i18n-placeholder="to-hit-place"/>
</div>
<input type="hidden" name="attr_damage_flag"/>
<div class="row attack_option"><span data-i18n="on-hit:-u">ON HIT:</span>
<input type="text" name="attr_attack_damage" placeholder="1d10" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype" placeholder="slashing" style="width: 100px;" data-i18n-placeholder="on-hit-place"/>
<input type="hidden" name="attr_attack_crit"/>
</div>
<div class="row attack_option"><span data-i18n="on-hit2:-u">ON HIT 2:</span>
<input type="text" name="attr_attack_damage2" placeholder="2d6+5" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype2" placeholder="fire" style="width: 100px;" data-i18n-placeholder="on-hit-2-place"/>
<input type="hidden" name="attr_attack_crit2"/>
</div>
<div class="row attack_option"><span data-i18n="desc:-u">DESCRIPTION:</span>
<select name="attr_show_desc">
<option value="@{description}" data-i18n="show">Show</option>
<option value=" " data-i18n="hide">Hide</option>
</select>
</div>
<div class="row">
<textarea name="attr_description"></textarea>
</div>
<div class="row attack_option actiondamage"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_attack_damage" placeholder="1d10" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype" placeholder="slashing" style="width: 100px;" data-i18n-placeholder="on-hit-place"/>
<input type="hidden" name="attr_attack_crit"/>
</div>
</div>
<div class="display">
<button class="pick" type="roll" name="roll_npc_action" value="@{rollbase}">
<div class="row wide"><span class="bold wide" name="attr_name"></span></div>
</button>
<input class="attack_options" type="hidden" name="attr_attack_flag"/>
<div class="row attack attack_option"><span class="italics" name="attr_attack_type"></span><span class="italics"> Weapon Attack:</span><span name="attr_attack_tohitrange"></span></div>
<div class="row attack attack_option"><span class="italics" style="font-size: 13px; font-weight: normal; vertical-align: middle;" data-i18n="hit:">Hit:</span><span name="attr_attack_onhit" style="vertical-align: middle;"></span></div><span class="description" name="attr_description"></span>
</div>
<input type="hidden" name="attr_rollbase"/>
<button type="roll" style="display: none;" name="roll_npc_dmg" value="@{wtype}&{template:npcdmg} @{damage_flag} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{description=@{show_desc}}}"></button>
<button type="roll" style="display: none;" name="roll_npc_crit" value="@{wtype}&{template:npcdmg} @{damage_flag} {{crit=1}} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}} {{description=@{show_desc}}}"></button>
</div>
</fieldset>
</div>
<input class="reaction_flag" type="hidden" name="attr_npcreactionsflag"/>
<div class="row actions reaction">
<div class="row actiontitle"><span class="title red" data-i18n="reactions">Reactions</span></div>
<fieldset class="repeating_npcreaction">
<div class="trait"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_npcreaction_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="npc_options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_name" placeholder="Parry." data-i18n-placeholder="npc-repeating-name-place"/>
</div>
<div class="row">
<textarea name="attr_description" placeholder="The creature adds 2 to its AC against..." data-i18n-placeholder="npc-repeating-desc-place"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<button class="pick btn ui-draggable" type="roll" name="roll_npc_roll_output" value="@{wtype}&amp;{template:traits} @{charname_output} {{name=@{name}}} {{description=@{description}}}">
<div class="row wide"></div><span class="bold wide" name="attr_name"></span>
</button><span class="description" name="attr_description"></span>
</div>
</div>
</fieldset>
</div>
<input class="legendary_flag" type="hidden" name="attr_npc_legendary_actions" value="0"/>
<div class="row actions legendary">
<div class="row actiontitle"><span class="title red" data-i18n="leg-actions">Legendary Actions</span></div>
<div class="row">
<div class="legendaryactions" data-i18n="leg-actions-desc"><span name="attr_npc_legendary_actions_desc"></span></div>
</div>
<fieldset class="repeating_npcaction-l">
<div class="action"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_npcaction-l_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="npc_options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_name"/>
</div>
<div class="row">
<input type="checkbox" name="attr_attack_flag" value="on"/><span data-i18n="attack-u">ATTACK</span>
</div>
<input class="attack_options" type="hidden" name="attr_attack_flag" value="0"/>
<div class="row attack_option"><span data-i18n="type:-u">TYPE:</span>
<select name="attr_attack_type">
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select><span style="margin-left: 15px;" data-i18n="range-reach:-u">RANGE/REACH:</span>
<input type="text" name="attr_attack_range" placeholder="5 ft." style="width: 100px;" data-i18n-placeholder="range-reach-place"/>
</div>
<div class="row attack_option"><span data-i18n="to-hit:-u">TO HIT:</span>
<input type="text" name="attr_attack_tohit" value="0" placeholder="5" style="width: 30px;"/><span style="margin-left: 15px;" data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_attack_target" placeholder="One target" style="width: 100px;" data-i18n-placeholder="to-hit-place"/>
</div>
<input type="hidden" name="attr_damage_flag"/>
<div class="row attack_option"><span data-i18n="on-hit:-u">ON HIT:</span>
<input type="text" name="attr_attack_damage" placeholder="1d10" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype" placeholder="slashing" style="width: 100px;" data-i18n-placeholder="on-hit-place"/>
<input type="hidden" name="attr_attack_crit"/>
</div>
<div class="row attack_option"><span data-i18n="on-hit2:-u">ON HIT 2:</span>
<input type="text" name="attr_attack_damage2" placeholder="2d6+5" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype2" placeholder="fire" style="width: 100px;" data-i18n-placeholder="on-hit-2-place"/>
<input type="hidden" name="attr_attack_crit2"/>
</div>
<div class="row attack_option"><span data-i18n="desc:-u">DESCRIPTION:</span>
<select name="attr_show_desc">
<option value="@{description}" data-i18n="show">Show</option>
<option value=" " data-i18n="hide">Hide</option>
</select>
</div>
<div class="row">
<textarea name="attr_description"></textarea>
</div>
</div>
<div class="display">
<button class="pick" type="roll" name="roll_npc_action" value="@{rollbase}">
<div class="row wide"><span class="bold wide" name="attr_name"></span></div>
</button>
<input class="attack_options" type="hidden" name="attr_attack_flag"/>
<div class="row attack attack_option"><span class="italics" name="attr_attack_type"></span><span class="italics"> Weapon Attack:</span><span name="attr_attack_tohitrange"></span></div>
<div class="row attack attack_option"><span class="italics" style="font-size: 13px; font-weight: normal; vertical-align: middle;" data-i18n="hit:">Hit:</span><span name="attr_attack_onhit" style="vertical-align: middle;"></span></div><span class="description" name="attr_description"></span>
</div>
<input type="hidden" name="attr_rollbase"/>
<button type="roll" style="display: none;" name="roll_npc_dmg" value="@{wtype}&{template:npcdmg} @{damage_flag} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}}"></button>
<button type="roll" style="display: none;" name="roll_npc_crit" value="@{wtype}&{template:npcdmg} @{damage_flag} {{crit=1}} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}}"></button>
</div>
</fieldset>
</div>
<input class="mythic_flag" type="hidden" name="attr_npc_mythic_actions" value="0"/>
<div class="row actions mythic">
<div class="row actiontitle"><span class="title red" data-i18n="myt-actions">Mythic Actions</span></div>
<div class="row">
<div class="legendaryactions" data-i18n="myt-actions-desc"><span name="attr_npc_mythic_actions_desc"></span></div>
</div>
<fieldset class="repeating_npcaction-m">
<div class="action"><span class="editing-repeating" name="attr_name"></span>
<button class="toggle_repeating_npcaction-m_edit npc_options-flag" type="action" name="act_edit"></button><span>y</span>
<div class="npc_options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_name"/>
</div>
<div class="row">
<input type="checkbox" name="attr_attack_flag" value="on"/><span data-i18n="attack-u">ATTACK</span>
</div>
<input class="attack_options" type="hidden" name="attr_attack_flag" value="0"/>
<div class="row attack_option"><span data-i18n="type:-u">TYPE:</span>
<select name="attr_attack_type">
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select><span style="margin-left: 15px;" data-i18n="range-reach:-u">RANGE/REACH:</span>
<input type="text" name="attr_attack_range" placeholder="5 ft." style="width: 100px;" data-i18n-placeholder="range-reach-place"/>
</div>
<div class="row attack_option"><span data-i18n="to-hit:-u">TO HIT:</span>
<input type="text" name="attr_attack_tohit" value="0" placeholder="5" style="width: 30px;"/><span style="margin-left: 15px;" data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_attack_target" placeholder="One target" style="width: 100px;" data-i18n-placeholder="to-hit-place"/>
</div>
<input type="hidden" name="attr_damage_flag"/>
<div class="row attack_option"><span data-i18n="on-hit:-u">ON HIT:</span>
<input type="text" name="attr_attack_damage" placeholder="1d10" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype" placeholder="slashing" style="width: 100px;" data-i18n-placeholder="on-hit-place"/>
<input type="hidden" name="attr_attack_crit"/>
</div>
<div class="row attack_option"><span data-i18n="on-hit2:-u">ON HIT 2:</span>
<input type="text" name="attr_attack_damage2" placeholder="2d6+5" style="width: 65px;"/>
<input type="text" name="attr_attack_damagetype2" placeholder="fire" style="width: 100px;" data-i18n-placeholder="on-hit-2-place"/>
<input type="hidden" name="attr_attack_crit2"/>
</div>
<div class="row attack_option"><span data-i18n="desc:-u">DESCRIPTION:</span>
<select name="attr_show_desc">
<option value="@{description}" data-i18n="show">Show</option>
<option value=" " data-i18n="hide">Hide</option>
</select>
</div>
<div class="row">
<textarea name="attr_description"></textarea>
</div>
</div>
<div class="display">
<button class="pick" type="roll" name="roll_npc_action" value="@{rollbase}">
<div class="row wide"><span class="bold wide" name="attr_name"></span></div>
</button>
<input class="attack_options" type="hidden" name="attr_attack_flag"/>
<div class="row attack attack_option"><span class="italics" name="attr_attack_type"></span><span class="italics"> Weapon Attack:</span><span name="attr_attack_tohitrange"></span></div>
<div class="row attack attack_option"><span class="italics" style="font-size: 13px; font-weight: normal; vertical-align: middle;" data-i18n="hit:">Hit:</span><span name="attr_attack_onhit" style="vertical-align: middle;"></span></div><span class="description" name="attr_description"></span>
</div>
<input type="hidden" name="attr_rollbase"/>
<button type="roll" style="display: none;" name="roll_npc_dmg" value="@{wtype}&{template:npcdmg} @{damage_flag} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}}"></button>
<button type="roll" style="display: none;" name="roll_npc_crit" value="@{wtype}&{template:npcdmg} @{damage_flag} {{crit=1}} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}}"></button>
</div>
</fieldset>
</div>
</div>
</div>
<div class="container pc">
<input class="tab-button core" name="attr_tab" type="radio" value="core" checked="checked"/><span data-i18n="core-u">CORE</span>
<input class="tab-button bio" name="attr_tab" type="radio" value="bio"/><span data-i18n="bio-u">BIO</span>
<input class="tab-button spells" name="attr_tab" type="radio" value="spells"/><span data-i18n="spells-u">SPELLS</span>
<input class="tab-button options" name="attr_tab" type="radio" value="options"/><span style="font-family: pictos">y</span>
<div class="page core">
<div class="header">
<div class="name-container"><img src="https://app.roll20.net/images/dndstyling/srd5_360.png" style="padding-bottom: 5px;" alt="SRD5 by Roll20"/>
<input type="text" name="attr_character_name" style="width: 100%;"/><span class="label" data-i18n="char-name-u">CHARACTER NAME</span>
</div>
<input class="options-flag" type="checkbox" name="attr_options-class-selection" checked="checked"/><span>y</span>
<div class="header-info options">
<div class="row" style="border-top: 2px dashed gold;"><span data-i18n="class:-u">CLASS:</span>
<input class="flag" type="hidden" name="attr_custom_class"/>
<select class="hiding class" name="attr_class">
<option value="" data-i18n="choose">Choose</option>
<option value="Artificer" data-i18n="artificer"></option>
<option value="Bard" data-i18n="bard"></option>
<option value="Barbarian" data-i18n="barbarian"></option>
<option value="Beastheart" data-i18n="beastheart"></option>
<option value="Cleric" data-i18n="cleric"></option>
<option value="Druid" data-i18n="druid"></option>
<option value="Fighter" data-i18n="fighter"></option>
<option value="Illrigger" data-i18n="illrigger"></option>
<option value="Monk" data-i18n="monk"></option>
<option value="Paladin" data-i18n="paladin"></option>
<option value="Ranger" data-i18n="ranger"></option>
<option value="Rogue" data-i18n="rogue"></option>
<option value="Sorcerer" data-i18n="sorcerer"></option>
<option value="Tactician" data-i18n="tactician"></option>
<option value="Warlock" data-i18n="warlock"></option>
<option value="Wizard" data-i18n="wizard"></option>
</select>
<input class="showing" type="text" name="attr_cust_classname" style="width: 90px;"/><span data-i18n="subclass:-u">SUBCLASS:</span>
<input type="text" name="attr_subclass"/><span data-i18n="lvl:-u">LEVEL:</span>
<input class="class-level" type="number" style="width: 40px" name="attr_base_level" value="1"/>
</div>
<div class="row"><span data-i18n="race:-u">RACE:</span>
<input type="text" name="attr_race"/><span data-i18n="subrace:-u">SUBRACE:</span>
<input type="text" name="attr_subrace"/><span data-i18n="creaturetype:-u">CREATURE TYPE:</span>
<input type="text" name="attr_creaturetype"/>
</div>
</div>
<div class="header-info display">
<div class="top">
<div class="hlabel-container">
<div class="class-display__container">
<input class="l1mancermode" type="hidden" name="attr_l1mancer_status"/>
<input class="levelermode" type="hidden" name="attr_lvup_button_mode"/>
<input type="hidden" name="attr_base_level"/><span class="class-display no_events" name="attr_class_display"></span>
<!-- <input type="text" name="attr_class_display" class="class-display no_events">-->
<input class="showleveler" type="hidden" name="attr_showleveler" value="0"/>
<button class="levelerbutton" type="action" name="act_launch_the_right_mancer" title="Charactermancer Level Up"></button>
<!-- <div class="invalidXP"><span class="icon">!</span><span class="message" data-i18n="invalidxp">Enter a number for Level Up reminder</span></div>-->
</div><span class="label" data-i18n="class-level-u">CLASS &amp; LEVEL</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_background" style="width: 182px;"/><span class="label" data-i18n="background-u">BACKGROUND</span>
</div>
</div>
<div class="bottom">
<div class="hlabel-container">
<input class="no_events" type="text" name="attr_race_display"/><span class="label" data-i18n="race-u">RACE</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_alignment"/><span class="label" data-i18n="alignment-u">ALIGNMENT</span>
</div>
<div class="hlabel-container" style="position: relative;">
<input type="hidden" name="attr_invalidXP"/>
<input type="text" name="attr_experience"/><span class="label" data-i18n="exp-pts-u">EXPERIENCE POINTS</span>
<!--
<input class="showleveler" type="hidden" name="attr_showleveler" value!="0">
<button class="levelerbutton" type="action" name="act_launch_lvl+mancer" style="padding: 0px; position: absolute; top: -10px; right: 0px;" title="Charactermancer Level Up"><img src="https://s3.amazonaws.com/files.d20.io/images/175804938/qmVmtNHONHhHcd9cxCxm7w/med.png?1604618286" style="height: 18px; width: 18px; max-width: inherit;" alt="Wand & Hammer logo of Charactermancer"></button>
<div class="invalidXP"><span class="icon">!</span><span class="message" data-i18n="invalidxp">Enter a number for Level Up reminder</span></div>
-->
</div>
</div>
</div>
</div>
<div class="body">
<div class="col col1">
<div class="attributes-container" style="margin-top: 10px;">
<div class="attr-container">
<button type="roll" name="roll_strength" value="@{wtype}&{template:simple} {{rname=^{strength-u}}} {{mod=@{strength_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{strength_mod}@{jack_attr}[STR]]]}} @{rtype}+@{strength_mod}@{jack_attr}[STR]]]}} @{charname_output}" data-i18n="strength-u">STRENGTH</button><span class="attr" name="attr_strength_mod" title="@{strength_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_strength_base" title="@{strength}" value="10"/>
<input type="hidden" name="attr_strength" value="10"/>
<input class="attr-flag" type="hidden" name="attr_strength_flag" value="0"/><span class="finalattr" name="attr_strength"></span>
</div>
</div>
<div class="attr-container">
<button type="roll" name="roll_dexterity" value="@{wtype}&{template:simple} {{rname=^{dexterity-u}}} {{mod=@{dexterity_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{dexterity_mod}@{jack_attr}[DEX]]]}} @{rtype}+@{dexterity_mod}@{jack_attr}[DEX]]]}} @{charname_output}" data-i18n="dexterity-u">DEXTERITY</button><span class="attr" name="attr_dexterity_mod" title="@{dexterity_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_dexterity_base" title="@{dexterity}" value="10"/>
<input type="hidden" name="attr_dexterity" value="10"/>
<input class="attr-flag" type="hidden" name="attr_dexterity_flag" value="0"/><span class="finalattr" name="attr_dexterity"></span>
</div>
</div>
<div class="attr-container">
<button type="roll" name="roll_constitution" value="@{wtype}&{template:simple} {{rname=^{constitution-u}}} {{mod=@{constitution_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{constitution_mod}@{jack_attr}[CON]]]}} @{rtype}+@{constitution_mod}@{jack_attr}[CON]]]}} @{charname_output}" data-i18n="constitution-u">CONSTITUTION</button><span class="attr" name="attr_constitution_mod" title="@{constitution_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_constitution_base" title="@{constitution}" value="10"/>
<input type="hidden" name="attr_constitution" value="10"/>
<input class="attr-flag" type="hidden" name="attr_constitution_flag" value="0"/><span class="finalattr" name="attr_constitution"></span>
</div>
</div>
<div class="attr-container">
<button type="roll" name="roll_intelligence" value="@{wtype}&{template:simple} {{rname=^{intelligence-u}}} {{mod=@{intelligence_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{intelligence_mod}@{jack_attr}[INT]]]}} @{rtype}+@{intelligence_mod}@{jack_attr}[INT]]]}} @{charname_output}" data-i18n="intelligence-u">INTELLIGENCE</button><span class="attr" name="attr_intelligence_mod" title="@{intelligence_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_intelligence_base" title="@{intelligence}" value="10"/>
<input type="hidden" name="attr_intelligence" value="10"/>
<input class="attr-flag" type="hidden" name="attr_intelligence_flag" value="0"/><span class="finalattr" name="attr_intelligence"></span>
</div>
</div>
<div class="attr-container">
<button type="roll" name="roll_wisdom" value="@{wtype}&{template:simple} {{rname=^{wisdom-u}}} {{mod=@{wisdom_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{wisdom_mod}@{jack_attr}[WIS]]]}} @{rtype}+@{wisdom_mod}@{jack_attr}[WIS]]]}} @{charname_output}" data-i18n="wisdom-u">WISDOM</button><span class="attr" name="attr_wisdom_mod" title="@{wisdom_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_wisdom_base" title="@{wisdom}" value="10"/>
<input type="hidden" name="attr_wisdom" value="10"/>
<input class="attr-flag" type="hidden" name="attr_wisdom_flag" value="0"/><span class="finalattr" name="attr_wisdom"></span>
</div>
</div>
<div class="attr-container">
<button type="roll" name="roll_charisma" value="@{wtype}&{template:simple} {{rname=^{charisma-u}}} {{mod=@{charisma_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{charisma_mod}@{jack_attr}[CHA]]]}} @{rtype}+@{charisma_mod}@{jack_attr}[CHA]]]}} @{charname_output}" data-i18n="charisma-u">CHARISMA</button><span class="attr" name="attr_charisma_mod" title="@{charisma_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_charisma_base" title="@{charisma}" value="10"/>
<input type="hidden" name="attr_charisma" value="10"/>
<input class="attr-flag" type="hidden" name="attr_charisma_flag" value="0"/><span class="finalattr" name="attr_charisma"></span>
</div>
</div>
<input class="toggleVisibility" type="hidden" name="attr_honor_toggle"/>
<div class="attr-container toggleVisibilityBy-honor">
<button type="roll" name="roll_honor" value="@{wtype}&{template:simple} {{rname=^{honor-u}}} {{mod=@{honor_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{honor_mod}@{jack_attr}[CHA]]]}} @{rtype}+@{honor_mod}@{jack_attr}[CHA]]]}} @{charname_output}" data-i18n="honor-u">HONOR</button><span class="attr" name="attr_honor_mod" title="@{honor_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_honor_base" title="@{honor}" value="10"/>
<input type="hidden" name="attr_honor" value="10"/>
<input class="attr-flag" type="hidden" name="attr_honor_flag" value="0"/><span class="finalattr" name="attr_honor"></span>
</div>
</div>
<input class="toggleVisibility" type="hidden" name="attr_sanity_toggle"/>
<div class="attr-container toggleVisibilityBy-sanity">
<button type="roll" name="roll_sanity" value="@{wtype}&{template:simple} {{rname=^{sanity-u}}} {{mod=@{sanity_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{sanity_mod}@{jack_attr}[CHA]]]}} @{rtype}+@{sanity_mod}@{jack_attr}[CHA]]]}} @{charname_output}" data-i18n="sanity-u">SANITY</button><span class="attr" name="attr_sanity_mod" title="@{sanity_mod}">0</span>
<div class="attr-mod">
<input class="baseattr" type="text" name="attr_sanity_base" title="@{sanity}" value="10"/>
<input type="hidden" name="attr_sanity" value="10"/>
<input class="attr-flag" type="hidden" name="attr_sanity_flag" value="0"/><span class="finalattr" name="attr_sanity"></span>
</div>
</div>
</div>
<div class="skills-saves-container">
<div class="insp-prof-container">
<div class="value">
<input type="checkbox" name="attr_inspiration"/><span></span>
</div>
<div class="label"><span data-i18n="inspiration-u">INSPIRATION</span></div>
</div>
<div class="insp-prof-container">
<div class="value"><span name="attr_pb" title="@{pb}"></span></div>
<div class="label"><span data-i18n="prof-bonus-u">PROFICIENCY BONUS</span></div>
</div>
<div class="saving-throw-container">
<div class="saving-throw">
<input type="hidden" name="attr_strength_save_roll"/>
<input type="checkbox" name="attr_strength_save_prof" title="@{strength_save_prof}" value="(@{pb})"/><span name="attr_strength_save_bonus" title="@{strength_save_bonus}">0</span>
<button type="roll" name="roll_strength_save" value="@{strength_save_roll}" data-i18n="strength">Strength</button>
</div>
<div class="saving-throw">
<input type="hidden" name="attr_dexterity_save_roll"/>
<input type="checkbox" name="attr_dexterity_save_prof" title="@{dexterity_save_prof}" value="(@{pb})"/><span name="attr_dexterity_save_bonus" title="@{dexterity_save_bonus}">0</span>
<button type="roll" name="roll_dexterity_save" value="@{dexterity_save_roll}" data-i18n="dexterity">Dexterity</button>
</div>
<div class="saving-throw">
<input type="hidden" name="attr_constitution_save_roll"/>
<input type="checkbox" name="attr_constitution_save_prof" title="@{constitution_save_prof}" value="(@{pb})"/><span name="attr_constitution_save_bonus" title="@{constitution_save_bonus}">0</span>
<button type="roll" name="roll_constitution_save" value="@{constitution_save_roll}" data-i18n="constitution">Constitution</button>
</div>
<div class="saving-throw">
<input type="hidden" name="attr_intelligence_save_roll"/>
<input type="checkbox" name="attr_intelligence_save_prof" title="@{intelligence_save_prof}" value="(@{pb})"/><span name="attr_intelligence_save_bonus" title="@{intelligence_save_bonus}">0</span>
<button type="roll" name="roll_intelligence_save" value="@{intelligence_save_roll}" data-i18n="intelligence">Intelligence</button>
</div>
<div class="saving-throw">
<input type="hidden" name="attr_wisdom_save_roll"/>
<input type="checkbox" name="attr_wisdom_save_prof" title="@{wisdom_save_prof}" value="(@{pb})"/><span name="attr_wisdom_save_bonus" title="@{wisdom_save_bonus}">0</span>
<button type="roll" name="roll_wisdom_save" value="@{wisdom_save_roll}" data-i18n="wisdom">Wisdom</button>
</div>
<div class="saving-throw">
<input type="hidden" name="attr_charisma_save_roll"/>
<input type="checkbox" name="attr_charisma_save_prof" title="@{charisma_save_prof}" value="(@{pb})"/><span name="attr_charisma_save_bonus" title="@{charisma_save_bonus}">0</span>
<button type="roll" name="roll_charisma_save" value="@{charisma_save_roll}" data-i18n="charisma">Charisma</button>
</div>
<input class="toggleVisibility" type="hidden" name="attr_honor_toggle"/>
<div class="saving-throw toggleVisibilityBy-honor">
<input type="hidden" name="attr_honor_save_roll"/>
<input type="checkbox" name="attr_honor_save_prof" title="@{honor_save_prof}" value="(@{pb})"/><span name="attr_honor_save_bonus" title="@{honor_save_bonus}">0</span>
<button type="roll" name="roll_honor_save" value="@{honor_save_roll}" data-i18n="honor">Honor</button>
</div>
<input class="toggleVisibility" type="hidden" name="attr_sanity_toggle"/>
<div class="saving-throw toggleVisibilityBy-sanity">
<input type="hidden" name="attr_sanity_save_roll"/>
<input type="checkbox" name="attr_sanity_save_prof" title="@{sanity_save_prof}" value="(@{pb})"/><span name="attr_sanity_save_bonus" title="@{sanity_save_bonus}">0</span>
<button type="roll" name="roll_sanity_save" value="@{sanity_save_roll}" data-i18n="sanity">Sanity</button>
</div>
<input class="toggleflag" type="hidden" name="attr_global_save_mod_flag"/>
<div class="hidden textarea globalattack"><span class="label" data-i18n="global-save-u" style="position: relative; padding-bottom: 5px;">GLOBAL SAVE MODIFIER</span>
<fieldset class="repeating_savemod">
<div class="global-mod">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked" style="top: -15px;"/><span>y</span>
<input class="active-flag" type="checkbox" name="attr_global_save_active_flag" value="1"/>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_global_save_name" value="" placeholder="Bless" style="width:75px"/>
</div>
<div class="row"><span data-i18n="roll:-u">ROLL:</span>
<input type="text" name="attr_global_save_roll" value="" placeholder="1d4" style="width:75px"/>
</div>
</div>
<div class="globaldisplay" style="width: 75%">
<button class="ellipsis" type="roll" name="roll_global_save" value="@{wtype}&{template:simple} {{rname=@{global_save_name}}} {{r1=[[@{global_save_roll}]]}} {{normal=1}} @{charname_output}"><span name="attr_global_save_roll"></span><span name="attr_global_save_name"></span></button>
</div>
</div>
</fieldset>
</div>
<div class="label"><span data-i18n="saving-throws-u">SAVING THROWS</span></div>
</div>
<div class="skills-container skills-list">
<div data-i18n-list="skills-list">
<div class="skill" data-i18n-list-item="acrobatics">
<input type="hidden" name="attr_acrobatics_roll"/>
<input type="checkbox" name="attr_acrobatics_prof" title="@{acrobatics_prof}" value="(@{pb}*@{acrobatics_type})"/><span name="attr_acrobatics_bonus" title="@{acrobatics_bonus}">0</span>
<button type="roll" name="roll_acrobatics" value="@{acrobatics_roll}" data-i18n="acrobatics-core">Acrobatics <span>(Dex)</span></button>
</div>
<div class="skill" data-i18n-list-item="animal_handling">
<input type="hidden" name="attr_animal_handling_roll"/>
<input type="checkbox" name="attr_animal_handling_prof" title="@{animal_handling_prof}" value="(@{pb}*@{animal_handling_type})"/><span name="attr_animal_handling_bonus" title="@{animal_handling_bonus}">0</span>
<button type="roll" name="roll_animal_handling" value="@{animal_handling_roll}" data-i18n="animal_handling-core">Animal Handling <span>(Wis)</span></button>
</div>
<div class="skill" data-i18n-list-item="arcana">
<input type="hidden" name="attr_arcana_roll"/>
<input type="checkbox" name="attr_arcana_prof" title="@{arcana_prof}" value="(@{pb}*@{arcana_type})"/><span name="attr_arcana_bonus" title="@{arcana_bonus}">0</span>
<button type="roll" name="roll_arcana" value="@{arcana_roll}" data-i18n="arcana-core">Arcana <span>(Int)</span></button>
</div>
<div class="skill" data-i18n-list-item="athletics">
<input type="hidden" name="attr_athletics_roll"/>
<input type="checkbox" name="attr_athletics_prof" title="@{athletics_prof}" value="(@{pb}*@{athletics_type})"/><span name="attr_athletics_bonus" title="@{athletics_bonus}">0</span>
<button type="roll" name="roll_athletics" value="@{athletics_roll}" data-i18n="athletics-core">Athletics <span>(Str)</span></button>
</div>
<div class="skill" data-i18n-list-item="deception">
<input type="hidden" name="attr_deception_roll"/>
<input type="checkbox" name="attr_deception_prof" title="@{deception_prof}" value="(@{pb}*@{deception_type})"/><span name="attr_deception_bonus" title="@{deception_bonus}">0</span>
<button type="roll" name="roll_deception" value="@{deception_roll}" data-i18n="deception-core">Deception <span>(Cha)</span></button>
</div>
<div class="skill" data-i18n-list-item="history">
<input type="hidden" name="attr_history_roll"/>
<input type="checkbox" name="attr_history_prof" title="@{history_prof}" value="(@{pb}*@{history_type})"/><span name="attr_history_bonus" title="@{history_bonus}">0</span>
<button type="roll" name="roll_history" value="@{history_roll}" data-i18n="history-core">History <span>(Int)</span></button>
</div>
<div class="skill" data-i18n-list-item="insight">
<input type="hidden" name="attr_insight_roll"/>
<input type="checkbox" name="attr_insight_prof" title="@{insight_prof}" value="(@{pb}*@{insight_type})"/><span name="attr_insight_bonus" title="@{insight_bonus}">0</span>
<button type="roll" name="roll_insight" value="@{insight_roll}" data-i18n="insight-core">Insight <span>(Wis)</span></button>
</div>
<div class="skill" data-i18n-list-item="intimidation">
<input type="hidden" name="attr_intimidation_roll"/>
<input type="checkbox" name="attr_intimidation_prof" title="@{intimidation_prof}" value="(@{pb}*@{intimidation_type})"/><span name="attr_intimidation_bonus" title="@{intimidation_bonus}">0</span>
<button type="roll" name="roll_intimidation" value="@{intimidation_roll}" data-i18n="intimidation-core">Intimidation <span>(Cha)</span></button>
</div>
<div class="skill" data-i18n-list-item="investigation">
<input type="hidden" name="attr_investigation_roll"/>
<input type="checkbox" name="attr_investigation_prof" title="@{investigation_prof}" value="(@{pb}*@{investigation_type})"/><span name="attr_investigation_bonus" title="@{investigation_bonus}">0</span>
<button type="roll" name="roll_investigation" value="@{investigation_roll}" data-i18n="investigation-core">Investigation <span>(Int)</span></button>
</div>
<div class="skill" data-i18n-list-item="medicine">
<input type="hidden" name="attr_medicine_roll"/>
<input type="checkbox" name="attr_medicine_prof" title="@{medicine_prof}" value="(@{pb}*@{medicine_type})"/><span name="attr_medicine_bonus" title="@{medicine_bonus}">0</span>
<button type="roll" name="roll_medicine" value="@{medicine_roll}" data-i18n="medicine-core">Medicine <span>(Wis)</span></button>
</div>
<div class="skill" data-i18n-list-item="nature">
<input type="hidden" name="attr_nature_roll"/>
<input type="checkbox" name="attr_nature_prof" title="@{nature_prof}" value="(@{pb}*@{nature_type})"/><span name="attr_nature_bonus" title="@{nature_bonus}">0</span>
<button type="roll" name="roll_nature" value="@{nature_roll}" data-i18n="nature-core">Nature <span>(Int)</span></button>
</div>
<div class="skill" data-i18n-list-item="navigation">
<input type="hidden" name="attr_navigation_roll"/>
<input type="checkbox" name="attr_navigation_prof" title="@{navigation_prof}" value="(@{pb}*@{navigation_type})"/><span name="attr_navigation_bonus" title="@{navigation_bonus}">0</span>
<button type="roll" name="roll_navigation" value="@{navigation_roll}" data-i18n="navigation-core">navigation <span>(Int)</span></button>
</div>
<div class="skill" data-i18n-list-item="perception">
<input type="hidden" name="attr_perception_roll"/>
<input type="checkbox" name="attr_perception_prof" title="@{perception_prof}" value="(@{pb}*@{perception_type})"/><span name="attr_perception_bonus" title="@{perception_bonus}">0</span>
<button type="roll" name="roll_perception" value="@{perception_roll}" data-i18n="perception-core">Perception <span>(Wis)</span></button>
</div>
<div class="skill" data-i18n-list-item="performance">
<input type="hidden" name="attr_performance_roll"/>
<input type="checkbox" name="attr_performance_prof" title="@{performance_prof}" value="(@{pb}*@{performance_type})"/><span name="attr_performance_bonus" title="@{performance_bonus}">0</span>
<button type="roll" name="roll_performance" value="@{performance_roll}" data-i18n="performance-core">Performance <span>(Cha)</span></button>
</div>
<div class="skill" data-i18n-list-item="persuasion">
<input type="hidden" name="attr_persuasion_roll"/>
<input type="checkbox" name="attr_persuasion_prof" title="@{persuasion_prof}" value="(@{pb}*@{persuasion_type})"/><span name="attr_persuasion_bonus" title="@{persuasion_bonus}">0</span>
<button type="roll" name="roll_persuasion" value="@{persuasion_roll}" data-i18n="persuasion-core">Persuasion <span>(Wis)</span></button>
</div>
<div class="skill" data-i18n-list-item="religion">
<input type="hidden" name="attr_religion_roll"/>
<input type="checkbox" name="attr_religion_prof" title="@{religion_prof}" value="(@{pb}*@{religion_type})"/><span name="attr_religion_bonus" title="@{religion_bonus}">0</span>
<button type="roll" name="roll_religion" value="@{religion_roll}" data-i18n="religion-core">Religion <span>(Int)</span></button>
</div>
<div class="skill" data-i18n-list-item="sleight_of_hand">
<input type="hidden" name="attr_sleight_of_hand_roll"/>
<input type="checkbox" name="attr_sleight_of_hand_prof" title="@{sleight_of_hand_prof}" value="(@{pb}*@{sleight_of_hand_type})"/><span name="attr_sleight_of_hand_bonus" title="@{sleight_of_hand_bonus}">0</span>
<button type="roll" name="roll_sleight_of_hand" value="@{sleight_of_hand_roll}" data-i18n="sleight_of_hand-core">Sleight of Hand <span>(Dex)</span></button>
</div>
<div class="skill" data-i18n-list-item="stealth">
<input type="hidden" name="attr_stealth_roll"/>
<input type="checkbox" name="attr_stealth_prof" title="@{stealth_prof}" value="(@{pb}*@{stealth_type})"/><span name="attr_stealth_bonus" title="@{stealth_bonus}">0</span>
<button type="roll" name="roll_stealth" value="@{stealth_roll}" data-i18n="stealth-core">Stealth <span>(Dex)</span></button>
</div>
<div class="skill" data-i18n-list-item="survival">
<input type="hidden" name="attr_survival_roll"/>
<input type="checkbox" name="attr_survival_prof" title="@{survival_prof}" value="(@{pb}*@{survival_type})"/><span name="attr_survival_bonus" title="@{survival_bonus}">0</span>
<button type="roll" name="roll_survival" value="@{survival_roll}" data-i18n="survival-core">Survival <span>(Wis)</span></button>
</div>
</div>
<div class="label"><span data-i18n="skills-u">SKILLS</span></div>
</div>
</div>
<div class="insp-prof-container" style="width: 100%; margin-top: 2px;">
<div class="value">
<!-- <input type="text" name="attr_passive_wisdom" title="@{passive_wisdom}" value!="(10+@{perception_bonus}+@{passiveperceptionmod})" disabled="true" ><span></span>--><span name="attr_passive_wisdom"></span>
</div>
<div class="label" style="width: 217px;"><span data-i18n="pass-wis-u">PASSIVE WISDOM (PERCEPTION)</span></div>
</div>
<div class="tool_proficiencies" style="margin-bottom: 10px; min-height: 100px; position: relative;">
<div class="top"><span class="label" style="width: 106px;" data-i18n="tool-u">TOOL</span><span class="label" style="width: 35px;" data-i18n="pro-u">PRO</span><span class="label" style="width: 50px;" data-i18n="attr-u">ATTRIBUTE</span></div>
<fieldset class="repeating_tool">
<div class="tool">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_toolname" style="width: 120px;"/>
</div>
<div class="row"><span data-i18n="prof-bonus:-u">PROFICIENCY BONUS:</span>
<select name="attr_toolbonus_base">
<option value="(@{pb})" selected="selected" data-i18n="prof-u">PROFICIENT</option>
<option value="(@{pb}*2)" data-i18n="expertise-u">EXPERTISE</option>
<option value="(floor(@{pb}/2))" data-i18n="jack-of-all-u">JACK OF ALL TRADES</option>
</select>
</div>
<div class="row"><span data-i18n="attr:-u">ATTRIBUTE:</span>
<select name="attr_toolattr_base">
<option value="@{strength_mod}" selected="selected" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}" data-i18n="charisma-u">CHARISMA</option>
<option value="?{Attribute?|Strength,@{strength_mod}|Dexterity,@{dexterity_mod}|Constitution,@{constitution_mod}|Intelligence,@{intelligence_mod}|Wisdom,@{wisdom_mod}|Charisma,@{charisma_mod}}" data-i18n="query-attr-u">QUERY ATTRIBUTE</option>
</select><span data-i18n="mods:-u">MODS:</span>
<input class="num" type="text" name="attr_tool_mod" title="@{tool_mod}" value="0"/>
</div>
</div>
<div class="display">
<input type="hidden" name="attr_toolroll"/>
<!-- <button type="roll" name="roll_tool" value!="@{wtype}&{template:simple} {{rname=@{toolname}}} {{mod=@{toolbonus}}} {{r1=[[@{d20}+@{toolbonus}@{pbd_safe}]]}} @{rtype}+@{toolbonus}@{pbd_safe}]]}} {{global=@{global_skill_mod}}} @{charname_output}"></button>-->
<button type="roll" name="roll_tool" value="@{toolroll}"><span name="attr_toolname" style="width: 100px;"></span>
<input type="text" name="attr_toolbonus_display" style="width: 40px; text-align: center;"/>
<input type="hidden" name="attr_toolbonus"/>
<input type="text" name="attr_toolattr" style="width: 80px;"/>
</button>
</div>
</div>
</fieldset>
<input class="toggleflag" type="hidden" name="attr_global_skill_mod_flag"/>
<div class="hidden textarea globalattack"><span class="label" data-i18n="global-skill-u">GLOBAL SKILL MODIFIER</span>
<fieldset class="repeating_skillmod">
<div class="global-mod">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked" style="top: -15px;"/><span>y</span>
<input class="active-flag" type="checkbox" name="attr_global_skill_active_flag" value="1"/>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_global_skill_name" value="" placeholder="Guidance"/>
</div>
<div class="row"><span data-i18n="roll:-u">ROLL:</span>
<input type="text" name="attr_global_skill_roll" value="" placeholder="1d4"/>
</div>
</div>
<div class="globaldisplay">
<button class="ellipsis" type="roll" name="roll_global_save" value="@{wtype}&{template:simple} {{rname=@{global_skill_name}}} {{r1=[[@{global_skill_roll}]]}} {{normal=1}} @{charname_output}"><span name="attr_global_skill_roll"></span><span name="attr_global_skill_name"></span></button>
</div>
</div>
</fieldset>
</div>
<div class="label" style="position: absolute; bottom: 0px; width: 100%; text-align: center;"><span data-i18n="tool-prof-u" style="display: inline;">TOOL PROFICIENCIES</span><span style="display: inline;"> &amp; </span><span data-i18n="custom-skills-u" style="display: inline;">CUSTOM SKILLS</span></div>
</div>
<input class="toggle-consolidated" type="hidden" name="attr_consolidatedproficencies"/>
<div class="textbox-container proficiencies proficiencies--old">
<input class="inventoryflag" type="hidden" name="attr_simpleproficencies" value="complex"/>
<textarea class="simple" name="attr_other_proficiencies_and_languages" style="min-height: 91px; height: 91px;"></textarea>
<div class="complex">
<div class="top"><span class="label" style="width: 60px;" data-i18n="type-u">TYPE</span><span class="label" style="width: 135px;" data-i18n="proficency-u">PROFICIENCY</span></div>
<fieldset class="repeating_proficiencies">
<div class="proficiency">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<select name="attr_prof_type">
<option selected="selected" data-i18n="lang-u">LANGUAGE</option>
<option data-i18n="weapon-u">WEAPON</option>
<option data-i18n="armor-u">ARMOR</option>
<option data-i18n="other-u">OTHER</option>
</select>
</div>
<div class="row" style="margin-bottom: 3px;"><span data-i18n="proficency-u">PROFICIENCY</span><span>:</span>
<input type="text" name="attr_name" style="width: 165px;" placeholder="Elven, Dwarven, and Common"/>
</div>
</div>
<div class="display" style="margin-bottom: 2px;">
<button type="roll" name="roll_output" value="@{wtype}&{template:traits} @{charname_output} {{name=@{prof_type} PROFICIENCY}} {{description=@{name}}}"><span name="attr_prof_type" style="width: 55px;"></span><span name="attr_name" style="width: 159px;"></span></button>
</div>
</div>
</fieldset>
</div>
<div class="label"><span style="margin-top: -10px;" data-i18n="other-prof-langs-u">OTHER PROFICIENCIES &amp; LANGUAGES</span></div>
</div>
<div class="textbox-container proficiencies proficiencies--new">
<input class="inventoryflag" type="hidden" name="attr_simpleproficencies" value="complex"/>
<textarea class="simple" name="attr_other_proficiencies_and_languages"></textarea>
<div class="complex">
<input class="options-flag" type="checkbox" name="attr_editotherprof-flag"/><span>y</span>
<div class="other__container--screen">
<div class="display">
<div class="top"><span class="label" data-i18n="type-u">TYPE</span><span class="label" data-i18n="proficency-u">PROFICIENCY</span></div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_languages"/>
<div class="other-type languages">
<button type="roll" name="roll_output" value="@{wtype}&{template:traits} @{charname_output} {{name=^{language-proficiencies}}} {{description=@{other_languages}}}"><span class="translation proficiency-type" data-i18n="lang-u"></span><span class="attribute proficiency-name" name="attr_other_languages"></span></button>
</div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_armor"/>
<div class="other-type armor">
<button type="roll" name="roll_output" value="@{wtype}&{template:traits} @{charname_output} {{name=^{armor-proficiencies}}}} {{description=@{other_armor}}}"><span class="translation proficiency-type" data-i18n="armor-u"></span><span class="attribute proficiency-name" name="attr_other_armor"></span></button>
</div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_weapon"/>
<div class="other-type weapons">
<button type="roll" name="roll_output" value="@{wtype}&{template:traits} @{charname_output} {{name=^{weapon-proficiencies}}}} {{description=@{other_weapon}}}"><span class="translation proficiency-type" data-i18n="weapon-u"></span><span class="attribute proficiency-name" name="attr_other_weapon"></span></button>
</div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_other"/>
<div class="other-type other">
<button type="roll" name="roll_output" value="@{wtype}&{template:traits} @{charname_output} {{name=^{other-proficiencies}}}} {{description=@{other_other}}}"><span class="translation proficiency-type" data-i18n="other-u"></span><span class="attribute proficiency-name" name="attr_other_other"></span></button>
</div>
</div>
<div class="options">
<fieldset class="repeating_proficiencies">
<div class="proficiency">
<div class="options">
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<select name="attr_prof_type">
<option selected="selected" data-i18n="lang-u">LANGUAGE</option>
<option data-i18n="weapon-u">WEAPON</option>
<option data-i18n="armor-u">ARMOR</option>
<option data-i18n="other-u">OTHER</option>
</select>
</div>
<div class="row" style="margin-bottom: 3px;"><span data-i18n="proficency-u">PROFICIENCY</span><span>:</span>
<input type="text" name="attr_name" style="width: 150px;" placeholder="Elven, Dwarven, and Common"/>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div><span class="translation box-label" data-i18n="other-prof-langs-u"></span>
</div>
<input type="hidden" name="attr_use_piety"/>
<div class="piety">
<input class="details-flag" type="checkbox" name="attr_piety_details-flag" checked="checked"/><span>i</span>
<div class="container">
<div class="header">
<div class="score">
<input type="number" name="attr_piety" value="1"/><span>Piety</span>
</div>
<label class="deity"><span>Deity:</span>
<input type="text" placeholder="Deity's name..." name="attr_piety_deity"/>
</label>
</div>
<div class="earning"><span class="title">Earning Piety</span>
<fieldset class="repeating_earning">
<div class="desc">
<input type="hidden" name="attr_desc" value=""/>
<textarea name="attr_desc" placeholder="Enter description..."></textarea><span name="attr_desc"></span>
</div>
</fieldset>
</div>
<div class="losing"><span class="title">Losing Piety</span>
<fieldset class="repeating_losing">
<div class="desc">
<input type="hidden" name="attr_desc" value=""/>
<textarea name="attr_desc" placeholder="Enter description..."></textarea><span name="attr_desc"></span>
</div>
</fieldset>
</div>
<input class="toggle" type="hidden" name="attr_piety" value="1"/>
<div class="none"><span>You haven&apos;t aquired enough piety. Keep true to you deity and good things will come.</span></div>
<div class="devotee"><span class="rank"><span name="attr_piety_deity"></span><span> Devotee</span></span><span class="subheader">Piety 3+ <span name="attr_piety_deity"></span>trait</span>
<div class="desc">
<input type="hidden" name="attr_piety_devotee" value=""/>
<textarea name="attr_piety_devotee" placeholder="Enter description..."></textarea><span name="attr_piety_devotee"></span>
</div>
</div>
<div class="votary"><span class="rank"><span name="attr_piety_deity"></span><span> Votary</span></span><span class="subheader">Piety 10+ <span name="attr_piety_deity"></span>trait</span>
<div class="desc">
<input type="hidden" name="attr_piety_votary" value=""/>
<textarea name="attr_piety_votary" placeholder="Enter description..."></textarea><span name="attr_piety_votary"></span>
</div>
</div>
<div class="disciple"><span class="rank"><span name="attr_piety_deity"></span><span> Disciple</span></span><span class="subheader">Piety 25+ <span name="attr_piety_deity"></span>trait</span>
<div class="desc">
<input type="hidden" name="attr_piety_disciple" value=""/>
<textarea name="attr_piety_disciple" placeholder="Enter description..."></textarea><span name="attr_piety_disciple"></span>
</div>
</div>
<div class="champion">
<input class="rank" type="text" name="attr_piety_champion_title" value="Champion of"/><span class="subheader">Piety 50+ <span name="attr_piety_deity"></span>trait</span>
<div class="desc">
<input type="hidden" name="attr_piety_champion" value=""/>
<textarea name="attr_piety_champion" placeholder="Enter description..."></textarea><span name="attr_piety_champion"></span>
</div>
</div>
</div>
</div>
</div>
<div class="col col2">
<div class="vitals">
<div class="vitals-stats">
<div class="ac-init-speed-container">
<div class="ac">
<input type="text" name="attr_ac" title="@{ac}"/><span class="label pc-ac" data-i18n="armor-class-u">ARMOR CLASS</span>
</div>
<div class="init"><span name="attr_initiative_bonus" title="@{initiative_bonus}"></span>
<button type="roll" name="roll_initiative" value="@{wtype}&{template:simple} {{rname=^{init-u}}} {{mod=@{initiative_bonus}}} {{r1=[[@{initiative_style}+@{initiative_bonus}@{pbd_safe}[INIT] &{tracker}]]}} {{normal=1}} @{charname_output}" data-i18n="init-u">INITIATIVE</button>
</div>
<div class="speed">
<input type="text" name="attr_speed" title="@{speed}"/><span class="label" data-i18n="speed-u">SPEED</span>
</div>
</div>
<div class="hp">
<div class="top"><span data-i18n="hp-max-u">Hit Point Maximum</span>
<input type="text" name="attr_hp_max" title="@{hp_max}"/>
</div>
<input type="number" name="attr_hp" title="@{hp}"/><span class="label" data-i18n="hp-current-u">CURRENT HIT POINTS</span>
</div>
<div class="hp" style="height: 56px;">
<input type="number" name="attr_hp_temp" title="@{hp_temp}"/><span class="label" data-i18n="hp-temp-u">TEMPORARY HIT POINTS</span>
</div>
<input type="hidden" name="attr_use_per_class_hit_dice"/>
<div class="new-hit-dice">
<input type="hidden" name="attr_hit_dice_max"/>
<input type="hidden" name="attr_hit_dice"/>
<div class="hd-header"><span data-i18n="type-u">TYPE</span><span data-i18n="available-u">AVAILABLE</span><span data-i18n="max-u">MAX</span></div>
<fieldset class="repeating_hd">
<input type="hidden" name="attr_type"/>
<input type="hidden" name="attr_available"/>
<button class="roll-hd" type="roll" value="/roll 1d@{type} + @{constitution_mod}" title="Roll 1d@{type} + @{constitution_mod}"><span name="attr_type"></span>
<input type="checkbox" name="attr_consumed" value="1"/>
</button>
<input type="number" name="attr_available" min="0"/>
<input type="number" name="attr_size" min="0"/>
</fieldset>
<div class="row show-hit-dice">
<input type="hidden" name="attr_hitdie_final"/>
<input type="hidden" name="attr_hitdieroll"/>
<button type="action" name="act_show_hit_dice"><span data-i18n="hit-dice-u">HIT DICE</span><span name="attr_hit_dice_max"></span></button>
</div>
</div>
<div class="hdice-dsaves-container old-hit-dice" style="width: 242px;">
<div class="subcontainer">
<div class="top"><span data-i18n="total">Total</span>
<input type="text" name="attr_hit_dice_max" title="@{hit_dice_max}"/>
</div>
<input type="hidden" name="attr_hit_dice" value="@{hit_dice}"/>
<input type="number" name="attr_hit_dice" title="@{hit_dice}" style="margin-bottom: -6px;"/>
<div class="row roll-hitdie">
<button type="roll" name="roll_hit_dice" value="@{wtype}&{template:simple} {{rname=^{hit-dice-u}}} {{mod=D@{hitdieroll}+[[@{constitution_mod}[CON]]]}} {{r1=[[1d@{hitdieroll}+[[@{constitution_mod}]][CON]]]}} {{normal=1}} @{charname_output}" data-i18n="hit-dice-u">HIT DICE</button>
<input type="hidden" name="attr_hitdie_final"/>
<input type="hidden" name="attr_hitdieroll"/> (
<select name="attr_hitdieroll">
<option value="4">D4</option>
<option value="6">D6</option>
<option value="8">D8</option>
<option value="10">D10</option>
<option value="12">D12</option>
</select>)
</div>
</div>
<div class="subcontainer" style="float: right;">
<div class="row-container"><span class="label" data-i18n="successes-u">SUCCESSES</span>
<input type="checkbox" name="attr_deathsave_succ1"/>
<input type="checkbox" name="attr_deathsave_succ2"/>
<input type="checkbox" name="attr_deathsave_succ3"/>
</div>
<div class="row-container"><span class="label" data-i18n="failures-u">FAILURES</span>
<input type="checkbox" name="attr_deathsave_fail1"/>
<input type="checkbox" name="attr_deathsave_fail2"/>
<input type="checkbox" name="attr_deathsave_fail3"/>
</div>
<button type="roll" name="roll_death_save" value="@{wtype}&{template:simple} {{rname=^{death-save-u}}} {{mod=@{death_save_bonus}}} {{r1=[[@{d20}+@{death_save_bonus}[MOD]@{globalsavingthrowbonus}]]}} {{normal=1}} {{global=@{global_save_mod}}} @{charname_output}" data-i18n="death-saves-u">DEATH SAVES</button>
</div>
</div>
<input type="hidden" name="attr_show_hit_dice" value=""/>
<div class="hd-modal"><span class="title"><span data-i18n="hit-dice-u">HIT DICE</span>
<button type="action" name="act_hide_hit_dice"></button></span>
<input type="hidden" name="attr_show_hit_dice_roll"/>
<div class="hitdie-d4">
<label><span data-i18n="roll">Roll</span>
<input name="attr_current_d4" type="number" min="0"/><strong> d4 </strong><span data-i18n="of">of</span>
<input name="attr_current_d4_max" type="hidden"/><span name="attr_current_d4_max"></span><span> </span><span data-i18n="available-u">available</span>
</label>
</div>
<div class="hitdie-d6">
<label><span data-i18n="roll">Roll</span>
<input name="attr_current_d6" type="number" min="0"/><strong> d6 </strong><span data-i18n="of">of</span>
<input name="attr_current_d6_max" type="hidden"/><span name="attr_current_d6_max"></span><span> </span><span data-i18n="available-u">available</span>
</label>
</div>
<div class="hitdie-d8">
<label><span data-i18n="roll">Roll</span>
<input name="attr_current_d8" type="number" min="0"/><strong> d8 </strong><span data-i18n="of">of</span>
<input name="attr_current_d8_max" type="hidden"/><span name="attr_current_d8_max"></span><span> </span><span data-i18n="available-u">available</span>
</label>
</div>
<div class="hitdie-d10">
<label><span data-i18n="roll">Roll</span>
<input name="attr_current_d10" type="number" min="0"/><strong> d10 </strong><span data-i18n="of">of</span>
<input name="attr_current_d10_max" type="hidden"/><span name="attr_current_d10_max"></span><span> </span><span data-i18n="available-u">available</span>
</label>
</div>
<div class="hitdie-d12">
<label><span data-i18n="roll">Roll</span>
<input name="attr_current_d12" type="number" min="0"/><strong> d12 </strong><span data-i18n="of">of</span>
<input name="attr_current_d12_max" type="hidden"/><span name="attr_current_d12_max"></span><span> </span><span data-i18n="available-u">available</span>
</label>
</div>
<div class="actions">
<input type="hidden" name="attr_show_hit_dice_roll_formula" value="@{wtype}&{template:simple} {{rname=^{hit-dice-u}}} {{r1=[[@{show_hit_dice_roll}]]}} {{query=1}} {{normal=1}} {{r2=[[0d20]}} @{charname_output}"/>
<button type="roll" value="@{show_hit_dice_roll_formula}"><span data-i18n="roll">Roll</span>
<input type="checkbox" name="attr_hit_dice_rolled" value="1"/>
</button>
</div>
</div>
</div>
</div>
<input class="exhaustion-toggle" type="hidden" name="attr_exhaustion_toggle" value="0"/>
<div class="exhaustion" style="position: relative;">
<input type="number" name="attr_exhaustion_level" step="1" min="0" max="6"/>
<div class="ex-descriptions"><span class="ex-description" name="attr_exhaustion_1"></span><span class="ex-description" name="attr_exhaustion_2"></span><span class="ex-description" name="attr_exhaustion_3"></span><span class="ex-description" name="attr_exhaustion_4"></span><span class="ex-description" name="attr_exhaustion_5"></span><span class="ex-description" name="attr_exhaustion_6"></span></div><span class="label" data-i18n="exhaustion-u">EXHAUSTION LEVEL</span>
</div>
<input class="ammoflag" type="hidden" name="attr_ammotracking"/>
<div class="attacks" style="position: relative;">
<div class="top"><span class="label" style="width: 95px;" data-i18n="name-u">NAME</span><span class="label" style="width: 35px;" data-i18n="atk-u">ATK</span><span class="label" style="width: 65px;" data-i18n="dmg-type-u">DAMAGE/TYPE</span></div>
<fieldset class="repeating_attack">
<div class="attack">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_atkname"/>
</div>
<div class="row">
<input type="checkbox" name="attr_atkflag" value="{{attack=1}}" checked="checked"/><span data-i18n="attack:-u">ATTACK:</span>
<select name="attr_atkattr_base">
<option value="@{strength_mod}" selected="selected" data-i18n="str-u">STR</option>
<option value="@{dexterity_mod}" data-i18n="dex-u">DEX</option>
<option value="@{constitution_mod}" data-i18n="con-u">CON</option>
<option value="@{intelligence_mod}" data-i18n="int-u">INT</option>
<option value="@{wisdom_mod}" data-i18n="wis-u">WIS</option>
<option value="@{charisma_mod}" data-i18n="cha-u">CHA</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="0">-</option>
</select><span>+</span>
<input class="num" type="text" name="attr_atkmod" placeholder="0"/><span>+</span>
<input type="checkbox" name="attr_atkprofflag" value="(@{pb})" checked="checked" style="margin-left: 3px;"/><span data-i18n="proficient-u">PROFICIENT</span>
</div>
<div class="row"><span style="margin-left: 17px;" data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_atkrange" placeholder="Self (60-foot cone)" style="width: 170px;" data-i18n-placeholder="range-place"/>
</div>
<div class="row"><span style="margin-left: 17px;" data-i18n="magic-bonus:-u">MAGIC BONUS:</span>
<input class="num" type="text" name="attr_atkmagic" placeholder="0"/><span data-i18n="crit-range-u">CRIT RANGE:</span>
<input class="num" type="text" name="attr_atkcritrange" value="20" placeholder="20"/>
</div>
<div class="row">
<input type="checkbox" name="attr_dmgflag" value="{{damage=1}} {{dmg1flag=1}}" checked="checked"/><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_dmgbase" placeholder="1d6" style="width: 50px;"/><span>+</span>
<select name="attr_dmgattr">
<option value="@{strength_mod}" selected="selected" data-i18n="str-u">STR</option>
<option value="@{dexterity_mod}" data-i18n="dex-u">DEX</option>
<option value="@{constitution_mod}" data-i18n="con-u">CON</option>
<option value="@{intelligence_mod}" data-i18n="int-u">INT</option>
<option value="@{wisdom_mod}" data-i18n="wis-u">WIS</option>
<option value="@{charisma_mod}" data-i18n="cha-u">CHA</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="0">-</option>
</select><span>+</span>
<input class="num" type="text" name="attr_dmgmod" placeholder="0"/>
</div>
<div class="row"><span style="margin-left: 17px;" data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_dmgtype" placeholder="Slashing" style="width: 50px;" data-i18n-placeholder="dmg-type-place"/><span data-i18n="crit:-u">CRIT:</span>
<input type="text" name="attr_dmgcustcrit" placeholder="1d6" style="width: 80px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_dmg2flag" value="{{damage=1}} {{dmg2flag=1}}"/><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_dmg2base" placeholder="1d6" style="width: 50px;"/><span>+</span>
<select name="attr_dmg2attr">
<option value="@{strength_mod}" data-i18n="str-u">STR</option>
<option value="@{dexterity_mod}" data-i18n="dex-u">DEX</option>
<option value="@{constitution_mod}" data-i18n="con-u">CON</option>
<option value="@{intelligence_mod}" data-i18n="int-u">INT</option>
<option value="@{wisdom_mod}" data-i18n="wis-u">WIS</option>
<option value="@{charisma_mod}" data-i18n="cha-u">CHA</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="0" selected="selected">-</option>
</select><span>+</span>
<input class="num" type="text" name="attr_dmg2mod" placeholder="0"/>
</div>
<div class="row"><span style="margin-left: 17px;" data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_dmg2type" placeholder="Slashing" style="width: 50px;" data-i18n-placeholder="dmg-type-place"/><span data-i18n="crit:-u">CRIT:</span>
<input type="text" name="attr_dmg2custcrit" placeholder="1d6" style="width: 80px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_saveflag" value="{{save=1}} {{saveattr=@{saveattr}}} {{savedesc=@{saveeffect}}} {{savedc=[[[[@{savedc}]][SAVE]]]}}"/><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_saveattr">
<option value="Strength" selected="selected" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="vs-dc:-u">VS DC:</span>
<select name="attr_savedc">
<option value="(@{spell_save_dc})" selected="selected" data-i18n="spell-u">SPELL</option>
<option value="(@{strength_mod}+8+@{spell_dc_mod}+@{pb})" data-i18n="str-u">STR</option>
<option value="(@{dexterity_mod}+8+@{spell_dc_mod}+@{pb})" data-i18n="dex-u">DEX</option>
<option value="(@{constitution_mod}+8+@{spell_dc_mod}+@{pb})" data-i18n="con-u">CON</option>
<option value="(@{intelligence_mod}+8+@{spell_dc_mod}+@{pb})" data-i18n="int-u">INT</option>
<option value="(@{wisdom_mod}+8+@{spell_dc_mod}+@{pb})" data-i18n="wis-u">WIS</option>
<option value="(@{charisma_mod}+8+@{spell_dc_mod}+@{pb})" data-i18n="cha-u">CHA</option>
<option value="(@{saveflat})" data-i18n="flat-u">FLAT</option>
</select>
<input class="flatflag" type="hidden" name="attr_savedc"/>
<input class="num flat" type="text" name="attr_saveflat" placeholder="10" value="10"/>
</div>
<div class="row"><span data-i18n="save-effect:-u">SAVE EFFECT:</span>
<input type="text" name="attr_saveeffect" placeholder="half damage" style="width: 160px;" data-i18n-placeholder="save-effect-place"/>
</div>
<div class="row ammo"><span data-i18n="ammunition:-u">AMMUNITION:</span>
<input type="text" name="attr_ammo" placeholder="Arrows" style="width: 160px;" data-i18n-placeholder="ammunition-place"/>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span>
<textarea name="attr_atk_desc" placeholder="Up to 2 creatures within 5 feet" data-i18n-placeholder="description-place"></textarea>
</div>
</div>
<div class="display" style="display: grid; grid-template-columns: 1fr 90px; grid-template-columns: 1fr 78px; margin: 0 8px; gap: 5px;">
<button type="roll" name="roll_attack" value="@{rollbase}"><span name="attr_atkname" style="width: 85px; max-width: none;"></span>
<input type="text" name="attr_atkbonus" style="width: 40px; text-align: center; max-width: none;" value=""/>
</button>
<button type="roll" name="roll_dmg" value="@{rollbase_dmg}">
<input type="text" name="attr_atkdmgtype" style="width: 100%; max-width: none;" value=""/>
</button>
</div>
</div>
<input type="hidden" name="attr_rollbase"/>
<input type="hidden" name="attr_rollbase_dmg"/>
<input type="hidden" name="attr_rollbase_crit"/>
<input type="hidden" name="attr_hldmg"/>
<input type="hidden" name="attr_spelllevel"/>
<input type="hidden" name="attr_itemid"/>
<input type="hidden" name="attr_spellid"/>
<input type="hidden" name="attr_spell_innate"/>
<input type="hidden" name="attr_versatile_alt"/>
<input type="hidden" name="attr_spelldesc_link"/>
<button type="roll" name="roll_spelldesc_link" style="display: none;" value="@{spelldesc_link}"></button>
<button type="roll" name="roll_attack_dmg" style="display: none;" value="@{rollbase_dmg}"></button>
<button type="roll" name="roll_attack_crit" style="display: none;" value="@{rollbase_crit}"></button>
</fieldset>
<input class="toggleflag" type="hidden" name="attr_global_attack_mod_flag"/>
<div class="hidden textbox globalattack"><span class="label" data-i18n="global-attack-u">GLOBAL ATTACK MODIFIER</span>
<fieldset class="repeating_tohitmod">
<div class="global-mod">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked" style="top: -15px;"/><span>y</span>
<input class="active-flag" type="checkbox" name="attr_global_attack_active_flag" value="1"/>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_global_attack_name" value="" placeholder="Bless"/>
</div>
<div class="row"><span data-i18n="roll:-u">ROLL:</span>
<input type="text" name="attr_global_attack_roll" value="" placeholder="1d4"/>
</div>
</div>
<div class="globaldisplay">
<button class="ellipsis" type="roll" name="roll_global_save" value="@{wtype}&{template:simple} {{rname=@{global_attack_name}}} {{r1=[[@{global_attack_roll}]]}} {{normal=1}} @{charname_output}"><span name="attr_global_attack_roll"></span><span name="attr_global_attack_name"></span></button>
</div>
</div>
</fieldset>
</div>
<input class="toggleflag" type="hidden" name="attr_global_damage_mod_flag"/>
<div class="hidden globalattack global-dmg"><span class="label" data-i18n="global-damage-u">GLOBAL DAMAGE MODIFIER</span>
<fieldset class="repeating_damagemod">
<div class="global-mod">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked" style="top: -15px;"/><span>y</span>
<input class="active-flag" type="checkbox" name="attr_global_damage_active_flag" value="1"/>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_global_damage_name" value="" placeholder="Sneak Attack"/>
</div>
<div class="row"><span data-i18n="dmg:-u">DAMAGE:</span>
<input type="text" name="attr_global_damage_damage" value="" placeholder="1d6"/>
</div>
<div class="row"><span data-i18n="critical-dmg:-u">CRITICAL DAMAGE:</span>
<input type="text" name="attr_global_damage_critical_damage" value="" placeholder="1d6"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_global_damage_type" value="" placeholder="Sneak"/>
</div>
</div>
<div class="display"><span class="title" name="attr_global_damage_name"></span><span class="subheader">
<button class="ellipsis" type="roll" name="roll_global_save" value="@{wtype}&{template:simple} {{rname=@{global_damage_type}}} {{r1=[[@{global_damage_damage}]]}} {{normal=1}} @{charname_output}"><span name="attr_global_damage_damage"></span><span name="attr_global_damage_critical_damage"></span><span name="attr_global_damage_type"></span></button></span></div>
</div>
</fieldset>
</div><span class="label" style="margin-top: 0px; position: absolute; bottom: 0px;" data-i18n="atk-spellcasting-u">ATTACKS &amp; SPELLCASTING</span>
</div>
<input class="toggleflag" type="hidden" name="attr_global_ac_mod_flag"/>
<div class="equipment hidden">
<div class="textarea globalattack acmod">
<fieldset class="repeating_acmod">
<div class="global-mod">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked" style="top: -15px;"/><span>y</span>
<input class="active-flag" type="checkbox" name="attr_global_ac_active_flag" value="1"/>
<div class="options">
<label>Value:
<input type="number" name="attr_global_ac_val" placeholder="1"/>
</label>
<label>Name:
<input type="text" name="attr_global_ac_name" placeholder="Defense"/>
</label>
</div>
<div class="display"><span class="title ellipsis"><span name="attr_global_ac_val"></span> [<span name="attr_global_ac_name"></span>]</span></div>
</div>
</fieldset>
</div><span class="label" data-i18n="global-ac-u">GLOBAL AC MODIFIER</span>
</div>
<div class="equipment">
<div class="money">
<div class="coin"><span class="label" data-i18n="copper-piece-u">CP</span>
<input type="text" name="attr_cp" title="@{cp}"/>
</div>
<div class="coin"><span class="label" data-i18n="silver-piece-u">SP</span>
<input type="text" name="attr_sp" title="@{sp}"/>
</div>
<div class="coin"><span class="label" data-i18n="electrum-piece-u">EP</span>
<input type="text" name="attr_ep" title="@{ep}"/>
</div>
<div class="coin"><span class="label" data-i18n="gold-piece-u">GP</span>
<input type="text" name="attr_gp" title="@{gp}"/>
</div>
<div class="coin"><span class="label" data-i18n="platinum-piece-u">PP</span>
<input type="text" name="attr_pp" title="@{pp}"/>
</div>
</div>
<input class="inventoryflag" type="hidden" name="attr_simpleinventory" value="complex"/>
<textarea class="simple" name="attr_equipment"></textarea>
<div class="complex">
<input class="warningflag" type="hidden" name="attr_armorwarningflag"/>
<div class="warning"><span>
<label>
<input type="checkbox" name="attr_armorwarningflag" value="hide"/>*
</label><span data-i18n="warning-armor-sets-u">WARNING - MULTIPLE SETS OF ARMOR OR SHIELDS HAVE BEEN ADDED AND AC IS NOT CORRECTLY CALCULATED. UNEQUIP THE ARMOR PIECE FROM THE ITEM DETAILS.</span></span></div>
<input class="warningflag" type="hidden" name="attr_customacwarningflag"/>
<div class="warning"><span>
<label>
<input type="checkbox" name="attr_customacwarningflag" value="hide"/>*
</label><span data-i18n="warning-customac-u">ATTENTION - YOUR CUSTOM AC CALCULATION IS NOT BEING USED AS YOU HAVE ARMOR EQUIPPED. IF YOU WANT TO USE YOUR CUSTOM AC, UNEQUIP THE ARMOR PIECE FROM THE ITEM DETAILS.</span></span></div>
<div class="header" style="height: auto;"><span class="pictos" style="width: 25px; font-size: 15px;">*</span><span style="width: 100px; text-align: left;" data-i18n="item-name-u">ITEM NAME</span><span style="width: 25px;"><img src="https://app.roll20.net/images/dndstyling/weight_lbs.png" style="width: 15px; margin-bottom: -3px;" alt="Weight symbol"/></span></div>
<fieldset class="repeating_inventory">
<input class="equippedflag" type="hidden" name="attr_equipped"/>
<div class="item">
<input class="equipped main" type="checkbox" name="attr_equipped" value="1" checked="checked"/>
<input class="weight" type="text" name="attr_itemcount" value="1"/>
<input class="name" type="text" name="attr_itemname"/>
<input class="weight" type="text" name="attr_itemweight"/>
<input class="inventorysubflag" type="checkbox" name="attr_inventorysubflag"/><span>i</span>
<div class="subitem">
<input class="equipped" type="checkbox" name="attr_equipped" value="1" checked="checked"/><span class="equippedlabel" data-i18n="equipped-u">EQUIPPED</span>
<input class="equipped" type="checkbox" name="attr_useasresource" value="1"/><span class="equippedlabel" data-i18n="use-as-resource-u">USE AS A RESOURCE</span>
<input class="equipped" type="checkbox" name="attr_hasattack" value="1"/><span class="equippedlabel" data-i18n="has-attack-u">HAS AN ATTACK</span><span class="label" data-i18n="prop:-u">PROP:</span>
<input class="subfield" type="text" name="attr_itemproperties"/><span class="label" data-i18n="mods:-u">MODS:</span>
<input class="subfield" type="text" name="attr_itemmodifiers"/>
<textarea class="subtextarea" name="attr_itemcontent"></textarea>
</div>
<input type="hidden" name="attr_itemattackid"/>
<input type="hidden" name="attr_itemresourceid"/>
</div>
</fieldset>
<div class="weighttotal"><span class="label" data-i18n="total-weight-u">TOTAL WEIGHT</span>
<input type="text" name="attr_weighttotal" value="0"/>
<input class="encumberance" type="hidden" name="attr_encumberance"/><span class="encumb immobile" data-i18n="immobile-u">IMMOBILE</span><span class="encumb heavily" data-i18n="heavily-encumbered-u">HEAVILY ENCUMBERED</span><span class="encumb encumbered" data-i18n="encumbered-u">ENCUMBERED</span><span class="encumb over" data-i18n="over-carrying-cap-u">OVER CARRYING CAPACITY</span>
</div>
</div><span class="label" style="margin-top: 0px; position: absolute; bottom: 0px;" data-i18n="equipment-u">EQUIPMENT</span>
</div>
</div>
<div class="col col3">
<div class="rp-traits">
<input class="toggle-rp-traits" type="hidden" name="attr_toggle_rp_traits"/>
<div class="rp-traits__title"><span data-i18n="characteristics"></span>
<input type="checkbox" value="1" name="attr_toggle_rp_traits"/>
</div>
<div class="textbox pibf">
<input class="options-flag" type="checkbox" name="attr_options-flag-personality" checked="checked"/><span>y</span>
<div class="options">
<textarea name="attr_personality_traits" style="min-height: 50px; height: 50px;"></textarea>
</div>
<div class="display"><span name="attr_personality_traits"></span></div><span class="label" data-i18n="personality-traits-u">PERSONALITY TRAITS</span>
</div>
<div class="textbox pibf">
<input class="options-flag" type="checkbox" name="attr_options-flag-ideals" checked="checked"/><span>y</span>
<div class="options">
<textarea name="attr_ideals"></textarea>
</div>
<div class="display"><span name="attr_ideals"></span></div><span class="label" data-i18n="ideals-u">IDEALS</span>
</div>
<div class="textbox pibf">
<input class="options-flag" type="checkbox" name="attr_options-flag-bonds" checked="checked"/><span>y</span>
<div class="options">
<textarea name="attr_bonds"></textarea>
</div>
<div class="display"><span name="attr_bonds"></span></div><span class="label" data-i18n="bonds-u">BONDS</span>
</div>
<div class="textbox pibf">
<input class="options-flag" type="checkbox" name="attr_options-flag-flaws" checked="checked"/><span>y</span>
<div class="options">
<textarea name="attr_flaws" style="min-height: 46px; height: 46px;"></textarea>
</div>
<div class="display"><span name="attr_flaws"></span></div><span class="label" data-i18n="flaws-u">FLAWS</span>
</div>
</div>
<input type="hidden" name="attr_use_hero_points"/>
<div class="hero-points">
<input type="hidden" name="attr_hero_points"/>
<div class="value">
<input type="text" name="attr_hero_points" value="0"/>
</div>
<div class="label">
<button class="roll-hd btn ui-draggable" type="roll" name="roll_hero_points" value="@{wtype}&{template:simple} {{rname=^{hero-points-u}}} {{r1=[[d6]]}} {{normal=1}} {{r2=[[0d20]]}} @{charname_output}" title="Roll Hero Points"><span><span data-i18n="hero-points-u">HERO POINTS</span> (1D6)</span><span data-i18n="none-left-u">NONE LEFT</span>
<input type="checkbox" name="attr_consumed_hero_points" value="1"/>
</button>
</div>
</div>
<input type="hidden" name="attr_use_rest_buttons" value="1"/>
<div class="resources">
<div class="rest">
<button type="action" name="act_short_rest"><span data-i18n="short-rest"></span></button>
<button type="action" name="act_long_rest"><span data-i18n="long-rest"></span></button>
</div>
<input class="pb-value" type="hidden" name="attr_pb"/>
<div class="hdice-dsaves-container" style="width: 246px; margin-top: 10px;">
<div class="subcontainer" style="height: 64px; width: 119px; position: relative;">
<input class="options-flag" type="checkbox" name="attr_options-flag-resource"/><span>y</span>
<div class="display">
<div class="top">
<input class="use-pb" type="hidden" name="attr_class_resource_use_pb"/><span data-i18n="total">Total</span><span class="pb"></span>
<input type="text" name="attr_class_resource_max" title="@{class_resource_max}"/>
</div>
<input type="number" name="attr_class_resource" title="@{class_resource}" style="margin-bottom: -6px;"/>
<input class="label" type="text" name="attr_class_resource_name" title="@{class_resource_name}" placeholder="CLASS RESOURCE" data-i18n-placeholder="class-resource-u"/>
</div>
<div class="options">
<label>
<input type="checkbox" name="attr_class_resource_use_pb" value="1"/><span data-i18n="use-pb"></span>
</label><span data-i18n="reset-on"></span>
<select name="attr_class_resource_reset">
<option data-i18n="disabled"></option>
<option value="short" data-i18n="short-rest"></option>
<option value="long" data-i18n="long-rest"></option>
</select>
</div>
</div>
<div class="subcontainer" style="height: 64px; width: 119px; position: relative; float: right;">
<input class="options-flag" type="checkbox" name="attr_options-flag-other-resource"/><span>y</span>
<div class="display">
<div class="top">
<input class="use-pb" type="hidden" name="attr_other_resource_use_pb"/><span data-i18n="total">Total</span><span class="pb"></span>
<input type="text" name="attr_other_resource_max" title="@{other_resource_max}"/>
</div>
<input type="number" name="attr_other_resource" title="@{other_resource}" style="margin-bottom: -6px;"/>
<input class="label" type="text" name="attr_other_resource_name" title="@{other_resource_name}" placeholder="OTHER RESOURCE" data-i18n-placeholder="other-resource-u"/>
<input type="hidden" name="attr_other_resource_itemid"/>
</div>
<div class="options">
<label>
<input type="checkbox" name="attr_other_resource_use_pb" value="1"/><span data-i18n="use-pb"></span>
</label><span data-i18n="reset-on"></span>
<select name="attr_other_resource_reset">
<option data-i18n="disabled"></option>
<option value="short" data-i18n="short-rest"></option>
<option value="long" data-i18n="long-rest"></option>
</select>
</div>
</div>
</div>
<fieldset class="repeating_resource">
<div class="hdice-dsaves-container" style="width: 246px;">
<div class="subcontainer" style="height: 64px; width: 119px; position: relative;">
<input class="options-flag" type="checkbox" name="attr_options-flag-left"/><span>y</span>
<div class="display">
<div class="top">
<input class="use-pb" type="hidden" name="attr_resource_left_use_pb"/><span data-i18n="total">Total</span><span class="pb"></span>
<input type="text" name="attr_resource_left_max" title="@{repeating_resource_left_max}"/>
</div>
<input type="number" name="attr_resource_left" title="@{repeating_resource_left}" style="margin-bottom: -6px;"/>
<input class="label" type="text" name="attr_resource_left_name" title="@{repeating_resource_left_name}" placeholder="OTHER RESOURCE" data-i18n-placeholder="other-resource-u"/>
<input type="hidden" name="attr_resource_left_itemid"/>
</div>
<div class="options">
<label>
<input type="checkbox" name="attr_resource_left_use_pb" value="1"/><span data-i18n="use-pb"></span>
</label><span data-i18n="reset-on"></span>
<select name="attr_resource_left_reset">
<option data-i18n="disabled"></option>
<option value="short" data-i18n="short-rest"></option>
<option value="long" data-i18n="long-rest"></option>
</select>
</div>
</div>
<div class="subcontainer" style="height: 64px; width: 119px; position: relative;">
<input class="options-flag" type="checkbox" name="attr_options-flag-right"/><span>y</span>
<div class="display">
<div class="top">
<input class="use-pb" type="hidden" name="attr_resource_right_use_pb"/><span data-i18n="total">Total</span><span class="pb"></span>
<input type="text" name="attr_resource_right_max" title="@{repeating_resource_right_max}"/>
</div>
<input type="number" name="attr_resource_right" title="@{repeating_resource_right}" style="margin-bottom: -6px;"/>
<input class="label" type="text" name="attr_resource_right_name" title="@{repeating_resource_right_name}" placeholder="OTHER RESOURCE" data-i18n-placeholder="other-resource-u"/>
<input type="hidden" name="attr_resource_right_itemid"/>
</div>
<div class="options">
<label>
<input type="checkbox" name="attr_resource_right_use_pb" value="1"/><span data-i18n="use-pb"></span>
</label><span data-i18n="reset-on"></span>
<select name="attr_resource_right_reset">
<option data-i18n="disabled"></option>
<option value="short" data-i18n="short-rest"></option>
<option value="long" data-i18n="long-rest"></option>
</select>
</div>
</div>
</div>
</fieldset>
</div>
<div class="textbox traits" style="min-height: 480px;">
<input class="empty-select" type="hidden" name="attr_filter_traits_by" value=""/>
<select name="attr_filter_traits_by">
<option value="" selected="selected" data-i18n="filter">Filter by...</option>
<option data-i18n="racial" value="Racial">Racial</option>
<option data-i18n="class" value="Class">Class</option>
<option data-i18n="feats" value="Feat">Feats</option>
<option data-i18n="traits" value="Trait">Traits</option>
<option data-i18n="background" value="Background">Background</option>
<option data-i18n="items" value="Item">Items</option>
<option data-i18n="other" value="Other">Other</option>
</select>
<input class="filter-traits-by" type="hidden" name="attr_filter_traits_by"/>
<input class="inventoryflag" type="hidden" name="attr_simpletraits" value="complex"/>
<textarea class="simple" name="attr_features_and_traits" style="min-height: 461px; height: 461px;"></textarea>
<div class="complex">
<fieldset class="repeating_traits">
<input class="newly-added" type="checkbox" name="attr_options-flag" checked="checked"/>
<input class="filter-trait" type="hidden" name="attr_source" value=""/>
<div class="trait">
<button class="output" type="roll" name="roll_output" value="@{wtype}&{template:traits} @{charname_output} {{name=@{name}}} {{source=@{source}: @{source_type}}} {{description=@{description}}}">w</button>
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<input class="display-flag" type="checkbox" name="attr_display_flag"/>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_name" style="width: 120px;"/>
</div>
<div class="row"><span data-i18n="source:-u">SOURCE:</span>
<input class="empty-select" type="hidden" name="attr_source" value=""/>
<select name="attr_source">
<option selected="selected" value="">Choose...</option>
<option data-i18n="racial" value="Racial">Racial</option>
<option data-i18n="class" value="Class">Class</option>
<option data-i18n="feat" value="Feat">Feat</option>
<option data-i18n="trait" value="Trait">Trait</option>
<option data-i18n="background" value="Background">Background</option>
<option data-i18n="item" value="Item">Item</option>
<option data-i18n="other" value="Other">Other</option>
</select>
</div>
<div class="row"><span data-i18n="source-type:-u">SOURCE TYPE:</span>
<input type="text" name="attr_source_type" style="width: 160px;" placeholder="Dwarf/Fighter/4th Level"/>
</div>
<div class="row">
<textarea name="attr_description" style="min-height: 200px; height: 200px;"></textarea>
</div>
</div>
<div class="display" style="margin-bottom: 2px;"><span class="title" name="attr_name"></span><span class="subheader"><span name="attr_source"></span><span name="attr_source_type"></span></span><span class="desc" name="attr_description"></span></div>
</div>
</fieldset>
</div><span class="label" data-i18n="feats-traits-u" style="margin-top: 0px; position: absolute; bottom: 0px;">FEATURES &amp; TRAITS</span>
</div>
<input class="missing-info-flag" type="hidden" name="attr_missing_info"/>
<div class="missing-info"><span><span class="info-popup pictos">i</span> Looking for missing data?</span><span class="missing-info-desc">Your data is in the simple version that can be re-enabled from the Settings(gear) tab of the character sheet. Under GENERAL OPTIONS and then FEATS &amp; TRAITS. Set the section to be &apos;Simple&apos;.</span></div>
</div>
</div>
</div>
<div class="page bio"> <div class="header">
<div class="name-container">
<img src="https://app.roll20.net/images/dndstyling/srd5_360.png" style="padding-bottom: 5px;" alt="SRD5 by Roll20">
<input type="text" name="attr_character_name" style="width: 100%;">
<span class="label" data-i18n="char-name-u">CHARACTER NAME</span>
</div>
<div class="header-info">
<div class="top" style="position: relative; top: 10px;">
<div class="hlabel-container">
<input type="text" name="attr_age" style="width: 120px;">
<span class="label" data-i18n="age-u">AGE</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_size" style="width: 120px;">
<span class="label" data-i18n="size-u">SIZE</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_height" style="width: 120px;">
<span class="label" data-i18n="height-u">HEIGHT</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_weight" style="width: 120px;">
<span class="label" data-i18n="weight-u">WEIGHT</span>
</div>
</div>
<div class="bottom">
<div class="hlabel-container">
<input type="text" name="attr_eyes" style="width: 160px;">
<span class="label" data-i18n="eye-u">EYES</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_skin" style="width: 160px;">
<span class="label" data-i18n="skin-u">SKIN</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_hair" style="width: 160px;">
<span class="label" data-i18n="hair-u">HAIR</span>
</div>
</div>
</div>
</div>
<div class="body">
<div class="col col1">
<div class="textbox">
<textarea name="attr_character_appearance" style="min-height: 300px; height: 300px;"></textarea>
<span class="label" data-i18n="char-appearance-u">CHARACTER APPEARANCE</span>
</div>
<div class="textbox">
<textarea name="attr_character_backstory" style="min-height: 623px; height: 623px;"></textarea>
<span class="label" data-i18n="char-backstory-u">CHARACTER BACKSTORY</span>
</div>
</div>
<div class="col2-3">
<div class="textbox">
<textarea name="attr_allies_and_organizations" style="min-height: 288px; height: 300px;"></textarea>
<span class="label" data-i18n="allies-orgs-u">ALLIES & ORGANIZATIONS</span>
</div>
<div class="textbox">
<textarea name="attr_additional_feature_and_traits" style="min-height: 289px; height: 300px;"></textarea>
<span class="label" data-i18n="add-feats-traits-u">ADDITIONAL FEATURES & TRAITS</span>
</div>
<div class="textbox">
<textarea name="attr_treasure" style="min-height: 300px; height: 300px;"></textarea>
<span class="label" data-i18n="treasure-u">TREASURE</span>
</div>
</div>
</div>
</div>
<div class="page spells">
<div class="header">
<div class="name-container"><img src="https://app.roll20.net/images/dndstyling/srd5_360.png" style="padding-bottom: 5px;" alt="SRD5e by Roll20"/>
<input type="text" name="attr_character_name" style="width: 100%;"/><span class="label" data-i18n="char-name-u">CHARACTER NAME</span>
</div>
<div class="header-info">
<div class="part">
<select name="attr_spellcasting_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select><span class="label" data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
</div>
<div class="part"><span class="display" name="attr_spell_save_dc"></span><span class="label" data-i18n="spell-save-dc-u">SPELL SAVE DC</span></div>
<div class="part"><span class="display" name="attr_spell_attack_bonus"></span><span class="label" data-i18n="spell-atk-bonus-u">SPELL ATTACK BONUS</span></div>
</div>
</div>
<input class="spellpoints_flag" type="hidden" name="attr_use_spell_points"/>
<input class="spellicon_flag" type="hidden" name="attr_spellicon_flag" value="all"/>
<div class="body">
<div class="col col1">
<div class="spell-level">
<div class="level"><span class="label">0</span></div>
<div class="cantrips"><span class="label" data-i18n="cantrips-u">CANTRIPS</span></div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-cantrip">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="cantrip-progression:-u">CANTRIP PROGRESSION</span>
<select name="attr_spell_damage_progression">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Cantrip Dice" data-i18n="cantrip-dice-u">CANTRIP DICE</option>
<option value="Cantrip Beam" data-i18n="cantrip-beam-u">CANTRIP BEAM</option>
</select>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="cantrip"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div><span class="spell-labels"><span class="spell-points" data-i18n="spell-points-u" style="margin-right: 45px;">SPELL POINTS</span><span data-i18n="slots_total-u" style="margin-right: 45px;">SLOTS TOTAL</span><span class="spell-point-hide" data-i18n="slots_remaining-u">SLOTS REMAINING</span><span class="spell-points" data-i18n="remaining-u">REMAINING</span></span>
<div class="spell-level">
<div class="level"><span class="label">1</span></div>
<div class="spell-points"><span>2</span></div>
<div class="total"><span name="attr_lvl1_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl1_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-1">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="1"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="spell-level">
<div class="level"><span class="label">2</span></div>
<div class="spell-points"><span>3</span></div>
<div class="total"><span name="attr_lvl2_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl2_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-2">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="2"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
<div class="col col2">
<div class="spell-level">
<div class="level"><span class="label">3</span></div>
<div class="spell-points"><span>5</span></div>
<div class="total"><span name="attr_lvl3_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl3_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-3">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="3"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="spell-level">
<div class="level"><span class="label">4</span></div>
<div class="spell-points"><span>6</span></div>
<div class="total"><span name="attr_lvl4_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl4_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-4">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="4"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="spell-level">
<div class="level"><span class="label">5</span></div>
<div class="spell-points"><span>7</span></div>
<div class="total"><span name="attr_lvl5_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl5_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-5">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="5"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
<div class="col col3">
<div class="spell-level">
<div class="level"><span class="label">6</span></div>
<div class="spell-points"><span>9</span></div>
<div class="total"><span name="attr_lvl6_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl6_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-6">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="6"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="spell-level">
<div class="level"><span class="label">7</span></div>
<div class="spell-points"><span>10</span></div>
<div class="total"><span name="attr_lvl7_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl7_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-7">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="7"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="spell-level">
<div class="level"><span class="label">8</span></div>
<div class="spell-points"><span>11</span></div>
<div class="total"><span name="attr_lvl8_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl8_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-8">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="8"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="spell-level">
<div class="level"><span class="label">9</span></div>
<div class="spell-points"><span>13</span></div>
<div class="total"><span name="attr_lvl9_slots_total"></span></div>
<div class="expended">
<input type="number" name="attr_lvl9_slots_expended" value="0"/>
</div>
</div>
<div class="spell-container">
<fieldset class="repeating_spell-9">
<div class="spell">
<div class="display">
<input type="hidden" name="attr_rollcontent" value="@{wtype}&{template:spell} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}"/>
<input class="prep-box" type="checkbox" name="attr_spellprepared" value="1"/><span class="prep"></span>
<button class="spellcard ellipsis" type="roll" name="roll_spell" value="@{rollcontent}"><span class="spellname" name="attr_spellname"></span><span class="innate" name="attr_innate"></span></button>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
<input class="spellritual" type="hidden" name="attr_spellritual" value="0"/>
<input class="spellconcentration" type="hidden" name="attr_spellconcentration" value="0"/>
<input class="v" type="hidden" name="attr_spellcomp_v" value="{{v=1}}"/>
<input class="s" type="hidden" name="attr_spellcomp_s" value="{{s=1}}"/>
<input class="m" type="hidden" name="attr_spellcomp_m" value="{{m=1}}"/><span class="spellritual" data-i18n="spell-ritual">R</span><span class="spellconcentration" data-i18n="spell-concentration">C</span><span class="componentscontainer"><span class="v" data-i18n="spell-component-verbal">V</span><span class="s" data-i18n="spell-component-somatic">S</span><span class="m" data-i18n="spell-component-material">M</span></span>
</div>
<input type="hidden" name="attr_roll_output_dc" value="@{spell_save_dc}"/>
<button class="output btn ui-draggable" type="roll" name="roll_output" value="@{wtype}&{template:spelloutput} {{level=@{spellschool} @{spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{roll_output_dc}}} @{spellconcentration} @{charname_output}">w</button>
<input class="details-flag" type="checkbox" name="attr_details-flag" checked="checked"/><span>i</span>
<div class="wrapper">
<input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"/><span>y</span>
<div class="options">
<div class="row"><span data-i18n="name:-u">NAME:</span>
<input type="text" name="attr_spellname"/>
</div>
<div class="row"><span data-i18n="school:-u">SCHOOL:</span>
<select name="attr_spellschool">
<option value="abjuration" data-i18n="abjuration">Abjuration</option>
<option value="conjuration" data-i18n="conjuration">Conjuration</option>
<option value="divination" data-i18n="divination">Divination</option>
<option value="enchantment" data-i18n="enchantment">Enchantment</option>
<option value="evocation" data-i18n="evocation">Evocation</option>
<option value="illusion" data-i18n="illusion">Illusion</option>
<option value="necromancy" data-i18n="necromancy">Necromancy</option>
<option value="transmutation" data-i18n="transmutation">Transmutation</option>
</select>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span data-i18n="ritual-u">RITUAL</span>
</div>
<div class="row"><span data-i18n="casting-time:-u">CASTING TIME:</span>
<input type="text" name="attr_spellcastingtime"/>
</div>
<div class="row"><span data-i18n="range:-u">RANGE:</span>
<input type="text" name="attr_spellrange"/>
</div>
<div class="row"><span data-i18n="target:-u">TARGET:</span>
<input type="text" name="attr_spelltarget"/>
</div>
<input type="hidden" name="attr_spellcomp"/>
<div class="row"><span data-i18n="components:-u">COMPONENTS:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span data-i18n="spell-component-material">M</span>
<input type="text" name="attr_spellcomp_materials" placeholder="ruby dust worth 50gp" data-i18n-placeholder="ruby-dust-place"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration-u">CONCENTRATION</span>
</div>
<div class="row"><span data-i18n="duration:-u">DURATION:</span>
<input type="text" name="attr_spellduration"/>
</div>
<div class="row"><span data-i18n="spell-ability-u">SPELLCASTING ABILITY</span>
<select name="attr_spell_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="spell" data-i18n="spell-u">SPELL</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="innate:-u">INNATE:</span>
<input type="text" name="attr_innate" placeholder="1/day" value=""/>
</div>
<div class="row"><span data-i18n="output:-u">OUTPUT:</span>
<select name="attr_spelloutput">
<option value="SPELLCARD" data-i18n="spellcard-u">SPELLCARD</option>
<option value="ATTACK" data-i18n="attack-u">ATTACK</option>
</select>
</div>
<input class="spellattackinfoflag" type="hidden" name="attr_spelloutput"/>
<div class="spellattackinfo">
<div class="row"><span data-i18n="spell-atk:-u">SPELL ATTACK:</span>
<select name="attr_spellattack">
<option value="None" data-i18n="none">None</option>
<option value="Melee" data-i18n="melee">Melee</option>
<option value="Ranged" data-i18n="ranged">Ranged</option>
</select>
</div>
<div class="row"><span data-i18n="damage:-u">DAMAGE:</span>
<input type="text" name="attr_spelldamage" style="width: 50px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="damage2:-u">DAMAGE2:</span>
<input type="text" name="attr_spelldamage2" style="width: 45px;"/><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spelldamagetype2" style="width: 103px;"/>
</div>
<div class="row"><span data-i18n="healing:-u">HEALING:</span>
<input type="text" name="attr_spellhealing" style="width: 45px;"/>
</div>
<div class="row">
<input type="checkbox" name="attr_spelldmgmod" value="Yes"/><span data-i18n="add-ability-mod-u">ADD ABILITY MOD TO DAMAGE OR HEALING</span>
</div>
<div class="row"><span data-i18n="saving-throw:-u">SAVING THROW:</span>
<select name="attr_spellsave">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span data-i18n="effect:-u">EFFECT:</span>
<input type="text" name="attr_spellsavesuccess" style="width: 78px;" placeholder="Half damage" data-i18n-placeholder="half-dmg-place"/>
</div>
<div class="row"><span data-i18n="at-higher-lvl-dmg:-u">HIGHER LVL CAST DMG:</span>
<input type="text" name="attr_spellhldie" placeholder="1" style="width: 15px; text-align: right;"/>
<select name="attr_spellhldietype">
<option value="" selected="selected" data-i18n="none-u">NONE</option>
<option value="d4">d4</option>
<option value="d6">d6</option>
<option value="d8">d8</option>
<option value="d10">d10</option>
<option value="d12">d12</option>
<option value="d20">d20</option>
</select><span>+</span>
<input type="text" name="attr_spellhlbonus" placeholder="0" style="width: 15px; text-align: center;"/>
</div>
<div class="row"><span data-i18n="include_spell_desc:-u">INCLUDE SPELL DESCRIPTION IN ATTACK:</span>
<select name="attr_includedesc">
<option value="off" selected="selected" data-i18n="off">Off</option>
<option value="partial" data-i18n="partial">Partial</option>
<option value="on" data-i18n="on">On</option>
</select>
</div>
</div>
<div class="row"><span data-i18n="description:-u">DESCRIPTION:</span></div>
<div class="row">
<textarea name="attr_spelldescription"></textarea>
</div>
<div class="row"><span data-i18n="at-higher-lvl:-u">AT HIGHER LEVELS:</span></div>
<div class="row">
<textarea name="attr_spellathigherlevels" style="height: 36px; min-height: 36px;"></textarea>
</div>
<div class="row"><span data-i18n="class:-u">CLASS:</span>
<input type="text" name="attr_spellclass" style="width: 45px;"/>
</div>
<div class="row"><span data-i18n="type:-u">TYPE:</span>
<input type="text" name="attr_spellsource" style="width: 45px;"/>
</div>
<input type="hidden" name="attr_spellattackid"/>
<input type="hidden" name="attr_spelllevel" value="9"/>
<label class="options-done">
<input type="checkbox" name="attr_options-flag" checked="checked"/><span class="label" data-i18n="done">Done</span>
</label>
</div>
<div class="details">
<div class="row ellipsis"><span name="attr_spellname"></span>
<input class="innate_flag" type="hidden" name="attr_innate" value=""/>
</div>
<div class="row italics"><span name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span name="attr_spelllevel"></span>
<input type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span>(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="row"><span class="bold" data-i18n="casting-time:">Casting Time:</span><span name="attr_spellcastingtime"></span></div>
<div class="row"><span class="bold" data-i18n="range:">Range:</span><span name="attr_spellrange"></span></div>
<div class="row"><span class="bold" data-i18n="target:">Target:</span><span name="attr_spelltarget"></span></div>
<div class="row"><span class="bold" data-i18n="components:">Components:</span>
<input type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span data-i18n="spell-component-verbal">V</span>
<input type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span data-i18n="spell-component-somatic">S</span>
<input type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="row"><span name="attr_spellcomp_materials"></span></div>
<div class="row"><span class="bold" data-i18n="duration:">Duration:</span>
<input type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="row"><span class="bold" data-i18n="description:">Description:</span></div>
<div class="row"><span name="attr_spelldescription"></span></div>
<input type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="row"><span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span><span name="attr_spellathigherlevels"></span></div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</div>
<div class="page options">
<div class="header">
<div class="name-container"><img src="https://app.roll20.net/images/dndstyling/srd5_360.png" style="padding-bottom: 5px;" alt="SRD5e by Roll20"/>
<input type="text" name="attr_character_name" style="width: 100%;"/><span class="label" data-i18n="char-name-u">CHARACTER NAME</span>
</div>
<input class="options-flag" type="checkbox" name="attr_options-class-selection" checked="checked"/><span>y</span>
<div class="header-info options">
<div class="row" style="border-top: 2px dashed gold;"><span data-i18n="class:-u">CLASS:</span>
<input class="flag" type="hidden" name="attr_custom_class"/>
<select class="hiding class" name="attr_class">
<option value="" data-i18n="choose">Choose</option>
<option value="Artificer" data-i18n="artificer"></option>
<option value="Bard" data-i18n="bard"></option>
<option value="Barbarian" data-i18n="barbarian"></option>
<option value="Beastheart" data-i18n="beastheart"></option>
<option value="Cleric" data-i18n="cleric"></option>
<option value="Druid" data-i18n="druid"></option>
<option value="Fighter" data-i18n="fighter"></option>
<option value="Illrigger" data-i18n="illrigger"></option>
<option value="Monk" data-i18n="monk"></option>
<option value="Paladin" data-i18n="paladin"></option>
<option value="Ranger" data-i18n="ranger"></option>
<option value="Rogue" data-i18n="rogue"></option>
<option value="Sorcerer" data-i18n="sorcerer"></option>
<option value="Tactician" data-i18n="tactician"></option>
<option value="Warlock" data-i18n="warlock"></option>
<option value="Wizard" data-i18n="wizard"></option>
</select>
<input class="showing" type="text" name="attr_cust_classname" style="width: 90px;"/><span data-i18n="subclass:-u">SUBCLASS:</span>
<input type="text" name="attr_subclass"/><span data-i18n="lvl:-u">LEVEL:</span>
<input class="class-level" type="number" style="width: 40px" name="attr_base_level" value="1"/>
</div>
<div class="row"><span data-i18n="race:-u">RACE:</span>
<input type="text" name="attr_race"/><span data-i18n="subrace:-u">SUBRACE:</span>
<input type="text" name="attr_subrace"/><span data-i18n="creaturetype:-u">CREATURE TYPE:</span>
<input type="text" name="attr_creaturetype"/>
</div>
</div>
<div class="header-info display">
<div class="top">
<div class="hlabel-container">
<div class="class-display__container">
<input class="l1mancermode" type="hidden" name="attr_l1mancer_status"/>
<input class="levelermode" type="hidden" name="attr_lvup_button_mode"/>
<input type="hidden" name="attr_base_level"/><span class="class-display no_events" name="attr_class_display"></span>
<!-- <input type="text" name="attr_class_display" class="class-display no_events">-->
<input class="showleveler" type="hidden" name="attr_showleveler" value="0"/>
<button class="levelerbutton" type="action" name="act_launch_the_right_mancer" title="Charactermancer Level Up"></button>
<!-- <div class="invalidXP"><span class="icon">!</span><span class="message" data-i18n="invalidxp">Enter a number for Level Up reminder</span></div>-->
</div><span class="label" data-i18n="class-level-u">CLASS &amp; LEVEL</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_background" style="width: 182px;"/><span class="label" data-i18n="background-u">BACKGROUND</span>
</div>
</div>
<div class="bottom">
<div class="hlabel-container">
<input class="no_events" type="text" name="attr_race_display"/><span class="label" data-i18n="race-u">RACE</span>
</div>
<div class="hlabel-container">
<input type="text" name="attr_alignment"/><span class="label" data-i18n="alignment-u">ALIGNMENT</span>
</div>
<div class="hlabel-container" style="position: relative;">
<input type="hidden" name="attr_invalidXP"/>
<input type="text" name="attr_experience"/><span class="label" data-i18n="exp-pts-u">EXPERIENCE POINTS</span>
<!--
<input class="showleveler" type="hidden" name="attr_showleveler" value!="0">
<button class="levelerbutton" type="action" name="act_launch_lvl+mancer" style="padding: 0px; position: absolute; top: -10px; right: 0px;" title="Charactermancer Level Up"><img src="https://s3.amazonaws.com/files.d20.io/images/175804938/qmVmtNHONHhHcd9cxCxm7w/med.png?1604618286" style="height: 18px; width: 18px; max-width: inherit;" alt="Wand & Hammer logo of Charactermancer"></button>
<div class="invalidXP"><span class="icon">!</span><span class="message" data-i18n="invalidxp">Enter a number for Level Up reminder</span></div>
-->
</div>
</div>
</div>
</div>
<div class="body">
<div class="col col1">
<div class="class_options">
<div class="row"><span data-i18n="hit-die:-u">HIT DIE:</span>
<select name="attr_hitdietype">
<option value="4">D4</option>
<option value="6">D6</option>
<option value="8">D8</option>
<option value="10">D10</option>
<option value="12">D12</option>
</select>
</div>
<div class="row"><span data-i18n="carrying-capacity-mod:-u">CARRYING CAPACITY MODIFIER:</span>
<input class="num" type="text" name="attr_carrying_capacity_mod" placeholder="*2"/>
</div>
<div class="row"><span data-i18n="glob-atk-mod:-u">GLOBAL MAGIC ATTACK MODIFIER:</span>
<input class="num" type="text" name="attr_globalmagicmod" placeholder="0" value="0"/>
</div>
<div class="row"><span data-i18n="magic-caster-lvl:-u">MAGIC CASTER LEVEL:</span>
<input class="num" type="text" name="attr_caster_level"/>
</div>
<div class="row"><span data-i18n="spell_dc_mod:-u">SPELL SAVE DC MOD:</span>
<input class="num" type="text" name="attr_spell_dc_mod" placeholder="0" value="0"/>
</div>
<div class="row"><span data-i18n="spell-icons:-u">SPELL ICONS:</span>
<select name="attr_spellicon_flag">
<option value="all" data-i18n="show_all">Show All</option>
<option value="cp" data-i18n="cr_only">C&amp;R Only</option>
<option value="none" data-i18n="none">None</option>
</select>
</div>
<div class="row">
<input type="checkbox" name="attr_powerful_build" value="1"/><span data-i18n="powerful_build-u">POWERFUL BUILD</span>
</div>
<div class="row">
<input type="checkbox" name="attr_halflingluck_flag" value="1"/><span data-i18n="halfling-luck-u">HALFLING LUCK</span>
</div>
<div class="row">
<input type="checkbox" name="attr_arcane_fighter" value="1"/><span data-i18n="arcane-fighter-u">ARCANE FIGHTER</span>
</div>
<div class="row">
<input type="checkbox" name="attr_arcane_rogue" value="1"/><span data-i18n="arcane-rogue-u">ARCANE ROGUE</span>
</div>
<div class="row">
<input type="checkbox" name="attr_third_caster" value="1"/><span data-i18n="third-caster-u">THIRD CASTER</span>
</div>
<div class="row title"><span data-i18n="class-options-u">CLASS OPTIONS (<span name="attr_class" data-i18n-dynamic=""></span>)</span></div>
</div>
<div class="class_options">
<div class="row">
<input type="checkbox" name="attr_multiclass1_flag" value="1"/><span data-i18n="2nd-class:-u">2ND CLASS:</span>
<select name="attr_multiclass1" style="width: 64px;">
<option value="Artificer" data-i18n="artificer"></option>
<option value="Bard" data-i18n="bard"></option>
<option value="Barbarian" data-i18n="barbarian"></option>
<option value="Beastheart" data-i18n="beastheart"></option>
<option value="Cleric" data-i18n="cleric"></option>
<option value="Druid" data-i18n="druid"></option>
<option value="Fighter" data-i18n="fighter"></option>
<option value="Illrigger" data-i18n="illrigger"></option>
<option value="Monk" data-i18n="monk"></option>
<option value="Paladin" data-i18n="paladin"></option>
<option value="Ranger" data-i18n="ranger"></option>
<option value="Rogue" data-i18n="rogue"></option>
<option value="Sorcerer" data-i18n="sorcerer"></option>
<option value="Tactician" data-i18n="tactician"></option>
<option value="Warlock" data-i18n="warlock"></option>
<option value="Wizard" data-i18n="wizard"></option>
</select><span data-i18n="lvl:-u">LEVEL:</span>
<input type="text" name="attr_multiclass1_lvl" value="1" style="width: 28px; text-align: center;"/><span class="subclass" data-i18n="subclass:-u">SUBCLASS:</span>
<input type="text" name="attr_multiclass1_subclass"/>
</div>
<div class="row">
<input type="checkbox" name="attr_multiclass2_flag" value="1"/><span data-i18n="3rd-class:-u">3RD CLASS:</span>
<select name="attr_multiclass2" style="width: 64px;">
<option value="Artificer" data-i18n="artificer"></option>
<option value="Bard" data-i18n="bard"></option>
<option value="Barbarian" data-i18n="barbarian"></option>
<option value="Beastheart" data-i18n="beastheart"></option>
<option value="Cleric" data-i18n="cleric"></option>
<option value="Druid" data-i18n="druid"></option>
<option value="Fighter" data-i18n="fighter"></option>
<option value="Illrigger" data-i18n="illrigger"></option>
<option value="Monk" data-i18n="monk"></option>
<option value="Paladin" data-i18n="paladin"></option>
<option value="Ranger" data-i18n="ranger"></option>
<option value="Rogue" data-i18n="rogue"></option>
<option value="Sorcerer" data-i18n="sorcerer"></option>
<option value="Tactician" data-i18n="tactician"></option>
<option value="Warlock" data-i18n="warlock"></option>
<option value="Wizard" data-i18n="wizard"></option>
</select><span data-i18n="lvl:-u">LEVEL:</span>
<input type="text" name="attr_multiclass2_lvl" value="1" style="width: 28px; text-align: center;"/><span class="subclass" data-i18n="subclass:-u">SUBCLASS:</span>
<input type="text" name="attr_multiclass2_subclass"/>
</div>
<div class="row">
<input type="checkbox" name="attr_multiclass3_flag" value="1"/><span data-i18n="4th-class:-u">4TH CLASS:</span>
<select name="attr_multiclass3" style="width: 64px;">
<option value="Artificer" data-i18n="artificer"></option>
<option value="Bard" data-i18n="bard"></option>
<option value="Barbarian" data-i18n="barbarian"></option>
<option value="Beastheart" data-i18n="beastheart"></option>
<option value="Cleric" data-i18n="cleric"></option>
<option value="Druid" data-i18n="druid"></option>
<option value="Fighter" data-i18n="fighter"></option>
<option value="Illrigger" data-i18n="illrigger"></option>
<option value="Monk" data-i18n="monk"></option>
<option value="Paladin" data-i18n="paladin"></option>
<option value="Ranger" data-i18n="ranger"></option>
<option value="Rogue" data-i18n="rogue"></option>
<option value="Sorcerer" data-i18n="sorcerer"></option>
<option value="Tactician" data-i18n="tactician"></option>
<option value="Warlock" data-i18n="warlock"></option>
<option value="Wizard" data-i18n="wizard"></option>
</select><span data-i18n="lvl:-u">LEVEL:</span>
<input type="text" name="attr_multiclass3_lvl" value="1" style="width: 28px; text-align: center;"/><span class="subclass" data-i18n="subclass:-u">SUBCLASS:</span>
<input type="text" name="attr_multiclass3_subclass"/>
</div>
<div class="row title"><span data-i18n="mutliclass-opts-u">MULTICLASS OPTIONS</span></div>
</div>
<div class="class_options">
<div class="row">
<input type="checkbox" name="attr_custom_class" value="1"/><span data-i18n="use-cust-class-u">USE CUSTOM CLASS</span>
</div>
<div class="row"><span data-i18n="class-name:-u">CLASS NAME:</span>
<input type="text" name="attr_cust_classname"/>
</div>
<div class="row"><span data-i18n="hit-die:-u">HIT DIE:</span>
<select name="attr_cust_hitdietype">
<option value="4">D4</option>
<option value="6">D6</option>
<option value="8">D8</option>
<option value="10">D10</option>
<option value="12">D12</option>
</select>
</div>
<div class="row"><span data-i18n="spellcasting-ability:-u">SPELLCASTING ABILITY:</span>
<select name="attr_cust_spellcasting_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select>
</div>
<div class="row"><span data-i18n="spell-slots:-u">SPELL SLOTS:</span>
<select name="attr_cust_spellslots">
<option value="none" data-i18n="none-u">NONE</option>
<option value="full" data-i18n="spell-full-u">FULL (Cleric, Druid, Wizard)</option>
<option value="half" data-i18n="spell-half-u">HALF (Artificer, Paladin, Ranger)</option>
<option value="third" data-i18n="spell-third-u">THIRD (Arcane Fighter/Rogue)</option>
</select>
</div>
<div class="row"><span data-i18n="saves-u">SAVES</span></div>
<div class="row">
<input type="checkbox" name="attr_cust_strength_save_prof" value="(@{pb})"/><span data-i18n="strength">Strength</span>
</div>
<div class="row">
<input type="checkbox" name="attr_cust_dexterity_save_prof" value="(@{pb})"/><span data-i18n="dexterity">Dexterity</span>
</div>
<div class="row">
<input type="checkbox" name="attr_cust_constitution_save_prof" value="(@{pb})"/><span data-i18n="constitution">Constitution</span>
</div>
<div class="row">
<input type="checkbox" name="attr_cust_intelligence_save_prof" value="(@{pb})"/><span data-i18n="intelligence">Intelligence</span>
</div>
<div class="row">
<input type="checkbox" name="attr_cust_wisdom_save_prof" value="(@{pb})"/><span data-i18n="wisdom">Wisdom</span>
</div>
<div class="row">
<input type="checkbox" name="attr_cust_charisma_save_prof" value="(@{pb})"/><span data-i18n="charisma">Charisma</span>
</div>
<input class="toggleVisibility" type="hidden" name="attr_honor_toggle"/>
<div class="row toggleVisibilityBy-honor">
<input type="checkbox" name="attr_cust_honor_save_prof" value="(@{pb})"/><span data-i18n="honor">Honor</span>
</div>
<input class="toggleVisibility" type="hidden" name="attr_sanity_toggle"/>
<div class="row toggleVisibilityBy-sanity">
<input type="checkbox" name="attr_cust_sanity_save_prof" value="(@{pb})"/><span data-i18n="sanity">Sanity</span>
</div>
<div class="row title"><span data-i18n="cust-class-opts">CUSTOM CLASS OPTIONS</span></div>
</div>
<div class="class_options spell_slot_mods">
<div class="column">
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">1 +</span>
<input class="num" type="text" name="attr_lvl1_slots_mod" value="0" placeholder="0" title="@{lvl1_slots_mod}"/>
</div>
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">2 +</span>
<input class="num" type="text" name="attr_lvl2_slots_mod" value="0" placeholder="0" title="@{lvl2_slots_mod}"/>
</div>
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">3 +</span>
<input class="num" type="text" name="attr_lvl3_slots_mod" value="0" placeholder="0" title="@{lvl3_slots_mod}"/>
</div>
</div>
<div class="column">
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">4 +</span>
<input class="num" type="text" name="attr_lvl4_slots_mod" value="0" placeholder="0" title="@{lvl4_slots_mod}"/>
</div>
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">5 +</span>
<input class="num" type="text" name="attr_lvl5_slots_mod" value="0" placeholder="0" title="@{lvl5_slots_mod}"/>
</div>
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">6 +</span>
<input class="num" type="text" name="attr_lvl6_slots_mod" value="0" placeholder="0" title="@{lvl6_slots_mod}"/>
</div>
</div>
<div class="column">
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">7 +</span>
<input class="num" type="text" name="attr_lvl7_slots_mod" value="0" placeholder="0" title="@{lvl7_slots_mod}"/>
</div>
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">8 +</span>
<input class="num" type="text" name="attr_lvl8_slots_mod" value="0" placeholder="0" title="@{lvl8_slots_mod}"/>
</div>
<div class="row"><span data-i18n="lvl-u">LEVEL</span><span class="num_plus">9 +</span>
<input class="num" type="text" name="attr_lvl9_slots_mod" value="0" placeholder="0" title="@{lvl9_slots_mod}"/>
</div>
</div>
<div class="row title"><span data-i18n="spell_slot_modifiers-u">SPELL SLOT MODIFIERS</span></div>
</div>
</div>
<div class="col col2">
<div class="attribute_options" style="width: 234px;">
<div class="row skill"><span data-i18n="strength-u">STRENGTH</span><span>+</span>
<input class="num" type="text" name="attr_strength_bonus" value="0" placeholder="0" title="@{strength_bonus}"/>
</div>
<div class="row skill"><span data-i18n="dexterity-u">DEXTERITY</span><span>+</span>
<input class="num" type="text" name="attr_dexterity_bonus" value="0" placeholder="0" title="@{dexterity_bonus}"/>
</div>
<div class="row skill"><span data-i18n="constitution-u">CONSTITUTION</span><span>+</span>
<input class="num" type="text" name="attr_constitution_bonus" value="0" placeholder="0" title="@{constitution_bonus}"/>
</div>
<div class="row skill"><span data-i18n="intelligence-u">INTELLIGENCE</span><span>+</span>
<input class="num" type="text" name="attr_intelligence_bonus" value="0" placeholder="0" title="@{intelligence_bonus}"/>
</div>
<div class="row skill"><span data-i18n="wisdom-u">WISDOM</span><span>+</span>
<input class="num" type="text" name="attr_wisdom_bonus" value="0" placeholder="0" title="@{wisdom_bonus}"/>
</div>
<div class="row skill"><span data-i18n="charisma-u">CHARISMA</span><span>+</span>
<input class="num" type="text" name="attr_charisma_bonus" value="0" placeholder="0" title="@{charisma_bonus}"/>
</div>
<input class="toggleVisibility" type="hidden" name="attr_honor_toggle"/>
<div class="row skill toggleVisibilityBy-honor"><span data-i18n="honor-u">HONOR</span><span>+</span>
<input class="num" type="text" name="attr_honor_bonus" value="0" placeholder="0" title="@{honor_bonus}"/>
</div>
<input class="toggleVisibility" type="hidden" name="attr_sanity_toggle"/>
<div class="row skill toggleVisibilityBy-sanity"><span data-i18n="sanity-u">SANITY</span><span>+</span>
<input class="num" type="text" name="attr_sanity_bonus" value="0" placeholder="0" title="@{sanity_bonus}"/>
</div>
<div class="row"><span data-i18n="initiative-mod:-u">INITIATIVE MODIFIER:</span>
<input class="num" type="text" name="attr_initmod" placeholder="0" value="0"/>
</div>
<div class="row"><span data-i18n="initiative-style:-u">INITIATIVE STYLE:</span>
<select name="attr_initiative_style">
<option value="@{d20}" selected="selected" data-i18n="norm">Normal</option>
<option value="{@{d20},@{d20}}kh1" data-i18n="adv">Advantage</option>
<option value="{@{d20},@{d20}}kl1" data-i18n="disadv">Disadvantage</option>
</select>
</div>
<div class="row">
<input type="checkbox" name="attr_init_tiebreaker" value="@{dexterity}/100"/><span data-i18n="add-dex-tiebreaker-u">ADD DEX TIEBREAKER TO INITIATIVE</span>
</div>
<div class="row"><span data-i18n="armor-class-tracking:-u">ARMOR CLASS TRACKING:</span>
<select name="attr_custom_ac_flag">
<option value="0" selected="selected" data-i18n="automatic">Automatic</option>
<option value="1" data-i18n="custom">Custom</option>
<option value="2" data-i18n="off">Off</option>
</select>
</div>
<input class="toggleflag" type="hidden" name="attr_custom_ac_flag"/>
<div class="row hidden"><span data-i18n="custom_ac:-u">CUSTOM AC:</span>
<input class="num" type="text" name="attr_custom_ac_base" placeholder="10" value="10"/><span>+</span>
<select name="attr_custom_ac_part1">
<option value="none" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><span>+</span>
<select name="attr_custom_ac_part2">
<option value="none" selected="selected" data-i18n="none-u">NONE</option>
<option value="Strength" data-i18n="str-u">STR</option>
<option value="Dexterity" data-i18n="dex-u">DEX</option>
<option value="Constitution" data-i18n="con-u">CON</option>
<option value="Intelligence" data-i18n="int-u">INT</option>
<option value="Wisdom" data-i18n="wis-u">WIS</option>
<option value="Charisma" data-i18n="cha-u">CHA</option>
</select><br/>
<input type="checkbox" name="attr_custom_ac_shield" value="yes" checked=""/><span data-i18n="allow-shields-u">ALLOW SHIELDS?</span>
<hr style="margin: 2px"/>
</div>
<div class="row"><span data-i18n="hpmods:-u">HP MODIFIERS:</span></div>
<fieldset class="repeating_hpmod">
<div style="padding-left: 15px; padding-bottom: 10px;"><span data-i18n="levels:-u">LEVELS:</span>
<select name="attr_levels">
<option value="total" selected="selected" data-i18n="total-level-u">TOTAL LEVEL</option>
<option value="base" data-i18n="main-class-level-u">MAIN CLASS LEVEL</option>
<option value="multiclass1" data-i18n="class-2-level-u">CLASS 2 LEVEL</option>
<option value="multiclass2" data-i18n="class-3-level-u">CLASS 3 LEVEL</option>
<option value="multiclass3" data-i18n="class-4-level-u">CLASS 4 LEVEL</option>
</select><br/><span data-i18n="source:-u">SOURCE:</span>
<input type="text" name="attr_source"/><span data-i18n="mod:-u">MOD:</span>
<input class="num" type="text" name="attr_mod"/>
</div>
</fieldset>
<div class="row title"><span data-i18n="attribute-opts-u">ATTRIBUTE OPTIONS</span></div>
</div>
<div class="save_options" style="width: 234px;">
<div class="row skill"><span data-i18n="strength-save-u">Strength Save</span><span>+</span>
<input class="num" type="text" name="attr_strength_save_mod" value="0" placeholder="0" title="@{strength_save_mod}"/>
</div>
<div class="row skill"><span data-i18n="dexterity-save-u">Dexterity Save</span><span>+</span>
<input class="num" type="text" name="attr_dexterity_save_mod" value="0" placeholder="0" title="@{dexterity_save_mod}"/>
</div>
<div class="row skill"><span data-i18n="constitution-save-u">Constitution Save</span><span>+</span>
<input class="num" type="text" name="attr_constitution_save_mod" value="0" placeholder="0" title="@{constitution_save_mod}"/>
</div>
<div class="row skill"><span data-i18n="intelligence-save-u">Intelligence Save</span><span>+</span>
<input class="num" type="text" name="attr_intelligence_save_mod" value="0" placeholder="0" title="@{intelligence_save_mod}"/>
</div>
<div class="row skill"><span data-i18n="wisdom-save-u">Wisdom Save</span><span>+</span>
<input class="num" type="text" name="attr_wisdom_save_mod" value="0" placeholder="0" title="@{wisdom_save_mod}"/>
</div>
<div class="row skill"><span data-i18n="charisma-save-u">Charisma Save</span><span>+</span>
<input class="num" type="text" name="attr_charisma_save_mod" value="0" placeholder="0" title="@{charisma_save_mod}"/>
</div>
<input class="toggleVisibility" type="hidden" name="attr_honor_toggle"/>
<div class="row skill toggleVisibilityBy-honor"><span data-i18n="honor-save-u">Honor Save</span><span>+</span>
<input class="num" type="text" name="attr_honor_save_mod" value="0" placeholder="0" title="@{honor_save_mod}"/>
</div>
<input class="toggleVisibility" type="hidden" name="attr_sanity_toggle"/>
<div class="row skill toggleVisibilityBy-sanity"><span data-i18n="sanity-save-u">Sanity Save</span><span>+</span>
<input class="num" type="text" name="attr_sanity_save_mod" value="0" placeholder="0" title="@{sanity_save_mod}"/>
</div>
<div class="row"><span data-i18n="death-save-mod:-u">DEATH SAVE MODIFIER:</span>
<input class="num" type="text" name="attr_death_save_mod" placeholder="0" value="0"/>
</div>
<div class="row"><span data-i18n="glob-saving-mod:-u">GLOBAL SAVING THROW MODIFIER:</span>
<input class="num" type="text" name="attr_globalsavemod" placeholder="0" value="0"/>
</div>
<div class="row title"><span data-i18n="save-opts-u">SAVE OPTIONS</span></div>
</div>
<div class="skill_options" style="width: 234px;">
<div data-i18n-list="skills-list">
<div class="row skill" data-i18n-list-item="acrobatics">
<select name="attr_acrobatics_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="acrobatics-core">Acrobatics <span>(Dex)</span></span><span>+</span>
<input class="num" type="text" name="attr_acrobatics_flat" value="0" placeholder="0" title="@{acrobatics_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="animal_handling">
<select name="attr_animal_handling_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="animal_handling-core">Animal Handling <span>(Wis)</span></span><span>+</span>
<input class="num" type="text" name="attr_animal_handling_flat" value="0" placeholder="0" title="@{animal_handling_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="arcana">
<select name="attr_arcana_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="arcana-core">Arcana <span>(Int)</span></span><span>+</span>
<input class="num" type="text" name="attr_arcana_flat" value="0" placeholder="0" title="@{arcana_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="athletics">
<select name="attr_athletics_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="athletics-core">Athletics <span>(Str)</span></span><span>+</span>
<input class="num" type="text" name="attr_athletics_flat" value="0" placeholder="0" title="@{athletics_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="deception">
<select name="attr_deception_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="deception-core">Deception <span>(Cha)</span></span><span>+</span>
<input class="num" type="text" name="attr_deception_flat" value="0" placeholder="0" title="@{deception_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="history">
<select name="attr_history_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="history-core">History <span>(Int)</span></span><span>+</span>
<input class="num" type="text" name="attr_history_flat" value="0" placeholder="0" title="@{history_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="insight">
<select name="attr_insight_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="insight-core">Insight <span>(Wis)</span></span><span>+</span>
<input class="num" type="text" name="attr_insight_flat" value="0" placeholder="0" title="@{insight_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="intimidation">
<select name="attr_intimidation_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="intimidation-core">Intimidation <span>(Cha)</span></span><span>+</span>
<input class="num" type="text" name="attr_intimidation_flat" value="0" placeholder="0" title="@{intimidation_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="investigation">
<select name="attr_investigation_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="investigation-core">Investigation <span>(Int)</span></span><span>+</span>
<input class="num" type="text" name="attr_investigation_flat" value="0" placeholder="0" title="@{investigation_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="medicine">
<select name="attr_medicine_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="medicine-core">Medicine <span>(Wis)</span></span><span>+</span>
<input class="num" type="text" name="attr_medicine_flat" value="0" placeholder="0" title="@{medicine_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="nature">
<select name="attr_nature_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="nature-core">Nature <span>(Int)</span></span><span>+</span>
<input class="num" type="text" name="attr_nature_flat" value="0" placeholder="0" title="@{nature_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="navigation">
<select name="attr_navigation_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="navigation-core">navigation <span>(Int)</span></span><span>+</span>
<input class="num" type="text" name="attr_navigation_flat" value="0" placeholder="0" title="@{navigation_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="perception">
<select name="attr_perception_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="perception-core">Perception <span>(Wis)</span></span><span>+</span>
<input class="num" type="text" name="attr_perception_flat" value="0" placeholder="0" title="@{perception_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="performance">
<select name="attr_performance_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="performance-core">Performance <span>(Cha)</span></span><span>+</span>
<input class="num" type="text" name="attr_performance_flat" value="0" placeholder="0" title="@{performance_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="persuasion">
<select name="attr_persuasion_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="persuasion-core">Persuasion <span>(Cha)</span></span><span>+</span>
<input class="num" type="text" name="attr_persuasion_flat" value="0" placeholder="0" title="@{persuasion_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="religion">
<select name="attr_religion_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="religion-core">Religion <span>(Int)</span></span><span>+</span>
<input class="num" type="text" name="attr_religion_flat" value="0" placeholder="0" title="@{religion_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="sleight_of_hand">
<select name="attr_sleight_of_hand_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="sleight_of_hand-core">Sleight of Hand <span>(Dex)</span></span><span>+</span>
<input class="num" type="text" name="attr_sleight_of_hand_flat" value="0" placeholder="0" title="@{sleight_of_hand_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="stealth">
<select name="attr_stealth_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="stealth-core">Stealth <span>(Dex)</span></span><span>+</span>
<input class="num" type="text" name="attr_stealth_flat" value="0" placeholder="0" title="@{stealth_flat}"/>
</div>
<div class="row skill" data-i18n-list-item="survival">
<select name="attr_survival_type">
<option value="1" selected="selected" data-i18n="normal">Normal</option>
<option value="2" data-i18n="expirtise">Expertise</option>
</select><span data-i18n="survival-core">Survival <span>(Wis)</span></span><span>+</span>
<input class="num" type="text" name="attr_survival_flat" value="0" placeholder="0" title="@{survival_flat}"/>
</div>
</div>
<div class="row" style="margin-top:10px;">
<input type="checkbox" name="attr_jack_of_all_trades" value="@{jack}"/><span data-i18n="jack-of-all-u">JACK OF ALL TRADES</span>
</div>
<div class="row"><span data-i18n="reliable-talent-u">RELIABLE TALENT:</span>
<select class="dtype" name="attr_reliable_talent">
<option value="10" data-i18n="on">On</option>
<option value="0" selected="selected" data-i18n="off">Off</option>
</select>
</div>
<div class="row"><span data-i18n="pass-perc-mod:-u">PASSIVE PERCEPTION MODIFIER:</span>
<input class="num" type="text" name="attr_passiveperceptionmod" placeholder="0" value="0"/>
</div>
<div class="row title"><span data-i18n="skill-opts-u">SKILL OPTIONS</span></div>
</div>
</div>
<div class="col col3">
<div class="general_options">
<div class="version"><span data-i18n="version">v</span><span name="attr_version"></span></div>
<div class="row"><span data-i18n="character-sheet-type-u"></span>
<select name="attr_charactersheet_type">
<option value="pc">PC</option>
<option value="npc">NPC</option>
<option value="vehicle">VEHICLE</option>
<option value="kingdom">KINGDOM</option>
</select>
</div>
<input type="hidden" name="attr_kingdom" value="0"/>
<input type="hidden" name="attr_npc" value="0"/>
<div class="row"><span data-i18n="roll-queries:-u">ROLL QUERIES:</span>
<select name="attr_rtype">
<option value="{{always=1}} {{r2=[[@{d20}" data-i18n="always-roll-adv">Always Roll Advantage</option>
<option value="@{advantagetoggle}" data-i18n="toggle-roll-adv">Advantage Toggle</option>
<option value="@{queryadvantage}" data-i18n="query-roll-adv">Query Advantage</option>
<option value="{{normal=1}} {{r2=[[0d20" data-i18n="never-roll-adv">Never Roll Advantage</option>
</select>
</div>
<div class="row"><span data-i18n="whisp-rolls-gm:-u">WHISPER ROLLS TO GM:</span>
<select name="attr_wtype">
<option value="" data-i18n="never-whisper-roll">Never Whisper Rolls</option>
<option value="@{whispertoggle}" data-i18n="toggle-roll-whisper">Whisper Toggle</option>
<option value="?{Whisper?|Public Roll,|Whisper Roll,/w gm }" data-i18n="query-whisper-roll">Query Whisper</option>
<option value="/w gm " data-i18n="always-whisper-roll">Always Whisper Rolls</option>
</select>
</div>
<div class="row"><span data-i18n="auto-dmg-roll:-u">AUTO DAMAGE ROLL:</span>
<select class="dtype" name="attr_dtype">
<option value="pick" data-i18n="never-roll-dmg">Don&apos;t Auto Roll Damage</option>
<option value="full" data-i18n="always-roll-dmg">Auto Roll Damage &amp; Crit</option>
</select>
</div>
<div class="row"><span data-i18n="core-die-roll:-u">CORE DIE ROLL:</span>
<input type="text" name="attr_core_die" value="1d20" placeholder="1d20" title="@{core_die}"/>
</div>
<div class="row"><span data-i18n="default-critical-range:-u">CRITICAL RANGE:</span>
<input type="number" name="attr_default_critical_range" value="20" placeholder="20"/>
</div>
<div class="row"><span data-i18n="prof-bonus:-u">PROFICIENCY BONUS:</span>
<select class="dtype" name="attr_pb_type">
<option value="level" data-i18n="by-level">By Level (Default)</option>
<option value="die" data-i18n="prof-die">Proficency Die (DMG)</option>
<option value="custom" data-i18n="custom">Custom</option>
</select>
<input type="hidden" name="attr_pb_type"/>
<input class="pb_custom" type="text" style="float:right;" name="attr_pb_custom" placeholder="2d10"/>
</div>
<div class="row">
<input type="checkbox" name="attr_global_save_mod_flag" value="1"/><span data-i18n="show-global-save-field-u">SHOW GLOBAL SAVE MODIFIER FIELD</span>
</div>
<div class="row">
<input type="checkbox" name="attr_global_skill_mod_flag" value="1"/><span data-i18n="show-global-skill-field-u">SHOW GLOBAL SKILL MODIFIER FIELD</span>
</div>
<div class="row">
<input type="checkbox" name="attr_global_attack_mod_flag" value="1"/><span data-i18n="show-global-attack-field-u">SHOW GLOBAL ATTACK MODIFIER FIELD</span>
</div>
<div class="row">
<input type="checkbox" name="attr_global_damage_mod_flag" value="1"/><span data-i18n="show-global-damage-field-u">SHOW GLOBAL DAMAGE MODIFIER FIELD</span>
</div>
<div class="row">
<input type="checkbox" name="attr_global_ac_mod_flag" value="1"/><span data-i18n="show-global-ac-field-u">SHOW GLOBAL AC MODIFIER FIELD</span>
</div>
<div class="row"><span data-i18n="add-char-to-templates:-u">ADD CHARACTER NAME TO TEMPLATES:</span>
<select class="dtype" name="attr_charname_output">
<option value="{{charname=@{character_name}}}" data-i18n="on">On</option>
<option value="charname=@{character_name}" selected="selected" data-i18n="off">Off</option>
</select>
</div>
<div class="row"><span data-i18n="level-calculations:-u">LEVEL CALCULATIONS:</span>
<select class="dtype" name="attr_level_calculations">
<option value="on" selected="selected" data-i18n="on">On</option>
<option value="off" data-i18n="off">Off</option>
</select>
</div>
<div class="row"><span data-i18n="encumbrance:-u">ENCUMBRANCE:</span>
<select name="attr_encumberance_setting">
<option value="off" data-i18n="simple">Simple</option>
<option value="on" data-i18n="variant-phb">Variant (PHB p176)</option>
<option value="disabled" data-i18n="off">Off</option>
</select>
</div>
<div class="row">
<input type="checkbox" name="attr_ingore_non_equipped_weight" value="1"/><span data-i18n="ingore-non-equipped-weight">IGNORE NON-EQUIPPED ITEMS WEIGHT</span>
</div>
<div class="row"><span data-i18n="inventory:-u">INVENTORY:</span>
<select name="attr_simpleinventory">
<option value="complex" data-i18n="compendium-compatible">Compendium Compatible</option>
<option value="simple" data-i18n="simple">Simple</option>
</select>
</div>
<div class="row"><span data-i18n="feats-traits-u">FEATURES AND TRAITS</span><span>:</span>
<select name="attr_simpletraits">
<option value="complex" data-i18n="compendium-compatible">Compendium Compatible</option>
<option value="simple" data-i18n="simple">Simple</option>
</select>
</div>
<div class="row"><span data-i18n="prof-langs-u">PROFICENCIES AND LANGUAGES</span><span>:</span>
<select name="attr_simpleproficencies">
<option value="complex" data-i18n="compendium-compatible">Compendium Compatible</option>
<option value="simple" data-i18n="simple">Simple</option>
</select>
</div>
<div class="row"><span data-i18n="consolidated-proficiencies-u">CONSOLIDATED PROFICIENCIES</span><span>:</span>
<select name="attr_consolidatedproficencies">&lt;
<option value="on" data-i18n="on">On</option>
<option value="off" selected="selected" data-i18n="off">Off</option>
</select>
</div>
<div class="row"><span data-i18n="ammo-tracking:-u">AMMO TRACKING:</span>
<select name="attr_ammotracking">
<option value="on" data-i18n="on">On</option>
<option value="off" selected="selected" data-i18n="off">Off</option>
</select><span data-i18n-title="api-required-title" data-i18n="api-required-u" title="Try it now with easy one click API installation available with your Pro subscription." style="color:#666; cursor:help;">(API REQUIRED)</span>
</div>
<div class="row"><span data-i18n="exhaustion-setting-u">SHOW EXHAUSTION TRACKING:</span>
<select name="attr_exhaustion_toggle">
<option value="1" data-i18n="on">On</option>
<option value="0" data-i18n="off" selected="selected">Off</option>
</select>
</div>
<div class="row"><span data-i18n="hit-dice-setting-u">TRACK HIT DICE PER CLASS</span>
<select name="attr_use_per_class_hit_dice">
<option value="1" data-i18n="on">On</option>
<option value="0" data-i18n="off" selected="selected">Off</option>
</select>
</div>
<div class="row"><span data-i18n="hero-points-u">HERO POINTS</span>
<select name="attr_use_hero_points">
<option value="1" data-i18n="on">On</option>
<option value="0" data-i18n="off" selected="selected">Off</option>
</select>
</div>
<div class="row"><span data-i18n="piety-u">PIETY</span>
<select name="attr_use_piety">
<option value="1" data-i18n="on">On</option>
<option value="0" data-i18n="off" selected="selected">Off</option>
</select>
</div>
<div class="row"><span data-i18n="spell-points-u">SPELL POINTS</span>
<select name="attr_use_spell_points">
<option value="1" data-i18n="on">On</option>
<option value="0" data-i18n="off" selected="selected">Off</option>
</select>
</div>
<div class="row"><span data-i18n="rest-buttons-u">SHORT/LONG REST</span>
<select name="attr_use_rest_buttons">
<option value="1" data-i18n="on" selected="selected">On</option>
<option value="0" data-i18n="off">Off</option>
</select>
</div>
<div class="row title"><span data-i18n="general-opts-u">GENERAL OPTIONS</span></div>
</div>
<div class="general_options charactermancer_options">
<input class="warningflag" type="hidden" name="attr_leveler_warningflag"/>
<div class="warning"><span>
<label>
<input type="checkbox" name="attr_leveler_warningflag" value="hide"/>*
</label><span data-i18n="warning-leveler_start-u">ATTENTION - YOU CANNOT LAUNCH THE CHARACTERMANCER LEVEL UP UNTIL YOU HAVE A CLASS, LEVEL, AND HP SET FOR LEVEL ONE. PLEASE RUN THE CHARACTERMANCER CREATOR FIRST. THE LEVEL UP TOOL DOESN&apos;T WORK WITH CUSTOM CLASSES OR IF THE MULTICLASS OPTIONS ARE SET UP INCORRECTLY.</span></span></div>
<div class="row">
<button type="action" name="act_relaunch_lvl1mancer" style="padding: 0px;"><img src="https://s3.amazonaws.com/files.d20.io/images/175804938/qmVmtNHONHhHcd9cxCxm7w/med.png?1604618286" style="height: 32px; width: 32px" alt="Wand &amp; Hammer of Charactermancer"/></button><span style="width: 180px; margin-left: 10px; display: inline-block;" data-i18n="relaunch-lvl1mancer-u">LAUNCH LEVEL 1 CHARACTERMANCER (CANNOT BE UNDONE)</span>
</div>
<div class="row">
<button type="action" name="act_launch_lvl+mancer" style="padding: 0px;"><img src="https://s3.amazonaws.com/files.d20.io/images/175804938/qmVmtNHONHhHcd9cxCxm7w/med.png?1604618286" style="height: 32px; width: 32px" alt="Wand &amp; Hammer of Charactermancer"/></button><span style="width: 180px; margin-left: 10px; display: inline-block;">LAUNCH LEVEL+ CHARACTERMANCER</span>
</div>
<div class="row title"><span data-i18n="mancer-opts-u">CHARACTERMANCER OPTIONS</span></div>
</div>
</div>
</div>
</div>
<div class="footer">
<div style="margin-right: 50px; margin-left: 10px;">
<span data-i18n="portions-sheet-utilize">
Portions of this sheet utilize content from the System Reference Document 5.0 under the OGL License. View OGL License and Copyright Notice. https://wiki.roll20.net/SRD_5.0_OGL_License
</span>
</div>
<div>
<span data-i18n="footer-wiki-link">
For more information about this character sheet, its functionality, or uses, please check out the Roll20 Wiki for official documentation. https://wiki.roll20.net/5th_Edition_OGL_by_Roll20
</span>
</div>
</div>
</div>
<div class="container kingdom">
<div class="charactersheet-type">
<div class="row"><span data-i18n="character-sheet-type-u"></span>
<select name="attr_charactersheet_type">
<option value="pc">PC</option>
<option value="npc">NPC</option>
<option value="vehicle">VEHICLE</option>
<option value="kingdom">KINGDOM</option>
</select>
</div>
</div><!-- sectionName:kingmaker -->
<input name="attr_kingmaker_tab" value="nav-tabs-kingmaker--settings" type="hidden" title="@{kingmaker_tab}"/>
<div class="tabs tabs--kingmaker compendium-drop-target" id="main">
<nav class="tabs__header tabs--kingmaker__header">
<button class="tabs__button tabs--kingmaker__button tabs--kingmaker__button--kingdom" name="act_nav-tabs-kingmaker--kingdom" data-container-button="kingmaker" data-button="kingdom" data-i18n="tabs-kingmaker-kingdom" type="action" title="%{nav-tabs-kingmaker--kingdom}" data-container-tab="kingmaker" data-tab="kingdom">
</button>
<button class="tabs__button tabs--kingmaker__button tabs--kingmaker__button--army" name="act_nav-tabs-kingmaker--army" data-container-button="kingmaker" data-button="army" data-i18n="tabs-kingmaker-army" type="action" title="%{nav-tabs-kingmaker--army}" data-container-tab="kingmaker" data-tab="army">
</button>
<button class="tabs__button tabs--kingmaker__button tabs--kingmaker__button--settlement" name="act_nav-tabs-kingmaker--settlement" data-container-button="kingmaker" data-button="settlement" data-i18n="tabs-kingmaker-settlement" type="action" title="%{nav-tabs-kingmaker--settlement}" data-container-tab="kingmaker" data-tab="settlement">
</button>
<button class="tabs__button tabs--kingmaker__button tabs--kingmaker__button--settings pictos" name="act_nav-tabs-kingmaker--settings" data-container-button="kingmaker" data-button="settings" type="action" title="%{nav-tabs-kingmaker--settings}" data-container-tab="kingmaker" data-tab="settings">y
</button>
</nav>
<div class="tabs__body tabs--kingmaker__body">
<article class="tabs__container tabs--kingmaker__container tabs--kingmaker__container--kingdom kingdom" data-container-tab="kingmaker" data-tab="kingdom" id="kingdom">
<input name="attr_kingdom_drop_name" accept="Name" value="" type="hidden" title="@{kingdom_drop_name}"/>
<input name="attr_kingdom_drop_data" accept="data" value="" type="hidden" title="@{kingdom_drop_data}"/>
<input name="attr_kingdom_template_start" value="@{kingdom_whisper}&{template:kingmaker} {{character_name=@{character_name}}} {{character_id=@{character_id}}}" type="hidden" title="@{kingdom_template_start}"/>
<input name="attr_kingdom_sheet_version" value="0" type="hidden" title="@{kingdom_sheet_version}"/>
<input name="attr_kingdom_sheet_state" value="settings" type="hidden" title="@{kingdom_sheet_state}"/>
<header class="kingdom-header" id="kingdom-header">
<label class="input-label"><span class="input-label__text" data-i18n="km_kingdom name"></span>
<input class="underlined input-label__input" name="attr_character_name" role="heading" aria-level="2" title="@{character_name}"/>
</label>
<div class="kingdom-level-container">
<label class="input-label boxed level"><span class="tiny-text input-label__text" data-i18n="km_level"></span>
<input class="big-text input-label__input" name="attr_kingdom_level" type="number" title="@{kingdom_level}"/>
</label>
<label class="input-label boxed infamy"><span class="tiny-text input-label__text" name="attr_kingdom_fame_type" data-i18n-dynamic=""></span>
<input class="big-text input-label__input" name="attr_kingdom_fame/infamy" type="number" title="@{kingdom_fame/infamy}"/>
</label>
</div>
<div class="commodities-work span-all">
<div class="commodities">
<div class="input-label stacked center input-label--kingmaker-box">
<h6 class="input-label--kingmaker-box__label" data-i18n="km_food">//- I'd typically do this using the input-label mixin and give the span a role:'heading' and 'aria-level':X. That way clicking on the input or the label selects the input. But that's nitpicking</h6>
<div class="boxed dual-input">
<input class="ratio1-1" name="attr_kingdom_food" value="1" type="number" title="@{kingdom_food}"/><span class="slash">/</span>
<input class="ratio1-1" name="attr_kingdom_food_max" value="1" type="number" title="@{kingdom_food|max}"/>
</div>
<label class="input-label">
<h6 data-i18n="km_farmlands">//- See comment above; but again nitpicks</h6>
<input class="boxed ratio1-1" name="attr_kingdom_farmlands" value="1" type="number" title="@{kingdom_farmlands}"/>
</label>
</div>
<div class="input-label stacked center input-label--kingmaker-box">
<h6 class="input-label--kingmaker-box__label" data-i18n="km_lumber">//- I'd typically do this using the input-label mixin and give the span a role:'heading' and 'aria-level':X. That way clicking on the input or the label selects the input. But that's nitpicking</h6>
<div class="boxed dual-input">
<input class="ratio1-1" name="attr_kingdom_lumber" value="1" type="number" title="@{kingdom_lumber}"/><span class="slash">/</span>
<input class="ratio1-1" name="attr_kingdom_lumber_max" value="1" type="number" title="@{kingdom_lumber|max}"/>
</div>
<label class="input-label">
<h6 data-i18n="km_lumber camps">//- See comment above; but again nitpicks</h6>
<input class="boxed ratio1-1" name="attr_kingdom_lumber_camps" value="1" type="number" title="@{kingdom_lumber_camps}"/>
</label>
</div>
<div class="input-label stacked center input-label--kingmaker-box">
<h6 class="input-label--kingmaker-box__label" data-i18n="km_luxuries">//- I'd typically do this using the input-label mixin and give the span a role:'heading' and 'aria-level':X. That way clicking on the input or the label selects the input. But that's nitpicking</h6>
<div class="boxed dual-input">
<input class="ratio1-1" name="attr_kingdom_luxuries" value="1" type="number" title="@{kingdom_luxuries}"/><span class="slash">/</span>
<input class="ratio1-1" name="attr_kingdom_luxuries_max" value="1" type="number" title="@{kingdom_luxuries|max}"/>
</div>
</div>
<div class="input-label stacked center input-label--kingmaker-box">
<h6 class="input-label--kingmaker-box__label" data-i18n="km_ore">//- I'd typically do this using the input-label mixin and give the span a role:'heading' and 'aria-level':X. That way clicking on the input or the label selects the input. But that's nitpicking</h6>
<div class="boxed dual-input">
<input class="ratio1-1" name="attr_kingdom_ore" value="1" type="number" title="@{kingdom_ore}"/><span class="slash">/</span>
<input class="ratio1-1" name="attr_kingdom_ore_max" value="1" type="number" title="@{kingdom_ore|max}"/>
</div>
<label class="input-label">
<h6 data-i18n="km_mines">//- See comment above; but again nitpicks</h6>
<input class="boxed ratio1-1" name="attr_kingdom_mines" value="1" type="number" title="@{kingdom_mines}"/>
</label>
</div>
<div class="input-label stacked center input-label--kingmaker-box">
<h6 class="input-label--kingmaker-box__label" data-i18n="km_stone">//- I'd typically do this using the input-label mixin and give the span a role:'heading' and 'aria-level':X. That way clicking on the input or the label selects the input. But that's nitpicking</h6>
<div class="boxed dual-input">
<input class="ratio1-1" name="attr_kingdom_stone" value="1" type="number" title="@{kingdom_stone}"/><span class="slash">/</span>
<input class="ratio1-1" name="attr_kingdom_stone_max" value="1" type="number" title="@{kingdom_stone|max}"/>
</div>
<label class="input-label">
<h6 data-i18n="km_quarries">//- See comment above; but again nitpicks</h6>
<input class="boxed ratio1-1" name="attr_kingdom_quarries" value="1" type="number" title="@{kingdom_quarries}"/>
</label>
</div>
</div>
<div class="miscellany">
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_trade agreements" role="heading" aria-level="6"></span>
<input class="boxed big-text input-label__input" name="attr_kingdom_trade-agreements" type="number" value="1" style="width:100%;" title="@{kingdom_trade-agreements}"/>
</label>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_event check dc" role="heading" aria-level="6"></span>
<input class="boxed big-text input-label__input" name="attr_kingdom_event-check-dc" type="number" value="1" style="width:100%;" title="@{kingdom_event-check-dc}"/>
</label>
<label class="input-label span-all"><span class="input-label__text" data-i18n="km_control dc"></span>
<input class="underlined input-label__input" name="attr_kingdom_control_dc" type="number" title="@{kingdom_control_dc}"/>
</label>
</div>
</div>
</header>
<section class="kingdom-info justify-space-around" id="kingdom-info">
<label class="input-label span-all"><span class="input-label__text" data-i18n="km_charter" aria-level="3"></span>
<input class="underlined input-label__input" name="attr_kingdom_charter" type="text" title="@{kingdom_charter}"/>
</label>
<label class="input-label span-all"><span class="input-label__text" data-i18n="km_heartland" aria-level="3"></span>
<input class="underlined input-label__input" name="attr_kingdom_heartland" type="text" title="@{kingdom_heartland}"/>
</label>
<label class="input-label span-all"><span class="input-label__text" data-i18n="km_government" aria-level="3"></span>
<input class="underlined input-label__input" name="attr_kingdom_government" type="text" title="@{kingdom_government}"/>
</label>
<label class="input-label span-all"><span class="input-label__text" data-i18n="km_capital city" aria-level="3"></span>
<input class="underlined input-label__input" name="attr_kingdom_capital_city" type="text" title="@{kingdom_capital_city}"/>
</label>
<label class="input-label span-all"><span class="input-label__text" data-i18n="km_experience points" aria-level="4"></span>
<input class="underlined input-label__input" name="attr_kingdom_experience_points" type="text" title="@{kingdom_experience_points}"/>
</label>
<label class="input-label span-all"><span class="input-label__text" data-i18n="km_resource dice" aria-level="4"></span>
<input class="underlined input-label__input" name="attr_kingdom_resource_dice" type="text" title="@{kingdom_resource_dice}"/>
</label>
<label class="input-label stacked center"><span class="input-label__text" data-i18n="km_size"></span>
<input class="boxed input-label__input" type="text" name="attr_kingdom_size" title="@{kingdom_size}"/>
</label>
<label class="input-label stacked center"><span class="input-label__text" data-i18n="km_rp"></span>
<input class="boxed input-label__input" type="text" name="attr_kingdom_rp" title="@{kingdom_rp}"/>
</label>
<label class="input-label stacked center"><span class="input-label__text" data-i18n="km_unrest"></span>
<input class="boxed input-label__input" type="text" name="attr_kingdom_unrest" title="@{kingdom_unrest}"/>
</label>
<label class="input-label stacked center"><span class="input-label__text" data-i18n="km_unrest penalty"></span>
<input class="boxed input-label__input" type="text" name="attr_kingdom_unrest_penalty" title="@{kingdom_unrest_penalty}"/>
</label>
<label class="input-label stacked center"><span class="input-label__text" data-i18n="km_consumption"></span>
<input class="boxed input-label__input" type="text" name="attr_kingdom_consumption" title="@{kingdom_consumption}"/>
</label>
</section>
<section class="kingdom-ability-scores collapse-container" id="kingdom-ability-scores">
<input class="collapse" name="attr_kingdom_ability_score_collapse" value="1" type="checkbox" title="@{kingdom_ability_score_collapse}"/>
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_ability scores"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<div class="score-container collapse-container">
<input class="boxed ratio1-1" name="attr_kingdom_culture" value="0" readonly="" type="number" title="@{kingdom_culture}"/>
<button class="score-container--content roller" name="roll_kingdom_culture" data-i18n="km_culture" value="@{kingdom_culture_action}" title="%{kingdom_culture}" type="roll">
</button>
<button name="act_kingdom-culture-action" hidden="" type="action" title="%{kingdom-culture-action}">
</button>
<input name="attr_kingdom_culture_action" type="hidden" title="@{kingdom_culture_action}"/>
<input class="boxed ratio1-1" name="attr_kingdom_culture_score" value="10" type="number" title="@{kingdom_culture_score}"/>
<div class="score-container expanded span-all">
<h5 data-i18n="km_corruption"></h5>
<input class="boxed ratio1-1" name="attr_kingdom_corruption" value="0" type="number" title="@{kingdom_corruption}"/>
<input class="boxed ratio1-1" name="attr_kingdom_corruption_penalty" value="0" type="number" title="@{kingdom_corruption_penalty}"/>
<input class="boxed ratio1-1" name="attr_kingdom_corruption_threshold" value="10" type="number" title="@{kingdom_corruption_threshold}"/>
</div>
</div>
<div class="score-container collapse-container">
<input class="boxed ratio1-1" name="attr_kingdom_economy" value="0" readonly="" type="number" title="@{kingdom_economy}"/>
<button class="score-container--content roller" name="roll_kingdom_economy" data-i18n="km_economy" value="@{kingdom_economy_action}" title="%{kingdom_economy}" type="roll">
</button>
<button name="act_kingdom-economy-action" hidden="" type="action" title="%{kingdom-economy-action}">
</button>
<input name="attr_kingdom_economy_action" type="hidden" title="@{kingdom_economy_action}"/>
<input class="boxed ratio1-1" name="attr_kingdom_economy_score" value="10" type="number" title="@{kingdom_economy_score}"/>
<div class="score-container expanded span-all">
<h5 data-i18n="km_crime"></h5>
<input class="boxed ratio1-1" name="attr_kingdom_crime" value="0" type="number" title="@{kingdom_crime}"/>
<input class="boxed ratio1-1" name="attr_kingdom_crime_penalty" value="0" type="number" title="@{kingdom_crime_penalty}"/>
<input class="boxed ratio1-1" name="attr_kingdom_crime_threshold" value="10" type="number" title="@{kingdom_crime_threshold}"/>
</div>
</div>
<div class="score-container collapse-container">
<input class="boxed ratio1-1" name="attr_kingdom_loyalty" value="0" readonly="" type="number" title="@{kingdom_loyalty}"/>
<button class="score-container--content roller" name="roll_kingdom_loyalty" data-i18n="km_loyalty" value="@{kingdom_loyalty_action}" title="%{kingdom_loyalty}" type="roll">
</button>
<button name="act_kingdom-loyalty-action" hidden="" type="action" title="%{kingdom-loyalty-action}">
</button>
<input name="attr_kingdom_loyalty_action" type="hidden" title="@{kingdom_loyalty_action}"/>
<input class="boxed ratio1-1" name="attr_kingdom_loyalty_score" value="10" type="number" title="@{kingdom_loyalty_score}"/>
<div class="score-container expanded span-all">
<h5 data-i18n="km_decay"></h5>
<input class="boxed ratio1-1" name="attr_kingdom_decay" value="0" type="number" title="@{kingdom_decay}"/>
<input class="boxed ratio1-1" name="attr_kingdom_decay_penalty" value="0" type="number" title="@{kingdom_decay_penalty}"/>
<input class="boxed ratio1-1" name="attr_kingdom_decay_threshold" value="10" type="number" title="@{kingdom_decay_threshold}"/>
</div>
</div>
<div class="score-container collapse-container">
<input class="boxed ratio1-1" name="attr_kingdom_stability" value="0" readonly="" type="number" title="@{kingdom_stability}"/>
<button class="score-container--content roller" name="roll_kingdom_stability" data-i18n="km_stability" value="@{kingdom_stability_action}" title="%{kingdom_stability}" type="roll">
</button>
<button name="act_kingdom-stability-action" hidden="" type="action" title="%{kingdom-stability-action}">
</button>
<input name="attr_kingdom_stability_action" type="hidden" title="@{kingdom_stability_action}"/>
<input class="boxed ratio1-1" name="attr_kingdom_stability_score" value="10" type="number" title="@{kingdom_stability_score}"/>
<div class="score-container expanded span-all">
<h5 data-i18n="km_strife"></h5>
<input class="boxed ratio1-1" name="attr_kingdom_strife" value="0" type="number" title="@{kingdom_strife}"/>
<input class="boxed ratio1-1" name="attr_kingdom_strife_penalty" value="0" type="number" title="@{kingdom_strife_penalty}"/>
<input class="boxed ratio1-1" name="attr_kingdom_strife_threshold" value="10" type="number" title="@{kingdom_strife_threshold}"/>
</div>
</div>
</section>
<section class="kingmaker__skills" data-i18n-list="km_skills-order" id="kingmaker-skills">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_skills"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="agriculture">
<input class="collapse" name="attr_kingdom_agriculture_collapse" value="1" checked="" type="checkbox" title="@{kingdom_agriculture_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_agriculture" data-i18n="km_agriculture" value="@{kingdom_agriculture_action}" title="%{kingdom_agriculture}" type="roll">
</button>
<button name="act_kingdom-agriculture-action" hidden="" type="action" title="%{kingdom-agriculture-action}">
</button>
<input name="attr_kingdom_agriculture_action" type="hidden" title="@{kingdom_agriculture_action}"/>
<input name="attr_kingdom_agriculture_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_agriculture_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_agriculture_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_agriculture_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_agriculture" value="0" readonly="" type="number" title="@{kingdom_agriculture}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_stability"></span>
<input class="input-label__input" name="attr_kingdom_stability" type="number" readonly="" value="0" title="@{kingdom_stability}"/>
</label>
<input class="boxed" name="attr_kingdom_agriculture_proficiency_total" readonly="" type="number" title="@{kingdom_agriculture_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_agriculture_proficiency" value="0" type="hidden" title="@{kingdom_agriculture_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_agriculture_proficiency" value="2" type="checkbox" title="@{kingdom_agriculture_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_agriculture_proficiency" value="4" type="checkbox" title="@{kingdom_agriculture_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_agriculture_proficiency" value="6" type="checkbox" title="@{kingdom_agriculture_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_agriculture_proficiency" value="8" type="checkbox" title="@{kingdom_agriculture_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_agriculture_status" type="number" title="@{kingdom_agriculture_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_agriculture_circumstance" type="number" title="@{kingdom_agriculture_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_agriculture_item" type="number" title="@{kingdom_agriculture_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_agriculture_other" type="number" title="@{kingdom_agriculture_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="arts">
<input class="collapse" name="attr_kingdom_arts_collapse" value="1" checked="" type="checkbox" title="@{kingdom_arts_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_arts" data-i18n="km_arts" value="@{kingdom_arts_action}" title="%{kingdom_arts}" type="roll">
</button>
<button name="act_kingdom-arts-action" hidden="" type="action" title="%{kingdom-arts-action}">
</button>
<input name="attr_kingdom_arts_action" type="hidden" title="@{kingdom_arts_action}"/>
<input name="attr_kingdom_arts_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_arts_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_arts_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_arts_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_arts" value="0" readonly="" type="number" title="@{kingdom_arts}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_culture"></span>
<input class="input-label__input" name="attr_kingdom_culture" type="number" readonly="" value="0" title="@{kingdom_culture}"/>
</label>
<input class="boxed" name="attr_kingdom_arts_proficiency_total" readonly="" type="number" title="@{kingdom_arts_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_arts_proficiency" value="0" type="hidden" title="@{kingdom_arts_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_arts_proficiency" value="2" type="checkbox" title="@{kingdom_arts_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_arts_proficiency" value="4" type="checkbox" title="@{kingdom_arts_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_arts_proficiency" value="6" type="checkbox" title="@{kingdom_arts_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_arts_proficiency" value="8" type="checkbox" title="@{kingdom_arts_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_arts_status" type="number" title="@{kingdom_arts_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_arts_circumstance" type="number" title="@{kingdom_arts_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_arts_item" type="number" title="@{kingdom_arts_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_arts_other" type="number" title="@{kingdom_arts_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="boating">
<input class="collapse" name="attr_kingdom_boating_collapse" value="1" checked="" type="checkbox" title="@{kingdom_boating_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_boating" data-i18n="km_boating" value="@{kingdom_boating_action}" title="%{kingdom_boating}" type="roll">
</button>
<button name="act_kingdom-boating-action" hidden="" type="action" title="%{kingdom-boating-action}">
</button>
<input name="attr_kingdom_boating_action" type="hidden" title="@{kingdom_boating_action}"/>
<input name="attr_kingdom_boating_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_boating_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_boating_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_boating_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_boating" value="0" readonly="" type="number" title="@{kingdom_boating}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_economy"></span>
<input class="input-label__input" name="attr_kingdom_economy" type="number" readonly="" value="0" title="@{kingdom_economy}"/>
</label>
<input class="boxed" name="attr_kingdom_boating_proficiency_total" readonly="" type="number" title="@{kingdom_boating_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_boating_proficiency" value="0" type="hidden" title="@{kingdom_boating_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_boating_proficiency" value="2" type="checkbox" title="@{kingdom_boating_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_boating_proficiency" value="4" type="checkbox" title="@{kingdom_boating_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_boating_proficiency" value="6" type="checkbox" title="@{kingdom_boating_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_boating_proficiency" value="8" type="checkbox" title="@{kingdom_boating_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_boating_status" type="number" title="@{kingdom_boating_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_boating_circumstance" type="number" title="@{kingdom_boating_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_boating_item" type="number" title="@{kingdom_boating_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_boating_other" type="number" title="@{kingdom_boating_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="defense">
<input class="collapse" name="attr_kingdom_defense_collapse" value="1" checked="" type="checkbox" title="@{kingdom_defense_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_defense" data-i18n="km_defense" value="@{kingdom_defense_action}" title="%{kingdom_defense}" type="roll">
</button>
<button name="act_kingdom-defense-action" hidden="" type="action" title="%{kingdom-defense-action}">
</button>
<input name="attr_kingdom_defense_action" type="hidden" title="@{kingdom_defense_action}"/>
<input name="attr_kingdom_defense_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_defense_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_defense_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_defense_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_defense" value="0" readonly="" type="number" title="@{kingdom_defense}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_stability"></span>
<input class="input-label__input" name="attr_kingdom_stability" type="number" readonly="" value="0" title="@{kingdom_stability}"/>
</label>
<input class="boxed" name="attr_kingdom_defense_proficiency_total" readonly="" type="number" title="@{kingdom_defense_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_defense_proficiency" value="0" type="hidden" title="@{kingdom_defense_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_defense_proficiency" value="2" type="checkbox" title="@{kingdom_defense_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_defense_proficiency" value="4" type="checkbox" title="@{kingdom_defense_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_defense_proficiency" value="6" type="checkbox" title="@{kingdom_defense_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_defense_proficiency" value="8" type="checkbox" title="@{kingdom_defense_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_defense_status" type="number" title="@{kingdom_defense_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_defense_circumstance" type="number" title="@{kingdom_defense_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_defense_item" type="number" title="@{kingdom_defense_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_defense_other" type="number" title="@{kingdom_defense_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="engineering">
<input class="collapse" name="attr_kingdom_engineering_collapse" value="1" checked="" type="checkbox" title="@{kingdom_engineering_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_engineering" data-i18n="km_engineering" value="@{kingdom_engineering_action}" title="%{kingdom_engineering}" type="roll">
</button>
<button name="act_kingdom-engineering-action" hidden="" type="action" title="%{kingdom-engineering-action}">
</button>
<input name="attr_kingdom_engineering_action" type="hidden" title="@{kingdom_engineering_action}"/>
<input name="attr_kingdom_engineering_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_engineering_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_engineering_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_engineering_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_engineering" value="0" readonly="" type="number" title="@{kingdom_engineering}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_stability"></span>
<input class="input-label__input" name="attr_kingdom_stability" type="number" readonly="" value="0" title="@{kingdom_stability}"/>
</label>
<input class="boxed" name="attr_kingdom_engineering_proficiency_total" readonly="" type="number" title="@{kingdom_engineering_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_engineering_proficiency" value="0" type="hidden" title="@{kingdom_engineering_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_engineering_proficiency" value="2" type="checkbox" title="@{kingdom_engineering_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_engineering_proficiency" value="4" type="checkbox" title="@{kingdom_engineering_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_engineering_proficiency" value="6" type="checkbox" title="@{kingdom_engineering_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_engineering_proficiency" value="8" type="checkbox" title="@{kingdom_engineering_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_engineering_status" type="number" title="@{kingdom_engineering_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_engineering_circumstance" type="number" title="@{kingdom_engineering_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_engineering_item" type="number" title="@{kingdom_engineering_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_engineering_other" type="number" title="@{kingdom_engineering_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="exploration">
<input class="collapse" name="attr_kingdom_exploration_collapse" value="1" checked="" type="checkbox" title="@{kingdom_exploration_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_exploration" data-i18n="km_exploration" value="@{kingdom_exploration_action}" title="%{kingdom_exploration}" type="roll">
</button>
<button name="act_kingdom-exploration-action" hidden="" type="action" title="%{kingdom-exploration-action}">
</button>
<input name="attr_kingdom_exploration_action" type="hidden" title="@{kingdom_exploration_action}"/>
<input name="attr_kingdom_exploration_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_exploration_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_exploration_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_exploration_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_exploration" value="0" readonly="" type="number" title="@{kingdom_exploration}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_economy"></span>
<input class="input-label__input" name="attr_kingdom_economy" type="number" readonly="" value="0" title="@{kingdom_economy}"/>
</label>
<input class="boxed" name="attr_kingdom_exploration_proficiency_total" readonly="" type="number" title="@{kingdom_exploration_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_exploration_proficiency" value="0" type="hidden" title="@{kingdom_exploration_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_exploration_proficiency" value="2" type="checkbox" title="@{kingdom_exploration_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_exploration_proficiency" value="4" type="checkbox" title="@{kingdom_exploration_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_exploration_proficiency" value="6" type="checkbox" title="@{kingdom_exploration_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_exploration_proficiency" value="8" type="checkbox" title="@{kingdom_exploration_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_exploration_status" type="number" title="@{kingdom_exploration_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_exploration_circumstance" type="number" title="@{kingdom_exploration_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_exploration_item" type="number" title="@{kingdom_exploration_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_exploration_other" type="number" title="@{kingdom_exploration_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="folklore">
<input class="collapse" name="attr_kingdom_folklore_collapse" value="1" checked="" type="checkbox" title="@{kingdom_folklore_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_folklore" data-i18n="km_folklore" value="@{kingdom_folklore_action}" title="%{kingdom_folklore}" type="roll">
</button>
<button name="act_kingdom-folklore-action" hidden="" type="action" title="%{kingdom-folklore-action}">
</button>
<input name="attr_kingdom_folklore_action" type="hidden" title="@{kingdom_folklore_action}"/>
<input name="attr_kingdom_folklore_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_folklore_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_folklore_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_folklore_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_folklore" value="0" readonly="" type="number" title="@{kingdom_folklore}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_culture"></span>
<input class="input-label__input" name="attr_kingdom_culture" type="number" readonly="" value="0" title="@{kingdom_culture}"/>
</label>
<input class="boxed" name="attr_kingdom_folklore_proficiency_total" readonly="" type="number" title="@{kingdom_folklore_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_folklore_proficiency" value="0" type="hidden" title="@{kingdom_folklore_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_folklore_proficiency" value="2" type="checkbox" title="@{kingdom_folklore_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_folklore_proficiency" value="4" type="checkbox" title="@{kingdom_folklore_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_folklore_proficiency" value="6" type="checkbox" title="@{kingdom_folklore_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_folklore_proficiency" value="8" type="checkbox" title="@{kingdom_folklore_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_folklore_status" type="number" title="@{kingdom_folklore_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_folklore_circumstance" type="number" title="@{kingdom_folklore_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_folklore_item" type="number" title="@{kingdom_folklore_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_folklore_other" type="number" title="@{kingdom_folklore_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="industry">
<input class="collapse" name="attr_kingdom_industry_collapse" value="1" checked="" type="checkbox" title="@{kingdom_industry_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_industry" data-i18n="km_industry" value="@{kingdom_industry_action}" title="%{kingdom_industry}" type="roll">
</button>
<button name="act_kingdom-industry-action" hidden="" type="action" title="%{kingdom-industry-action}">
</button>
<input name="attr_kingdom_industry_action" type="hidden" title="@{kingdom_industry_action}"/>
<input name="attr_kingdom_industry_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_industry_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_industry_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_industry_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_industry" value="0" readonly="" type="number" title="@{kingdom_industry}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_economy"></span>
<input class="input-label__input" name="attr_kingdom_economy" type="number" readonly="" value="0" title="@{kingdom_economy}"/>
</label>
<input class="boxed" name="attr_kingdom_industry_proficiency_total" readonly="" type="number" title="@{kingdom_industry_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_industry_proficiency" value="0" type="hidden" title="@{kingdom_industry_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_industry_proficiency" value="2" type="checkbox" title="@{kingdom_industry_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_industry_proficiency" value="4" type="checkbox" title="@{kingdom_industry_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_industry_proficiency" value="6" type="checkbox" title="@{kingdom_industry_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_industry_proficiency" value="8" type="checkbox" title="@{kingdom_industry_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_industry_status" type="number" title="@{kingdom_industry_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_industry_circumstance" type="number" title="@{kingdom_industry_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_industry_item" type="number" title="@{kingdom_industry_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_industry_other" type="number" title="@{kingdom_industry_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="intrigue">
<input class="collapse" name="attr_kingdom_intrigue_collapse" value="1" checked="" type="checkbox" title="@{kingdom_intrigue_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_intrigue" data-i18n="km_intrigue" value="@{kingdom_intrigue_action}" title="%{kingdom_intrigue}" type="roll">
</button>
<button name="act_kingdom-intrigue-action" hidden="" type="action" title="%{kingdom-intrigue-action}">
</button>
<input name="attr_kingdom_intrigue_action" type="hidden" title="@{kingdom_intrigue_action}"/>
<input name="attr_kingdom_intrigue_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_intrigue_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_intrigue_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_intrigue_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_intrigue" value="0" readonly="" type="number" title="@{kingdom_intrigue}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_loyalty"></span>
<input class="input-label__input" name="attr_kingdom_loyalty" type="number" readonly="" value="0" title="@{kingdom_loyalty}"/>
</label>
<input class="boxed" name="attr_kingdom_intrigue_proficiency_total" readonly="" type="number" title="@{kingdom_intrigue_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_intrigue_proficiency" value="0" type="hidden" title="@{kingdom_intrigue_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_intrigue_proficiency" value="2" type="checkbox" title="@{kingdom_intrigue_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_intrigue_proficiency" value="4" type="checkbox" title="@{kingdom_intrigue_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_intrigue_proficiency" value="6" type="checkbox" title="@{kingdom_intrigue_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_intrigue_proficiency" value="8" type="checkbox" title="@{kingdom_intrigue_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_intrigue_status" type="number" title="@{kingdom_intrigue_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_intrigue_circumstance" type="number" title="@{kingdom_intrigue_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_intrigue_item" type="number" title="@{kingdom_intrigue_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_intrigue_other" type="number" title="@{kingdom_intrigue_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="magic">
<input class="collapse" name="attr_kingdom_magic_collapse" value="1" checked="" type="checkbox" title="@{kingdom_magic_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_magic" data-i18n="km_magic" value="@{kingdom_magic_action}" title="%{kingdom_magic}" type="roll">
</button>
<button name="act_kingdom-magic-action" hidden="" type="action" title="%{kingdom-magic-action}">
</button>
<input name="attr_kingdom_magic_action" type="hidden" title="@{kingdom_magic_action}"/>
<input name="attr_kingdom_magic_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_magic_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_magic_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_magic_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_magic" value="0" readonly="" type="number" title="@{kingdom_magic}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_culture"></span>
<input class="input-label__input" name="attr_kingdom_culture" type="number" readonly="" value="0" title="@{kingdom_culture}"/>
</label>
<input class="boxed" name="attr_kingdom_magic_proficiency_total" readonly="" type="number" title="@{kingdom_magic_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_magic_proficiency" value="0" type="hidden" title="@{kingdom_magic_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_magic_proficiency" value="2" type="checkbox" title="@{kingdom_magic_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_magic_proficiency" value="4" type="checkbox" title="@{kingdom_magic_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_magic_proficiency" value="6" type="checkbox" title="@{kingdom_magic_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_magic_proficiency" value="8" type="checkbox" title="@{kingdom_magic_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_magic_status" type="number" title="@{kingdom_magic_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_magic_circumstance" type="number" title="@{kingdom_magic_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_magic_item" type="number" title="@{kingdom_magic_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_magic_other" type="number" title="@{kingdom_magic_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="politics">
<input class="collapse" name="attr_kingdom_politics_collapse" value="1" checked="" type="checkbox" title="@{kingdom_politics_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_politics" data-i18n="km_politics" value="@{kingdom_politics_action}" title="%{kingdom_politics}" type="roll">
</button>
<button name="act_kingdom-politics-action" hidden="" type="action" title="%{kingdom-politics-action}">
</button>
<input name="attr_kingdom_politics_action" type="hidden" title="@{kingdom_politics_action}"/>
<input name="attr_kingdom_politics_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_politics_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_politics_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_politics_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_politics" value="0" readonly="" type="number" title="@{kingdom_politics}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_loyalty"></span>
<input class="input-label__input" name="attr_kingdom_loyalty" type="number" readonly="" value="0" title="@{kingdom_loyalty}"/>
</label>
<input class="boxed" name="attr_kingdom_politics_proficiency_total" readonly="" type="number" title="@{kingdom_politics_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_politics_proficiency" value="0" type="hidden" title="@{kingdom_politics_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_politics_proficiency" value="2" type="checkbox" title="@{kingdom_politics_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_politics_proficiency" value="4" type="checkbox" title="@{kingdom_politics_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_politics_proficiency" value="6" type="checkbox" title="@{kingdom_politics_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_politics_proficiency" value="8" type="checkbox" title="@{kingdom_politics_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_politics_status" type="number" title="@{kingdom_politics_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_politics_circumstance" type="number" title="@{kingdom_politics_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_politics_item" type="number" title="@{kingdom_politics_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_politics_other" type="number" title="@{kingdom_politics_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="scholarship">
<input class="collapse" name="attr_kingdom_scholarship_collapse" value="1" checked="" type="checkbox" title="@{kingdom_scholarship_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_scholarship" data-i18n="km_scholarship" value="@{kingdom_scholarship_action}" title="%{kingdom_scholarship}" type="roll">
</button>
<button name="act_kingdom-scholarship-action" hidden="" type="action" title="%{kingdom-scholarship-action}">
</button>
<input name="attr_kingdom_scholarship_action" type="hidden" title="@{kingdom_scholarship_action}"/>
<input name="attr_kingdom_scholarship_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_scholarship_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_scholarship_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_scholarship_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_scholarship" value="0" readonly="" type="number" title="@{kingdom_scholarship}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_culture"></span>
<input class="input-label__input" name="attr_kingdom_culture" type="number" readonly="" value="0" title="@{kingdom_culture}"/>
</label>
<input class="boxed" name="attr_kingdom_scholarship_proficiency_total" readonly="" type="number" title="@{kingdom_scholarship_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_scholarship_proficiency" value="0" type="hidden" title="@{kingdom_scholarship_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_scholarship_proficiency" value="2" type="checkbox" title="@{kingdom_scholarship_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_scholarship_proficiency" value="4" type="checkbox" title="@{kingdom_scholarship_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_scholarship_proficiency" value="6" type="checkbox" title="@{kingdom_scholarship_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_scholarship_proficiency" value="8" type="checkbox" title="@{kingdom_scholarship_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_scholarship_status" type="number" title="@{kingdom_scholarship_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_scholarship_circumstance" type="number" title="@{kingdom_scholarship_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_scholarship_item" type="number" title="@{kingdom_scholarship_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_scholarship_other" type="number" title="@{kingdom_scholarship_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="statecraft">
<input class="collapse" name="attr_kingdom_statecraft_collapse" value="1" checked="" type="checkbox" title="@{kingdom_statecraft_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_statecraft" data-i18n="km_statecraft" value="@{kingdom_statecraft_action}" title="%{kingdom_statecraft}" type="roll">
</button>
<button name="act_kingdom-statecraft-action" hidden="" type="action" title="%{kingdom-statecraft-action}">
</button>
<input name="attr_kingdom_statecraft_action" type="hidden" title="@{kingdom_statecraft_action}"/>
<input name="attr_kingdom_statecraft_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_statecraft_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_statecraft_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_statecraft_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_statecraft" value="0" readonly="" type="number" title="@{kingdom_statecraft}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_loyalty"></span>
<input class="input-label__input" name="attr_kingdom_loyalty" type="number" readonly="" value="0" title="@{kingdom_loyalty}"/>
</label>
<input class="boxed" name="attr_kingdom_statecraft_proficiency_total" readonly="" type="number" title="@{kingdom_statecraft_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_statecraft_proficiency" value="0" type="hidden" title="@{kingdom_statecraft_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_statecraft_proficiency" value="2" type="checkbox" title="@{kingdom_statecraft_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_statecraft_proficiency" value="4" type="checkbox" title="@{kingdom_statecraft_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_statecraft_proficiency" value="6" type="checkbox" title="@{kingdom_statecraft_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_statecraft_proficiency" value="8" type="checkbox" title="@{kingdom_statecraft_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_statecraft_status" type="number" title="@{kingdom_statecraft_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_statecraft_circumstance" type="number" title="@{kingdom_statecraft_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_statecraft_item" type="number" title="@{kingdom_statecraft_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_statecraft_other" type="number" title="@{kingdom_statecraft_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="trade">
<input class="collapse" name="attr_kingdom_trade_collapse" value="1" checked="" type="checkbox" title="@{kingdom_trade_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_trade" data-i18n="km_trade" value="@{kingdom_trade_action}" title="%{kingdom_trade}" type="roll">
</button>
<button name="act_kingdom-trade-action" hidden="" type="action" title="%{kingdom-trade-action}">
</button>
<input name="attr_kingdom_trade_action" type="hidden" title="@{kingdom_trade_action}"/>
<input name="attr_kingdom_trade_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_trade_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_trade_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_trade_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_trade" value="0" readonly="" type="number" title="@{kingdom_trade}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_economy"></span>
<input class="input-label__input" name="attr_kingdom_economy" type="number" readonly="" value="0" title="@{kingdom_economy}"/>
</label>
<input class="boxed" name="attr_kingdom_trade_proficiency_total" readonly="" type="number" title="@{kingdom_trade_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_trade_proficiency" value="0" type="hidden" title="@{kingdom_trade_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_trade_proficiency" value="2" type="checkbox" title="@{kingdom_trade_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_trade_proficiency" value="4" type="checkbox" title="@{kingdom_trade_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_trade_proficiency" value="6" type="checkbox" title="@{kingdom_trade_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_trade_proficiency" value="8" type="checkbox" title="@{kingdom_trade_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_trade_status" type="number" title="@{kingdom_trade_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_trade_circumstance" type="number" title="@{kingdom_trade_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_trade_item" type="number" title="@{kingdom_trade_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_trade_other" type="number" title="@{kingdom_trade_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="warfare">
<input class="collapse" name="attr_kingdom_warfare_collapse" value="1" checked="" type="checkbox" title="@{kingdom_warfare_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_warfare" data-i18n="km_warfare" value="@{kingdom_warfare_action}" title="%{kingdom_warfare}" type="roll">
</button>
<button name="act_kingdom-warfare-action" hidden="" type="action" title="%{kingdom-warfare-action}">
</button>
<input name="attr_kingdom_warfare_action" type="hidden" title="@{kingdom_warfare_action}"/>
<input name="attr_kingdom_warfare_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_warfare_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_warfare_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_warfare_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_warfare" value="0" readonly="" type="number" title="@{kingdom_warfare}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_loyalty"></span>
<input class="input-label__input" name="attr_kingdom_loyalty" type="number" readonly="" value="0" title="@{kingdom_loyalty}"/>
</label>
<input class="boxed" name="attr_kingdom_warfare_proficiency_total" readonly="" type="number" title="@{kingdom_warfare_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_warfare_proficiency" value="0" type="hidden" title="@{kingdom_warfare_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_warfare_proficiency" value="2" type="checkbox" title="@{kingdom_warfare_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_warfare_proficiency" value="4" type="checkbox" title="@{kingdom_warfare_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_warfare_proficiency" value="6" type="checkbox" title="@{kingdom_warfare_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_warfare_proficiency" value="8" type="checkbox" title="@{kingdom_warfare_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_warfare_status" type="number" title="@{kingdom_warfare_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_warfare_circumstance" type="number" title="@{kingdom_warfare_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_warfare_item" type="number" title="@{kingdom_warfare_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_warfare_other" type="number" title="@{kingdom_warfare_other}"/>
</label>
</div>
</div>
<div class="kingmaker__skill-row collapse-container" data-i18n-list-item="wilderness">
<input class="collapse" name="attr_kingdom_wilderness_collapse" value="1" checked="" type="checkbox" title="@{kingdom_wilderness_collapse}"/>
<div class="input-label stacked no-gap justify-center">
<button class="roller" name="roll_kingdom_wilderness" data-i18n="km_wilderness" value="@{kingdom_wilderness_action}" title="%{kingdom_wilderness}" type="roll">
</button>
<button name="act_kingdom-wilderness-action" hidden="" type="action" title="%{kingdom-wilderness-action}">
</button>
<input name="attr_kingdom_wilderness_action" type="hidden" title="@{kingdom_wilderness_action}"/>
<input name="attr_kingdom_wilderness_proficiency_translation" value="km_untrained" type="hidden" title="@{kingdom_wilderness_proficiency_translation}"/><span class="tiny-text align-self-start" name="attr_kingdom_wilderness_proficiency_translation" data-i18n-dynamic="" title="@{kingdom_wilderness_proficiency_translation}"></span>
</div>
<input class="boxed" name="attr_kingdom_wilderness" value="0" readonly="" type="number" title="@{kingdom_wilderness}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_stability"></span>
<input class="input-label__input" name="attr_kingdom_stability" type="number" readonly="" value="0" title="@{kingdom_stability}"/>
</label>
<input class="boxed" name="attr_kingdom_wilderness_proficiency_total" readonly="" type="number" title="@{kingdom_wilderness_proficiency_total}"/>
<div class="fill-left kingmaker__teml-container">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_kingdom_wilderness_proficiency" value="0" type="hidden" title="@{kingdom_wilderness_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_wilderness_proficiency" value="2" type="checkbox" title="@{kingdom_wilderness_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_wilderness_proficiency" value="4" type="checkbox" title="@{kingdom_wilderness_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_wilderness_proficiency" value="6" type="checkbox" title="@{kingdom_wilderness_proficiency}"/>
<input class="fill-left__radio" name="attr_kingdom_wilderness_proficiency" value="8" type="checkbox" title="@{kingdom_wilderness_proficiency}"/>
</div>
<div class="expanded span-all">
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_status"></span>
<input class="input-label__input" name="attr_kingdom_wilderness_status" type="number" title="@{kingdom_wilderness_status}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_circumstance"></span>
<input class="input-label__input" name="attr_kingdom_wilderness_circumstance" type="number" title="@{kingdom_wilderness_circumstance}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_item"></span>
<input class="input-label__input" name="attr_kingdom_wilderness_item" type="number" title="@{kingdom_wilderness_item}"/>
</label>
<label class="input-label stacked boxed input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_other"></span>
<input class="input-label__input" name="attr_kingdom_wilderness_other" type="number" title="@{kingdom_wilderness_other}"/>
</label>
</div>
</div>
</section>
<section class="leaders collapse-container" id="leaders">
<input name="attr_kingdom_invested" value="" type="hidden" title="@{kingdom_invested}"/>
<input class="collapse" name="attr_kingdom_leaders_collapse" value="1" type="checkbox" title="@{kingdom_leaders_collapse}"/>
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_leaders"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<input class="ruler" name="attr_kingdom_ruler_invested" value="0" type="checkbox" title="@{kingdom_ruler_invested}"/>
<h3 class="ruler" data-i18n="km_ruler"></h3>
<input class="ruler underlined" name="attr_kingdom_ruler_name" type="text" title="@{kingdom_ruler_name}"/>
<input class="counselor" name="attr_kingdom_counselor_invested" value="0" type="checkbox" title="@{kingdom_counselor_invested}"/>
<h3 class="counselor" data-i18n="km_counselor"></h3>
<input class="counselor underlined" name="attr_kingdom_counselor_name" type="text" title="@{kingdom_counselor_name}"/>
<input class="general" name="attr_kingdom_general_invested" value="0" type="checkbox" title="@{kingdom_general_invested}"/>
<h3 class="general" data-i18n="km_general"></h3>
<input class="general underlined" name="attr_kingdom_general_name" type="text" title="@{kingdom_general_name}"/>
<input class="emissary" name="attr_kingdom_emissary_invested" value="0" type="checkbox" title="@{kingdom_emissary_invested}"/>
<h3 class="emissary" data-i18n="km_emissary"></h3>
<input class="emissary underlined" name="attr_kingdom_emissary_name" type="text" title="@{kingdom_emissary_name}"/>
<input class="magister" name="attr_kingdom_magister_invested" value="0" type="checkbox" title="@{kingdom_magister_invested}"/>
<h3 class="magister" data-i18n="km_magister"></h3>
<input class="magister underlined" name="attr_kingdom_magister_name" type="text" title="@{kingdom_magister_name}"/>
<input class="treasurer" name="attr_kingdom_treasurer_invested" value="0" type="checkbox" title="@{kingdom_treasurer_invested}"/>
<h3 class="treasurer" data-i18n="km_treasurer"></h3>
<input class="treasurer underlined" name="attr_kingdom_treasurer_name" type="text" title="@{kingdom_treasurer_name}"/>
<input class="viceroy" name="attr_kingdom_viceroy_invested" value="0" type="checkbox" title="@{kingdom_viceroy_invested}"/>
<h3 class="viceroy" data-i18n="km_viceroy"></h3>
<input class="viceroy underlined" name="attr_kingdom_viceroy_name" type="text" title="@{kingdom_viceroy_name}"/>
<input class="warden" name="attr_kingdom_warden_invested" value="0" type="checkbox" title="@{kingdom_warden_invested}"/>
<h3 class="warden" data-i18n="km_warden"></h3>
<input class="warden underlined" name="attr_kingdom_warden_name" type="text" title="@{kingdom_warden_name}"/>
</section>
<section class="kingdom-feats repeating-container" id="kingdom-feats">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_feats"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-kingdom-feats" type="action" title="%{add-kingdom-feats}">
</button>
<fieldset class="repeating_kingdom-feats">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_kingdom-feats_$X_collapse}"/>
<input name="attr_level" readonly="" value="1" type="number" title="@{repeating_kingdom-feats_$X_level}"/>
<input class="collapsed" name="attr_name" readonly="" type="text" title="@{repeating_kingdom-feats_$X_name}"/>
<input class="expanded underlined" name="attr_name" type="text" title="@{repeating_kingdom-feats_$X_name}"/>
<div class="headed-textarea expanded">
<h3 class="headed-textarea__header" data-i18n="km_description">
</h3>
<textarea class="underlined headed-textarea__textarea" name="attr_description" title="@{repeating_kingdom-feats_$X_description}"></textarea>
</div>
</fieldset>
</section>
<section class="abilities repeating-container" id="abilities">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_abilities"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-kingdom-abilities" type="action" title="%{add-kingdom-abilities}">
</button>
<fieldset class="repeating_kingdom-abilities">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_kingdom-abilities_$X_collapse}"/>
<input class="collapsed" name="attr_name" readonly="" type="text" title="@{repeating_kingdom-abilities_$X_name}"/>
<input class="expanded underlined" name="attr_name" type="text" title="@{repeating_kingdom-abilities_$X_name}"/>
<div class="headed-textarea expanded">
<h3 class="headed-textarea__header" data-i18n="km_description">
</h3>
<textarea class="underlined headed-textarea__textarea" name="attr_description" title="@{repeating_kingdom-abilities_$X_description}"></textarea>
</div>
</fieldset>
</section>
<section class="events repeating-container" id="events">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_events"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-kingdom-events" type="action" title="%{add-kingdom-events}">
</button>
<fieldset class="repeating_kingdom-events">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_kingdom-events_$X_collapse}"/>
<input class="collapsed" name="attr_name" readonly="" type="text" title="@{repeating_kingdom-events_$X_name}"/>
<input class="expanded underlined" name="attr_name" type="text" title="@{repeating_kingdom-events_$X_name}"/>
<div class="headed-textarea expanded">
<h3 class="headed-textarea__header" data-i18n="km_description">
</h3>
<textarea class="underlined headed-textarea__textarea" name="attr_description" title="@{repeating_kingdom-events_$X_description}"></textarea>
</div>
</fieldset>
</section>
</article>
<article class="tabs__container tabs--kingmaker__container tabs--kingmaker__container--army army" data-container-tab="kingmaker" data-tab="army" id="army">
<section class="span-all" id="kingmaker__army-general-info">
<label class="input-label"><span class="input-label__text" data-i18n="km_army name"></span>
<input class="underlined input-label__input" name="attr_character_name" type="text" title="@{character_name}"/>
</label>
<label class="input-label stacked input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_level"></span>
<input class="boxed input-label__input" name="attr_army_level" type="number" title="@{army_level}"/>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="km_alignment"></span>
<input class="underlined input-label__input" name="attr_army_alignment" type="text" title="@{army_alignment}"/>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="km_army type"></span>
<input class="underlined input-label__input" name="attr_army_type" type="text" title="@{army_type}"/>
</label>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_recruitment dc"></span>
<input class="input-label__input" name="attr_army_recruitment_dc" type="number" title="@{army_recruitment_dc}"/>
</label>
<label class="input-label input-label--kingmaker-box stacked center"><span class="uppercase input-label--kingmaker-box__label input-label__text" data-i18n="km_ac"></span>
<input class="input-label__input" name="attr_army_ac" type="number" title="@{army_ac}"/>
</label>
<div class="combined-army-stat">
<div class="input-label input-label--button">
<button class="roller input-label__text" data-i18n="km_scouting" name="roll_army_scouting" value="@{army_scouting_action}" title="%{army_scouting}" type="roll">
</button>
<input class="underlined input-label__input" name="attr_army_scouting" type="number" title="@{army_scouting}"/>
</div>
<button name="act_army-scouting-action" hidden="" type="action" title="%{army-scouting-action}">
</button>
<input name="attr_army_scouting_action" type="hidden" title="@{army_scouting_action}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_scouting dc"></span>
<input class="input-label__input" name="attr_army_scouting_dc" type="number" readonly="" value="10" title="@{army_scouting_dc}"/>
</label>
</div>
<div class="combined-army-stat">
<div class="input-label input-label--button">
<button class="roller input-label__text" data-i18n="km_maneuver" name="roll_army_maneuver" value="@{army_maneuver_action}" title="%{army_maneuver}" type="roll">
</button>
<input class="underlined input-label__input" name="attr_army_maneuver" type="number" title="@{army_maneuver}"/>
</div>
<button name="act_army-maneuver-action" hidden="" type="action" title="%{army-maneuver-action}">
</button>
<input name="attr_army_maneuver_action" type="hidden" title="@{army_maneuver_action}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_maneuver dc"></span>
<input class="input-label__input" name="attr_army_maneuver_dc" type="number" readonly="" value="10" title="@{army_maneuver_dc}"/>
</label>
</div>
<div class="combined-army-stat">
<div class="input-label input-label--button">
<button class="roller input-label__text" data-i18n="km_morale" name="roll_army_morale" value="@{army_morale_action}" title="%{army_morale}" type="roll">
</button>
<input class="underlined input-label__input" name="attr_army_morale" type="number" title="@{army_morale}"/>
</div>
<button name="act_army-morale-action" hidden="" type="action" title="%{army-morale-action}">
</button>
<input name="attr_army_morale_action" type="hidden" title="@{army_morale_action}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_morale dc"></span>
<input class="input-label__input" name="attr_army_morale_dc" type="number" readonly="" value="10" title="@{army_morale_dc}"/>
</label>
</div>
<div class="input-label stacked input-label--kingmaker-box"><span class="uppercase input-label--kingmaker-box__label" data-i18n="km_hp"></span>
<div class="dual-input">
<input name="attr_army_hp" type="number" title="@{army_hp}"/><span class="slash">/</span>
<input name="attr_army_hp_max" type="number" title="@{army_hp|max}"/>
</div>
</div>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_rout threshold"></span>
<input class="input-label__input" name="attr_army_rout_threshold" type="number" title="@{army_rout_threshold}"/>
</label>
</section>
<section class="kingmaker__army--tactics" id="kingmaker__army--tactics">
<div class="attack-info">
<label class="input-label"><span class="input-label__text" data-i18n="km_melee"></span>
<input class="underlined input-label__input" name="attr_army_melee" type="number" title="@{army_melee}"/>
</label>
<div class="combined-army-stat">
<div class="input-label input-label--button">
<button class="roller input-label__text" data-i18n="km_ranged" name="roll_army_ranged" value="@{army_ranged_action}" title="%{army_ranged}" type="roll">
</button>
<input class="underlined input-label__input" name="attr_army_ranged" type="number" title="@{army_ranged}"/>
</div>
<button name="act_army-ranged-action" hidden="" type="action" title="%{army-ranged-action}">
</button>
<input name="attr_army_ranged_action" type="hidden" title="@{army_ranged_action}"/>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_shots"></span>
<input class="input-label__input" name="attr_army_shots" type="number" title="@{army_shots}"/>
</label>
</div>
</div>
<div class="repeating-container">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_tactics"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-army-tactic" type="action" title="%{add-army-tactic}">
</button>
<fieldset class="repeating_army-tactic">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_army-tactic_$X_collapse}"/>
<input class="underlined" name="attr_level" value="1" type="number" title="@{repeating_army-tactic_$X_level}"/>
<input class="underlined" name="attr_name" type="text" title="@{repeating_army-tactic_$X_name}"/>
<textarea class="expanded underlined span-all" name="attr_description" title="@{repeating_army-tactic_$X_description}"></textarea>
</fieldset>
</div>
<div class="repeating-container">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_tactical actions"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-army-tactical-action" type="action" title="%{add-army-tactical-action}">
</button>
<fieldset class="repeating_army-tactical-action">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_army-tactical-action_$X_collapse}"/>
<input class="underlined" name="attr_name" type="text" title="@{repeating_army-tactical-action_$X_name}"/>
<textarea class="expanded underlined" name="attr_description" title="@{repeating_army-tactical-action_$X_description}"></textarea>
</fieldset>
</div>
</section>
<section class="kingmaker__army--conditions" id="kingmaker__army--conditions">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_conditions"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_concealed_collapse" value="1" checked="" type="checkbox" title="@{army_concealed_collapse}"/>
<div class="input-label">
<input name="attr_army_concealed" value="1" type="checkbox" title="@{army_concealed}"/><span data-i18n="km_concealed"></span>
</div>
<p class="expanded" data-i18n="km_concealed description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_defeated_collapse" value="1" checked="" type="checkbox" title="@{army_defeated_collapse}"/>
<div class="input-label">
<input name="attr_army_defeated" value="1" type="checkbox" title="@{army_defeated}"/><span data-i18n="km_defeated"></span>
</div>
<p class="expanded" data-i18n="km_defeated description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_destroyed_collapse" value="1" checked="" type="checkbox" title="@{army_destroyed_collapse}"/>
<div class="input-label">
<input name="attr_army_destroyed" value="1" type="checkbox" title="@{army_destroyed}"/><span data-i18n="km_destroyed"></span>
</div>
<p class="expanded" data-i18n="km_destroyed description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_efficient_collapse" value="1" checked="" type="checkbox" title="@{army_efficient_collapse}"/>
<div class="input-label">
<input name="attr_army_efficient" value="1" type="checkbox" title="@{army_efficient}"/><span data-i18n="km_efficient"></span>
</div>
<p class="expanded" data-i18n="km_efficient description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_engaged_collapse" value="1" checked="" type="checkbox" title="@{army_engaged_collapse}"/>
<div class="input-label">
<input name="attr_army_engaged" value="1" type="checkbox" title="@{army_engaged}"/><span data-i18n="km_engaged"></span>
</div>
<p class="expanded" data-i18n="km_engaged description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_fortified_collapse" value="1" checked="" type="checkbox" title="@{army_fortified_collapse}"/>
<div class="input-label">
<input name="attr_army_fortified" value="1" type="checkbox" title="@{army_fortified}"/><span data-i18n="km_fortified"></span>
</div>
<p class="expanded" data-i18n="km_fortified description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_lost_collapse" value="1" checked="" type="checkbox" title="@{army_lost_collapse}"/>
<div class="input-label">
<input name="attr_army_lost" value="1" type="checkbox" title="@{army_lost}"/><span data-i18n="km_lost"></span>
</div>
<p class="expanded" data-i18n="km_lost description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_mired_collapse" value="1" checked="" type="checkbox" title="@{army_mired_collapse}"/>
<div class="input-label"><span data-i18n="km_mired"></span>
<input class="boxed" name="attr_army_mired" min="0" type="number" title="@{army_mired}"/>
</div>
<p class="expanded" data-i18n="km_mired description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_distant_collapse" value="1" checked="" type="checkbox" title="@{army_distant_collapse}"/>
<div class="input-label">
<input name="attr_army_distant" value="1" type="checkbox" title="@{army_distant}"/><span data-i18n="km_distant"></span>
</div>
<p class="expanded" data-i18n="km_distant description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_outflanked_collapse" value="1" checked="" type="checkbox" title="@{army_outflanked_collapse}"/>
<div class="input-label">
<input name="attr_army_outflanked" value="1" type="checkbox" title="@{army_outflanked}"/><span data-i18n="km_outflanked"></span>
</div>
<p class="expanded" data-i18n="km_outflanked description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_pinned_collapse" value="1" checked="" type="checkbox" title="@{army_pinned_collapse}"/>
<div class="input-label">
<input name="attr_army_pinned" value="1" type="checkbox" title="@{army_pinned}"/><span data-i18n="km_pinned"></span>
</div>
<p class="expanded" data-i18n="km_pinned description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_routed_collapse" value="1" checked="" type="checkbox" title="@{army_routed_collapse}"/>
<div class="input-label">
<input name="attr_army_routed" value="1" type="checkbox" title="@{army_routed}"/><span data-i18n="km_routed"></span>
</div>
<p class="expanded" data-i18n="km_routed description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_shaken_collapse" value="1" checked="" type="checkbox" title="@{army_shaken_collapse}"/>
<div class="input-label"><span data-i18n="km_shaken"></span>
<div class="fill-left">
<input class="fill-left__radio fill-left__radio--clearer" name="attr_army_shaken" value="0" type="hidden" title="@{army_shaken}"/>
<input class="fill-left__radio" name="attr_army_shaken" value="-1" data-value="-1" type="checkbox" title="@{army_shaken}"/>
<input class="fill-left__radio" name="attr_army_shaken" value="-2" data-value="-2" type="checkbox" title="@{army_shaken}"/>
<input class="fill-left__radio" name="attr_army_shaken" value="-3" data-value="-3" type="checkbox" title="@{army_shaken}"/>
<input class="fill-left__radio" name="attr_army_shaken" value="-4" data-value="-4" type="checkbox" title="@{army_shaken}"/>
</div>
</div>
<p class="expanded" data-i18n="km_shaken description"></p>
</div>
<div class="condition-row collapse-container">
<input class="collapse" name="attr_army_weary_collapse" value="1" checked="" type="checkbox" title="@{army_weary_collapse}"/>
<div class="input-label"><span data-i18n="km_weary"></span>
<input class="boxed" name="attr_army_weary" min="0" type="number" title="@{army_weary}"/>
</div>
<p class="expanded" data-i18n="km_weary description"></p>
</div>
</section>
<section class="kingmaker__army--gear repeating-container" id="kingmaker__army--gear">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_gear"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-army-gear" type="action" title="%{add-army-gear}">
</button>
<fieldset class="repeating_army-gear">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_army-gear_$X_collapse}"/>
<select class="underlined" name="attr_type" title="@{repeating_army-gear_$X_type}">
<option value="armor" data-i18n="km_armor"></option>
<option value="melee" data-i18n="km_melee"></option>
<option value="ranged" data-i18n="km_ranged"></option>
</select>
<input class="underlined" name="attr_name" type="text" title="@{repeating_army-gear_$X_name}"/>
<textarea class="expanded underlined span-all" name="attr_description" title="@{repeating_army-gear_$X_description}"></textarea>
</fieldset>
<div class="combined-army-stat healing-potions"><span data-i18n="km_healing potions"></span>
<label class="input-label input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_max 3"></span>
<input class="input-label__input" name="attr_army_healing_potions" type="number" title="@{army_healing_potions}"/>
</label>
</div>
</section>
<section class="repeating-container" id="kingmaker__army-abilities">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_abilities"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-army-kingmaker-army-ability" type="action" title="%{add-army-kingmaker-army-ability}">
</button>
<fieldset class="repeating_army-kingmaker-army-ability">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_army-kingmaker-army-ability_$X_collapse}"/>
<input class="underlined" name="attr_name" type="text" title="@{repeating_army-kingmaker-army-ability_$X_name}"/>
<textarea class="underlined expanded span-all" name="attr_description" title="@{repeating_army-kingmaker-army-ability_$X_description}"></textarea>
</fieldset>
</section>
</article>
<article class="tabs__container tabs--kingmaker__container tabs--kingmaker__container--settlement settlement" data-container-tab="kingmaker" data-tab="settlement" id="settlement">
<section class="kingmaker__settlements--general_info flex-box flex-wrap gap" id="kingmaker__settlements--general-info">
<div class="input-label stacked flex-min-content">
<label class="input-label align-self-stretch"><span class="input-label__text" data-i18n="km_settlement name" role="heading" aria-level="4"></span>
<input class="underlined input-label__input" name="attr_character_name" type="text" title="@{character_name}"/>
</label>
<div class="flex-box gap">
<label class="input-label row-reverse tiny-gap"><span class="input-label__text" data-i18n="km_village"></span>
<input class="input-label__input" name="attr_settlement_type" type="radio" value="village" title="@{settlement_type}"/>
</label>
<label class="input-label row-reverse tiny-gap"><span class="input-label__text" data-i18n="km_town"></span>
<input class="input-label__input" name="attr_settlement_type" type="radio" value="town" title="@{settlement_type}"/>
</label>
<label class="input-label row-reverse tiny-gap"><span class="input-label__text" data-i18n="km_city"></span>
<input class="input-label__input" name="attr_settlement_type" type="radio" value="city" title="@{settlement_type}"/>
</label>
<label class="input-label row-reverse tiny-gap"><span class="input-label__text" data-i18n="km_metropolis"></span>
<input class="input-label__input" name="attr_settlement_type" type="radio" value="metropolis" title="@{settlement_type}"/>
</label>
</div>
</div>
<label class="input-label column-reverse input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_level"></span>
<input class="underlined input-label__input" name="attr_settlement_level" type="number" title="@{settlement_level}"/>
</label>
<label class="input-label column-reverse input-label--kingmaker-box stacked center"><span class="input-label--kingmaker-box__label input-label__text" data-i18n="km_consumption"></span>
<input class="underlined input-label__input" name="attr_settlement_consumption" type="number" title="@{settlement_consumption}"/>
</label>
<label class="input-label row-reverse"><span class="input-label__text" data-i18n="km_overcrowded"></span>
<input class="input-label__input" name="attr_settlement_overcrowded" type="checkbox" value="0" title="@{settlement_overcrowded}"/>
</label>
<div class="input-label"><span data-i18n="km_infrastructure"></span>
<label class="input-label row-reverse"><span class="input-label__text" data-i18n="km_magical streetlamps"></span>
<input class="input-label__input" name="attr_settlement_magical_streetlamps" type="checkbox" title="@{settlement_magical_streetlamps}"/>
</label>
<label class="input-label row-reverse"><span class="input-label__text" data-i18n="km_paved streets"></span>
<input class="input-label__input" name="attr_settlement_paved_streets" type="checkbox" title="@{settlement_paved_streets}"/>
</label>
<label class="input-label row-reverse"><span class="input-label__text" data-i18n="km_sewer system"></span>
<input class="input-label__input" name="attr_settlement_sewer_system" type="checkbox" title="@{settlement_sewer_system}"/>
</label>
</div>
</section>
<section class="kingmaker__settlements--garrisoned_armies repeating-container" id="kingmaker__settlements--garrisoned_armies">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_buildings"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-settlement-building" type="action" title="%{add-settlement-building}">
</button>
<fieldset class="repeating_settlement-building">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_settlement-building_$X_collapse}"/>
<input class="collapsed" name="attr_name" readonly="" type="text" title="@{repeating_settlement-building_$X_name}"/>
<input class="expanded underlined" name="attr_name" type="text" title="@{repeating_settlement-building_$X_name}"/>
<div class="headed-textarea expanded">
<h3 class="headed-textarea__header" data-i18n="description">
</h3>
<textarea class="underlined headed-textarea__textarea" name="attr_description" title="@{repeating_settlement-building_$X_description}"></textarea>
</div>
</fieldset>
</section>
<section class="kingmaker__settlements--garrisoned_armies repeating-container" id="kingmaker__settlements--garrisoned_armies">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_garrisoned armies"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-settlement-army" type="action" title="%{add-settlement-army}">
</button>
<fieldset class="repeating_settlement-army">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_settlement-army_$X_collapse}"/>
<input class="collapsed" name="attr_name" readonly="" type="text" title="@{repeating_settlement-army_$X_name}"/>
<input class="expanded underlined" name="attr_name" type="text" title="@{repeating_settlement-army_$X_name}"/>
<div class="headed-textarea expanded">
<h3 class="headed-textarea__header" data-i18n="description">
</h3>
<textarea class="underlined headed-textarea__textarea" name="attr_description" title="@{repeating_settlement-army_$X_description}"></textarea>
</div>
</fieldset>
</section>
<section class="kingmaker__settlements--ongoing_events repeating-container" id="kingmaker__settlements--ongoing_events">
<div class="header span-all header--kingmaker">
<h2 class="header--kingmaker__header" data-i18n="km_ongoing events"></h2>
<div class="header--kingmaker__vine header--kingmaker__vine--center"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--left"></div>
<div class="header--kingmaker__vine header--kingmaker__vine--right"></div>
</div>
<button class="repcontrol-button repcontrol-button--add" name="act_add-settlement-event" type="action" title="%{add-settlement-event}">
</button>
<fieldset class="repeating_settlement-event">
<input class="collapse" name="attr_collapse" value="1" type="checkbox" title="@{repeating_settlement-event_$X_collapse}"/>
<input class="collapsed" name="attr_name" readonly="" type="text" title="@{repeating_settlement-event_$X_name}"/>
<input class="expanded underlined" name="attr_name" type="text" title="@{repeating_settlement-event_$X_name}"/>
<div class="headed-textarea expanded">
<h3 class="headed-textarea__header" data-i18n="description">
</h3>
<textarea class="underlined headed-textarea__textarea" name="attr_description" title="@{repeating_settlement-event_$X_description}"></textarea>
</div>
</fieldset>
</section>
</article>
<article class="tabs__container tabs--kingmaker__container tabs--kingmaker__container--settings settings" data-container-tab="kingmaker" data-tab="settings" id="settings">
<!--settings content will go here-->
<section>
<label class="input-label"><span class="input-label__text" data-i18n="km_sheet represents"></span>
<select class="underlined input-label__input" name="attr_kingmaker_type" title="@{kingmaker_type}">
<option value="kingdom" data-i18n="km_kingdom" selected=""></option>
<option value="army" data-i18n="km_army"></option>
<option value="settlement" data-i18n="km_settlement"></option>
</select>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="km_whisper"></span>
<select class="underlined input-label__input" name="attr_kingdom_whisper" title="@{kingdom_whisper}">
<option value=" " data-i18n="km_never whisper" selected=""></option>
<option value="/w gm " data-i18n="km_always whisper"></option>
</select>
</label>
</section>
<section>
<div class="input-label stacked grid-gap"><span class="align-self-start" data-i18n="km_fame type"></span>
<div class="flex half-gap">
<label class="input-label"><span class="input-label__text" data-i18n="km_fame"></span>
<input class="input-label__input" name="attr_kingdom_fame_type" type="radio" value="km_fame" checked="" title="@{kingdom_fame_type}"/>
</label>
<label class="input-label"><span class="input-label__text" data-i18n="km_infamy"></span>
<input class="input-label__input" name="attr_kingdom_fame_type" type="radio" value="km_infamy" title="@{kingdom_fame_type}"/>
</label>
</div>
</div>
</section>
</article>
</div>
</div>
<rolltemplate class="sheet-rolltemplate-kingmaker">
<div class="template kingmaker">
<div class="image-border"></div>
<div class="content">
<div class="header">{{#title}}
<h2 class="title">{{title}}</h2>{{/title}}{{#character_name}}{{#character_id}}
<h4 class="character_name">[{{character_name}}](http://journal.roll20.net/character/{{character_id}})</h4>{{/character_id}}{{^character_id}}
<h4 class="character_name">{{character_name}}</h4>{{/character_id}}{{/character_name}}{{#roll}}<span class="flex center align-content-center result text-content">{{roll}}</span>{{/roll}}
</div>{{#description}}
<div class="description flex flex-column">
<h3 class="align-self-start" data-i18n="km_description"></h3>
<p class="text-content">{{description}}</p>
</div>{{/description}}
</div>
</div>
</rolltemplate>
<script type="text/worker">
const k = (function(){
const kFuncs = {};
const cascades = {"attr_character_name":{"name":"character_name","type":"text","defaultValue":"","affects":[],"triggeredFuncs":["setActionCalls"],"listenerFunc":"accessSheet","listener":"change:character_name"},"attr_kingmaker_tab":{"name":"kingmaker_tab","type":"hidden","defaultValue":"nav-tabs-kingmaker--settings","triggeredFuncs":[],"affects":[]},"act_nav-tabs-kingmaker--kingdom":{"triggeredFuncs":["kSwitchTab"],"name":"nav-tabs-kingmaker--kingdom","listener":"clicked:nav-tabs-kingmaker--kingdom","listenerFunc":"accessSheet","type":"action"},"act_nav-tabs-kingmaker--army":{"triggeredFuncs":["kSwitchTab"],"name":"nav-tabs-kingmaker--army","listener":"clicked:nav-tabs-kingmaker--army","listenerFunc":"accessSheet","type":"action"},"act_nav-tabs-kingmaker--settlement":{"triggeredFuncs":["kSwitchTab"],"name":"nav-tabs-kingmaker--settlement","listener":"clicked:nav-tabs-kingmaker--settlement","listenerFunc":"accessSheet","type":"action"},"act_nav-tabs-kingmaker--settings":{"triggeredFuncs":["kSwitchTab"],"name":"nav-tabs-kingmaker--settings","listener":"clicked:nav-tabs-kingmaker--settings","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_drop_name":{"triggeredFuncs":["kingdomDrop"],"name":"kingdom_drop_name","listener":"change:kingdom_drop_name","listenerFunc":"accessSheet","type":"hidden","defaultValue":"","affects":[]},"attr_kingdom_drop_data":{"name":"kingdom_drop_data","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_template_start":{"name":"kingdom_template_start","type":"hidden","defaultValue":"@{kingdom_whisper}&{template:kingmaker} {{character_name=@{character_name}}} {{character_id=@{character_id}}}","triggeredFuncs":[],"affects":[]},"attr_kingdom_sheet_version":{"name":"kingdom_sheet_version","type":"hidden","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_sheet_state":{"name":"kingdom_sheet_state","type":"hidden","defaultValue":"settings","triggeredFuncs":[],"affects":[]},"attr_kingdom_level":{"affects":["kingdom_agriculture","kingdom_arts","kingdom_boating","kingdom_defense","kingdom_engineering","kingdom_exploration","kingdom_folklore","kingdom_industry","kingdom_intrigue","kingdom_magic","kingdom_politics","kingdom_scholarship","kingdom_statecraft","kingdom_trade","kingdom_warfare","kingdom_wilderness"],"name":"kingdom_level","listener":"change:kingdom_level","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_fame/infamy":{"name":"kingdom_fame/infamy","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_food":{"name":"kingdom_food","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_food_max":{"name":"kingdom_food_max","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_farmlands":{"name":"kingdom_farmlands","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_lumber":{"name":"kingdom_lumber","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_lumber_max":{"name":"kingdom_lumber_max","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_lumber_camps":{"name":"kingdom_lumber_camps","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_luxuries":{"name":"kingdom_luxuries","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_luxuries_max":{"name":"kingdom_luxuries_max","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_ore":{"name":"kingdom_ore","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_ore_max":{"name":"kingdom_ore_max","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_mines":{"name":"kingdom_mines","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_stone":{"name":"kingdom_stone","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_stone_max":{"name":"kingdom_stone_max","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_quarries":{"name":"kingdom_quarries","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_trade-agreements":{"name":"kingdom_trade-agreements","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_event-check-dc":{"name":"kingdom_event-check-dc","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_kingdom_control_dc":{"name":"kingdom_control_dc","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_charter":{"name":"kingdom_charter","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_heartland":{"name":"kingdom_heartland","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_government":{"name":"kingdom_government","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_capital_city":{"name":"kingdom_capital_city","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_experience_points":{"name":"kingdom_experience_points","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_resource_dice":{"name":"kingdom_resource_dice","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_size":{"name":"kingdom_size","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_rp":{"name":"kingdom_rp","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_unrest":{"name":"kingdom_unrest","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_unrest_penalty":{"calculation":"calcUnrestPenalty","name":"kingdom_unrest_penalty","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_consumption":{"name":"kingdom_consumption","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_ability_score_collapse":{"name":"kingdom_ability_score_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_culture":{"calculation":"calcKingdomAbilityMod","affects":["kingdom_arts","kingdom_folklore","kingdom_magic","kingdom_scholarship"],"name":"kingdom_culture","listener":"change:kingdom_culture","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"act_kingdom-culture-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-culture-action","listener":"clicked:kingdom-culture-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_culture_action":{"name":"kingdom_culture_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_culture_score":{"affects":["kingdom_culture"],"name":"kingdom_culture_score","listener":"change:kingdom_culture_score","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_corruption":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_corruption_penalty"],"name":"kingdom_corruption","listener":"change:kingdom_corruption","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_corruption_penalty":{"affects":["kingdom_culture_mod"],"name":"kingdom_corruption_penalty","listener":"change:kingdom_corruption_penalty","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_corruption_threshold":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_corruption_penalty"],"name":"kingdom_corruption_threshold","listener":"change:kingdom_corruption_threshold","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_economy":{"calculation":"calcKingdomAbilityMod","affects":["kingdom_boating","kingdom_exploration","kingdom_industry","kingdom_trade"],"name":"kingdom_economy","listener":"change:kingdom_economy","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"act_kingdom-economy-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-economy-action","listener":"clicked:kingdom-economy-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_economy_action":{"name":"kingdom_economy_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_economy_score":{"affects":["kingdom_economy"],"name":"kingdom_economy_score","listener":"change:kingdom_economy_score","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_crime":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_crime_penalty"],"name":"kingdom_crime","listener":"change:kingdom_crime","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_crime_penalty":{"affects":["kingdom_economy_mod"],"name":"kingdom_crime_penalty","listener":"change:kingdom_crime_penalty","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_crime_threshold":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_crime_penalty"],"name":"kingdom_crime_threshold","listener":"change:kingdom_crime_threshold","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_loyalty":{"calculation":"calcKingdomAbilityMod","affects":["kingdom_intrigue","kingdom_politics","kingdom_statecraft","kingdom_warfare"],"name":"kingdom_loyalty","listener":"change:kingdom_loyalty","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"act_kingdom-loyalty-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-loyalty-action","listener":"clicked:kingdom-loyalty-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_loyalty_action":{"name":"kingdom_loyalty_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_loyalty_score":{"affects":["kingdom_loyalty"],"name":"kingdom_loyalty_score","listener":"change:kingdom_loyalty_score","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_decay":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_decay_penalty"],"name":"kingdom_decay","listener":"change:kingdom_decay","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_decay_penalty":{"affects":["kingdom_loyalty_mod"],"name":"kingdom_decay_penalty","listener":"change:kingdom_decay_penalty","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_decay_threshold":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_decay_penalty"],"name":"kingdom_decay_threshold","listener":"change:kingdom_decay_threshold","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_stability":{"calculation":"calcKingdomAbilityMod","affects":["kingdom_agriculture","kingdom_defense","kingdom_engineering","kingdom_wilderness"],"name":"kingdom_stability","listener":"change:kingdom_stability","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"act_kingdom-stability-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-stability-action","listener":"clicked:kingdom-stability-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_stability_action":{"name":"kingdom_stability_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_stability_score":{"affects":["kingdom_stability"],"name":"kingdom_stability_score","listener":"change:kingdom_stability_score","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_strife":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_strife_penalty"],"name":"kingdom_strife","listener":"change:kingdom_strife","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_strife_penalty":{"affects":["kingdom_stability_mod"],"name":"kingdom_strife_penalty","listener":"change:kingdom_strife_penalty","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_strife_threshold":{"initialFunc":"calcKingdomRuinPenalty","affects":["kingdom_strife_penalty"],"name":"kingdom_strife_threshold","listener":"change:kingdom_strife_threshold","listenerFunc":"accessSheet","type":"number","defaultValue":10,"triggeredFuncs":[]},"attr_kingdom_agriculture_collapse":{"name":"kingdom_agriculture_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-agriculture-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-agriculture-action","listener":"clicked:kingdom-agriculture-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_agriculture_action":{"name":"kingdom_agriculture_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_agriculture_proficiency_translation":{"name":"kingdom_agriculture_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_agriculture":{"calculation":"calcSkillTotal","name":"kingdom_agriculture","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_agriculture_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_agriculture"],"name":"kingdom_agriculture_proficiency_total","listener":"change:kingdom_agriculture_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_agriculture_proficiency":{"affects":["kingdom_agriculture_proficiency_total","kingdom_agriculture_proficiency_translation"],"name":"kingdom_agriculture_proficiency","listener":"change:kingdom_agriculture_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_agriculture_status":{"affects":["kingdom_agriculture"],"name":"kingdom_agriculture_status","listener":"change:kingdom_agriculture_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_agriculture_circumstance":{"affects":["kingdom_agriculture"],"name":"kingdom_agriculture_circumstance","listener":"change:kingdom_agriculture_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_agriculture_item":{"affects":["kingdom_agriculture"],"name":"kingdom_agriculture_item","listener":"change:kingdom_agriculture_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_agriculture_other":{"affects":["kingdom_agriculture"],"name":"kingdom_agriculture_other","listener":"change:kingdom_agriculture_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_arts_collapse":{"name":"kingdom_arts_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-arts-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-arts-action","listener":"clicked:kingdom-arts-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_arts_action":{"name":"kingdom_arts_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_arts_proficiency_translation":{"name":"kingdom_arts_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_arts":{"calculation":"calcSkillTotal","name":"kingdom_arts","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_arts_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_arts"],"name":"kingdom_arts_proficiency_total","listener":"change:kingdom_arts_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_arts_proficiency":{"affects":["kingdom_arts_proficiency_total","kingdom_arts_proficiency_translation"],"name":"kingdom_arts_proficiency","listener":"change:kingdom_arts_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_arts_status":{"affects":["kingdom_arts"],"name":"kingdom_arts_status","listener":"change:kingdom_arts_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_arts_circumstance":{"affects":["kingdom_arts"],"name":"kingdom_arts_circumstance","listener":"change:kingdom_arts_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_arts_item":{"affects":["kingdom_arts"],"name":"kingdom_arts_item","listener":"change:kingdom_arts_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_arts_other":{"affects":["kingdom_arts"],"name":"kingdom_arts_other","listener":"change:kingdom_arts_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_boating_collapse":{"name":"kingdom_boating_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-boating-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-boating-action","listener":"clicked:kingdom-boating-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_boating_action":{"name":"kingdom_boating_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_boating_proficiency_translation":{"name":"kingdom_boating_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_boating":{"calculation":"calcSkillTotal","name":"kingdom_boating","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_boating_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_boating"],"name":"kingdom_boating_proficiency_total","listener":"change:kingdom_boating_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_boating_proficiency":{"affects":["kingdom_boating_proficiency_total","kingdom_boating_proficiency_translation"],"name":"kingdom_boating_proficiency","listener":"change:kingdom_boating_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_boating_status":{"affects":["kingdom_boating"],"name":"kingdom_boating_status","listener":"change:kingdom_boating_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_boating_circumstance":{"affects":["kingdom_boating"],"name":"kingdom_boating_circumstance","listener":"change:kingdom_boating_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_boating_item":{"affects":["kingdom_boating"],"name":"kingdom_boating_item","listener":"change:kingdom_boating_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_boating_other":{"affects":["kingdom_boating"],"name":"kingdom_boating_other","listener":"change:kingdom_boating_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_defense_collapse":{"name":"kingdom_defense_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-defense-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-defense-action","listener":"clicked:kingdom-defense-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_defense_action":{"name":"kingdom_defense_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_defense_proficiency_translation":{"name":"kingdom_defense_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_defense":{"calculation":"calcSkillTotal","name":"kingdom_defense","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_defense_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_defense"],"name":"kingdom_defense_proficiency_total","listener":"change:kingdom_defense_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_defense_proficiency":{"affects":["kingdom_defense_proficiency_total","kingdom_defense_proficiency_translation"],"name":"kingdom_defense_proficiency","listener":"change:kingdom_defense_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_defense_status":{"affects":["kingdom_defense"],"name":"kingdom_defense_status","listener":"change:kingdom_defense_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_defense_circumstance":{"affects":["kingdom_defense"],"name":"kingdom_defense_circumstance","listener":"change:kingdom_defense_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_defense_item":{"affects":["kingdom_defense"],"name":"kingdom_defense_item","listener":"change:kingdom_defense_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_defense_other":{"affects":["kingdom_defense"],"name":"kingdom_defense_other","listener":"change:kingdom_defense_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_engineering_collapse":{"name":"kingdom_engineering_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-engineering-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-engineering-action","listener":"clicked:kingdom-engineering-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_engineering_action":{"name":"kingdom_engineering_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_engineering_proficiency_translation":{"name":"kingdom_engineering_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_engineering":{"calculation":"calcSkillTotal","name":"kingdom_engineering","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_engineering_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_engineering"],"name":"kingdom_engineering_proficiency_total","listener":"change:kingdom_engineering_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_engineering_proficiency":{"affects":["kingdom_engineering_proficiency_total","kingdom_engineering_proficiency_translation"],"name":"kingdom_engineering_proficiency","listener":"change:kingdom_engineering_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_engineering_status":{"affects":["kingdom_engineering"],"name":"kingdom_engineering_status","listener":"change:kingdom_engineering_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_engineering_circumstance":{"affects":["kingdom_engineering"],"name":"kingdom_engineering_circumstance","listener":"change:kingdom_engineering_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_engineering_item":{"affects":["kingdom_engineering"],"name":"kingdom_engineering_item","listener":"change:kingdom_engineering_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_engineering_other":{"affects":["kingdom_engineering"],"name":"kingdom_engineering_other","listener":"change:kingdom_engineering_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_exploration_collapse":{"name":"kingdom_exploration_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-exploration-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-exploration-action","listener":"clicked:kingdom-exploration-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_exploration_action":{"name":"kingdom_exploration_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_exploration_proficiency_translation":{"name":"kingdom_exploration_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_exploration":{"calculation":"calcSkillTotal","name":"kingdom_exploration","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_exploration_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_exploration"],"name":"kingdom_exploration_proficiency_total","listener":"change:kingdom_exploration_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_exploration_proficiency":{"affects":["kingdom_exploration_proficiency_total","kingdom_exploration_proficiency_translation"],"name":"kingdom_exploration_proficiency","listener":"change:kingdom_exploration_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_exploration_status":{"affects":["kingdom_exploration"],"name":"kingdom_exploration_status","listener":"change:kingdom_exploration_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_exploration_circumstance":{"affects":["kingdom_exploration"],"name":"kingdom_exploration_circumstance","listener":"change:kingdom_exploration_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_exploration_item":{"affects":["kingdom_exploration"],"name":"kingdom_exploration_item","listener":"change:kingdom_exploration_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_exploration_other":{"affects":["kingdom_exploration"],"name":"kingdom_exploration_other","listener":"change:kingdom_exploration_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_folklore_collapse":{"name":"kingdom_folklore_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-folklore-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-folklore-action","listener":"clicked:kingdom-folklore-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_folklore_action":{"name":"kingdom_folklore_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_folklore_proficiency_translation":{"name":"kingdom_folklore_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_folklore":{"calculation":"calcSkillTotal","name":"kingdom_folklore","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_folklore_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_folklore"],"name":"kingdom_folklore_proficiency_total","listener":"change:kingdom_folklore_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_folklore_proficiency":{"affects":["kingdom_folklore_proficiency_total","kingdom_folklore_proficiency_translation"],"name":"kingdom_folklore_proficiency","listener":"change:kingdom_folklore_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_folklore_status":{"affects":["kingdom_folklore"],"name":"kingdom_folklore_status","listener":"change:kingdom_folklore_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_folklore_circumstance":{"affects":["kingdom_folklore"],"name":"kingdom_folklore_circumstance","listener":"change:kingdom_folklore_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_folklore_item":{"affects":["kingdom_folklore"],"name":"kingdom_folklore_item","listener":"change:kingdom_folklore_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_folklore_other":{"affects":["kingdom_folklore"],"name":"kingdom_folklore_other","listener":"change:kingdom_folklore_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_industry_collapse":{"name":"kingdom_industry_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-industry-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-industry-action","listener":"clicked:kingdom-industry-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_industry_action":{"name":"kingdom_industry_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_industry_proficiency_translation":{"name":"kingdom_industry_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_industry":{"calculation":"calcSkillTotal","name":"kingdom_industry","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_industry_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_industry"],"name":"kingdom_industry_proficiency_total","listener":"change:kingdom_industry_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_industry_proficiency":{"affects":["kingdom_industry_proficiency_total","kingdom_industry_proficiency_translation"],"name":"kingdom_industry_proficiency","listener":"change:kingdom_industry_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_industry_status":{"affects":["kingdom_industry"],"name":"kingdom_industry_status","listener":"change:kingdom_industry_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_industry_circumstance":{"affects":["kingdom_industry"],"name":"kingdom_industry_circumstance","listener":"change:kingdom_industry_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_industry_item":{"affects":["kingdom_industry"],"name":"kingdom_industry_item","listener":"change:kingdom_industry_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_industry_other":{"affects":["kingdom_industry"],"name":"kingdom_industry_other","listener":"change:kingdom_industry_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_intrigue_collapse":{"name":"kingdom_intrigue_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-intrigue-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-intrigue-action","listener":"clicked:kingdom-intrigue-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_intrigue_action":{"name":"kingdom_intrigue_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_intrigue_proficiency_translation":{"name":"kingdom_intrigue_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_intrigue":{"calculation":"calcSkillTotal","name":"kingdom_intrigue","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_intrigue_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_intrigue"],"name":"kingdom_intrigue_proficiency_total","listener":"change:kingdom_intrigue_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_intrigue_proficiency":{"affects":["kingdom_intrigue_proficiency_total","kingdom_intrigue_proficiency_translation"],"name":"kingdom_intrigue_proficiency","listener":"change:kingdom_intrigue_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_intrigue_status":{"affects":["kingdom_intrigue"],"name":"kingdom_intrigue_status","listener":"change:kingdom_intrigue_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_intrigue_circumstance":{"affects":["kingdom_intrigue"],"name":"kingdom_intrigue_circumstance","listener":"change:kingdom_intrigue_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_intrigue_item":{"affects":["kingdom_intrigue"],"name":"kingdom_intrigue_item","listener":"change:kingdom_intrigue_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_intrigue_other":{"affects":["kingdom_intrigue"],"name":"kingdom_intrigue_other","listener":"change:kingdom_intrigue_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_magic_collapse":{"name":"kingdom_magic_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-magic-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-magic-action","listener":"clicked:kingdom-magic-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_magic_action":{"name":"kingdom_magic_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_magic_proficiency_translation":{"name":"kingdom_magic_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_magic":{"calculation":"calcSkillTotal","name":"kingdom_magic","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_magic_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_magic"],"name":"kingdom_magic_proficiency_total","listener":"change:kingdom_magic_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_magic_proficiency":{"affects":["kingdom_magic_proficiency_total","kingdom_magic_proficiency_translation"],"name":"kingdom_magic_proficiency","listener":"change:kingdom_magic_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_magic_status":{"affects":["kingdom_magic"],"name":"kingdom_magic_status","listener":"change:kingdom_magic_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_magic_circumstance":{"affects":["kingdom_magic"],"name":"kingdom_magic_circumstance","listener":"change:kingdom_magic_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_magic_item":{"affects":["kingdom_magic"],"name":"kingdom_magic_item","listener":"change:kingdom_magic_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_magic_other":{"affects":["kingdom_magic"],"name":"kingdom_magic_other","listener":"change:kingdom_magic_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_politics_collapse":{"name":"kingdom_politics_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-politics-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-politics-action","listener":"clicked:kingdom-politics-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_politics_action":{"name":"kingdom_politics_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_politics_proficiency_translation":{"name":"kingdom_politics_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_politics":{"calculation":"calcSkillTotal","name":"kingdom_politics","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_politics_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_politics"],"name":"kingdom_politics_proficiency_total","listener":"change:kingdom_politics_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_politics_proficiency":{"affects":["kingdom_politics_proficiency_total","kingdom_politics_proficiency_translation"],"name":"kingdom_politics_proficiency","listener":"change:kingdom_politics_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_politics_status":{"affects":["kingdom_politics"],"name":"kingdom_politics_status","listener":"change:kingdom_politics_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_politics_circumstance":{"affects":["kingdom_politics"],"name":"kingdom_politics_circumstance","listener":"change:kingdom_politics_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_politics_item":{"affects":["kingdom_politics"],"name":"kingdom_politics_item","listener":"change:kingdom_politics_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_politics_other":{"affects":["kingdom_politics"],"name":"kingdom_politics_other","listener":"change:kingdom_politics_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_scholarship_collapse":{"name":"kingdom_scholarship_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-scholarship-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-scholarship-action","listener":"clicked:kingdom-scholarship-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_scholarship_action":{"name":"kingdom_scholarship_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_scholarship_proficiency_translation":{"name":"kingdom_scholarship_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_scholarship":{"calculation":"calcSkillTotal","name":"kingdom_scholarship","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_scholarship_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_scholarship"],"name":"kingdom_scholarship_proficiency_total","listener":"change:kingdom_scholarship_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_scholarship_proficiency":{"affects":["kingdom_scholarship_proficiency_total","kingdom_scholarship_proficiency_translation"],"name":"kingdom_scholarship_proficiency","listener":"change:kingdom_scholarship_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_scholarship_status":{"affects":["kingdom_scholarship"],"name":"kingdom_scholarship_status","listener":"change:kingdom_scholarship_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_scholarship_circumstance":{"affects":["kingdom_scholarship"],"name":"kingdom_scholarship_circumstance","listener":"change:kingdom_scholarship_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_scholarship_item":{"affects":["kingdom_scholarship"],"name":"kingdom_scholarship_item","listener":"change:kingdom_scholarship_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_scholarship_other":{"affects":["kingdom_scholarship"],"name":"kingdom_scholarship_other","listener":"change:kingdom_scholarship_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_statecraft_collapse":{"name":"kingdom_statecraft_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-statecraft-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-statecraft-action","listener":"clicked:kingdom-statecraft-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_statecraft_action":{"name":"kingdom_statecraft_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_statecraft_proficiency_translation":{"name":"kingdom_statecraft_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_statecraft":{"calculation":"calcSkillTotal","name":"kingdom_statecraft","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_statecraft_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_statecraft"],"name":"kingdom_statecraft_proficiency_total","listener":"change:kingdom_statecraft_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_statecraft_proficiency":{"affects":["kingdom_statecraft_proficiency_total","kingdom_statecraft_proficiency_translation"],"name":"kingdom_statecraft_proficiency","listener":"change:kingdom_statecraft_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_statecraft_status":{"affects":["kingdom_statecraft"],"name":"kingdom_statecraft_status","listener":"change:kingdom_statecraft_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_statecraft_circumstance":{"affects":["kingdom_statecraft"],"name":"kingdom_statecraft_circumstance","listener":"change:kingdom_statecraft_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_statecraft_item":{"affects":["kingdom_statecraft"],"name":"kingdom_statecraft_item","listener":"change:kingdom_statecraft_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_statecraft_other":{"affects":["kingdom_statecraft"],"name":"kingdom_statecraft_other","listener":"change:kingdom_statecraft_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_trade_collapse":{"name":"kingdom_trade_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-trade-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-trade-action","listener":"clicked:kingdom-trade-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_trade_action":{"name":"kingdom_trade_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_trade_proficiency_translation":{"name":"kingdom_trade_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_trade":{"calculation":"calcSkillTotal","name":"kingdom_trade","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_trade_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_trade"],"name":"kingdom_trade_proficiency_total","listener":"change:kingdom_trade_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_trade_proficiency":{"affects":["kingdom_trade_proficiency_total","kingdom_trade_proficiency_translation"],"name":"kingdom_trade_proficiency","listener":"change:kingdom_trade_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_trade_status":{"affects":["kingdom_trade"],"name":"kingdom_trade_status","listener":"change:kingdom_trade_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_trade_circumstance":{"affects":["kingdom_trade"],"name":"kingdom_trade_circumstance","listener":"change:kingdom_trade_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_trade_item":{"affects":["kingdom_trade"],"name":"kingdom_trade_item","listener":"change:kingdom_trade_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_trade_other":{"affects":["kingdom_trade"],"name":"kingdom_trade_other","listener":"change:kingdom_trade_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_warfare_collapse":{"name":"kingdom_warfare_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-warfare-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-warfare-action","listener":"clicked:kingdom-warfare-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_warfare_action":{"name":"kingdom_warfare_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_warfare_proficiency_translation":{"name":"kingdom_warfare_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_warfare":{"calculation":"calcSkillTotal","name":"kingdom_warfare","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_warfare_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_warfare"],"name":"kingdom_warfare_proficiency_total","listener":"change:kingdom_warfare_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_warfare_proficiency":{"affects":["kingdom_warfare_proficiency_total","kingdom_warfare_proficiency_translation"],"name":"kingdom_warfare_proficiency","listener":"change:kingdom_warfare_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_warfare_status":{"affects":["kingdom_warfare"],"name":"kingdom_warfare_status","listener":"change:kingdom_warfare_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_warfare_circumstance":{"affects":["kingdom_warfare"],"name":"kingdom_warfare_circumstance","listener":"change:kingdom_warfare_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_warfare_item":{"affects":["kingdom_warfare"],"name":"kingdom_warfare_item","listener":"change:kingdom_warfare_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_warfare_other":{"affects":["kingdom_warfare"],"name":"kingdom_warfare_other","listener":"change:kingdom_warfare_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_wilderness_collapse":{"name":"kingdom_wilderness_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"act_kingdom-wilderness-action":{"triggeredFuncs":["rollBasic"],"name":"kingdom-wilderness-action","listener":"clicked:kingdom-wilderness-action","listenerFunc":"accessSheet","type":"action"},"attr_kingdom_wilderness_action":{"name":"kingdom_wilderness_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_wilderness_proficiency_translation":{"name":"kingdom_wilderness_proficiency_translation","type":"hidden","defaultValue":"km_untrained","triggeredFuncs":[],"affects":[],"calculation":"translateProficiency"},"attr_kingdom_wilderness":{"calculation":"calcSkillTotal","name":"kingdom_wilderness","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_wilderness_proficiency_total":{"calculation":"calcSkillProficiency","affects":["kingdom_wilderness"],"name":"kingdom_wilderness_proficiency_total","listener":"change:kingdom_wilderness_proficiency_total","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_wilderness_proficiency":{"affects":["kingdom_wilderness_proficiency_total","kingdom_wilderness_proficiency_translation"],"name":"kingdom_wilderness_proficiency","listener":"change:kingdom_wilderness_proficiency","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_wilderness_status":{"affects":["kingdom_wilderness"],"name":"kingdom_wilderness_status","listener":"change:kingdom_wilderness_status","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_wilderness_circumstance":{"affects":["kingdom_wilderness"],"name":"kingdom_wilderness_circumstance","listener":"change:kingdom_wilderness_circumstance","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_wilderness_item":{"affects":["kingdom_wilderness"],"name":"kingdom_wilderness_item","listener":"change:kingdom_wilderness_item","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_wilderness_other":{"affects":["kingdom_wilderness"],"name":"kingdom_wilderness_other","listener":"change:kingdom_wilderness_other","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_kingdom_invested":{"name":"kingdom_invested","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_leaders_collapse":{"name":"kingdom_leaders_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_ruler_invested":{"name":"kingdom_ruler_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_ruler_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_ruler_name","listener":"change:kingdom_ruler_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_kingdom_counselor_invested":{"name":"kingdom_counselor_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_counselor_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_counselor_name","listener":"change:kingdom_counselor_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_kingdom_general_invested":{"name":"kingdom_general_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_general_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_general_name","listener":"change:kingdom_general_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_kingdom_emissary_invested":{"name":"kingdom_emissary_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_emissary_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_emissary_name","listener":"change:kingdom_emissary_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_kingdom_magister_invested":{"name":"kingdom_magister_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_magister_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_magister_name","listener":"change:kingdom_magister_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_kingdom_treasurer_invested":{"name":"kingdom_treasurer_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_treasurer_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_treasurer_name","listener":"change:kingdom_treasurer_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_kingdom_viceroy_invested":{"name":"kingdom_viceroy_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_viceroy_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_viceroy_name","listener":"change:kingdom_viceroy_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"attr_kingdom_warden_invested":{"name":"kingdom_warden_invested","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_kingdom_warden_name":{"triggeredFuncs":["setLeaderCollapsible"],"name":"kingdom_warden_name","listener":"change:kingdom_warden_name","listenerFunc":"accessSheet","type":"text","defaultValue":"","affects":[]},"act_add-kingdom-feats":{"listenerFunc":"addItem","name":"add-kingdom-feats","listener":"clicked:add-kingdom-feats","type":"action"},"attr_repeating_kingdom-feats_$X_collapse":{"name":"repeating_kingdom-feats_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_kingdom-feats_$X_level":{"name":"repeating_kingdom-feats_$X_level","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_repeating_kingdom-feats_$X_name":{"name":"repeating_kingdom-feats_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_kingdom-feats_$X_description":{"name":"repeating_kingdom-feats_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-kingdom-abilities":{"listenerFunc":"addItem","name":"add-kingdom-abilities","listener":"clicked:add-kingdom-abilities","type":"action"},"attr_repeating_kingdom-abilities_$X_collapse":{"name":"repeating_kingdom-abilities_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_kingdom-abilities_$X_name":{"name":"repeating_kingdom-abilities_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_kingdom-abilities_$X_description":{"name":"repeating_kingdom-abilities_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-kingdom-events":{"listenerFunc":"addItem","name":"add-kingdom-events","listener":"clicked:add-kingdom-events","type":"action"},"attr_repeating_kingdom-events_$X_collapse":{"name":"repeating_kingdom-events_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_kingdom-events_$X_name":{"name":"repeating_kingdom-events_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_kingdom-events_$X_description":{"name":"repeating_kingdom-events_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_level":{"name":"army_level","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_alignment":{"name":"army_alignment","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_type":{"name":"army_type","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_recruitment_dc":{"name":"army_recruitment_dc","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_ac":{"name":"army_ac","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_scouting":{"affects":["army_scouting_dc"],"name":"army_scouting","listener":"change:army_scouting","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"act_army-scouting-action":{"triggeredFuncs":["rollBasic"],"name":"army-scouting-action","listener":"clicked:army-scouting-action","listenerFunc":"accessSheet","type":"action"},"attr_army_scouting_action":{"name":"army_scouting_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_scouting_dc":{"calculation":"calcBasicArmyDC","name":"army_scouting_dc","type":"number","defaultValue":10,"triggeredFuncs":[],"affects":[]},"attr_army_maneuver":{"affects":["army_maneuver_dc"],"name":"army_maneuver","listener":"change:army_maneuver","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"act_army-maneuver-action":{"triggeredFuncs":["rollBasic"],"name":"army-maneuver-action","listener":"clicked:army-maneuver-action","listenerFunc":"accessSheet","type":"action"},"attr_army_maneuver_action":{"name":"army_maneuver_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_maneuver_dc":{"calculation":"calcBasicArmyDC","name":"army_maneuver_dc","type":"number","defaultValue":10,"triggeredFuncs":[],"affects":[]},"attr_army_morale":{"affects":["army_morale_dc"],"name":"army_morale","listener":"change:army_morale","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"act_army-morale-action":{"triggeredFuncs":["rollBasic"],"name":"army-morale-action","listener":"clicked:army-morale-action","listenerFunc":"accessSheet","type":"action"},"attr_army_morale_action":{"name":"army_morale_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_morale_dc":{"calculation":"calcBasicArmyDC","name":"army_morale_dc","type":"number","defaultValue":10,"triggeredFuncs":[],"affects":[]},"attr_army_hp":{"affects":["army_defeated"],"name":"army_hp","listener":"change:army_hp","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_army_hp_max":{"name":"army_hp_max","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_rout_threshold":{"name":"army_rout_threshold","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_melee":{"name":"army_melee","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_ranged":{"name":"army_ranged","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_army-ranged-action":{"triggeredFuncs":["rollBasic"],"name":"army-ranged-action","listener":"clicked:army-ranged-action","listenerFunc":"accessSheet","type":"action"},"attr_army_ranged_action":{"name":"army_ranged_action","type":"hidden","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_shots":{"name":"army_shots","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_add-army-tactic":{"listenerFunc":"addItem","name":"add-army-tactic","listener":"clicked:add-army-tactic","type":"action"},"attr_repeating_army-tactic_$X_collapse":{"name":"repeating_army-tactic_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_army-tactic_$X_level":{"name":"repeating_army-tactic_$X_level","type":"number","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_repeating_army-tactic_$X_name":{"name":"repeating_army-tactic_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_army-tactic_$X_description":{"name":"repeating_army-tactic_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-army-tactical-action":{"listenerFunc":"addItem","name":"add-army-tactical-action","listener":"clicked:add-army-tactical-action","type":"action"},"attr_repeating_army-tactical-action_$X_collapse":{"name":"repeating_army-tactical-action_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_army-tactical-action_$X_name":{"name":"repeating_army-tactical-action_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_army-tactical-action_$X_description":{"name":"repeating_army-tactical-action_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_concealed_collapse":{"name":"army_concealed_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_concealed":{"name":"army_concealed","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_defeated_collapse":{"name":"army_defeated_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_defeated":{"calculation":"triggerDefeated","name":"army_defeated","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_destroyed_collapse":{"name":"army_destroyed_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_destroyed":{"name":"army_destroyed","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_efficient_collapse":{"name":"army_efficient_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_efficient":{"name":"army_efficient","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_engaged_collapse":{"name":"army_engaged_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_engaged":{"name":"army_engaged","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_fortified_collapse":{"name":"army_fortified_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_fortified":{"name":"army_fortified","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_lost_collapse":{"name":"army_lost_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_lost":{"name":"army_lost","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_mired_collapse":{"name":"army_mired_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_mired":{"affects":["army_pinned"],"name":"army_mired","listener":"change:army_mired","listenerFunc":"accessSheet","type":"number","defaultValue":0,"triggeredFuncs":[]},"attr_army_distant_collapse":{"name":"army_distant_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_distant":{"name":"army_distant","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_outflanked_collapse":{"name":"army_outflanked_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_outflanked":{"name":"army_outflanked","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_pinned_collapse":{"name":"army_pinned_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_pinned":{"calculation":"calcPinned","name":"army_pinned","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_routed_collapse":{"name":"army_routed_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_routed":{"calculation":"triggerRouted","name":"army_routed","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_army_shaken_collapse":{"name":"army_shaken_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_shaken":{"affects":["army_routed"],"name":"army_shaken","listener":"change:army_shaken","listenerFunc":"accessSheet","type":"hidden","defaultValue":0,"triggeredFuncs":[]},"attr_army_weary_collapse":{"name":"army_weary_collapse","type":"checkbox","defaultValue":1,"triggeredFuncs":[],"affects":[]},"attr_army_weary":{"name":"army_weary","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_add-army-gear":{"listenerFunc":"addItem","name":"add-army-gear","listener":"clicked:add-army-gear","type":"action"},"attr_repeating_army-gear_$X_collapse":{"name":"repeating_army-gear_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_army-gear_$X_type":{"name":"repeating_army-gear_$X_type","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_army-gear_$X_name":{"name":"repeating_army-gear_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_army-gear_$X_description":{"name":"repeating_army-gear_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_army_healing_potions":{"name":"army_healing_potions","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_add-army-kingmaker-army-ability":{"listenerFunc":"addItem","name":"add-army-kingmaker-army-ability","listener":"clicked:add-army-kingmaker-army-ability","type":"action"},"attr_repeating_army-kingmaker-army-ability_$X_collapse":{"name":"repeating_army-kingmaker-army-ability_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_army-kingmaker-army-ability_$X_name":{"name":"repeating_army-kingmaker-army-ability_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_army-kingmaker-army-ability_$X_description":{"name":"repeating_army-kingmaker-army-ability_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_settlement_type":{"name":"settlement_type","type":"radio","defaultValue":"village","triggeredFuncs":[],"affects":[]},"attr_settlement_level":{"name":"settlement_level","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_settlement_consumption":{"name":"settlement_consumption","type":"number","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_settlement_overcrowded":{"name":"settlement_overcrowded","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_settlement_magical_streetlamps":{"name":"settlement_magical_streetlamps","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_settlement_paved_streets":{"name":"settlement_paved_streets","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_settlement_sewer_system":{"name":"settlement_sewer_system","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"act_add-settlement-building":{"listenerFunc":"addItem","name":"add-settlement-building","listener":"clicked:add-settlement-building","type":"action"},"attr_repeating_settlement-building_$X_collapse":{"name":"repeating_settlement-building_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_settlement-building_$X_name":{"name":"repeating_settlement-building_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_settlement-building_$X_description":{"name":"repeating_settlement-building_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-settlement-army":{"listenerFunc":"addItem","name":"add-settlement-army","listener":"clicked:add-settlement-army","type":"action"},"attr_repeating_settlement-army_$X_collapse":{"name":"repeating_settlement-army_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_settlement-army_$X_name":{"name":"repeating_settlement-army_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_settlement-army_$X_description":{"name":"repeating_settlement-army_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"act_add-settlement-event":{"listenerFunc":"addItem","name":"add-settlement-event","listener":"clicked:add-settlement-event","type":"action"},"attr_repeating_settlement-event_$X_collapse":{"name":"repeating_settlement-event_$X_collapse","type":"checkbox","defaultValue":0,"triggeredFuncs":[],"affects":[]},"attr_repeating_settlement-event_$X_name":{"name":"repeating_settlement-event_$X_name","type":"text","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_repeating_settlement-event_$X_description":{"name":"repeating_settlement-event_$X_description","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingmaker_type":{"triggeredFuncs":["kingdomTypeDisplay"],"name":"kingmaker_type","listener":"change:kingmaker_type","listenerFunc":"accessSheet","type":"select","defaultValue":"","affects":[]},"attr_kingdom_whisper":{"name":"kingdom_whisper","type":"select","defaultValue":"","triggeredFuncs":[],"affects":[]},"attr_kingdom_fame_type":{"name":"kingdom_fame_type","type":"radio","defaultValue":"km_fame","triggeredFuncs":[],"affects":[]}};
kFuncs.cascades = cascades;
const repeatingSectionDetails = [{"section":"repeating_kingdom-feats","fields":["collapse","level","name","description"]},{"section":"repeating_kingdom-abilities","fields":["collapse","name","description"]},{"section":"repeating_kingdom-events","fields":["collapse","name","description"]},{"section":"repeating_army-tactic","fields":["collapse","level","name","description"]},{"section":"repeating_army-tactical-action","fields":["collapse","name","description"]},{"section":"repeating_army-gear","fields":["collapse","type","name","description"]},{"section":"repeating_army-kingmaker-army-ability","fields":["collapse","name","description"]},{"section":"repeating_settlement-building","fields":["collapse","name","description"]},{"section":"repeating_settlement-army","fields":["collapse","name","description"]},{"section":"repeating_settlement-event","fields":["collapse","name","description"]}];
kFuncs.repeatingSectionDetails = repeatingSectionDetails;
/**
* This stores the name of your sheet for use in the logging functions {@link log} and {@link debug}. Accessible by `k.sheetName`
* @var
* @type {string}
*/
let sheetName = 'kScaffold Powered Sheet';
kFuncs.sheetName = sheetName;
/**
* This stores the version of your sheet for use in the logging functions{@link log} and {@link debug}. It is also stored in the sheet_version attribute on your character sheet. Accessible via `k.version`
* @var
* @type {number}
*/
let version = 0;
kFuncs.version = version;
/**
* A boolean flag that tells the script whether to enable or disable {@link debug} calls. If the version of the sheet is `0`, or an attribute named `debug_mode` is found on opening this is set to true for your entire session. Otherwise, it remains false.
* @var
* @type {boolean}
*/
let debugMode = false;
kFuncs.debugMode = debugMode;
const funcs = {};
kFuncs.funcs = funcs;
const updateHandlers = {};
const openHandlers = {};
const initialSetups = {};
const allHandlers = {};
const addFuncs = {};
const kscaffoldJSVersion = '0.0.4';
const kscaffoldPUGVersion = '0.0.4';
/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
/**
* Replaces problem characters to use a string as a regex
* @param {string} text - The text to replace characters in
* @returns {string}
* @example
* const textForRegex = k.sanitizeForRegex('.some thing[with characters]');
* console.log(textForRegex);// => "\.some thing\[with characters\]"
*/
const sanitizeForRegex = function(text){
return text.replace(/\.|\||\(|\)|\[|\]|\-|\+|\?|\/|\{|\}|\^|\$|\*/g,'\\$&');
};
kFuncs.sanitizeForRegex = sanitizeForRegex;
/**
* Converts a value to a number, it\'s default value, or `0` if no default value passed.
* @param {string|number} val - Value to convert to a number
* @param {number} def - The default value, uses 0 if not passed
* @returns {number|undefined}
* @example
* const num = k.value('100');
* console.log(num);// => 100
*/
const value = function(val,def){
return (+val||def||0);
};
kFuncs.value = value;
/**
* Extracts the section (e.g. `repeating_equipment`), rowID (e.g `-;lkj098J:LKj`), and field name (e.g. `bulk`) from a repeating attribute name.
* @param {string} string - The string to parse
* @returns {array} - Array of matches. Index 0: the section name, e.g. repeating_equipment | Index 1:the row ID | index 2: The name of the attribute
* @returns {string[]}
* @example
* //Extract info from a full repeating name
* const [section,rowID,attrName] = k.parseRepeatName('repeating_equipment_-8908asdflkjZlkj23_name');
* console.log(section);// => "repeating_equipment"
* console.log(rowID);// => "-8908asdflkjZlkj23"
* console.log(attrName);// => "name"
*
* //Extract info from just a row name
* const [section,rowID,attrName] = k.parseRepeatName('repeating_equipment_-8908asdflkjZlkj23');
* console.log(section);// => "repeating_equipment"
* console.log(rowID);// => "-8908asdflkjZlkj23"
* console.log(attrName);// => undefined
*/
const parseRepeatName = function(string){
let match = string.match(/(repeating_[^_]+)_([^_]+)(?:_(.+))?/);
match.shift();
return match;
};
kFuncs.parseRepeatName = parseRepeatName;
/**
* Parses out the components of a trigger name similar to [parseRepeatName](#parserepeatname). Aliases: parseClickTrigger.
*
* Aliases: `k.parseClickTrigger`
* @param {string} string The triggerName property of the
* @returns {array} - For a repeating button named `repeating_equipment_-LKJhpoi98;lj_roll`, the array will be `['repeating_equipment','-LKJhpoi98;lj','roll']`. For a non repeating button named `roll`, the array will be `[undefined,undefined,'roll']`
* @returns {string[]}
* @example
* //Parse a non repeating trigger
* const [section,rowID,attrName] = k.parseTriggerName('clicked:some-button');
* console.log(section);// => undefined
* console.log(rowID);// => undefined
* console.log(attrName);// => "some-button"
*
* //Parse a repeating trigger
* const [section,rowID,attrName] = k.parseTriggerName('clicked:repeating_attack_-234lkjpd8fu8usadf_some-button');
* console.log(section);// => "repeating_attack"
* console.log(rowID);// => "-234lkjpd8fu8usadf"
* console.log(attrName);// => "some-button"
*
* //Parse a repeating name
* const [section,rowID,attrName] = k.parseTriggerName('repeating_attack_-234lkjpd8fu8usadf_some-button');
* console.log(section);// => "repeating_attack"
* console.log(rowID);// => "-234lkjpd8fu8usadf"
* console.log(attrName);// => "some-button"
*/
const parseTriggerName = function(string){
let match = string.replace(/^clicked:/,'').match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
match.shift();
return match;
};
kFuncs.parseTriggerName = parseTriggerName;
const parseClickTrigger = parseTriggerName;
kFuncs.parseClickTrigger = parseClickTrigger;
/**
* Parses out the attribute name from the htmlattribute name.
* @param {string} string - The triggerName property of the [event](https://wiki.roll20.net/Sheet_Worker_Scripts#eventInfo_Object).
* @returns {string}
* @example
* //Parse a name
* const attrName = k.parseHtmlName('attr_attribute_1');
* console.log(attrName);// => "attribute_1"
*/
const parseHTMLName = function(string){
let match = string.match(/(?:attr|act|roll)_(.+)/);
match.shift();
return match[0];
};
kFuncs.parseHTMLName = parseHTMLName;
/**
* Capitalize each word in a string
* @param {string} string - The string to capitalize
* @returns {string}
* @example
* const capitalized = k.capitalize('a word');
* console.log(capitalized);// => "A Word"
*/
const capitalize = function(string){
return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase());
};
kFuncs.capitalize = capitalize;
/**
* Extracts a roll query result for use in later functions. Must be awaited as per [startRoll documentation](https://wiki.roll20.net/Sheet_Worker_Scripts#Roll_Parsing.28NEW.29). Stolen from [Oosh\'s Adventures with Startroll thread](https://app.roll20.net/forum/post/10346883/adventures-with-startroll).
* @param {string} query - The query should be just the text as the `?{` and `}` at the start/end of the query are added by the function.
* @returns {Promise} - Resolves to the selected value from the roll query
* @example
* const rollFunction = async function(){
* //Get the result of a choose from list query
* const queryResult = await extractQueryResult('Prompt Text Here|Option 1|Option 2');
* console.log(queryResult);//=> "Option 1" or "Option 2" depending on what the user selects
*
* //Get free from input from the user
* const freeResult = await extractQueryResult('Prompt Text Here');
* consoel.log(freeResult);// => Whatever the user entered
* }
*/
const extractQueryResult = async function(query){
debug('entering extractQueryResult');
let queryRoll = await startRoll(`!{{query=[[0[response=?{${query}}]]]}}`);
finishRoll(queryRoll.rollId);
return queryRoll.results.query.expression.replace(/^.+?response=|\]$/g,'');
};
kFuncs.extractQueryResult = extractQueryResult;
/**
* Simulates a query for ensuring that async/await works correctly in the sheetworker environment when doing conditional startRolls. E.g. if you have an if/else and only one of the conditions results in `startRoll` being called (and thus an `await`), the sheetworker environment would normally crash. Awaiting this in the condition that does not actually need to call `startRoll` will keep the environment in sync.
* @param {string|number} [value] - The value to return. Optional.
* @returns {Promise} - Resolves to the value passed to the function
* @example
* const rollFunction = async function(){
* //Get the result of a choose from list query
* const queryResult = await pseudoQuery('a value');
* console.log(queryResult);//=> "a value"
* }
*/
const pseudoQuery = async function(value){
debug('entering pseudoQuery');
let queryRoll = await startRoll(`!{{query=[[0[response=${value}]]]}}`);
finishRoll(queryRoll.rollId);
return queryRoll.results.query.expression.replace(/^.+?response=|\]$/g,'');
};
kFuncs.pseudoQuery = pseudoQuery;
/**
* An alias for console.log.
* @param {any} msg - The message can be a straight string, an object, or an array. If it is an object or array, the object will be broken down so that each key is used as a label to output followed by the value of that key. If the value of the key is an object or array, it will be output via `console.table`.
*/
const log = function(msg){
if(typeof msg === 'string'){
console.log(`%c${kFuncs.sheetName} log| ${msg}`,"background-color:#159ccf");
}else if(typeof msg === 'object'){
Object.keys(msg).forEach((m)=>{
if(typeof msg[m] === 'string'){
console.log(`%c${kFuncs.sheetName} log| ${m}: ${msg[m]}`,"background-color:#159ccf");
}else{
console.log(`%c${kFuncs.sheetName} log| ${typeof msg[m]} ${m}`,"background-color:#159ccf");
console.table(msg[m]);
}
});
}
};
kFuncs.log = log;
/**
* Alias for console.log that only triggers when debug mode is enabled or when the sheet\'s version is `0`. Useful for entering test logs that will not pollute the console on the live sheet.
* @param {any} msg - 'See {@link k.log}
* @param {boolean} force - Pass as a truthy value to force the debug output to be output to the console regardless of debug mode.
* @returns {void}
*/
const debug = function(msg,force){
if(!kFuncs.debugMode && !force && kFuncs.version > 0) return;
if(typeof msg === 'string'){
console.log(`%c${kFuncs.sheetName} DEBUG| ${msg}`,"background-color:tan;color:red;");
}else if(typeof msg === 'object'){
Object.keys(msg).forEach((m)=>{
if(typeof msg[m] === 'string'){
console.log(`%c${kFuncs.sheetName} DEBUG| ${m}: ${msg[m]}`,"background-color:tan;color:red;");
}else{
console.log(`%c${kFuncs.sheetName} DEBUG| ${typeof msg[m]} ${m}`,"background-color:tan;color:red;font-weight:bold;");
console.table(msg[m]);
}
});
}
};
kFuncs.debug = debug;
/**
* Orders the section id arrays for all sections in the `sections` object to match the repOrder attribute.
* @param {attributesProxy} attributes - The attributes object that must have a value for the reporder for each section.
* @param {[object]} sections - Object containing the IDs for the repeating sections, indexed by repeating section name.
*/
const orderSections = function(attributes,sections){
Object.keys(sections).forEach((section)=>{
attributes.attributes[`_reporder_${section}`] = commaArray(attributes[`_reporder_${section}`]);
orderSection(attributes.attributes[`_reporder_${section}`],sections[section]);
});
};
kFuncs.orderSections = orderSections;
/**
* Orders a single ID array.
* @param {[string]} repOrder - Array of IDs in the order they are in on the sheet.
* @param {[string]} IDs - Array of IDs to be ordered.
*/
const orderSection = function(repOrder,IDs=[]){
IDs.sort((a,b)=>{
return repOrder.indexOf(a.toLowerCase()) - repOrder.indexOf(b.toLowerCase());
});
};
kFuncs.orderSection = orderSection;
/**
* Splits a comma delimited string into an array
* @param {string} string - The string to split.
* @returns {array} - The string segments of the comma delimited list.
*/
const commaArray = function(string=''){
return string.toLowerCase().split(/\s*,\s*/);
};
kFuncs.commaArray = commaArray;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//# Attribute Obj Proxy handler
const createAttrProxy = function(attrs){
//creates a proxy for the attributes object so that values can be worked with more easily.
const getCascObj = function(event,casc){
const eventName = event.triggerName || event.sourceAttribute;
let typePrefix = eventName.startsWith('clicked:') ?
'act_' :
event.removedInfo ?
'fieldset_' :
'attr_';
let cascName = `${typePrefix}${eventName.replace(/(?:removed|clicked):/,'')}`;
let cascObj = casc[cascName];
if(typePrefix === 'attr_'){
cascObj.previousValue = event.previousValue;
}
return cascObj;
};
const triggerFunctions = function(obj,attributes,sections,casc){
if(obj.triggeredFuncs && obj.triggeredFuncs.length){
debug(`triggering functions for ${obj.name}`);
obj.triggeredFuncs && obj.triggeredFuncs.forEach(func=>funcs[func] ?
funcs[func]({trigger:obj,attributes,sections,casc}) :
debug(`!!!Warning!!! no function named ${func} found. Triggered function not called for ${obj.name}`,true));
}
};
const initialFunction = function(obj,attributes,sections){
if(obj.initialFunc){
debug(`initial functions for ${obj.name}`);
funcs[obj.initialFunc] ?
funcs[obj.initialFunc]({trigger:obj,attributes,sections}) :
debug(`!!!Warning!!! no function named ${obj.initialFunc} found. Initial function not called for ${obj.name}`,true);
}
};
const alwaysFunctions = function(trigger,attributes,sections,casc){
Object.values(allHandlers).forEach((handler)=>{
handler({trigger,attributes,sections,casc});
});
};
const processChange = function({event,trigger,attributes,sections,casc}){
if(event && !trigger){
debug(`${event.sourceAttribute} change detected. No trigger found`);
return;
}
if(!attributes || !sections || !casc){
debug(`!!! Insufficient arguments || attributes > ${!!attributes} | sections > ${!!sections} | casc > ${!!casc} !!!`);
return;
}
debug({trigger});
if(event){
debug('checking for initial & always functions');
alwaysFunctions(trigger,attributes,sections,casc);//Functions that should be run for all events.
initialFunction(trigger,attributes,sections,casc);//functions that should only be run if the attribute was the thing changed by the user
}
if(trigger){
debug(`processing ${trigger.name}`);
triggerFunctions(trigger,attributes,sections,casc);
if(!event && trigger.calculation && funcs[trigger.calculation]){
attributes[trigger.name] = funcs[trigger.calculation]({trigger,attributes,sections,casc});
}else if(trigger.calculation && !funcs[trigger.calculation]){
debug(`K-Scaffold Error: No function named ${trigger.calculation} found`);
}
if(Array.isArray(trigger.affects)){
attributes.queue.push(...trigger.affects);
}
}
attributes.set({attributes,sections,casc});
};
const attrTarget = {
updates:{},
attributes:{...attrs},
repOrders:{},
queue: [],
casc:{},
alwaysFunctions,
processChange,
triggerFunctions,
initialFunction,
getCascObj
};
const attrHandler = {
get:function(obj,prop){//gets the most value of the attribute.
//If it is a repeating order, returns the array, otherwise returns the update value or the original value
if(prop === 'set'){
return function(){
let {attributes,sections,casc,callback,vocal} = arguments[0] ? arguments[0] : {};
if(attributes && attributes.queue.length && sections && casc){
let triggerName = attributes.queue.shift();
let trigger = getCascObj({sourceAttribute:triggerName},casc);
attributes.processChange({trigger,attributes,sections,casc});
}else{
debug({updates:obj.updates});
let trueCallback = Object.keys(obj.repOrders).length ?
function(){
Object.entries(obj.repOrders).forEach(([section,order])=>{
_setSectionOrder(section,order,)
});
callback && callback();
}:
callback;
Object.keys(obj.updates).forEach((key)=>obj.attributes[key] = obj.updates[key]);
const update = obj.updates;
obj.updates = {};
set(update,vocal,trueCallback);
}
}
}else if(Object.keys(obj).some(key=>key===prop)){
return Reflect.get(...arguments)
}else{
let retValue;
switch(true){
case obj.repOrders.hasOwnProperty(prop):
retValue = obj.repOrders[prop];
break;
case obj.updates.hasOwnProperty(prop):
retValue = obj.updates[prop];
break;
default:
retValue = obj.attributes[prop];
break;
}
let cascRef = `attr_${prop.replace(/(repeating_[^_]+_)[^_]+/,'$1\$X')}`;
let numRetVal = +retValue;
if(!Number.isNaN(numRetVal) && retValue !== ''){
retValue = numRetVal;
}else if(cascades[cascRef] && (typeof cascades[cascRef].defaultValue === 'number' || cascades[cascRef].type === 'number')){
retValue = cascades[cascRef].defaultValue;
}
return retValue;
}
},
set:function(obj,prop,value){
//Sets the value. Also verifies that the value is a valid attribute value
//e.g. not undefined, null, or NaN
if(value || value===0 || value===''){
if(/reporder|^repeating_[^_]+$/.test(prop)){
let section = prop.replace(/_reporder_/,'');
obj.repOrders[section] = value;
}else if(`${obj.attributes}` !== `${value}` ||
(obj.updates[prop] && `${obj.updates}` !== `${value}`)
){
obj.updates[prop] = value;
}
}else{
debug(`!!!Warning: Attempted to set ${prop} to an invalid value:${value}; value not stored!!!`);
}
return true;
},
deleteProperty(obj,prop){
//removes the property from the original attributes, updates, and the reporders
Object.keys(obj).forEach((key)=>{
delete obj[key][prop.toLowerCase()];
});
}
};
return new Proxy(attrTarget,attrHandler);
};
/**
* Function that registers a function for being called via the funcs object. Returns true if the function was successfully registered, and false if it could not be registered for any reason.
* @param {object} funcObj - Object with keys that are names to register functions under and values that are functions.
* @param {object} optionsObj - Object that contains options to use for this registration.
* @param {string[]} optionsObj.type - Array that contains the types of specialized functions that apply to the functions being registered. Valid types are `"opener"`, `"updater"`, and `"default"`. `"default"` is always used, and never needs to be passed.
* @returns {boolean} - True if the registration succeeded, false if it failed.
* @example
* //Basic Registration
* const myFunc = function({trigger,attributes,sections,casc}){};
* k.registerFuncs({myFunc});
*
* //Register a function to run on sheet open
* const openFunc = function({trigger,attributes,sections,casc}){};
* k.registerFuncs({openFunc},{type:['opener']})
*
* //Register a function to run on all events
* const allFunc = function({trigger,attributes,sections,casc}){};
* k.registerFuncs({allFunc},{type:['all']})
*/
const registerFuncs = function(funcObj,optionsObj = {}){
if(typeof funcObj !== 'object' || typeof optionsObj !== 'object'){
debug(`!!!! K-scaffold error: Improper arguments to register functions !!!!`);
return false;
}
const typeArr = optionsObj.type ? ['default',...optionsObj.type] : ['default'];
const typeSwitch = {
'opener':openHandlers,
'updater':updateHandlers,
'new':initialSetups,
'all':allHandlers,
'default':funcs
};
let setState;
Object.entries(funcObj).map(([prop,value])=>{
typeArr.forEach((type)=>{
if(typeSwitch[type][prop]){
debug(`!!! Duplicate function name for ${prop} as ${type}!!!`);
setState = false;
}else if(typeof value === 'function'){
typeSwitch[type][prop] = value;
setState = setState !== false ? true : false;
}else{
debug(`!!! K-scaffold error: Function registration requires a function. Invalid value to register as ${type} !!!`);
setState = false;
}
});
});
return setState;
};
kFuncs.registerFuncs = registerFuncs;
const setActionCalls = function({attributes,sections}){
actionAttributes.forEach((base)=>{
let [section,,field] = k.parseTriggerName(base);
let fieldAction = field.replace(/_/g,'-');
if(section){
sections[section].forEach((id)=>{
attributes[`${section}_${id}_${field}`] = `%{${attributes.character_name}|${section}_${id}_${fieldAction}}`;
});
}else{
attributes[`${field}`] = `%{${attributes.character_name}|${fieldAction}}`;
}
});
};
funcs.setActionCalls = setActionCalls;
/**
* Function to call a function previously registered to the funcs object. May not be used that much. Either returns the function or null if no function exists.
* @param {string} funcName - The name of the function to invoke.
* @param {...any} args - The arguments to call the function with.
* @returns {any}
* @example
* //Call myFunc with two arguments
* k.callFunc('myFunc','an argument','another argument');
*/
const callFunc = function(funcName,...args){
if(funcs[funcName]){
debug(`calling ${funcName}`);
return funcs[funcName](...args);
}else{
debug(`Invalid function name: ${funcName}`);
return null;
}
};
kFuncs.callFunc = callFunc;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
//Sheet Updaters and styling functions
const updateSheet = function(){
log('updating sheet');
getAllAttrs({props:['debug_mode',...baseGet],callback:(attributes,sections,casc)=>{
kFuncs.debugMode = kFuncs.debugMode || !!attributes.debug_mode;
debug({sheet_version:attributes.sheet_version});
if(!attributes.sheet_version){
Object.entries(initialSetups).forEach(([funcName,handler])=>{
if(typeof funcs[funcName] === 'function'){
debug(`running ${funcName}`);
funcs[funcName]({attributes,sections,casc});
}else{
debug(`!!!Warning!!! no function named ${funcName} found. Initial sheet setup not performed.`);
}
});
}else{
Object.entries(updateHandlers).forEach(([ver,handler])=>{
if(attributes.sheet_version < +ver){
handler({attributes,sections,casc});
}
});
}
Object.entries(openHandlers).forEach(([funcName,func])=>{
if(typeof funcs[funcName] === 'function'){
debug(`running ${funcName}`);
funcs[funcName]({attributes,sections,casc});
}else{
debug(`!!!Warning!!! no function named ${funcName} found. Sheet open handling not performed.`);
}
});
setActionCalls({attributes,sections});
attributes.sheet_version = kFuncs.version;
log(`Sheet Update applied. Current Sheet Version ${kFuncs.version}`);
attributes.set();
log('Sheet ready for use');
}});
};
const initialSetup = function(attributes,sections){
debug('Initial sheet setup');
};
/**
* This is the default listener function for attributes that the K-Scaffold uses. It utilizes the `triggerFuncs`, `listenerFunc`, `calculation`, and `affects` properties of the K-scaffold trigger object (see the Pug section of the scaffold for more details).
* @param {Roll20Event} event
* @returns {void}
* @example
* //Call from an attribute change
* on('change:an_attribute',k.accessSheet);
*/
const accessSheet = function(event){
debug({funcs:Object.keys(funcs)});
debug({event});
getAllAttrs({callback:(attributes,sections,casc)=>{
let trigger = attributes.getCascObj(event,casc);
attributes.processChange({event,trigger,attributes,sections,casc});
}});
};
funcs.accessSheet = accessSheet;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
/*
Cascade Expansion functions
*/
//Expands the repeating section templates in cascades to reflect the rows actually available
const expandCascade = function(cascade,sections,attributes){
return _.keys(cascade).reduce((memo,key)=>{//iterate through cascades and replace references to repeating attributes with correct row ids.
if(/^(?:act|attr)_repeating_/.test(key)){//If the attribute is a repeating attribute, do special logic
expandRepeating(memo,key,cascade,sections,attributes);
}else if(key){//for non repeating attributes do this logic
expandNormal(memo,key,cascade,sections);
}
return memo;
},{});
};
const expandRepeating = function(memo,key,cascade,sections,attributes){
key.replace(/((?:attr|act)_)(repeating_[^_]+)_[^_]+?_(.+)/,(match,type,section,field)=>{
(sections[section]||[]).forEach((id)=>{
memo[`${type}${section}_${id}_${field}`]=_.clone(cascade[key]);//clone the details so that each row's attributes have correct ids
memo[`${type}${section}_${id}_${field}`].name = `${section}_${id}_${field}`;
if(key.startsWith('attr_')){
memo[`${type}${section}_${id}_${field}`].affects = memo[`${type}${section}_${id}_${field}`].affects.reduce((m,affected)=>{
if(section === affected){//otherwise if the affected attribute is in the same section, simply set the affected attribute to have the same row id.
m.push(applyID(affected,id));
}else if(/repeating/.test(affected)){//If the affected attribute isn't in the same repeating section but is still a repeating attribute, add all the rows of that section
addAllRows(affected,m,sections);
}else{//otherwise the affected attribute is a non repeating attribute. Simply add it to the computed affected array
m.push(affected);
}
return m;
},[]);
}
});
});
};
const applyID = function(affected,id){
return affected.replace(/(repeating_[^_]+_)[^_]+(.+)/,`$1${id}$2`);
};
const expandNormal = function(memo,key,cascade,sections){
memo[key] = _.clone(cascade[key]);
if(key.startsWith('attr_')){
memo[key].affects = memo[key].affects || [];
memo[key].affects = memo[key].affects.reduce((m,a)=>{
if(/^repeating/.test(a)){
addAllRows(a,m,sections);
}else{
m.push(a);
}
return m;
},[]);
}
};
const addAllRows = function(affected,memo,sections){
affected.replace(/(repeating_[^_]+?)_[^_]+?_(.+)/,(match,section,field)=>{
sections[section].forEach(id=>memo.push(`${section}_${id}_${field}`));
});
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
/**
* Alias for [setSectionOrder()](https://wiki.roll20.net/Sheet_Worker_Scripts#setSectionOrder.28.3CRepeating_Section_Name.3E.2C_.3CSection_Array.3E.2C_.3CCallback.3E.29) that allows you to use the section name in either `repeating_section` or `section` formats. Note that the Roll20 sheetworker [setSectionOrder](https://wiki.roll20.net/Sheet_Worker_Scripts#setSectionOrder.28.3CRepeating_Section_Name.3E.2C_.3CSection_Array.3E.2C_.3CCallback.3E.29) currently causes some display issues on sheets.
* @param {string} section
* @param {string[]} order
* @returns {void}
* @example
* //Set the order of a repeating_weapon section
* k.setSectionOrder('repeating_equipment',['id1','id2','id3']);
* //Can also specify the section name without the repeating_ prefix
* k.setSectionOrder('equipment',['id1','id2','id3']);
*/
const _setSectionOrder = function(section,order){
let trueSection = section.replace(/repeating_/,'');
setSectionOrder(trueSection,order);
};
kFuncs.setSectionOrder = _setSectionOrder;
/**
* Alias for [removeRepeatingRow](https://wiki.roll20.net/Sheet_Worker_Scripts#removeRepeatingRow.28_RowID_.29) that also removes the row from the current object of attribute values and array of section IDs to ensure that erroneous updates are not issued.
* @param {string} row - The row id to be removed
* @param {attributesProxy} attributes - The attribute values currently in memory
* @param {object} sections - Object that contains arrays of all the IDs in sections on the sheet indexed by repeating name.
* @returns {void}
* @example
* //Remove a repeating Row
* k.getAllAttrs({
* callback:(attributes,sections)=>{
* const rowID = sections.repeating_equipment[0];
* k.removeRepeatingRow(`repeating_equipment_${rowID}`,attributes,sections);
* console.log(sections.repeating_equipment); // => rowID no longer exists in the array.
* console.log(attributes[`repeating_equipment_${rowID}_name`]); // => undefined
* }
* })
*/
const _removeRepeatingRow = function(row,attributes,sections){
debug(`removing ${row}`);
Object.keys(attributes.attributes).forEach((key)=>{
if(key.startsWith(row)){
delete attributes[key];
}
});
let [,section,rowID] = row.match(/(repeating_[^_]+)_(.+)/,'');
sections[section] = sections[section].filter((id)=>id!==rowID);
removeRepeatingRow(row);
};
kFuncs.removeRepeatingRow = _removeRepeatingRow;
/**
* Alias for [getAttrs()](https://wiki.roll20.net/Sheet_Worker_Scripts#getAttrs.28attributeNameArray.2C_callback.29) that converts the default object of attribute values into an {@link attributesProxy} and passes that back to the callback function.
* @param {Object} args
* @param {string[]} [args.props=baseGet] - Array of attribute names to get the value of. Defaults to {@link baseGet} if not passed.
* @param {function(attributesProxy)} args.callback - The function to call after the attribute values have been gotten. An {@link attributesProxy} is passed to the callback.
* @example
* //Gets the attributes named in props.
* k.getAttrs({
* props:['attribute_1','attribute_2'],
* callback:(attributes)=>{
* //Work with the attributes as you would in a normal getAttrs, or use the superpowers of the K-scaffold attributes object like so:
* attributes.attribute_1 = 'new value';
* attributes.set();
* }
* })
*/
const _getAttrs = function({props=baseGet,callback}){
getAttrs(props,(values)=>{
const attributes = createAttrProxy(values);
callback(attributes);
});
};
kFuncs.getAttrs = _getAttrs;
/**
* Alias for [getAttrs()](https://wiki.roll20.net/Sheet_Worker_Scripts#getAttrs.28attributeNameArray.2C_callback.29) and [getSectionIDs](https://wiki.roll20.net/Sheet_Worker_Scripts#getSectionIDs.28section_name.2Ccallback.29) that combines the actions of both sheetworker functions and converts the default object of attribute values into an {@link attributesProxy}. Also gets the details on how to handle all attributes from the master {@link cascades} object and.
* @param {Object} args
* @param {string[]} [args.props=baseGet] - Array of attribute names to get the value of. Defaults to {@link baseGet} if not passed.
* @param {repeatingSectionDetails} sectionDetails - Array of details about a section to get the IDs for and attributes that need to be gotten.
* @param {function(attributesProxy,sectionObj,expandedCascade):void} args.callback - The function to call after the attribute values have been gotten. An {@link attributesProxy} is passed to the callback along with a {@link sectionObj} and {@link expandedCascade}.
* @example
* //Get every K-scaffold linked attribute on the sheet
* k.getAllAttrs({
* callback:(attributes,sections,casc)=>{
* //Work with the attributes as you please.
* attributes.some_attribute = 'a value';
* attributes.set();//Apply our change
* }
* })
*/
const getAllAttrs = function({props=baseGet,sectionDetails=repeatingSectionDetails,callback}){
getSections(sectionDetails,(repeats,sections)=>{
getAttrs([...props,...repeats],(values)=>{
const attributes = createAttrProxy(values);
orderSections(attributes,sections);
const casc = expandCascade(cascades,sections,attributes);
callback(attributes,sections,casc);
})
});
};
kFuncs.getAllAttrs = getAllAttrs;
/**
* Alias for [getSectionIDs()](https://wiki.roll20.net/Sheet_Worker_Scripts#getSectionIDs.28section_name.2Ccallback.29) that allows you to iterate through several functions at once. Also assembles an array of repeating attributes to get.
* @param {object[]} sectionDetails - Array of details about a section to get the IDs for and attributes that need to be gotten.
* @param {string} sectionDetails.section - The full name of the repeating section including the `repeating_` prefix.
* @param {string[]} sectionDetails.fields - Array of field names that need to be gotten from the repeating section
* @param {function(string[],sectionObj)} callback - The function to call once all IDs have been gotten and the array of repating attributes to get has been assembled. The callback is passed the array of repating attributes to get and a {@link sectionObj}.
* @example
* // Get some section details
* const sectionDetails = {
* {section:'repeating_equipment',fields:['name','weight','cost']},
* {section:'repeating_weapon',fields:['name','attack','damage']}
* };
* k.getSections(sectionDetails,(attributeNames,sections)=>{
* console.log(attributeNames);// => Array containing all row specific attribute names
* console.log(sections);// => Object with arrays containing the row ids. Indexed by section name (e.g. repeating_eqiupment)
* })
*/
const getSections = function(sectionDetails,callback){
let queueClone = _.clone(sectionDetails);
const worker = (queue,repeatAttrs=[],sections={})=>{
let detail = queue.shift();
getSectionIDs(detail.section,(IDs)=>{
sections[detail.section] = IDs;
IDs.forEach((id)=>{
detail.fields.forEach((f)=>{
repeatAttrs.push(`${detail.section}_${id}_${f}`);
});
});
repeatAttrs.push(`_reporder_${detail.section}`);
if(queue.length){
worker(queue,repeatAttrs,sections);
}else{
callback(repeatAttrs,sections);
}
});
};
if(!queueClone[0]){
callback([],{});
}else{
worker(queueClone);
}
};
kFuncs.getSections = getSections;
// Sets the attributes while always calling with {silent:true}
// Can be awaited to get the values returned from _setAttrs
/**
* Alias for [setAttrs()](https://wiki.roll20.net/Sheet_Worker_Scripts#setAttrs.28values.2Coptions.2Ccallback.29) that sets silently by default.
* @param {object} obj - The object containting attributes to set
* @param {boolean} [vocal=false] - Whether to set silently (default value) or not.
* @param {function()} [callback] - The callback function to invoke after the setting has been completed. No arguments are passed to the callback function.
* @example
* //Set some attributes silently
* k.setAttrs({attribute_1:'new value'})
* //Set some attributes and triggers listeners
* k.setAttrs({attribute_1:'new value',true})
* //Set some attributes and call a callback function
* k.setAttrs({attribute_1:'new value'},null,()=>{
* //Do something after the attribute is set
* })
*/
const set = function(obj,vocal=false,callback){
setAttrs(obj,{silent:!vocal},callback);
};
kFuncs.setAttrs = set;
const generateCustomID = function(string){
if(!string.startsWith('-')){
string = `-${string}`;
}
rowID = generateRowID();
let re = new RegExp(`^.{${string.length}}`);
return `${string}${rowID.replace(re,'')}`;
};
/**
* Alias for generateRowID that adds the new id to the {@link sectionObj}. Also allows for creation of custom IDs that conform to the section ID requirements.
* @param {sectionObj} sections
* @param {string} [customText] - Custom text to start the ID with. This text should not be longer than the standard repeating section ID format.
* @returns {string} - The created ID
* @example
* k.getAllAttrs({
* callback:(attributes,sections,casc)=>{
* //Create a new row ID
* const rowID = k.generateRowID('repeating_equipment',sections);
* console.log(rowID);// => -p8rg908ug0suzz
* //Create a custom row ID
* const customID = k.generateRowID('repeating_equipment',sections,'custom');
* console.log(customID);// => -custom98uadj89kj
* }
* });
*/
const _generateRowID = function(section,sections,customText){
let rowID = customText ?
generateCustomID(customText) :
generateRowID();
section = section.match(/^repeating_[^_]+$/) ?
section :
`repeating_${section}`;
sections[section] = sections[section] || [];
sections[section].push(rowID);
return `${section}_${rowID}`;
};
kFuncs.generateRowID = _generateRowID;/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
const listeners = {};
const baseGet = Object.entries(cascades).reduce((memo,[attrName,detailObj])=>{
if(!/repeating/.test(attrName) && detailObj.type !== 'action'){
memo.push(detailObj.name);
}
if(detailObj.listener){
listeners[detailObj.listener] = detailObj.listenerFunc;
}
return memo;
},[]);
kFuncs.baseGet = baseGet;
const registerEventHandlers = function(){
on('sheet:opened',updateSheet);
debug({funcKeys:Object.keys(funcs),funcs});
//Roll20 change and click listeners
Object.entries(listeners).forEach(([event,funcName])=>{
if(funcs[funcName]){
on(event,funcs[funcName]);
}else{
debug(`!!!Warning!!! no function named ${funcName} found. No listener created for ${event}`,true);
}
});
log(`kScaffold Loaded`);
};
kFuncs.registerEventHandlers = registerEventHandlers;
const addItem = function(event){
let [,,section] = parseClickTrigger(event.triggerName);
section = section.replace(/add-/,'');
getAllAttrs({
callback:(attributes,sections,casc) => {
let row = _generateRowID(section,sections);
debug({row});
attributes[`${row}_name`] = '';
setActionCalls({attributes,sections});
const trigger = cascades[`fieldset_repeating_${section}`];
if(trigger && trigger.addFuncs){
trigger.addFuncs.forEach((funcName) => {
if(funcs[funcName]){
funcs[funcName]({attributes,sections,casc,trigger});
}
});
}
attributes.set({attributes,sections,casc});
}
});
};
funcs.addItem = addItem;
const editSection = function(event){
let [,,section] = parseClickTrigger(event.triggerName);
section = section.replace(/edit-/,'');
let target = `fieldset.repeating_${section} + .repcontainer`;
$20(target).toggleClass('ui-sortable');
$20(target).toggleClass('editmode');
};
registerFuncs({editSection});/**
* The default tab navigation function of the K-scaffold. Courtesy of Riernar. It will add `k-active-tab` to the active tab-container and `k-active-button` to the active button. You can either write your own CSS to control display of these, or use the default CSS included in `scaffold/_k.scss`. Note that `k-active-button` has no default CSS as it is assumed that you will want to style the active button to match your system.
* @param {Object} trigger - The trigger object
*/
const kSwitchTab = function ({ trigger, attributes }) {
const [container, tab] = (
trigger.name.match(/nav-tabs-(.+)--(.+)/) ||
[]
).slice(1);
$20(`[data-container-tab="${container}"]`).removeClass('k-active-tab');
$20(`[data-container-tab="${container}"][data-tab="${tab}"]`).addClass('k-active-tab');
$20(`[data-container-button="${container}"]`).removeClass('k-active-button');
$20(`[data-container-button="${container}"][data-button="${tab}"]`).addClass('k-active-button');
const tabInputName = `${container.replace(/\-/g,'_')}_tab`;
if(persistentTabs.indexOf(tabInputName) > -1){
attributes[tabInputName] = trigger.name;
}
}
registerFuncs({ kSwitchTab });
/**
* Sets persistent tabs to their last active state
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const kTabOnOpen = function({trigger,attributes,sections,casc}){
persistentTabs.forEach((tabInput) => {
const pseudoTrigger = {name:attributes[tabInput]};
kSwitchTab({trigger:pseudoTrigger, attributes});
});
};
registerFuncs({ kTabOnOpen },{type:['opener']});
return kFuncs;
}());
const actionAttributes = ["kingdom_culture_action","kingdom_economy_action","kingdom_loyalty_action","kingdom_stability_action","kingdom_agriculture_action","kingdom_arts_action","kingdom_boating_action","kingdom_defense_action","kingdom_engineering_action","kingdom_exploration_action","kingdom_folklore_action","kingdom_industry_action","kingdom_intrigue_action","kingdom_magic_action","kingdom_politics_action","kingdom_scholarship_action","kingdom_statecraft_action","kingdom_trade_action","kingdom_warfare_action","kingdom_wilderness_action","army_scouting_action","army_maneuver_action","army_morale_action","army_ranged_action"];const abilityScores = {"culture":"corruption","economy":"crime","loyalty":"decay","stability":"strife"};const leaders = ["ruler","counselor","general","emissary","magister","treasurer","viceroy","warden"];const skills = {"agriculture":"stability","arts":"culture","boating":"economy","defense":"stability","engineering":"stability","exploration":"economy","folklore":"culture","industry":"economy","intrigue":"loyalty","magic":"culture","politics":"loyalty","scholarship":"culture","statecraft":"loyalty","trade":"economy","warfare":"loyalty","wilderness":"stability"};const persistentTabs = ["kingmaker_tab"];
k.version = 1;
k.sheetName = 'Roll20 Kingmaker Module';/**
* Removes the kingmaker specific prefixes (e.g. `kingdom`, `army`, `settlement`) from an attribute name. If no prefix is passed, will simply remove the first set of non _ characters in the string
* @param {string} string - The string to work on
* @param {string} prefix The prefix to look for
* @returns {string}
*/
const replacePrefix = (string,prefix = '[^_]+') => {
return string.replace(new RegExp(`^${prefix}_`),'');
}/**
* Calculates the ability score modifier total
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
* @returns {number}
*/
const calcKingdomAbilityMod = function({trigger,attributes,sections,casc}){
const baseAttr = replacePrefix(trigger.name,'kingdom');
const baseRuin = abilityScores[baseAttr];
return Math.floor((attributes[`kingdom_${baseAttr}_score`] - 10) / 2) + attributes[`kingdom_${baseRuin}_penalty`];
};
k.registerFuncs({calcKingdomAbilityMod});
/**
* Calculates the total skill modifier
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
* @returns {number}
*/
const calcSkillTotal = function({trigger,attributes,sections,casc}){
const baseSkill = replacePrefix(trigger.name,'kingdom');
const ability = skills[baseSkill];
const attrAdds = [
`${baseSkill}_proficiency_total`,
`${ability}`,
...['status','circumstance','item','other'].map(t=>`${baseSkill}_${t}`)
];
return attrAdds.reduce((total,attrName)=> total += attributes[`kingdom_${attrName}`], 0 );
};
k.registerFuncs({calcSkillTotal});
/**
* Calculates the total skill proficiency
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
* @returns {number}
*/
const calcSkillProficiency = function({trigger,attributes,sections,casc}){
const profName = trigger.name.replace(/_total$/,'');
const levelContribution = attributes[profName] ?
attributes.kingdom_level :
0;
return attributes[profName] + levelContribution;
};
k.registerFuncs({calcSkillProficiency});
/**
* Converts the numerical proficiency value to the translation key for the proficiency description
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @returns {string}
*/
const translateProficiency = ({trigger,attributes}) => {
const proficiencySwitch = {
'0':'untrained',
'2':'trained',
'4':'expert',
'6':'master',
'8':'legendary'
};
const profAttr = trigger.name.replace(/_translation/,'');
const profNum = attributes[profAttr];
return `km_${proficiencySwitch[`${profNum}`]}`;
};
k.registerFuncs({translateProficiency})
/**
* Calculates the army DCs from their base attribute
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
* @returns {number}
*/
const calcBasicArmyDC = function({trigger,attributes,sections,casc}){
const baseStat = trigger.name.replace(/_dc$/,'');
return attributes[baseStat] + 10;
};
k.registerFuncs({calcBasicArmyDC});
/**
* Switches the army to defeated if certain conditions have been met
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const triggerDefeated = function({trigger,attributes,sections,casc}){
return attributes.army_hp > 0 ?
0 :
1;
};
k.registerFuncs({triggerDefeated});
/**
* Switches the army to pinned if certain conditions have been met
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const calcPinned = function({trigger,attributes,sections,casc}){
return attributes.army_mired >= 4 ?
1 :
0;
};
k.registerFuncs({calcPinned});
/**
* Switches the army to routed if certain conditions are met
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const triggerRouted = function({trigger,attributes,sections,casc}){
return attributes.army_shaken <= -4 ?
1 :
0;
};
k.registerFuncs({triggerRouted});
/**
* Calculates the new ruin penalty when the ruin score exceeds the threshold.
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const calcKingdomRuinPenalty = function({trigger,attributes,sections,casc}){
// extract the base ruin name
const baseRuin = replacePrefix(trigger.name,'kingdom').replace(/_threshold/,'');
if(attributes[`kingdom_${baseRuin}`] > attributes[`kingdom_${baseRuin}_threshold`]){
attributes[`kingdom_${baseRuin}`] -= attributes[`kingdom_${baseRuin}_threshold`];
attributes[`kingdom_${baseRuin}_penalty`]--;
}
};
k.registerFuncs({calcKingdomRuinPenalty});
/**
* Manipulates the classes in the leader section so that leadership positions that are empty can be collapsed.
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const setLeaderCollapsible = function({trigger,attributes,sections,casc}){
leaders.forEach(leader =>{
const action = !attributes[`kingdom_${leader}_invested`] &&
!attributes[`kingdom_${leader}_name`] ?
'addClass' :
'removeClass';
$20(`.${leader}`)[action]('expanded');
});
};
k.registerFuncs({setLeaderCollapsible},{type:['opener']});
/**
* Shows/hides the sheets for the types of kingmaker sheet (kingdom, army, settlement)
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const kingdomTypeDisplay = function({trigger,attributes,sections,casc}){
const selector = `.tabs--kingmaker__button--${attributes.kingmaker_type}`;
$20(`.tabs__button.tabs--kingmaker__button:not(.tabs--kingmaker__button--settings)`).addClass('inactive');
$20(selector).removeClass('inactive');
};
k.registerFuncs({kingdomTypeDisplay},{type:['opener']});/**
* Initiates a basic roll, which is a roll where only a single modifier and a misc mods roll query are needed and the button name and modifier attribute name match. Used for skill, ability score, and army base stat rolls
* @param {object} trigger - The trigger that caused the function to be called
* @param {object} attributes - The attribute values of the character
* @param {object[]} sections - All the repeating section IDs
* @param {object} casc - Expanded cascade object
*/
const rollBasic = function({trigger,attributes,sections,casc}){
// Convert the button name from kebab-case to snake_case
const attributeName = trigger.name.replace(/-/g,'_').replace(/_action$/,'');
// convert the attribute name into the translation key for it
const translationKey = `km_${replacePrefix(attributeName).replace(/_/,' ')}`;
const rollObj = {
title: `^{${translationKey}}`,
roll:`[[1d20 + ${attributes[attributeName]}[${k.capitalize(getTranslationByKey(translationKey))}] + [[0?{${k.capitalize(getTranslationByKey('km_miscellaneous-query'))}}]][${k.capitalize(getTranslationByKey('km_miscellaneous abbreviation'))}]]]`
};
const rollString = assembleRoll(rollObj);
executeRoll(rollString);
};
k.registerFuncs({rollBasic});
/**
*
* @param {object} rollObj - Object describing the fields to send in the roll. Keys are field names; values are field contents
* @returns {string} - The assembled roll
*/
const assembleRoll = (rollObj) => {
return Object.entries(rollObj).reduce((str,[field,content]) => {
return str += ` {{${field}=${content}}}`
},'@{kingdom_template_start}')
}
/**
* Actually executes the rolls assembled by the specific rolling functions.
* @param {string} rollString - The message to send to chat
*/
const executeRoll = async (rollString) => {
const roll = await startRoll(rollString);
finishRoll(roll.rollId);
};/*jshint esversion: 11, laxcomma:true, eqeqeq:true*/
/*jshint -W014,-W084,-W030,-W033*/
const kingdomDrop = function({attributes,sections,casc}){
let data = parseDropData(attributes.kingdom_drop_data);
if(!data) return;
k.debug({data});
clearDropAttributes();
// return;
processDropData({data,attributes,sections,casc});
const protoQueue = Object.keys(attributes.updates)
.flatMap( key => {
const cascRef = key.replace(/(repeating_[^_]+)_[^_]+_(.+)/,'$1_\$X_$2');
casc[`attr_${cascRef}`] = casc[`attr_${cascRef}`] || k.cascades[`attr_${cascRef}`];
return k.cascades[`attr_${cascRef}`] ?
casc[`attr_${cascRef}`].affects
: [];
});
// Set any roller construct rollbutton - action button linkages that need to be set.
k.callFunc('setActionCalls',{attributes,sections});
attributes.queue.push(...[...new Set(protoQueue)]);
k.debug('Drop processing complete');
attributes.set({attributes,sections,casc});
};
k.registerFuncs({kingdomDrop});
const parseDropData = function(drop_data){
k.debug('parsing drop data');
k.debug({drop_data:`"${drop_data}"`});
const categorySectionSwitch = {
'Kingdom Feats':'repeating_kingdom-feats'
};
try{
// If this isn't a kingdom drop, then ignore it
let data = JSON.parse(drop_data);
if(!data.Category.startsWith('Kingdom ')) return;
data = Object.entries(data)
.reduce((memo,[key,val])=>{
if(val){
if(typeof val === 'string' && val.startsWith('[{')){
val = JSON.parse(val);
}
memo[key] = val;
}
return memo;
},{});
let category = categorySectionSwitch[data.Category] || data.Category;
data = {[category]:[data]};
return data;
}catch(err){
k.debug({'Data parse error! Drop aborted. Error:':err,drop_data},true);
return false;
}
};
const clearDropAttributes = function(){
k.setAttrs({kingdom_drop_name:'',kingdom_drop_data:''});
};
const processDropData = function({data, attributes, sections, casc,section=''}){
const repeatProcessors = {};//Object for specifying specialty drop functions. Not currently used.
section = section ? `${section}_` : '';
const worker = (queue) => {
let [key,val] = queue.shift();
try{
let setKey = key.replace(/^(?:data-)?cs(?:list)?-/,'');
if(typeof val === 'string' && val.startsWith('[{')){
val = JSON.parse(val);
}
if(!Array.isArray(val) && (key.startsWith('cs-') || key.startsWith('data-cs-'))){
attributes[`${section}${setKey}`] = val;
}else if(Array.isArray(val) && key.startsWith('repeating_')){
const processor = repeatProcessors[key] || dropRepeat;
processor(key,val,attributes,sections,casc);
}
}catch(err){
k.debug({[`Invalid JSON entered for ${key} on ${data.display_name}`]:err,JSON:val});
}
if(queue.length){
return worker(queue);
}else{
return true;
}
};
return worker(Object.entries(data));
};
/**
* Drops a standard repeating section
* @param {string} lookupKey - The lookupKey that triggered the drop
* @param {object} arr The drop data for this item
* @param {object} attributes The attributes object from the K-Scaffold
* @param {object} sections The sections object from the K-Scaffold
* @param {object} casc The expanded cascade object from the K-Scaffold
*/
const dropRepeat = function(lookupKey,arr,attributes,sections,casc){
if(!Array.isArray(arr)){
k.debug({'Invalid section details passed. Exiting. Object passed was:':arr},true);
return;
}
return repeatWorker(lookupKey,arr,sections,casc,
(itemObj,section)=>{
k.debug({section,itemObj});
processDropData({data:itemObj,attributes,sections,casc,section});
});
};
/**
* Adds the cascade objects for the indicated row to the casc object.
* @param {string} section - The row information (repeating section, and rowID)
* @param {object} casc - The cascade object for this particular character
* @param {number|string} [previousValue] - What to set the previous value to
*/
const addRowToCasc = function(section,casc,previousValue){
k.debug(`Adding ${section} to cascades`);
const match = section.match(/(.+?_.+?)_(.+)/);
match.shift();
const [repSection,rowID] = match;
k.debug({repSection,rowID});
Object
.entries(k.cascades)
.forEach(([key,obj]) => {
if(key.startsWith(`attr_${repSection}`)){
const newObj = {...obj};
newObj.name = newObj.name.replace(/\$X/,rowID)
const newKey = key.replace(/\$X/,rowID);
newObj.previousValue = previousValue === undefined ?
newObj.defaultValue :
previousValue;
newObj.affects = newObj.affects.map(a => a.replace(/\$X/,rowID));
casc[newKey] = newObj;
k.debug({[`casc[${newKey}]`]:casc[newKey]});
}
});
};
/**
* Does the basic prepwork for repeating section drops by creating the rowID and grabbing the item to work on. Passes the selected itemObj and the created section details to the callback function
* @param {string} lookupKey
* @param {array} arr
* @param {object} sections
* @param {function} callback
*/
const repeatWorker = function(lookupKey,arr,sections,casc,callback){
const worker = (queue)=>{
const itemObj = queue.shift();
const section = k.generateRowID(lookupKey,sections);
itemObj['data-cs-collapse'] = 1; //Newly dropped items should be collapsed by default
addRowToCasc(section,casc);
callback(itemObj,section);
if(queue.length){
return worker(queue);
}else{
return true;
}
};
return worker([...arr]);
}
k.registerEventHandlers();
</script>
</div>
<input accept="Category" name="attr_drop_category" type="hidden"/>
<input accept="Name" name="attr_drop_name" type="hidden"/>
<input accept="data" name="attr_drop_data" type="hidden"/>
<input accept="Content" name="attr_drop_content" type="hidden"/>
</div><input type="hidden" name="attr_d20" value="1d20">
<input type="hidden" name="attr_level" value="1">
<input type="hidden" name="attr_pb" value="2">
<input type="hidden" name="attr_pbd_safe" value="">
<input type="hidden" name="attr_jack" value="1">
<input type="hidden" name="attr_jack_attr" value="">
<input type="hidden" name="attr_jack_bonus" value="">
<input type="hidden" name="attr_passive_wisdom" value="10">
<input type="hidden" name="attr_hitdie_final" value="">
<input type="hidden" name="attr_globalsavingthrowbonus" value="">
<input type="hidden" name="attr_death_save_bonus" value="0">
<input type="hidden" name="attr_initiative_bonus" value="0">
<input type="hidden" name="attr_spell_save_dc" value="0">
<input type="hidden" name="attr_spell_attack_bonus" value="0">
<input type="hidden" name="attr_spell_attack_mod" value="0">
<input type="hidden" name="attr_npc_legendary_actions_desc" value="The creature can take 3 legendary actions, choosing from the options below. Only one legendary option can be used at a time and only at the end of another creature's turn. The creature regains spent legendary actions at the start of its turn.">
<input type="hidden" name="attr_global_damage_mod_crit" value="0">
<input type="hidden" name="attr_global_damage_mod_roll" value="0">
<input type="hidden" name="attr_global_damage_mod_type" value="">
<input type="hidden" name="attr_global_attack_mod" value="">
<input type="hidden" name="attr_global_save_mod" value="">
<input type="hidden" name="attr_global_skill_mod" value="">
<input type="hidden" name="attr_exhaustion_1" value="">
<input type="hidden" name="attr_exhaustion_2" value="">
<input type="hidden" name="attr_exhaustion_3" value="">
<input type="hidden" name="attr_exhaustion_4" value="">
<input type="hidden" name="attr_exhaustion_5" value="">
<input type="hidden" name="attr_exhaustion_6" value="">
<input type="hidden" name="attr_strength_mod" value="0">
<input type="hidden" name="attr_dexterity_mod" value="0">
<input type="hidden" name="attr_constitution_mod" value="0">
<input type="hidden" name="attr_intelligence_mod" value="0">
<input type="hidden" name="attr_wisdom_mod" value="0">
<input type="hidden" name="attr_charisma_mod" value="0">
<input type="hidden" name="attr_honor_mod" value="0">
<input type="hidden" name="attr_sanity_mod" value="0">
<input type="hidden" name="attr_strength_save_bonus" value="0">
<input type="hidden" name="attr_dexterity_save_bonus" value="0">
<input type="hidden" name="attr_constitution_save_bonus" value="0">
<input type="hidden" name="attr_intelligence_save_bonus" value="0">
<input type="hidden" name="attr_wisdom_save_bonus" value="0">
<input type="hidden" name="attr_charisma_save_bonus" value="0">
<input type="hidden" name="attr_honor_save_bonus" value="0">
<input type="hidden" name="attr_sanity_save_bonus" value="0">
<input type="hidden" name="attr_strength_maximum" value="20">
<input type="hidden" name="attr_dexterity_maximum" value="20">
<input type="hidden" name="attr_constitution_maximum" value="20">
<input type="hidden" name="attr_intelligence_maximum" value="20">
<input type="hidden" name="attr_wisdom_maximum" value="20">
<input type="hidden" name="attr_charisma_maximum" value="20">
<input type="hidden" name="attr_honor_maximum" value="20">
<input type="hidden" name="attr_sanity_maximum" value="20">
<input type='hidden' name='attr_acrobatics_bonus' value='0'>
<input type='hidden' name='attr_animal_handling_bonus' value='0'>
<input type='hidden' name='attr_arcana_bonus' value='0'>
<input type='hidden' name='attr_athletics_bonus' value='0'>
<input type='hidden' name='attr_deception_bonus' value='0'>
<input type='hidden' name='attr_history_bonus' value='0'>
<input type='hidden' name='attr_insight_bonus' value='0'>
<input type='hidden' name='attr_intimidation_bonus' value='0'>
<input type='hidden' name='attr_investigation_bonus' value='0'>
<input type='hidden' name='attr_medicine_bonus' value='0'>
<input type='hidden' name='attr_nature_bonus' value='0'>
<input type='hidden' name='attr_perception_bonus' value='0'>
<input type='hidden' name='attr_performance_bonus' value='0'>
<input type='hidden' name='attr_persuasion_bonus' value='0'>
<input type='hidden' name='attr_religion_bonus' value='0'>
<input type='hidden' name='attr_sleight_of_hand_bonus' value='0'>
<input type='hidden' name='attr_stealth_bonus' value='0'>
<input type='hidden' name='attr_survival_bonus' value='0'>
<input type='hidden' name='attr_lvl1_slots_total' value='0'>
<input type='hidden' name='attr_lvl2_slots_total' value='0'>
<input type='hidden' name='attr_lvl3_slots_total' value='0'>
<input type='hidden' name='attr_lvl4_slots_total' value='0'>
<input type='hidden' name='attr_lvl5_slots_total' value='0'>
<input type='hidden' name='attr_lvl6_slots_total' value='0'>
<input type='hidden' name='attr_lvl7_slots_total' value='0'>
<input type='hidden' name='attr_lvl8_slots_total' value='0'>
<input type='hidden' name='attr_lvl9_slots_total' value='0'>
<input type='hidden' name='attr_l1mancer_status' value=''>
<input type='hidden' name='attr_lpmancer_status' value=''>
<input type='hidden' name='attr_queryadvantage' value=''>
<input type='hidden' name='attr_syslog' value=''>
<input type='hidden' name='attr_utilitymancer_options' value=''>
<div class="compendium_warning"><span data-i18n="accepting-drop-u">ACCEPTING DROP FROM COMPENDIUM</span></div>
<div class="monster_confirm">
<span data-i18n="are_you_sure">Are you sure you want to turn this character into a </span><span name="attr_drop_name"></span><span>?</span>
<div style="margin-top: 20px;">
<div style="position: relative; display: inline-block; margin-right: 25px;">
<input class="yes" type="checkbox" name="attr_confirm"><span class="yes" data-i18n="yes">Yes</span>
</div>
<div style="position: relative; display: inline-block;">
<input class="cancel" type="checkbox" name="attr_cancel"><span class="cancel" data-i18n="cancel">Cancel</span>
</div>
</div>
</div>
<div class="mancer_confirm">
<span style="line-height: 25px" data-i18n="do_you_want_mancer">How do you want to create this character?</span>
<div style="margin-top: 20px;">
<div style="position: relative; display: inline-block; width:130px;">
<input class="yes" type="checkbox" style="width:130px;" name="attr_mancer_confirm"><span class="yes" style="width:130px;" data-i18n="mancer-confirm-yes">Use the Charactermancer</span>
</div>
<div style="position: relative; display: inline-block; width:80px;">
<input class="yes" style="width:80px;" type="checkbox" name="attr_mancer_npc"><span class="yes" style="width:80px;" data-i18n="npc-new">Create an NPC</span>
</div>
<div style="position: relative; display: inline-block; width:90px;">
<input class="yes" type="checkbox" style="width:90px;" name="attr_mancer_cancel"><span class="yes" style="width:90px;" data-i18n="manual">Edit sheet direclty</span>
</div>
</div>
</div>
<div class="rolltemplates"> <rolltemplate class="sheet-rolltemplate-simple">
<div class="container">
<div class="result">
{{#always}}
<div class="adv">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/always}}
{{#normal}}
<div class="solo">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/normal}}
{{#advantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollGreater() r1 r2}}
{{#imposed}}
<div class="label">
<span style="color: red">{{imposed}}</span>
</div>
{{/imposed}}
{{/disadvantage}}
</div>
{{#desctitle}}
{{#desc}}
<div class="description">
<span class="description__title">{{desctitle}}</span>
<span class="description__description">{{desc}}</span>
</div>
{{/desc}}
{{/desctitle}}
<div class="label">
<span>{{rname}}{{#mod}} <span>({{mod}})</span>{{/mod}}</span>
</div>
{{#charname}}
<div class="charname">
<span>{{charname}}</span>
</div>
{{/charname}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-atk">
<div class="container">
<div class="result">
{{#always}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/always}}
{{#normal}}
<div class="solo">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/normal}}
{{#advantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
{{#range}}
<div class="sublabel">
<span>{{range}}</span>
</div>
{{/range}}
<div class="label">
{{#normal}}
{{#rollWasCrit() r1}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r1}}
{{#^rollWasCrit() r1}}<span>{{rname}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/^rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{#^rollWasCrit() r1}}{{#^rollWasCrit() r2}}<span>{{rname}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/^rollWasCrit() r2}}{{/^rollWasCrit() r1}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{#^rollWasCrit() r1}}{{#^rollWasCrit() r2}}<span>{{rname}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/^rollWasCrit() r2}}{{/^rollWasCrit() r1}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span>{{rnamec}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollTotal() r1 r2}}{{#^rollWasCrit() r1}}<span>{{rname}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/^rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollLess() r1 r2}}<span>{{rname}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollLess() r1 r2}}
{{#rollGreater() r1 r2}}<span>{{rname}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}} <span>({{mod}})</span></span>{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
{{#charname}}
<div class="charname">
<span>{{charname}}</span>
</div>
{{/charname}}
{{#spelldesc_link}}
<div class="spelldesc-link">
<span>{{spelldesc_link}}</span>
</div>
{{/spelldesc_link}}
</div>
{{#desc}}
<div class="desc info">
<span>
<span class="top"></span><span class="middle">{{desc}}</span><span class="bottom"></span>
</span>
</div>
{{/desc}}
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-dmg">
{{#save}}
{{#attack}}
<div class="desc save">
{{/attack}}
{{^attack}}
<div class="atk save">
{{/attack}}
<div class="savedc">
<span class="rolltemplate-inline" data-i18n="difficulty-class-abv">DC</span><span class="rolltemplate-inline">{{savedc}}</span>
</div>
{{#savedesc}}
<div class="sublabel">
<span>{{savedesc}}</span>
</div>
{{/savedesc}}
<div class="label">
<span class="rolltemplate-inline">{{saveattr}}</span> <span class="rolltemplate-inline" data-i18n="save">Save</span>
</div>
</div>
{{/save}}
{{^attack}}
{{#desc}}
<div class="desc info">
<span>
<span class="top"></span><span class="middle">{{desc}}</span><span class="bottom"></span>
</span>
</div>
{{/desc}}
{{/attack}}
{{#globaldamage}}
{{#^rollTotal() globaldamage 0}}
<div class="desc">
<span>{{globaldamage}}{{#globaldamagecrit}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/globaldamagecrit}}</span>
<span class="sublabel">{{globaldamagetype}}</span>
</div>
{{/^rollTotal() globaldamage 0}}
{{/globaldamage}}
{{#globaldamage}}
{{#rollTotal() globaldamage 0}}
{{#globaldamagecrit}}
{{#^rollTotal() globaldamagecrit 0}}
<div class="desc">
<span>{{globaldamagecrit}}</span>
<span class="sublabel">{{globaldamagetype}}</span>
</div>
{{/^rollTotal() globaldamagecrit 0}}
{{/globaldamagecrit}}
{{/rollTotal() globaldamage 0}}
{{/globaldamage}}
{{^globaldamage}}
{{#globaldamagecrit}}
{{#^rollTotal() globaldamagecrit 0}}
<div class="desc">
<span>{{globaldamagecrit}}</span>
<span class="sublabel">{{globaldamagetype}}</span>
</div>
{{/^rollTotal() globaldamagecrit 0}}
{{/globaldamagecrit}}
{{/globaldamage}}
{{#hldmg}}
{{#^rollTotal() hldmg 0}}
<div class="desc">
<span>{{hldmg}}{{#hldmgcrit}}<span class="plus"> + </span>{{hldmgcrit}}{{/hldmgcrit}}</span>
<span class="sublabel">{{dmg1type}}</span>
<div class="label">
<span data-i18n="higher-lvl-cast">Higher Level Cast</span>
</div>
</div>
{{/^rollTotal() hldmg 0}}
{{/hldmg}}
<div class="container damagetemplate">
<div class="result">
{{#dmg1flag}}
{{^dmg2flag}}
<div class="solo">
<span class="damage">{{dmg1}}{{#crit}} + {{crit1}}{{/crit}}</span>
<span class="sublabel">{{dmg1type}}</span>
</div>
{{/dmg2flag}}
{{/dmg1flag}}
{{#dmg2flag}}
{{^dmg1flag}}
<div class="solo">
<span class="damage">{{dmg2}}{{#crit}} + {{crit2}}{{/crit}}</span>
<span class="sublabel">{{dmg2type}}</span>
</div>
{{/dmg1flag}}
{{/dmg2flag}}
{{#dmg1flag}}
{{#dmg2flag}}
<div class="adv">
<span class="damage">{{dmg1}}{{#crit}} + {{crit1}}{{/crit}}</span>
<span class="sublabel">{{dmg1type}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="damage">{{dmg2}}{{#crit}} + {{crit2}}{{/crit}}</span>
<span class="sublabel">{{dmg2type}}</span>
</div>
{{/dmg2flag}}
{{/dmg1flag}}
</div>
{{^attack}}
{{#range}}
<div class="sublabel" style="color: black;">
<span>{{range}}</span>
</div>
{{/range}}
<div class="label">
<span>{{rname}}</span>{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}}
</div>
{{#charname}}
<div class="charname">
<span>{{charname}}</span>
</div>
{{/charname}}
{{#spelldesc_link}}
<div class="spelldesc-link">
<span>{{spelldesc_link}}</span>
</div>
{{/spelldesc_link}}
{{/attack}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-atkdmg">
{{#attack}}
<div class="container atk">
<div class="result">
{{#always}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/always}}
{{#normal}}
<div class="solo">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/normal}}
{{#advantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{#globalattack}} + {{globalattack}}{{/globalattack}}</span>
</div>
{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
{{#range}}
<div class="sublabel">
<span>{{range}}</span>
</div>
{{/range}}
<div class="label">
<span>{{rname}}{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}}{{#attack}} <span>({{mod}})</span>{{/attack}}</span>
</div>
{{#charname}}
<div class="charname">
<span>{{charname}}</span>
</div>
{{/charname}}
{{#spelldesc_link}}
<div class="spelldesc-link">
<span>{{spelldesc_link}}</span>
</div>
{{/spelldesc_link}}
</div>
{{/attack}}
{{#save}}
{{#attack}}
<div class="container desc save">
{{/attack}}
{{^attack}}
<div class="container atk save">
{{/attack}}
<div class="savedc">
<span class="rolltemplate-inline" data-i18n="difficulty-class-abv">DC</span><span class="rolltemplate-inline">{{savedc}}</span>
</div>
{{#savedesc}}
<div class="sublabel">
<span>{{savedesc}}</span>
</div>
{{/savedesc}}
<div class="label">
<span class="rolltemplate-inline">{{saveattr}}</span> <span class="rolltemplate-inline" data-i18n="save">Save</span>
</div>
</div>
{{/save}}
{{#desc}}
<div class="desc info">
<span>
<span class="top"></span><span class="middle">{{desc}}</span><span class="bottom"></span>
</span>
</div>
{{/desc}}
{{#globaldamage}}
{{#^rollTotal() globaldamage 0}}
<div class="desc">
<span>
{{globaldamage}}
{{#normal}}
{{#rollWasCrit() r1}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{#^rollTotal() globaldamagecrit 0}}<span class="plus"> + </span>{{globaldamagecrit}}{{/^rollTotal() globaldamagecrit 0}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
</span>
<span class="sublabel">{{globaldamagetype}}</span>
</div>
{{/^rollTotal() globaldamage 0}}
{{#rollTotal() globaldamage 0}}
{{#^rollTotal() globaldamagecrit 0}}
{{#rollWasCrit() r1}}
<div class="desc">
<span>
{{#attack}}
{{#normal}}
{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{globaldamagecrit}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{globaldamagecrit}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{/attack}}
</span>
<span class="sublabel">{{globaldamagetype}}</span>
</div>
{{/rollWasCrit() r1}}
{{#rollWasCrit() r2}}
{{#^rollWasCrit() r1}}
<div class="desc">
<span>
{{#attack}}
{{#normal}}
{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{globaldamagecrit}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{globaldamagecrit}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{globaldamagecrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{/attack}}
</span>
<span class="sublabel">{{globaldamagetype}}</span>
</div>
{{/^rollWasCrit() r1}}
{{/rollWasCrit() r2}}
{{/^rollTotal() globaldamagecrit 0}}
{{/rollTotal() globaldamage 0}}
{{/globaldamage}}
{{#hldmg}}
{{#^rollTotal() hldmg 0}}
<div class="desc">
<span>
{{hldmg}}
<!-- {{#hldmgcrit}} + {{hldmgcrit}}{{/hldmgcrit}} -->
{{#attack}}
{{#normal}}
{{#rollWasCrit() r1}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span class="plus"> + </span>{{hldmgcrit}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{/attack}}
</span>
<span class="sublabel">{{dmg1type}}</span>
<div class="label">
<span data-i18n="higher-lvl-cast">Higher Level Cast</span>
</div>
</div>
{{/^rollTotal() hldmg 0}}
{{/hldmg}}
{{#damage}}
<div class="container damagetemplate">
<div class="result">
{{#dmg1flag}}
{{^dmg2flag}}
<div class="solo">
<span class="damage">
{{dmg1}}
{{#attack}}
{{#normal}}
{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{/attack}}
</span>
<span class="sublabel">{{dmg1type}}</span>
</div>
{{/dmg2flag}}
{{/dmg1flag}}
{{#dmg2flag}}
{{^dmg1flag}}
<div class="solo">
<span class="damage">
{{dmg2}}
{{#attack}}
{{#normal}}
{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{/attack}}
</span>
<span class="sublabel">{{dmg2type}}</span>
</div>
{{/dmg1flag}}
{{/dmg2flag}}
{{#dmg1flag}}
{{#dmg2flag}}
<div class="adv">
<span class="damage">
{{dmg1}}
{{#attack}}
{{#normal}}
{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{/attack}}
</span>
<span class="sublabel">{{dmg1type}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>
{{dmg2}}
{{#attack}}
{{#normal}}
{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}+ {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}+ {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{/attack}}
</span>
<span class="sublabel">{{dmg2type}}</span>
</div>
{{/dmg2flag}}
{{/dmg1flag}}
</div>
{{^attack}}
{{#range}}
<div class="sublabel">
<span>{{range}}</span>
</div>
{{/range}}
<div class="label">
<span>{{rname}}</span>{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}}
</div>
{{#charname}}
<div class="charname">
<span>{{charname}}</span>
</div>
{{/charname}}
{{#spelldesc_link}}
<div class="spelldesc-link">
<span>{{spelldesc_link}}</span>
</div>
{{/spelldesc_link}}
{{/attack}}
</div>
{{/damage}}
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-desc">
<div class="desc info">
<span>
<span class="top"></span><span class="middle">{{desc}}</span><span class="bottom"></span>
</span>
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-spell">
<div class="container">
<div class="title row">
<span>{{name}}</span>{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}}
</div>
<div class="italics row">
{{#level}}<span class="level">{{level}} </span>{{/level}}
{{#ritual}} (<span data-i18n="ritual-l"></span>){{/ritual}}
</div>
<div class="spacer"></div>
{{#castingtime}}
<div class="row">
<span class="bold" data-i18n="casting-time:">Casting Time:</span> <span>{{castingtime}}</span>
</div>
{{/castingtime}}
{{#range}}
<div class="row">
<span class="bold" data-i18n="range:">Range:</span> <span>{{range}}</span>
</div>
{{/range}}
{{#target}}
<div class="row">
<span class="bold" data-i18n="target:">Target:</span> <span>{{target}}</span>
</div>
{{/target}}
<div class="row">
<span class="bold" data-i18n="components:">Components:</span>
<span>
{{#v}}V{{#s}}, {{/s}}{{^s}}{{#m}}, {{/m}}{{/s}}{{/v}}
{{#s}}S{{#m}}, {{/m}}{{/s}}
{{#m}}M {{#material}}({{material}}){{/material}}{{/m}}
</span>
</div>
{{#duration}}
<div class="row">
<span class="bold" data-i18n="duration:">Duration:</span> <span>{{#concentration}}<span data-i18n="concentration">Concentration</span> {{/concentration}}{{duration}}</span>
</div>
{{/duration}}
<div class="spacer"></div>
{{#description}}
<div class="row">
<span class="description">{{description}}</span>
</div>
{{/description}}
{{#athigherlevels}}
<div class="row">
<span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span>. <span class="description">{{athigherlevels}}</span>
</div>
{{/athigherlevels}}
{{#savedc}}
<div class="row spellsavedc">
<span class="sheet-label" data-i18n="spell-save-dc-u">SPELL SAVE DC</span><span>: {{savedc}}</span>
</div>
{{/savedc}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-traits">
<div class="row header">
<span>{{name}}</span>
</div>
{{#source}}
<div class="row subheader">
<span class="italics">{{#charname}}{{charname}} {{/charname}}{{source}}</span>
</div>
{{/source}}
{{#description}}
<div class="row">
<span class="desc">{{description}}</span>
</div>
{{/description}}
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-npc">
<div class="row header">
<span>{{rname}}</span>
</div>
{{#name}}
<div class="row subheader">
<span class="italics">{{name}}</span>
</div>
{{/name}}
<div class="arrow-right"></div>
<div class="row">
{{#normal}}
<span class="italics">{{type}}: </span><span>{{r1}}</span>
{{/normal}}
{{#always}}
<span class="italics">{{type}}: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}
<span class="italics">{{type}}: </span><span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span class="italics">{{type}}: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span class="italics">{{type}}: </span><span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<span class="italics">{{type}}: </span><span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span class="italics">{{type}}: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span class="italics">{{type}}: </span><span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-npcatk">
<div class="row header">
{{#normal}}
{{#rollWasCrit() r1}}{{rnamec}}{{/rollWasCrit() r1}}
{{#^rollWasCrit() r1}}{{rname}}{{/^rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{rnamec}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{rnamec}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{rnamec}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{#^rollWasCrit() r1}}{{#^rollWasCrit() r2}}{{rname}}{{/^rollWasCrit() r2}}{{/^rollWasCrit() r1}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}{{rnamec}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{rnamec}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}{{rnamec}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{#^rollWasCrit() r1}}{{#^rollWasCrit() r2}}{{rname}}{{/^rollWasCrit() r2}}{{/^rollWasCrit() r1}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}{{rnamec}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollTotal() r1 r2}}{{#^rollWasCrit() r1}}{{rname}}{{/^rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r1}}{{rnamec}}{{/rollWasCrit() r1}}{{/rollLess() r1 r2}}
{{#rollLess() r1 r2}}{{#^rollWasCrit() r1}}{{rname}}{{/^rollWasCrit() r1}}{{/rollLess() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r2}}{{rnamec}}{{/rollWasCrit() r2}}{{/rollGreater() r1 r2}}
{{#rollGreater() r1 r2}}{{#^rollWasCrit() r2}}{{rname}}{{/^rollWasCrit() r2}}{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
{{#name}}
<div class="row subheader">
<span class="italics">{{name}}</span>
</div>
{{/name}}
<div class="arrow-right"></div>
<div class="row">
{{#normal}}
{{#rollWasCrit() r1}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r1}}
{{#^rollWasCrit() r1}}<span class="italics sheet-translated">{{type}}: </span>{{/^rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{#^rollWasCrit() r1}}{{#^rollWasCrit() r2}}<span class="italics sheet-translated">{{type}}: </span>{{/^rollWasCrit() r2}}{{/^rollWasCrit() r1}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}}<span class="italics sheet-translated">[{{typec}}: </span>{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{#^rollWasCrit() r1}}{{#^rollWasCrit() r2}}<span class="italics sheet-translated">{{type}}: </span>{{/^rollWasCrit() r2}}{{/^rollWasCrit() r1}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollTotal() r1 r2}}{{#^rollWasCrit() r1}}<span class="italics sheet-translated">{{type}}: </span>{{/^rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r1}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r1}}{{/rollLess() r1 r2}}
{{#rollLess() r1 r2}}{{#^rollWasCrit() r1}}<span class="italics sheet-translated">{{type}}: </span>{{/^rollWasCrit() r1}}{{/rollLess() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r2}}<span class="italics sheet-translated">{{typec}}: </span>{{/rollWasCrit() r2}}{{/rollGreater() r1 r2}}
{{#rollGreater() r1 r2}}{{#^rollWasCrit() r2}}<span class="italics sheet-translated">{{type}}: </span>{{/^rollWasCrit() r2}}{{/rollGreater() r1 r2}}
{{/disadvantage}}
{{#normal}}
<span>{{r1}}</span>
{{/normal}}
{{#always}}
<span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}
<span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
{{#description}}
<div class="row">
<span class="desc">{{description}}</span>
</div>
{{/description}}
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-npcdmg">
<div class="container dmgcontainer damagetemplate">
<span class="italics translated" data-i18n="dmg:-u">Damage: </span>
<span>
{{#dmg1flag}}
{{dmg1}}{{#crit}} + {{crit1}}{{/crit}}{{dmg1type}}
{{/dmg1flag}}
{{#dmg1flag}}{{#dmg2flag}}+{{/dmg2flag}}{{/dmg1flag}}
{{#dmg2flag}}
{{dmg2}}{{#crit}} + {{crit2}}{{/crit}}{{dmg2type}}
{{/dmg2flag}}
</span>
</div>
{{#description}}
<div class="row">
<span class="desc">{{description}}</span>
</div>
{{/description}}
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-dmgaction">
<div class="container dmgcontainer damagetemplate">
<div class="row header">
<span>{{rname}}</span>
</div>
{{#name}}
<div class="row subheader">
<span class="italics">{{name}}</span>
</div>
{{/name}}
<div class="arrow-right"></div>
<span class="italics translated" data-i18n="dmg:-u">Damage: </span>
<span>
{{#dmg1flag}}
{{dmg1}}{{#crit}} + {{crit1}}{{/crit}}{{dmg1type}}
{{/dmg1flag}}
{{#dmg1flag}}{{#dmg2flag}}+{{/dmg2flag}}{{/dmg1flag}}
{{#dmg2flag}}
{{dmg2}}{{#crit}} + {{crit2}}{{/crit}}{{dmg2type}}
{{/dmg2flag}}
</span>
</div>
{{#description}}
<div class="row">
<span class="desc">{{description}}</span>
</div>
{{/description}}
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-npcaction">
<div class="container">
<div class="row header">
<span>{{rname}}</span>
</div>
{{#name}}
<div class="row subheader">
<span class="italics">{{name}}</span>
</div>
{{/name}}
<div class="arrow-right"></div>
{{#attack}}
<div class="row">
{{#normal}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span>
{{/normal}}
{{#always}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
{{/attack}}
{{#description}}
<span class="desc">{{description}}</span>
{{/description}}
</div>
{{#damage}}
<div class="container dmgcontainer damagetemplate">
<span class="italics sheet-translated" data-i18n="dmg:-u">Damage: </span>
<span>
{{#dmg1flag}}
{{dmg1}}
{{#normal}}
{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{dmg1type}}
{{/dmg1flag}}
{{#dmg1flag}}{{#dmg2flag}}+{{/dmg2flag}}{{/dmg1flag}}
{{#dmg2flag}}
{{dmg2}}
{{#normal}}
{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{dmg2type}}
{{/dmg2flag}}
</span>
</div>
{{/damage}}
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-npcfullatk">
<div class="container">
<div class="row header">
<span>{{rname}}</span>
</div>
{{#name}}
<div class="row subheader">
<span class="italics">{{name}}</span>
</div>
{{/name}}
<div class="arrow-right"></div>
{{#attack}}
<div class="row">
{{#normal}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span>
{{/normal}}
{{#always}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span class="grey">{{r2}}</span>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span>{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<span class="italics sheet-translated" data-i18n="attack:-u">Attack: </span><span class="grey">{{r1}}</span><span> | </span><span>{{r2}}</span>
{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
{{/attack}}
</div>
<div class="container dmgcontainer damagetemplate">
<span class="italics sheet-translated" data-i18n="dmg:-u">Damage: </span>
{{#damage}}
<span>
{{#dmg1flag}}
{{dmg1}}
{{#normal}}
{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit1}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit1}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{dmg1type}}
{{/dmg1flag}}
{{#dmg1flag}}{{#dmg2flag}}+{{/dmg2flag}}{{/dmg1flag}}
{{#dmg2flag}}
{{dmg2}}
{{#normal}}
{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}
{{/normal}}
{{#always}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/always}}
{{#advantage}}
{{#rollLess() r1 r2}}{{#rollWasCrit() r2}} + {{crit2}}{{/rollWasCrit() r2}}{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollTotal() r1 r2}}{{#rollWasCrit() r1}} + {{crit2}}{{/rollWasCrit() r1}}{{/rollTotal() r1 r2}}
{{/disadvantage}}
{{dmg2type}}
{{/dmg2flag}}
</span>
{{/damage}}
{{#description}}
<div class="row">
<span class="desc">{{description}}</span>
</div>
{{/description}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-mancerroll">
<div class="container">
<div class="row header">
<span>
{{title}}
{{#c1}}
<span>{{c1}}</span>
{{/c1}}
</span>
</div>
{{#r1}}
<div class="row">
<span class="desc">Roll 1: </span>
<span class="result">{{r1}}</span>
</div>
{{/r1}}
{{#r2}}
<div class="row">
<span class="desc">Roll 2: </span>
<span class="result">{{r2}}</span>
</div>
{{/r2}}
{{#r3}}
<div class="row">
<span class="desc">Roll 3: </span>
<span class="result">{{r3}}</span>
</div>
{{/r3}}
{{#r4}}
<div class="row">
<span class="desc">Roll 4: </span>
<span class="result">{{r4}}</span>
</div>
{{/r4}}
{{#r5}}
<div class="row">
<span class="desc">Roll 5: </span>
<span class="result">{{r5}}</span>
</div>
{{/r5}}
{{#r6}}
<div class="row">
<span class="desc">Roll 6: </span>
<span class="result">{{r6}}</span>
</div>
{{/r6}}
<div class="row">
<span class="desc">
{{#rollTotal() c1 1}}{{option1}}{{/rollTotal() c1 1}}
{{#rollTotal() c1 2}}{{option2}}{{/rollTotal() c1 2}}
{{#rollTotal() c1 3}}{{option3}}{{/rollTotal() c1 3}}
{{#rollTotal() c1 4}}{{option4}}{{/rollTotal() c1 4}}
{{#rollTotal() c1 5}}{{option5}}{{/rollTotal() c1 5}}
{{#rollTotal() c1 6}}{{option6}}{{/rollTotal() c1 6}}
{{#rollTotal() c1 7}}{{option7}}{{/rollTotal() c1 7}}
{{#rollTotal() c1 8}}{{option8}}{{/rollTotal() c1 8}}
{{#rollTotal() c1 9}}{{option9}}{{/rollTotal() c1 9}}
{{#rollTotal() c1 10}}{{option10}}{{/rollTotal() c1 10}}
{{#rollTotal() c1 11}}{{option11}}{{/rollTotal() c1 11}}
{{#rollTotal() c1 12}}{{option12}}{{/rollTotal() c1 12}}
{{#rollTotal() c1 13}}{{option13}}{{/rollTotal() c1 13}}
{{#rollTotal() c1 14}}{{option14}}{{/rollTotal() c1 14}}
{{#rollTotal() c1 15}}{{option15}}{{/rollTotal() c1 15}}
{{#rollTotal() c1 16}}{{option16}}{{/rollTotal() c1 16}}
{{#rollTotal() c1 17}}{{option17}}{{/rollTotal() c1 17}}
{{#rollTotal() c1 18}}{{option18}}{{/rollTotal() c1 18}}
{{#rollTotal() c1 19}}{{option19}}{{/rollTotal() c1 19}}
{{#rollTotal() c1 20}}{{option20}}{{/rollTotal() c1 20}}
</span>
</div>
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-mancerhproll">
<div class="container">
<div class="row header">
<span>
{{title}}
{{#c1}}
<span>{{c1}}</span>
{{/c1}}
</span>
</div>
{{#a1}}
<div class="row">
<span class="desc">Average: </span><span class="result">{{a1}}</span>
</div>
{{/a1}}
{{#r1}}
<div class="row">
<span class="desc">Roll 1: </span><span class="result">{{r1}}</span>
</div>
{{/r1}}
{{#r2}}
<div class="row">
<span class="desc">Roll 2: </span><span class="result">{{r2}}</span>
</div>
{{/r2}}
{{#r3}}
<div class="row">
<span class="desc">Roll 3: </span><span class="result">{{r3}}</span>
</div>
{{/r3}}
{{#r4}}
<div class="row">
<span class="desc">Roll 4: </span><span class="result">{{r4}}</span>
</div>
{{/r4}}
{{#r5}}
<div class="row">
<span class="desc">Roll 5: </span><span class="result">{{r5}}</span>
</div>
{{/r5}}
{{#r6}}
<div class="row">
<span class="desc">Roll 6: </span><span class="result">{{r6}}</span>
</div>
{{/r6}}
{{#r7}}
<div class="row">
<span class="desc">Roll 7: </span><span class="result">{{r7}}</span>
</div>
{{/r7}}
{{#r8}}
<div class="row">
<span class="desc">Roll 8: </span><span class="result">{{r8}}</span>
</div>
{{/r8}}
{{#r9}}
<div class="row">
<span class="desc">Roll 9: </span><span class="result">{{r9}}</span>
</div>
{{/r9}}
{{#r10}}
<div class="row">
<span class="desc">Roll 10: </span><span class="result">{{r10}}</span>
</div>
{{/r10}}
{{#r11}}
<div class="row">
<span class="desc">Roll 11: </span><span class="result">{{r11}}</span>
</div>
{{/r11}}
{{#r12}}
<div class="row">
<span class="desc">Roll 12: </span><span class="result">{{r12}}</span>
</div>
{{/r12}}
{{#r13}}
<div class="row">
<span class="desc">Roll 13: </span><span class="result">{{r13}}</span>
</div>
{{/r13}}
{{#r14}}
<div class="row">
<span class="desc">Roll 14: </span><span class="result">{{r14}}</span>
</div>
{{/r14}}
{{#r15}}
<div class="row">
<span class="desc">Roll 15: </span><span class="result">{{r15}}</span>
</div>
{{/r15}}
{{#r16}}
<div class="row">
<span class="desc">Roll 16: </span><span class="result">{{r16}}</span>
</div>
{{/r16}}
{{#r17}}
<div class="row">
<span class="desc">Roll 17: </span><span class="result">{{r17}}</span>
</div>
{{/r17}}
{{#r18}}
<div class="row">
<span class="desc">Roll 18: </span><span class="result">{{r18}}</span>
</div>
{{/r18}}
{{#r19}}
<div class="row">
<span class="desc">Roll 19: </span><span class="result">{{r19}}</span>
</div>
{{/r19}}
{{#r20}}
<div class="row">
<span class="desc">Roll 20: </span><span class="result">{{r20}}</span>
</div>
{{/r20}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-skill">
<div class="container">
<div class="result">
{{#always}}
<div class="roll">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="roll">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{#rollGreater() reliablecheck 0}}
{{#rollLess() r1 reliableroll}}
{{#rollLess() r2 reliableroll}}
<div class="roll reliableroll">
<span class=>{{reliableroll}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r2 reliableroll}}
{{/rollLess() r1 reliableroll}}
{{/rollGreater() reliablecheck 0}}
{{/always}}
{{#normal}}
<div class="roll">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{#rollGreater() reliablecheck 0}}
{{#rollLess() r1 reliableroll}}
<div class="roll reliableroll">
<span class=>{{reliableroll}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 reliableroll}}
{{/rollGreater() reliablecheck 0}}
{{/normal}}
{{#advantage}}
{{#rollLess() r1 r2}}
<div class="roll">
<span class="grey">{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="roll">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="roll">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="roll">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="roll">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="roll">
<span class="grey">{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollGreater() r1 r2}}
{{#rollGreater() reliablecheck 0}}
{{#rollLess() r1 reliableroll}}
{{#rollLess() r2 reliableroll}}
<div class="roll reliableroll">
<span class=>{{reliableroll}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r2 reliableroll}}
{{/rollLess() r1 reliableroll}}
{{/rollGreater() reliablecheck 0}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<div class="roll">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="roll">
<span class="grey">{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{#rollGreater() reliablecheck 0}}
{{#rollLess() r1 reliableroll}}
<div class="roll reliableroll">
<span class=>{{reliableroll}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 reliableroll}}
{{/rollGreater() reliablecheck 0}}
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="roll">
<span>{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="roll">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{#rollGreater() reliablecheck 0}}
{{#rollLess() r1 reliableroll}}
<div class="roll reliableroll">
<span class=>{{reliableroll}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 reliableroll}}
{{/rollGreater() reliablecheck 0}}
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="roll">
<span class="grey">{{r1}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="roll">
<span>{{r2}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{#rollGreater() reliablecheck 0}}
{{#rollLess() r2 reliableroll}}
<div class="roll reliableroll">
<span class=>{{reliableroll}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r2 reliableroll}}
{{/rollGreater() reliablecheck 0}}
{{/rollGreater() r1 r2}}
{{/disadvantage}}
</div>
<div class="label">
<span>{{rname}}{{#mod}} <span>({{mod}})</span>{{/mod}}</span>
</div>
{{#charname}}
<div class="charname">
<span>{{charname}}</span>
</div>
{{/charname}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-simple3D">
<div class="container">
<div class="result">
{{#always}}
<div class="adv">
<span>{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/always}}
{{#normal}}
<div class="solo">
<span>{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/normal}}
{{#advantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span>{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollGreater() r1 r2}}
{{/advantage}}
{{#disadvantage}}
{{#rollLess() r1 r2}}
<div class="adv">
<span>{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span class="grey">{{r2}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollLess() r1 r2}}
{{#rollTotal() r1 r2}}
<div class="adv">
<span>{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollTotal() r1 r2}}
{{#rollGreater() r1 r2}}
<div class="adv">
<span class="grey">{{r1}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
<div class="advspacer"></div>
<div class="adv">
<span>{{r2}}{{r3}}{{#global}} + {{global}}{{/global}}</span>
</div>
{{/rollGreater() r1 r2}}
{{#imposed}}
<div class="label">
<span style="color: red">{{imposed}}</span>
</div>
{{/imposed}}
{{/disadvantage}}
</div>
<div class="label">
<span>{{rname}}{{#mod}} <span>({{mod}})</span>{{/mod}}</span>
</div>
{{#charname}}
<div class="charname">
<span>{{charname}}</span>
</div>
{{/charname}}
</div>
</rolltemplate>
<rolltemplate class="sheet-rolltemplate-spelloutput">
<div class="container">
<div class="title desconly row">
<span data-i18n="desconly">For Description Only</span>
</div>
<div class="title row">
<span>{{name}}</span>{{#innate}}<span class="grey">, {{innate}}</span>{{/innate}}
</div>
<div class="italics row">
{{#level}}<span class="level">{{level}} </span>{{/level}}
{{#ritual}} (<span data-i18n="ritual-l"></span>){{/ritual}}
</div>
<div class="spacer"></div>
{{#castingtime}}
<div class="row">
<span class="bold" data-i18n="casting-time:">Casting Time:</span> <span>{{castingtime}}</span>
</div>
{{/castingtime}}
{{#range}}
<div class="row">
<span class="bold" data-i18n="range:">Range:</span> <span>{{range}}</span>
</div>
{{/range}}
{{#target}}
<div class="row">
<span class="bold" data-i18n="target:">Target:</span> <span>{{target}}</span>
</div>
{{/target}}
<div class="row">
<span class="bold" data-i18n="components:">Components:</span>
<span>
{{#v}}V{{#s}}, {{/s}}{{^s}}{{#m}}, {{/m}}{{/s}}{{/v}}
{{#s}}S{{#m}}, {{/m}}{{/s}}
{{#m}}M {{#material}}({{material}}){{/material}}{{/m}}
</span>
</div>
{{#duration}}
<div class="row">
<span class="bold" data-i18n="duration:">Duration:</span> <span>{{#concentration}}<span data-i18n="concentration">Concentration</span> {{/concentration}}{{duration}}</span>
</div>
{{/duration}}
<div class="spacer"></div>
{{#description}}
<div class="row">
<span class="description">{{description}}</span>
</div>
{{/description}}
{{#athigherlevels}}
<div class="row">
<span class="bold italics" data-i18n="at-higher-lvl">At Higher Levels</span>. <span class="description">{{athigherlevels}}</span>
</div>
{{/athigherlevels}}
{{#savedc}}
<div class="row spellsavedc">
<span class="sheet-label" data-i18n="spell-save-dc-u">SPELL SAVE DC</span><span>: {{savedc}}</span>
</div>
{{/savedc}}
</div>
</rolltemplate>
</div>
<script type="text/worker">const characterClassList = [{"name":"Artificer","hitdie":"d8","caster":"half"},{"name":"Bard","hitdie":"d8","caster":"full"},{"name":"Barbarian","hitdie":"d12"},{"name":"Beastheart","hitdie":"d8"},{"name":"Cleric","hitdie":"d8","caster":"full"},{"name":"Druid","hitdie":"d8","caster":"full"},{"name":"Fighter","hitdie":"d10"},{"name":"Illrigger","hitdie":"d10"},{"name":"Monk","hitdie":"d8"},{"name":"Paladin","hitdie":"d10","caster":"half"},{"name":"Ranger","hitdie":"d10","caster":"half"},{"name":"Rogue","hitdie":"d8"},{"name":"Sorcerer","hitdie":"d6","caster":"full"},{"name":"Tactician","hitdie":"d8"},{"name":"Warlock","hitdie":"d8","caster":"pact"},{"name":"Wizard","hitdie":"d6","caster":"full"}];
const characterClasses = function( classList = characterClassList
) {
return {
filter: (filters={}, lowercase=false) => {
const filteredList = classList.filter(entry => {
let valid = true;
Object.entries(filters).forEach(([key, value]) => {
if(value === true) {
valid = (entry[key]) ? true : false;
}
else if(value === false) {
valid = (entry[key]) ? false : true;
}
else {
valid = (entry[key] === value) ? true : false;
}
});
return (valid) ? entry : false;
});
return characterClasses(filteredList);
},
get: (lowercase=false) => {
return classList.map(entry => {
return (lowercase) ? entry.name.toLowerCase() : entry.name;
});
}
};
};
(() => {
var $parcel$global =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {};
var $4b5d377488db7e7f$exports = {};
const $24d20168587231de$export$7934401d6b2b7bb9 = (html, isSanitize = true)=>{
let sanitizedHTML = `${html}`;
const sanitize = (finder)=>{
sanitizedHTML = sanitizedHTML.replace(finder, (match, open, value, close)=>{
if (!match.includes(">") && !match.includes("&gt;")) return match;
const sanitizedValue = isSanitize ? value.replace(/>/g, "&gt;") : value.replace(/&gt;/g, ">");
return `${open}${sanitizedValue}${close}`;
});
};
sanitize(/(\w+\s*?=\s*?")((?:.|\n)*?)?"/g);
sanitize(/(\w+\s*?=\s*?')((?:.|\n)*?)?'/g);
return sanitizedHTML;
};
const $fa68e1901038ec1b$var$getTagAttributes = (tag)=>{
const rawAttributes = [
...tag.matchAll(/(?<attribute>\w+\s*?)=\s*?"(?<value>(?:.|\n)*?)"/g),
...tag.matchAll(/(?<attribute>\w+\s*?)=\s*?'(?<value>(?:.|\n)*?)'/g)
];
const attributes = {};
rawAttributes.forEach((rawAttribute)=>{
if (!rawAttribute.groups || !rawAttribute.groups.attribute || !rawAttribute.groups.value) return;
attributes[rawAttribute.groups.attribute] = rawAttribute.groups.value.replace(/\\"/, "&quot").replace(/\\'/g, "&apos;");
});
return attributes;
};
const $fa68e1901038ec1b$var$getTagFlags = (tag, exclude)=>{
tag = tag.replace(/(?<attribute>\w+\s*?)=\s*?"(?<value>(?:.|\n)*?)"/g, "").replace(/(?<attribute>\w+\s*?)=\s*?'(?<value>(?:.|\n)*?)'/g, "").replace(/ +/, " ").replace(/[^a-z]/, "");
const allowedFlags = [
"allowfullscreen",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"defer",
"disabled",
"formnovalidate",
"inert",
"ismap",
"itemscope",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected"
];
const rawFlags = tag.split(" ");
const flags = new Set();
rawFlags.forEach((rawFlag)=>{
const trimmedFlag = rawFlag.trim().replace(/[^a-z]/gi, "");
if (trimmedFlag !== exclude && allowedFlags.includes(trimmedFlag)) flags.add(trimmedFlag);
});
return flags;
};
const $fa68e1901038ec1b$var$parseHTMLAttributes = (attributes)=>{
const attributeList = [];
Object.keys(attributes).forEach((attribute)=>{
attributeList.push(`${attribute}="${attributes[attribute]}"`);
});
return attributeList;
};
const $fa68e1901038ec1b$export$eaad88a3bb1a3078 = (html, normalTag)=>{
return $fa68e1901038ec1b$var$getHTMLTags(html, normalTag);
};
const $fa68e1901038ec1b$export$b1ccc9f4045c4095 = (html, normalTag)=>{
return $fa68e1901038ec1b$var$getHTMLTags(html, normalTag, true);
};
const $fa68e1901038ec1b$var$getHTMLTags = (html, htmlTag, isVoidTag = false)=>{
const normalTagFinder = `(?<open><${htmlTag}.*?>)(?<content>(.|\n)*?)(?<close></ *?${htmlTag} *?>)`;
const voidTagFinder = `(?<open><${htmlTag}(.|\n)*?)(?<close>/>|>)`;
const tagFinder = isVoidTag ? new RegExp(voidTagFinder, "g") : new RegExp(normalTagFinder, "g");
const rawTags = [
...html.matchAll(tagFinder)
];
const tags = [];
rawTags.forEach((rawTag)=>{
if (!rawTag.groups || !rawTag.groups.open || !rawTag.groups.close || !isVoidTag && !Object.prototype.hasOwnProperty.call(rawTag.groups, "content")) return;
const tagOpenRaw = [
...rawTag.groups.open.matchAll(/\w+/g)
][0][0];
const tagName = $fa68e1901038ec1b$var$getTagAttributes(rawTag.groups.open)["name"] || "";
const hasLinkedAttribute = tagName.includes("comp_");
const linkedAttribute = hasLinkedAttribute ? tagName.replace("comp_", "") : "";
const tag = {
raw: rawTag[0],
tag: tagOpenRaw,
linkedAttribute: linkedAttribute,
content: rawTag.groups.content,
attributes: $fa68e1901038ec1b$var$getTagAttributes(rawTag.groups.open),
flags: $fa68e1901038ec1b$var$getTagFlags(rawTag.groups.open, tagOpenRaw),
isVoidTag: isVoidTag,
toString: ()=>{
const closingString = tag.isVoidTag ? ` />` : `>${tag.content}</${tag.tag}>`;
return `<${tag.tag} ${[
...$fa68e1901038ec1b$var$parseHTMLAttributes(tag.attributes),
...Array.from(tag.flags)
].join(" ")}${closingString}`;
},
replaceIn: (html)=>{
return html.replace(new RegExp(tag.raw, "g"), tag.toString());
}
};
tags.push(tag);
});
return tags;
};
const $bb48c2576badbf22$export$50f1e9b9db903a6a = (html, mancerData)=>{
let parsedHTML = `${html}`;
const inputTags = (0, $fa68e1901038ec1b$export$b1ccc9f4045c4095)(parsedHTML, "input");
//Text Inputs
inputTags.filter((input)=>input.linkedAttribute && input.attributes.type && input.attributes.type === "text").forEach((input)=>{
if (!Object.prototype.hasOwnProperty.call(mancerData, input.linkedAttribute)) return;
input.attributes.value = mancerData[input.linkedAttribute];
parsedHTML = input.replaceIn(parsedHTML);
});
//Number Inputs
inputTags.filter((input)=>input.linkedAttribute && input.attributes.type && input.attributes.type === "number").forEach((input)=>{
if (!Object.prototype.hasOwnProperty.call(mancerData, input.linkedAttribute) || Number.isNaN(Number(mancerData[input.linkedAttribute]))) return;
input.attributes.value = mancerData[input.linkedAttribute];
parsedHTML = input.replaceIn(parsedHTML);
});
//Checkboxes
inputTags.filter((input)=>input.linkedAttribute && input.attributes.type && input.attributes.type === "checkbox" && Object.prototype.hasOwnProperty.call(input.attributes, "value")).forEach((input)=>{
if (!Object.prototype.hasOwnProperty.call(mancerData, input.linkedAttribute)) return;
let valueAsArray;
try {
valueAsArray = JSON.parse(mancerData[input.linkedAttribute]);
} catch (e) {
valueAsArray = [
mancerData[input.linkedAttribute]
];
}
if (!Array.isArray(valueAsArray)) valueAsArray = [
valueAsArray
];
if (valueAsArray.includes(input.attributes.value)) input.flags.add("checked");
else input.flags.delete("checked");
parsedHTML = input.replaceIn(parsedHTML);
});
const radioGroups = {};
inputTags.filter((input)=>input.linkedAttribute && input.attributes.type && input.attributes.type === "radio" && Object.prototype.hasOwnProperty.call(input.attributes, "value")).forEach((input)=>{
if (!Object.prototype.hasOwnProperty.call(mancerData, input.linkedAttribute)) return;
if (!radioGroups[input.linkedAttribute]) radioGroups[input.linkedAttribute] = [];
radioGroups[input.linkedAttribute].push(input);
});
Object.values(radioGroups).forEach((group)=>{
let groupIsChecked = false;
group.forEach((input)=>{
if (mancerData[input.linkedAttribute] === input.attributes.value && !groupIsChecked) {
input.flags.add("checked");
groupIsChecked = true;
} else input.flags.delete("checked");
parsedHTML = input.replaceIn(parsedHTML);
});
});
return parsedHTML;
};
const $567be3f0dbb7e122$export$34dafcd4a0ece55a = (html, mancerData)=>{
let parsedHTML = `${html}`;
const selectTags = (0, $fa68e1901038ec1b$export$eaad88a3bb1a3078)(parsedHTML, "select");
//Text Inputs
selectTags.filter((select)=>select.linkedAttribute).forEach((select)=>{
if (!Object.prototype.hasOwnProperty.call(mancerData, select.linkedAttribute)) return;
if (!select.content) return;
const optionTags = (0, $fa68e1901038ec1b$export$eaad88a3bb1a3078)(select.content, "option");
optionTags.filter((option)=>Object.prototype.hasOwnProperty.call(option.attributes, "value")).forEach((option)=>{
if (mancerData[select.linkedAttribute] === option.attributes.value) option.flags.add("selected");
else option.flags.delete("selected");
select.content = option.replaceIn(select.content || "");
});
parsedHTML = select.replaceIn(parsedHTML);
});
return parsedHTML;
};
const $72f3b47bb1cd8dfa$export$4fc24f487de871e7 = (html, mancerData)=>{
let parsedHTML = `${html}`;
const spanTags = (0, $fa68e1901038ec1b$export$eaad88a3bb1a3078)(parsedHTML, "span");
//Text Inputs
spanTags.filter((span)=>span.linkedAttribute).forEach((span)=>{
if (!Object.prototype.hasOwnProperty.call(mancerData, span.linkedAttribute)) return;
span.content = mancerData[span.linkedAttribute];
parsedHTML = span.replaceIn(parsedHTML);
});
return parsedHTML;
};
const $b44a26de175dc214$export$da67368667246d82 = (html, mancerData)=>{
let parsedHTML = `${html}`;
const textAreaTags = (0, $fa68e1901038ec1b$export$eaad88a3bb1a3078)(parsedHTML, "textarea");
//Text Inputs
textAreaTags.filter((textArea)=>textArea.linkedAttribute).forEach((textArea)=>{
if (!Object.prototype.hasOwnProperty.call(mancerData, textArea.linkedAttribute)) return;
textArea.content = mancerData[textArea.linkedAttribute];
parsedHTML = textArea.replaceIn(parsedHTML);
});
return parsedHTML;
};
const $2bd4375024c9a5be$export$c3a4acefcffc89e2 = (html, mancerData)=>{
let parsedHTML = `${(0, $24d20168587231de$export$7934401d6b2b7bb9)(html)}`;
parsedHTML = (0, $bb48c2576badbf22$export$50f1e9b9db903a6a)(parsedHTML, mancerData);
parsedHTML = (0, $567be3f0dbb7e122$export$34dafcd4a0ece55a)(parsedHTML, mancerData);
parsedHTML = (0, $72f3b47bb1cd8dfa$export$4fc24f487de871e7)(parsedHTML, mancerData);
parsedHTML = (0, $b44a26de175dc214$export$da67368667246d82)(parsedHTML, mancerData);
return parsedHTML;
};
$parcel$global.parseCharmancerHTML = (0, $2bd4375024c9a5be$export$c3a4acefcffc89e2);
})();
class SheetUtils {
static getUID(value) {
const regexp = /-.{19}(?=_)|-.{19}$/g;
const valueAsString = String(value);
const match = valueAsString.match(regexp);
return (match) ? match.join() : "";
}
static aOrAn(value) {
if(['a','e','i','o','u'].indexOf(value.toLowerCase()[0]) > -1) return `an ${value}`;
return `a ${value}`;
}
static toLowerCase(value) {
if(Array.isArray(value)) {
const lowerCaseArray = value.map(item => { return item.toLowerCase(); });
return lowerCaseArray;
}
return String(value).toLowerCase();
}
static toCapitalCase(value) {
if(Array.isArray(value)) {
const capitalCaseArray = value.map(item => { return item[0].toUpperCase() + item.toLowerCase().slice(1); });
return capitalCaseArray;
}
return String(value)[0].toUpperCase() + String(value).toLowerCase().slice(1);
}
static getSectionOrder(section, callback) {
const sectionName = `repeating_${section}`;
const attributeName = `_reporder_${sectionName}`;
getAttrs([attributeName], attributes => {
let values = new Array();
Object.keys(attributes).some(() => {
let results = SheetUtils.toLowerCase(attributes[attributeName].replace(/ /g, '').split(','));
values = values.concat(results);
return true;
});
getSectionIDs(sectionName, ids => {
const filteredOrder = new Set();
const order = values.concat(SheetUtils.toLowerCase(ids)).filter(entry => { return SheetUtils.toLowerCase(ids).indexOf(entry) > -1; });
order.forEach(item => {
filteredOrder.add(item);
});
callback(Array.from(filteredOrder));
});
});
}
static deepReadAttribute(attr, callback, defaultValue) {
if(/^(@{).+}$/.test(String(attr)) === true) {
const regExp = /@\{([^@\{\}]+)\}/;
const reference = regExp.exec(attr)[1];
var self = this;
getAttrs([reference], function(v) {
if(!v[reference]) {
callback(defaultValue);
} else {
self.deepReadAttribute(v[reference], callback, defaultValue);
}
});
}
else {
callback(attr);
}
}
static sanitizeXP(value) {
const trimmedValue = value.trim();
if(!trimmedValue) return [];
const valueWithDecimalsRemoved = trimmedValue.replace(/,|\.| +/g, '');
const regex = /[0-9]+/g;
let xp = valueWithDecimalsRemoved.match(regex);
if(Array.isArray(xp)) xp = xp.map(entry => { return parseInt(entry) });
return xp;
}
static checkPath(obj, path, returnFalseOnEmpty = false) {
const sanitizedPath = path.replace(/\]\[|[\[,\]]/g, '.').replace(/\.+/g, '.').replace(/[",',]|\.$|^\./g, '');
const properties = sanitizedPath.split('.');
let level = obj;
for(let property = 0; property < properties.length; property++) {
if((returnFalseOnEmpty && !level[properties[property]]) || (!returnFalseOnEmpty && !(properties[property] in level))) return false;
level = level[properties[property]];
}
return true;
}
}class RefereceSet extends Set {
constructor(...args) {
super(...args);
}
toggle(value) {
const toggleLookup = {
true: 'delete',
false: 'add'
};
const hasValue = this.has(value);
return this[toggleLookup[hasValue]](value);
}
}
class FlagManager {
constructor(reference) {
this._reference = reference;
this._delimiter = ' ';
this._delimiterSafe = '_';
}
async getFlags() {
const entries = await this._getReferenceSet();
return entries;
}
async hasFlag(flag) {
const entries = await this._getReferenceSet();
return entries.has(flag);
}
async addFlag(flag) {
const updatedReference = await this._updateReference(flag, 'add');
return updatedReference;
}
async deleteFlag(flag) {
const updatedReference = await this._updateReference(flag, 'delete');
return updatedReference;
}
async toggleFlag(flag) {
const updatedReference = await this._updateReference(flag, 'toggle');
return updatedReference;
}
async _updateReference(flag, method) {
const sanitizedFlag = this._sanitizeFlag(flag);
const updatedReference = await this._setReference(sanitizedFlag, method);
return updatedReference;
}
async syncFlagsWithRepeatingSection(section) {
const flags = await this._syncFlagsWithRepeatingSection(section);
return flags;
}
_syncFlagsWithRepeatingSection(section) {
return new Promise(resolve => {
SheetUtils.getSectionOrder(section, order => {
getAttrs([this._reference], attributes => {
let values = "";
Object.keys(attributes).some(() => {
values += attributes[this._reference];
return true;
});
const flags = (values) ? values.split(' ') : [];
const sectionName = `repeating_${section}`;
const filteredArray = Array.from(flags).filter(flag => flag.includes(sectionName)).filter(flag => order.includes(SheetUtils.getUID(flag).toLowerCase()));
filteredArray.forEach((flag, index) => {
const newIndex = order.indexOf(SheetUtils.getUID(flag).toLowerCase());
filteredArray[index] = flag.replace(this._getFlagIndex(flag), `_${newIndex}_`);
});
const updatedSet = Array.from(flags).filter(flag => !flag.includes(sectionName)).concat(filteredArray);
let update = {};
update[this._reference] = updatedSet.join(' ');
setAttrs(update, () => {
return resolve(new Set(updatedSet));
});
});
});
});
}
_getFlagIndex(flag) {
const regexp = /_[0-9]+_/g;
const valueAsString = String(flag);
const match = valueAsString.match(regexp);
return (match) ? match.join() : "";
}
_getReferenceSet() {
return new Promise(resolve => {
getAttrs([this._reference], attributes => {
let values = "";
Object.keys(attributes).some(() => {
values += attributes[this._reference];
return true;
});
const flags = values.split(this._delimiter).filter(e => {return e});
return resolve(new RefereceSet(flags));
});
});
}
_setReference(sanitizedFlag, method) {
return new Promise(resolve => {
getAttrs([this._reference], attributes => {
let values = "";
Object.keys(attributes).some(() => {
values += attributes[this._reference];
return true;
});
const flags = new RefereceSet(values.split(this._delimiter).filter(e => {return e}));
flags[method](sanitizedFlag);
const flagAsString = Array.from(flags).join(this._delimiter);
let update = {};
update[this._reference] = flagAsString;
setAttrs(update, () => {
return resolve(flags);
});
});
});
}
_sanitizeFlag(flag) {
const regex = new RegExp(`${this._delimiter}`, 'g');
const sanitizedFlag = flag.replace(regex, this._delimiterSafe);
return sanitizedFlag;
}
}
class SheetUpdater {
constructor(listOfUpdates, versionAttribute = 'version', appliedUpdatesAttribute = 'appliedUpdates', delimiter = ',', reportInvalidUpdates = false) {
this._listOfUpdates = (Array.isArray(listOfUpdates)) ? Array.from(new Set(listOfUpdates)) : [];
this._versionAttribute = versionAttribute;
this._appliedUpdatesAttribute = appliedUpdatesAttribute;
this._delimiter = delimiter;
this._reportInvalidUpdate = reportInvalidUpdates;
}
getAppliedUpdates(callback) {
getAttrs([this._appliedUpdatesAttribute], values => {
const performedUpdates = (values[this._appliedUpdatesAttribute]) ? values[this._appliedUpdatesAttribute].split(this._delimiter) : [];
callback(performedUpdates);
});
}
setAppliedUpdates(updates, callback) {
let set = {};
set[this._appliedUpdatesAttribute] = Array.from(new Set(updates)).join(this._delimiter);
setAttrs(set, () => {
callback();
});
}
checkUpdates(callback) {
let log = {};
this.getAppliedUpdates(appliedUpdates => {
const existingUpdates = this._listOfUpdates.filter(entry => { return appliedUpdates.indexOf(entry.id) > -1 }).map(entry => { return entry.id; });
existingUpdates.forEach(entry => {
log[entry] = {success: false, error: SheetUpgrade.getStatusMsg('ALREADY_INSTALLED')};
});
this.processNextUpdate(log, callback);
});
}
processNextUpdate(log = {}, callback) {
this.getAppliedUpdates(appliedUpdates => {
const missingUpdates = this._listOfUpdates.filter(entry => { return appliedUpdates.indexOf(entry.id) === -1 && Object.keys(log).indexOf(entry.id) === -1 });
if(missingUpdates.length === 0) {
this.finish(log);
callback(log);
} else {
missingUpdates[0].apply(this._versionAttribute, this, log, callback);
}
});
}
finish(log) {
getAttrs([this._versionAttribute], values => {
console.log('---------------------------------------------');
const updates = Object.keys(log);
if(updates.length > 0) {
console.groupCollapsed('%cINSTALLING UPDATES:', 'font-weight:bold');
updates.forEach(update => {
if(log[update].error) {
console.log(`Applying ${update}... %c${log[update].error}`, 'color:#e66f00');
} else {
console.log(`Applying ${update}... %c${SheetUpgrade.getStatusMsg('DONE')}`, 'color: #22a05d');
}
});
console.groupEnd();
}
if(this._versionAttribute in values) console.log(`%cD&D 5th Edition by Roll20 Version ${values[this._versionAttribute]}`, 'color: green; font-weight:bold');
console.log('---------------------------------------------');
});
}
rejectUpdate(log, callback) {
this.processNextUpdate(log, callback);
}
resolveUpdate(updateID, log, callback) {
this.getAppliedUpdates(appliedUpdates => {
const update = Array.from(new Set(appliedUpdates.concat([updateID])));
const matches = this._listOfUpdates.filter(entry => {
return update.indexOf(entry.id) > -1;
}).map(entry => { return entry.id });
this.setAppliedUpdates(matches, () => {
this.processNextUpdate(log, callback);
});
});
}
}
class SheetUpgrade {
constructor(id, patch, setVersion = 0, minVersion = 0) {
this.id = id;
this._patch = patch;
this._setVersion = setVersion;
this._minVersion = minVersion;
this._listener = null;
this._versionAttribute = null;
this._log = null;
this._callback = null;
}
static getStatusMsg(errorCode, ...details) {
switch (errorCode) {
case 'DONE':
return `Update completed!`
case 'ALREADY_INSTALLED':
return `Update already installed!`
case 'INVALID_VERSION':
return `The current version of your sheet is not compatible with this update!`
default:
return '';
}
}
getCurrentVersion(callback) {
getAttrs([this._versionAttribute], values => {
const version = (values.version) ? parseFloat(values.version) : 0.0;
callback(version);
});
}
setCurrentVersion(version, callback) {
if(version === 0) {
callback();
} else {
let set = {};
set[this._versionAttribute] = version;
setAttrs(set, () => {
callback()
});
}
}
apply(versionAttribute, listener, log, callback) {
this._versionAttribute = versionAttribute;
this._listener = listener;
this._log = log;
this._callback = callback;
this.getCurrentVersion(currentVersion => {
if(this.validateVersion(currentVersion)) {
const statusMsg = SheetUpgrade.getStatusMsg('INVALID_VERSION');
this._reject(statusMsg);
} else {
let newVersion = this._setVersion;
let failed = false;
let update = null;
this._patch(this._resolve.bind(this), this._reject.bind(this));
}
});
}
_reject(error) {
this._log[this.id] = { success : false, error: error }
this._listener.rejectUpdate(this._log, this._callback);
}
_resolve() {
this.setCurrentVersion(this._setVersion, () => {
this._log[this.id] = { success: true };
this._listener.resolveUpdate(this.id, this._log, this._callback);
});
}
validateVersion(currentVersion) {
return (currentVersion >= this._setVersion || currentVersion < this._minVersion);
}
}
class SheetUpdate extends SheetUpgrade {
constructor(id, patch, minVersion = 0, maxVersion = Infinity) {
super(id, patch, 0, minVersion);
this._maxVersion = maxVersion;
}
validateVersion(currentVersion) {
return (currentVersion > this._maxVersion || currentVersion < this._minVersion);
}
}class Rest {
static resetResources(types) {
const attributes = [
"class_resource_reset",
"class_resource_max",
"class_resource_use_pb",
"other_resource_reset",
"other_resource_max",
"other_resource_use_pb",
"pb"
];
getSectionIDs("resource", ids => {
ids.forEach(id => {
attributes.push(`repeating_resource_${id}_resource_left_reset`);
attributes.push(`repeating_resource_${id}_resource_left_max`);
attributes.push(`repeating_resource_${id}_resource_left_use_pb`);
attributes.push(`repeating_resource_${id}_resource_right_reset`);
attributes.push(`repeating_resource_${id}_resource_right_max`);
attributes.push(`repeating_resource_${id}_resource_right_use_pb`);
});
getAttrs(attributes, values => {
const update = {};
const resettable = Object.keys(values).filter(key => {
return (key.includes("_reset") && types.indexOf(values[key]) > -1);
});
resettable.forEach(reset => {
const current = reset.replace("_reset", "");
const resetMax = reset.replace("_reset", "_max");
const pb = reset.replace("_reset", "_use_pb");
const usePB = (values[pb] && values[pb] === "1") ? true : false;
const max = (usePB) ? "pb" : resetMax;
if(!isNaN(values[max])) update[current] = values[max];
});
setAttrs(update);
});
});
}
static resetHP() {
getAttrs(["hp_max"], values => {
if(!isNaN(values["hp_max"])) setAttrs({hp: values["hp_max"]});
});
}
static resetHitDice() {
getSectionIDs("hd", ids => {
const splitTypePool = ids.map(id => { return `repeating_hd_${id}_type`; });
const splitMaxPool = ids.map(id => { return `repeating_hd_${id}_size`; });
const splitCurrentPool = ids.map(id => { return `repeating_hd_${id}_available`; });
getAttrs(["level", "use_per_class_hit_dice", "hit_dice", "hit_dice_max", ...splitTypePool, ...splitMaxPool, ...splitCurrentPool], values => {
const usingSplitPool = (values["use_per_class_hit_dice"] && values["use_per_class_hit_dice"] === "1");
const update = {};
let maxHitDice = Math.max(Math.floor(parseInt(values["level"])/2), 1);
if(usingSplitPool) {
const repeating = Object.keys(values).filter(key => key.includes("repeating_hd"));
const repeatingTypePool = repeating.filter(key => key.includes("_type"));
repeatingTypePool.sort((first, second) => {
const firstValue = parseInt(values[first]);
const secondValue = parseInt(values[second]);
if (firstValue > secondValue) {
return -1;
}
if (firstValue < secondValue) {
return 1;
}
return 0;
});
repeatingTypePool.forEach(type => {
if(maxHitDice <= 0) return;
const max = type.replace("_type", "_size");
const current = type.replace("_type", "_available");
if(!isNaN(values[max])) {
let maxSpent = parseInt(values[max]);
if(!isNaN(values[current])) {
const spent = Math.min(maxHitDice, maxSpent-parseInt(values[current]));
update[current] = String(parseInt(values[current])+spent);
maxHitDice -= spent;
} else {
const spent = Math.min(maxHitDice, maxSpent);
update[current] = String(spent);
maxHitDice -= spent;
}
}
});
} else {
if(!isNaN(values["hit_dice_max"])) {
if(!isNaN(values["hit_dice"])) {
update["hit_dice"] = String(Math.min(parseInt(values["hit_dice"])+maxHitDice, parseInt(values["hit_dice_max"])));
} else {
update["hit_dice"] = String(maxHitDice);
}
}
}
setAttrs(update);
});
});
}
static resetPactMagic() {
const pactMagicSlots = [
[0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0],
[2,0,0,0,0,0,0,0,0],
[0,2,0,0,0,0,0,0,0],
[0,2,0,0,0,0,0,0,0],
[0,0,2,0,0,0,0,0,0],
[0,0,2,0,0,0,0,0,0],
[0,0,0,2,0,0,0,0,0],
[0,0,0,2,0,0,0,0,0],
[0,0,0,0,2,0,0,0,0],
[0,0,0,0,2,0,0,0,0],
[0,0,0,0,3,0,0,0,0],
[0,0,0,0,3,0,0,0,0],
[0,0,0,0,3,0,0,0,0],
[0,0,0,0,3,0,0,0,0],
[0,0,0,0,3,0,0,0,0],
[0,0,0,0,3,0,0,0,0],
[0,0,0,0,4,0,0,0,0],
[0,0,0,0,4,0,0,0,0],
[0,0,0,0,4,0,0,0,0],
[0,0,0,0,4,0,0,0,0]
];
const attributes = [
"lvl1_slots_total",
"lvl2_slots_total",
"lvl3_slots_total",
"lvl4_slots_total",
"lvl5_slots_total",
"lvl6_slots_total",
"lvl7_slots_total",
"lvl8_slots_total",
"lvl9_slots_total",
"lvl1_slots_expended",
"lvl2_slots_expended",
"lvl3_slots_expended",
"lvl4_slots_expended",
"lvl5_slots_expended",
"lvl6_slots_expended",
"lvl7_slots_expended",
"lvl8_slots_expended",
"lvl9_slots_expended"
];
get_class_level("warlock", pactMagicLvl => {
if(pactMagicLvl === 0) return;
getAttrs(attributes, values => {
const update = {};
for(let i=1; i<=9; i++) {
if(!values[`lvl${i}_slots_total`]) continue;
const total = parseInt(values[`lvl${i}_slots_total`]);
const current = (values[`lvl${i}_slots_expended`]) ? parseInt(values[`lvl${i}_slots_expended`]) : 0;
const warlockSlots = pactMagicSlots[pactMagicLvl][i-1];
let updatedSlots = current;
if(updatedSlots < total) updatedSlots = Math.min(updatedSlots+warlockSlots, total);
update[`lvl${i}_slots_expended`] = String(updatedSlots);
}
setAttrs(update);
});
});
}
static resetSpellSlots() {
const attributes = [
"lvl1_slots_total",
"lvl2_slots_total",
"lvl3_slots_total",
"lvl4_slots_total",
"lvl5_slots_total",
"lvl6_slots_total",
"lvl7_slots_total",
"lvl8_slots_total",
"lvl9_slots_total"
];
getAttrs(attributes, values => {
const update = {};
Object.keys(values).filter(key => !isNaN(values[key])).forEach(key => {
update[key.replace("_total","_expended")] = values[key];
});
setAttrs(update);
});
}
static resetDeathSave() {
const update = {};
for(let i=1; i<=3; i++) {
update[`deathsave_succ${i}`] = "";
update[`deathsave_fail${i}`] = "";
}
setAttrs(update);
}
static decreaseExhaustionLevels() {
const attributes = [
"exhaustion_toggle",
"exhaustion_level"
];
getAttrs(attributes, values => {
if(!values["exhaustion_toggle"] || values["exhaustion_toggle"] !== "1") return;
const level = isNaN(values["exhaustion_level"])
?
0
:
Math.max(parseInt(values["exhaustion_level"])-1, 0)
;
setAttrs({exhaustion_level: level});
});
}
static rollHitDie() {
getAttrs(["use_per_class_hit_dice"], values => {
const usingSplitPool = (values["use_per_class_hit_dice"] && values["use_per_class_hit_dice"] === "1");
if(usingSplitPool) showHD();
});
}
static long() {
Rest.resetResources(["short", "long"]);
Rest.resetHP();
Rest.resetHitDice();
Rest.resetSpellSlots();
Rest.decreaseExhaustionLevels();
Rest.resetDeathSave();
}
static short() {
Rest.resetResources(["short"]);
Rest.resetDeathSave();
Rest.resetPactMagic();
Rest.rollHitDie();
}
static checkPBResources() {
const attributes = [
"class_resource_max",
"class_resource_use_pb",
"other_resource_max",
"other_resource_use_pb",
"pb"
];
getSectionIDs("resource", ids => {
ids.forEach(id => {
attributes.push(`repeating_resource_${id}_resource_left_max`);
attributes.push(`repeating_resource_${id}_resource_left_use_pb`);
attributes.push(`repeating_resource_${id}_resource_right_max`);
attributes.push(`repeating_resource_${id}_resource_right_use_pb`);
});
getAttrs(attributes, values => {
const update = {};
const usingPB = Object.keys(values).filter(key => {
return (key.includes("_use_pb") && values[key] == 1);
});
usingPB.forEach(resource => {
const current = resource.replace("_use_pb", "");
const resetMax = resource.replace("_use_pb", "_max");
if(!values[resetMax] && !isNaN(values["pb"])) update[current] = values["pb"];
});
setAttrs(update);
});
});
}
}class Compendium {
static SYSLOG_ATTRIBUTE = "syslog"
static EXPECTING_ARRAY = "EXPECTING ARRAY";
static EXPECTING_OBJECT = "EXPECTING OBJECT";
static INVALID_JSON = "INVALID JSON";
static INVALID_STRING = "INVALID STRING";
static _stack = [];
static _saving = false;
static _notify = () => {};
static _isArray(value) {
return Array.isArray(value);
}
static _isJSON(value) {
try {
return (JSON.parse(value) && !!value);
} catch (e) {
return false;
}
}
static _isObject = function(value) {
return value === Object(value) && !Array.isArray(value);
};
static _log(error, source="") {
//Errors are not logged if a source is not declared.
//Otherwise, we would have a hard time fixing it on the Compendium.
if(!source && !Array.isArray(error)) return;
const newError = (!Array.isArray(error)) ? [`${error}: ${source}`] : error;
if(this._saving) {
this._stack = this._stack.concat(newError);
} else {
this._saving = true;
getAttrs([this.SYSLOG_ATTRIBUTE], values => {
const log = this.parseArray(values.syslog).concat(newError);
const update = Array.from(new Set(log.sort()));
setAttrs({[this.SYSLOG_ATTRIBUTE]: JSON.stringify(update)}, () => {
this._saving = false;
if(this._stack.length > 0) {
const currentStack = Array.from(this._stack);
this._stack = [];
this._log(currentStack);
}
});
});
}
}
static _maybeJsonArray(value) {
return /^\[.+\]$/g.test(value.replace(/\n|\r|\t/g, ""));
}
static _maybeJsonObject(value) {
return /^\{.+\}$/g.test(value.replace(/\n|\r|\t/g, ""));
}
static _maybeJsonObjectOrArray(value) {
return this._maybeJsonObject(value) || this._maybeJsonArray(value);
}
static enableNotifications(callback) {
this._notify = (typeof callback === "function") ? callback : this.notify;
on("change:syslog", eventInfo => {
if(eventInfo.newValue != eventInfo.previousValue) this._notify(eventInfo);
});
}
static disableNotifications() {
this._notify = () => {};
}
static notify(eventInfo) {
console.warn("The following erros were found while trying to parse Compendium data:");
console.warn(JSON.parse(eventInfo.newValue));
}
static parseInt(value, source="") {
return (isNaN(value)) ? 0 : parseInt(value);
}
static parseObject(value, source="") {
if(this._isObject(value)) return value;
value = this.sanitize(value, source);
try {
const parsedValue = JSON.parse(value);
if(this._isObject(parsedValue)) {
return parsedValue;
} else {
this._log(this.EXPECTING_OBJECT, source);
return {};
}
} catch(e) {
this._log(this.INVALID_JSON, source);
return {};
}
}
static parseArray(value, source="") {
if(this._isArray(value)) return value;
value = this.sanitize(value, source);
try {
const parsedValue = JSON.parse(value);
if(this._isArray(parsedValue)) {
return parsedValue;
} else {
this._log(this.EXPECTING_ARRAY, source);
return [];
}
} catch(e) {
this._log(this.INVALID_JSON, source);
return [];
}
}
static parseString(value, source="") {
return this.sanitize(value, source);
}
static sanitize(value, source="") {
value = String(value).trim();
const maybeJsonObjectOrArray = this._maybeJsonObjectOrArray(value);
const steps = [
() => {
//Replaces wrongly typed newlines in Strings properties
if(maybeJsonObjectOrArray) return;
if(/\/n/g.test(value)) this._log(this.INVALID_STRING, source);
value = value.replace(/\/n/g, "\n");
},
() => {
//Replaces wrongly typed newlines in Object and Array properties
if(!maybeJsonObjectOrArray) return;
if(/\/n/g.test(value)) this._log(this.INVALID_JSON, source);
value = value.replace(/\/n/g, "\\n");
},
() => {
//Replaces newlines for \n in Object and Array properties
if(!maybeJsonObjectOrArray) return;
if(/\n|\r/g.test(value)) this._log(this.INVALID_JSON, source);
value = value.replace(/\n|\r/g, "\\n")
},
() => {
//Replaces tabs for spaces in Object and Array properties
if(!maybeJsonObjectOrArray) return;
if(/\t/g.test(value)) this._log(this.INVALID_JSON, source);
value = value.replace(/\t/g, " ");
}
];
steps.forEach(step => {
step();
});
return value;
}
}var v2_old_values_check = function() {
// update_attacks("all");
var update = {};
var attrs = ["simpletraits","features_and_traits","initiative_bonus","npc","character_id"];
getSectionIDs("repeating_spell-npc", function(idarray) {
_.each(idarray, function(id) {
attrs.push("repeating_spell-npc_" + id + "_rollcontent");
});
getAttrs(attrs, function(v) {
if(v["npc"] && v["npc"] == 1 && (!v["initiative_bonus"] || v["initiative_bonus"] == 0)) {
update_initiative();
}
var spellflag = idarray && idarray.length > 0 ? 1 : 0;
var missing = v["features_and_traits"] && v["simpletraits"] === "complex" ? 1 : 0;
update["npcspell_flag"] = spellflag;
update["missing_info"] = missing;
_.each(idarray, function(id) {
var content = v["repeating_spell-npc_" + id + "_rollcontent"];
if(content.substring(0,3) === "%{-" && content.substring(22,41) === "|repeating_attack_-" && content.substring(60,68) === "_attack}") {
var thisid = content.substring(2,21);
if(thisid != v["character_id"]) {
update["repeating_spell-npc_" + id + "_rollcontent"] = content.substring(0,2) + v["character_id"] + content.substring(22,68);
}
}
});
setAttrs(update);
});
});
};
var clear_npc_spell_attacks = function(complete) {
getSectionIDs("repeating_attack", function(attack_ids) {
var getList = [];
var done = false;
_.each(attack_ids, function(id) {
getList.push(`repeating_attack_${id}_spellid`);
});
getAttrs(getList, function(v) {
_.each(attack_ids, function(id) {
if (v[`repeating_attack_${id}_spellid`] && v[`repeating_attack_${id}_spellid`].indexOf("npc_") != -1) {
removeRepeatingRow(`repeating_attack_${id}`);
}
});
complete();
});
});
}
var hasTrait = function(traits, value) {
let found = Object.keys(traits).filter(function(row) {
return traits[row] === value;
});
return found.length > 0;
};
var no_version_bugfix = function(doneupdating) {
getAttrs(["npc","class"], function(v) {
if(v["npc"] && v["npc"] != "0" || v["class"] && v["class"] != "") {
setAttrs({version: "2.1"});
}
else {
setAttrs({version: "2.3"}, {silent: true});
}
});
doneupdating();
};
const dndUpdates = [
new SheetUpgrade(
'upgrade_to_2_0',
(resolve, reject) => {
getAttrs(["npc","strength","dexterity","constitution","intelligence","wisdom","charisma","strength_base","dexterity_base","constitution_base","intelligence_base","wisdom_base","charisma_base","deathsavemod","death_save_mod","npc_str_save","npc_dex_save","npc_con_save","npc_int_save","npc_wis_save","npc_cha_save","npc_str_save_base","npc_dex_save_base","npc_con_save_base","npc_int_save_base","npc_wis_save_base","npc_cha_save_base","npc_acrobatics_base", "npc_animal_handling_base", "npc_arcana_base", "npc_athletics_base", "npc_deception_base", "npc_history_base", "npc_insight_base", "npc_intimidation_base", "npc_investigation_base", "npc_medicine_base", "npc_nature_base", "npc_perception_base", "npc_performance_base", "npc_persuasion_base", "npc_religion_base", "npc_sleight_of_hand_base", "npc_stealth_base", "npc_survival_base", "npc_acrobatics", "npc_animal_handling", "npc_arcana", "npc_athletics", "npc_deception", "npc_history", "npc_insight", "npc_intimidation", "npc_investigation", "npc_medicine", "npc_nature", "npc_perception", "npc_performance", "npc_persuasion", "npc_religion", "npc_sleight_of_hand", "npc_stealth", "npc_survival"], function(v) {
var update = {};
var stats = ["strength","dexterity","constitution","intelligence","wisdom","charisma"];
var npc_stats = ["npc_str_save","npc_dex_save","npc_con_save","npc_int_save","npc_wis_save","npc_cha_save","npc_acrobatics", "npc_animal_handling", "npc_arcana", "npc_athletics", "npc_deception", "npc_history", "npc_insight", "npc_intimidation", "npc_investigation", "npc_medicine", "npc_nature", "npc_perception", "npc_performance", "npc_persuasion", "npc_religion", "npc_sleight_of_hand", "npc_stealth", "npc_survival"];
_.each(stats, function(attr) {
if(v[attr] && v[attr] != "10" && v[attr + "_base"] == "10") {
update[attr + "_base"] = v[attr];
}
});
_.each(npc_stats, function(attr) {
if(v[attr] && !isNaN(v[attr]) && v[attr + "_base"] == "") {
update[attr + "_base"] = v[attr];
}
});
if(v["deathsavemod"] && v["deathsavemod"] != "0" && v["death_save_mod"] === "0") {v["death_save_mod"] = v["deathsavemod"];};
if(v["npc"] && v["npc"] == "1") {
var callback = function() {
update_attr("all");
update_mod("strength");
update_mod("dexterity");
update_mod("constitution");
update_mod("intelligence");
update_mod("wisdom");
update_mod("charisma");
update_npc_action("all");
update_npc_saves();
update_npc_skills();
update_initiative();
}
}
else {
var callback = function() {
update_attr("all");
update_mod("strength");
update_mod("dexterity");
update_mod("constitution");
update_mod("intelligence");
update_mod("wisdom");
update_mod("charisma");
update_all_saves();
update_skills(["athletics", "acrobatics", "sleight_of_hand", "stealth", "arcana", "history", "investigation", "nature", "religion", "animal_handling", "insight", "medicine", "perception", "survival","deception", "intimidation", "performance", "persuasion"]);
update_tool("all")
update_attacks("all");
update_pb();
update_jack_attr();
update_initiative();
update_weight();
update_spell_info();
update_ac();
}
}
setAttrs(update, {silent: true}, () => {
resolve();
});
});
},
2.0
),
new SheetUpgrade(
'upgrade_to_2_1',
(resolve, reject) => {
v2_old_values_check();
resolve();
},
2.1,
2.0
),
new SheetUpgrade(
'upgrade_to_2_3',
(resolve, reject) => {
getSectionIDs("damagemod", function(ids) {
var update = {};
_.each(ids, function(rowid) {
update[`repeating_damagemod_${rowid}_options-flag`] = "0";
});
getSectionIDs("tohitmod", function(ids) {
_.each(ids, function(rowid) {
update[`repeating_tohitmod_${rowid}_options-flag`] = "0";
});
setAttrs(update, () => {
resolve();
});
});
});
},
2.3,
2.1
),
new SheetUpgrade(
'upgrade_to_2_4',
(resolve, reject) => {
clear_npc_spell_attacks(function() {
update_globalskills(function() {
update_globalsaves(function() {
update_globalattack(function() {
update_globaldamage(function() {
getAttrs(["npc", "npcspellcastingflag", "spellcasting_ability", "caster_level", "level_calculations"], function(v) {
if(v.npc == "1" && v.npcspellcastingflag == "1") {
getSectionIDs("npctrait", function(secIds) {
var getList = [];
_.each(secIds, function(x) {
getList.push("repeating_npctrait_" + x + "_name");
getList.push("repeating_npctrait_" + x + "_desc");
});
getAttrs(getList, function(traits) {
var update = {};
if(v.spellcasting_ability == "0*" || v.caster_level == "0") {
var spellSec = "";
if (v.spellcasting_ability == "0*") {
update.spellcasting_ability = "@{intelligence_mod}+";
}
_.each(secIds, function(traitId) {
if(traits["repeating_npctrait_" + traitId + "_name"].toLowerCase().includes("spellcasting.")) spellSec = traitId;
});
if(spellSec != "") {
var spellcasting = traits["repeating_npctrait_" + spellSec + "_desc"].toLowerCase();
if (v.spellcasting_ability == "0*") {
var lastIndex = 9999;
_.each(["intelligence", "wisdom", "charisma"], function(ability) {
var found = spellcasting.indexOf(ability);
if(found > -1 && found < lastIndex) {
lastIndex = found;
update.spellcasting_ability = "@{" + ability + "_mod}+";
}
});
}
if (v.caster_level == "0") {
var foundLevelidx = spellcasting.search(/(\d|\d\d)(st|nd|rd|th)/);
if (foundLevelidx) {
var level = parseInt(spellcasting.substring(foundLevelidx, foundLevelidx+4));
console.log(`Found spellcasting level ${level} in trait, setting caster_level...`);
update.caster_level = level;
}
}
}
}
setAttrs(update, function() {
// Recalculate spell slots in case NPC level was restored
if(!v["level_calculations"] || v["level_calculations"] == "on") {
update_spell_slots();
};
// Set all spells without a given modifier to 'spell'
var spgetList = [];
getSectionIDs("spell-cantrip", function(secIds0) {
_.each(secIds0, function(x) {
spgetList.push("repeating_spell-cantrip_" + x + "_spell_ability");
});
getSectionIDs("spell-1", function(secIds1) {
_.each(secIds1, function(x) {
spgetList.push("repeating_spell-1_" + x + "_spell_ability");
});
getSectionIDs("spell-2", function(secIds2) {
_.each(secIds2, function(x) {
spgetList.push("repeating_spell-2_" + x + "_spell_ability");
});
getSectionIDs("spell-3", function(secIds3) {
_.each(secIds3, function(x) {
spgetList.push("repeating_spell-3_" + x + "_spell_ability");
});
getSectionIDs("spell-4", function(secIds4) {
_.each(secIds4, function(x) {
spgetList.push("repeating_spell-4_" + x + "_spell_ability");
});
getSectionIDs("spell-5", function(secIds5) {
_.each(secIds5, function(x) {
spgetList.push("repeating_spell-5_" + x + "_spell_ability");
});
getSectionIDs("spell-6", function(secIds6) {
_.each(secIds6, function(x) {
spgetList.push("repeating_spell-6_" + x + "_spell_ability");
});
getSectionIDs("spell-7", function(secIds7) {
_.each(secIds7, function(x) {
spgetList.push("repeating_spell-7_" + x + "_spell_ability");
});
getSectionIDs("spell-8", function(secIds8) {
_.each(secIds8, function(x) {
spgetList.push("repeating_spell-8_" + x + "_spell_ability");
});
getSectionIDs("spell-9", function(secIds9) {
_.each(secIds9, function(x) {
spgetList.push("repeating_spell-9_" + x + "_spell_ability");
});
getAttrs(spgetList, function(spellAbilities) {
spupdate = {};
_.each(spellAbilities, function(ability, attributeName) {
if (ability == "0*") {
console.log("UPDATING SPELL: " + attributeName);
spupdate[attributeName] = "spell";
}
});
setAttrs(spupdate, function() {
resolve()
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
} else if(v.npc != "1") {
resolve();
} else {
resolve();
}
});
});
});
});
});
});
},
2.4,
2.3
),
new SheetUpgrade(
'upgrade_to_2_5',
(resolve, reject) => {
getAttrs(["globalacmod"], function(v) {
var update = {};
if(v.globalacmod && v.globalacmod != "0") {
var rowid = generateRowID();
update[`repeating_acmod_${rowid}_global_ac_val`] = parseInt(v.globalacmod);
update[`repeating_acmod_${rowid}_global_ac_name`] = "GLOBAL ARMOR CLASS MODIFIER";
update[`repeating_acmod_${rowid}_global_ac_active_flag`] = "1";
update[`repeating_acmod_${rowid}_options-flag`] = "0";
update["global_ac_mod_flag"] = "1";
}
setAttrs(update, {silent: true}, function() {
update_ac();
resolve();
});
});
},
2.5,
2.4
),
new SheetUpgrade(
'upgrade_to_2_6',
(resolve, reject) => {
getSectionIDs("hpmod", function(ids) {
var getArray = [];
_.each(ids, function(sectionid) {
getArray.push("repeating_hpmod_" + sectionid + "_source");
});
getAttrs(getArray, function(v) {
_.each(v, function(val, key) {
if(val === "CON") removeRepeatingRow("repeating_hpmod_" + key.split("_")[2]);
});
resolve();
});
});
},
2.6,
2.5
),
new SheetUpgrade(
'upgrade_to_2_7',
(resolve, reject) => {
getSectionIDs("damagemod", function(ids) {
let getArray = [];
ids.forEach(sectionid => {
getArray.push(`repeating_damagemod_${sectionid}_global_damage_rollstring`);
getArray.push(`repeating_damagemod_${sectionid}_global_damage_type`);
});
getSectionIDs("savemod", function(ids) {
_.each(ids, (sectionid) => {
getArray.push(`repeating_savemod_${sectionid}_global_save_rollstring`);
});
getSectionIDs("tohitmod", function(ids) {
_.each(ids, (sectionid) => {
getArray.push(`repeating_tohitmod_${sectionid}_global_attack_rollstring`);
});
getSectionIDs("skillmod", function(ids) {
_.each(ids, (sectionid) => {
getArray.push(`repeating_skillmod_${sectionid}_global_skill_rollstring`);
});
getAttrs(getArray, function(v) {
let set = {};
_.each(v, (value, attr) => {
if(_.last(attr.split("_")) === "rollstring") {
const section = attr.slice(0, -10);
const brackets = value.split('['), roll = brackets[0];
let name = brackets[1] ? brackets[1].slice(0, -1) : "";
if(attr.split("_")[1] == "damagemod") {
name = name ? name : v[`${section}type`];
set[`${section}damage`] = roll;
} else {
set[`${section}roll`] = roll;
}
set[`${section}name`] = name;
}
});
setAttrs(set, () => {
resolve();
});
});
});
});
});
});
},
2.7,
2.6
),
new SheetUpgrade(
'upgrade_to_2_8',
(resolve, reject) => {
getAttrs(["npc"], function(v) {
const levels = ["cantrip", "1", "2", "3", "4", "5", "6", "6", "8", "9"];
let currentLevel = 0;
let spells = {};
const updateSpellLevels = function() {
getSectionIDs("spell-"+ levels[currentLevel], function(spellID) {
_.each(spellID, function(x) {
spells["repeating_spell-"+ levels[currentLevel] +"_" + x + "_details-flag"] = "0";
});
currentLevel++;
if(currentLevel <= levels.length) {
updateSpellLevels();
} else {
setAttrs(spells, function() {
if(v["npc"] && v["npc"] == "1") {
let traits = {};
getSectionIDs("npctrait", function(traitID) {
_.each(traitID, function(x) {
traits["repeating_npctrait_" + x + "_npc_options-flag"] = "0";
});
getSectionIDs("npcreaction", function(reactionID) {
_.each(reactionID, function(x) {
traits["repeating_npcreaction_" + x + "_npc_options-flag"] = "0";
});
setAttrs(traits, function() {
resolve();
});
});
});
} else {
resolve();
}
});
}
});
};
updateSpellLevels();
});
},
2.8,
2.7
),
new SheetUpgrade(
'upgrade_to_2_9',
(resolve, reject) => {
getAttrs(["npc"], function(v) {
if(v["npc"] && v["npc"] == "1") {
resolve();
} else {
getSectionIDs("repeating_traits", (traits) => {
let traitNames = [];
_.each(traits, (traitID, i) => {
traitNames.push("repeating_traits_" + traitID + "_name");
});
getAttrs(traitNames, function(v) {
if(hasTrait(v, "Reliable Talent")) {
setAttrs({reliable_talent: 10}, function() {
resolve();
});
} else {
resolve();
}
});
});
}
});
},
2.9,
2.8
),
new SheetUpgrade(
'upgrade_to_4_2_1',
(resolve, reject) => {
let sectionsDone = 0;
['npctrait', 'npcreaction'].forEach(repeatingSection => {
getSectionIDs(repeatingSection, idarray => {
let attributes = [];
let update = {};
idarray.forEach(id => {
attributes.push(`repeating_${repeatingSection}_${id}_desc`);
});
getAttrs(attributes, value => {
idarray.forEach(id => {
update[`repeating_${repeatingSection}_${id}_description`] = value[`repeating_${repeatingSection}_${id}_desc`]
});
setAttrs(update, () => {
sectionsDone++;
if(sectionsDone === 2) resolve();
});
});
});
});
},
4.21,
2.9
),
new SheetUpdate(
'fix_npc_missing_attack_display_flag_attribute',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
getSectionIDs('repeating_npcaction', idarray => {
const rollbases = idarray.map(id => { return `repeating_npcaction_${id}_rollbase`; });
let update = {}
getAttrs(rollbases, function(rolls) {
Object.keys(rolls).forEach(roll => {
update[roll] = rolls[roll].replace('@{attack_display_flag}', '{{attack=1}}')
});
setAttrs(update, () => {
resolve();
});
});
});
} else {
reject('This update is only valid for npcs sheets');
}
});
},
2.0
),
new SheetUpdate(
'fix_npc_version_number',
(resolve, reject) => {
getAttrs(["appliedUpdates", "npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
if(!values.appliedUpdates) { resolve(); }
else if(values.appliedUpdates.indexOf('upgrade_to_') === -1) { resolve(); }
else {
const regex = /upgrade_to_[0-9]_[0-9]_[0-9]|upgrade_to_[0-9]_[0-9]|upgrade_to_[0-9]/g;
const matches = values.appliedUpdates.match(regex);
const lastUpdate = matches[matches.length-1].replace('upgrade_to_', '').replace('_', '.').replace(/_/g, '');
const lastUpdateversion = parseFloat(lastUpdate)
setAttrs({version: lastUpdateversion}, () => {
resolve();
});
}
} else {
reject('This update is only valid for npcs sheets');
}
});
}
),
new SheetUpdate(
'fix_npcs_in_modules_saves_and_ability_mods',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
const bases = globalAttributesByCategory.abilities.map(ability => { return `${ability}_base`});
const mods = globalAttributesByCategory.abilities.map(ability => { return `${ability}_mod`});
getAttrs([...bases, ...mods], values => {
let update = {};
globalAttributesByCategory.abilities.forEach(ability => {
if(values[`${ability}_base`] && values[`${ability}_mod`]) {
const base = !isNaN(parseInt(values[`${ability}_base`], 10)) ? parseInt(values[`${ability}_base`], 10) : 0;
const mod = !isNaN(parseInt(values[`${ability}_mod`], 10)) ? parseInt(values[`${ability}_mod`], 10) : 0;
if(mod === 0) {
const newMod = Math.floor((base - 10) / 2);
update[`${ability}_mod`] = newMod;
}
}
});
setAttrs(update, () => { resolve(); });
});
} else {
reject('This update is only valid for npcs sheets');
}
});
}
),
new SheetUpdate(
'fix_spell_attacks',
(resolve, reject) => {
getAllSpellsIDS(spells => {
let spellAttributes = [];
spells.forEach(entry => {
spellAttributes.push(`${entry}_spelloutput`);
spellAttributes.push(`${entry}_spellattackid`);
spellAttributes.push(`${entry}_spelllevel`);
});
getAttrs(spellAttributes.concat(['character_id']), values => {
const attackSpells = Object.keys(values).filter(entry => { return (entry.indexOf('_spelloutput') > -1 && values[entry] === 'ATTACK'); });
attackSpells.forEach(entry => {
const spellID = SheetUtils.getUID(entry);
const lvl = values[entry.replace('_spelloutput', '_spelllevel')];
const attackID = values[entry.replace('_spelloutput', '_spellattackid')];
const characterID = values["character_id"];
create_attack_from_spell(lvl, spellID, characterID, attackID);
});
resolve();
});
});
}
),
new SheetUpdate(
'fix_npc_in_modules_triggering_popup',
(resolve, reject) => {
getAttrs(["npc", "npc_name"], function(values) {
if(values["npc"] && values["npc"] == "1" && values["npc_name"] && values["npc_name"].trim().length > 0) {
setAttrs({l1mancer_status: "completed"}, () => { resolve(); });
} else {
reject('This update is only valid for npcs sheets');
}
});
}
),
new SheetUpdate(
'fix_npc_actions_to_support_translation',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
update_npc_action("all");
resolve();
} else {
reject('This update is only valid for npcs sheets');
}
});
}
),
new SheetUpdate(
'fix_pc_skill_and_saving_rolls',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getSectionIDs("repeating_tool", idarray => {
updateSavingRolls(globalAttributesByCategory.abilitiesWithAllOptionals);
updateSkillRolls(globalAttributesByCategory.skills.all());
do_update_tool(idarray);
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_pc_saving_rolls',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getSectionIDs("repeating_tool", idarray => {
updateSavingRolls(globalAttributesByCategory.abilitiesWithAllOptionals);
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_pc_skill_and_saving_rolls_with_expertise',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getSectionIDs("repeating_tool", idarray => {
updateSavingRolls(globalAttributesByCategory.abilitiesWithAllOptionals);
updateSkillRolls(globalAttributesByCategory.skills.all());
do_update_tool(idarray);
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_pc_skill_and_saving_rolls_with_reliable_talent',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getSectionIDs("repeating_tool", idarray => {
updateSavingRolls(globalAttributesByCategory.abilitiesWithAllOptionals);
updateSkillRolls(globalAttributesByCategory.skills.all());
do_update_tool(idarray);
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_npc_attacks',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
getSectionIDs("repeating_npcaction", idarray => {
const rollbases = idarray.map(entry => { return `repeating_npcaction_${entry}_rollbase`;});
getAttrs(rollbases, values => {
let update = {};
Object.keys(values).forEach(rollbase => {
if(values[rollbase].indexOf('{{attack=1}}') > -1) {
if(values[rollbase].indexOf('{template:npcatk}') > -1) {
update[rollbase] = values[rollbase].replace(' {{description=@{show_desc}}} ', '');
} else if(values[rollbase].indexOf('{template:npcaction}') > -1) {
update[rollbase] = values[rollbase].replace('{template:npcaction}', '{template:npcfullatk}');
}
}
});
if(Object.keys(update).length > 0) {
setAttrs(update, () => {
resolve();
});
} else {
resolve();
}
});
});
} else {
reject('This update is only valid for npcs sheets');
}
});
}
),
new SheetUpdate(
'fix_npc_attacks_with_auto_damage_roll',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
update_npc_action("all");
resolve();
} else {
reject('This update is only valid for npcs sheets');
}
});
}
),
new SheetUpdate(
'enable_powerful_build_on_existing_characters',
(resolve, reject) => {
getAttrs(["npc"], function(v) {
if(v["npc"] && v["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getSectionIDs("repeating_traits", (traits) => {
let traitNames = [];
_.each(traits, (traitID, i) => {
traitNames.push("repeating_traits_" + traitID + "_name");
});
if(traitNames.length > 0) {
getAttrs(traitNames, function(v) {
if(hasTrait(v, "Powerful Build")) {
setAttrs({powerful_build: 1}, function() {
resolve();
});
} else {
resolve();
}
});
} else {
resolve();
}
});
}
});
}
),
new SheetUpdate(
'fix_pc_global_critical_damage_rolls',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
update_globaldamage(() => {
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_npc_actions_with_damage',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
update_npc_action("all");
resolve();
} else {
reject('This update is only valid for npcs sheets');
}
});
}
),
new SheetUpdate(
'fix_pc_global_critical_stacked_damage_rolls',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
update_globaldamage(() => {
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_npc_charname_output',
(resolve, reject) => {
const myreject = function() { reject('This update is only valid for npcs sheets'); };
updateCharnameOutput(resolve, myreject);
}
),
new SheetUpdate(
'fix_pc_skill_rolls_tooltips',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getSectionIDs("repeating_tool", idarray => {
updateSkillRolls(globalAttributesByCategory.skills.all());
do_update_tool(idarray);
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_pc_global_statical_critical_damage',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
update_globaldamage(() => {
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_spell_school_ouput',
(resolve, reject) => {
getAllSpellsIDS(spells => {
if(spells.length > 0) {
let spellAttributes = [];
spells.forEach(entry => {
spellAttributes.push(`${entry}_spellschool`);
});
getAttrs(spellAttributes, values => {
let update = {};
Object.keys(values).forEach(entry => {
update[entry] = values[entry].toLowerCase();
});
setAttrs(update, () => {
resolve();
});
});
} else {
resolve();
}
});
}
),
new SheetUpdate(
'fix_pc_global_multiple_statical_critical_damage',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
update_globaldamage(() => {
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_spell_savedc_output',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getAllSpellsIDS(spells => {
if(spells.length > 0) {
let spellAttributes = [];
spells.forEach(entry => {
spellAttributes.push(`${entry}_rollcontent`);
});
getAttrs(spellAttributes, values => {
let update = {};
Object.keys(values).forEach(entry => {
if(values[entry].includes('@{wtype}&{template:spell}') && !values[entry].includes('{{savedc=@{spell_save_dc}}}')) {
const newValue = values[entry].replace('{{innate=@{innate}}}','{{innate=@{innate}}} {{savedc=@{spell_save_dc}}}');
update[entry] = newValue;
}
});
setAttrs(update, () => {
resolve();
});
});
} else {
resolve();
}
});
}
});
}
),
new SheetUpdate(
'fix_spellpoints',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
getAttrs(['use_spell_points'], v=> {
if(v['use_spell_points'] && v['use_spell_points'] == 1) {
setSpellPoints();
} else {
deleteResource('Spell Points');
}
resolve();
});
}
});
}
),
new SheetUpdate(
'fix_advantage_query',
(resolve, reject) => {
getAttrs(['rtype'], function(values) {
const queryadvantage = '{{query=1}} ?{Advantage?|Normal Roll,&#123&#123normal=1&#125&#125 &#123&#123r2=[[0d20|Advantage,&#123&#123advantage=1&#125&#125 &#123&#123r2=[[@{d20}|Disadvantage,&#123&#123disadvantage=1&#125&#125 &#123&#123r2=[[@{d20}}';
setAttrs({queryadvantage: queryadvantage}, () => {
if(values.rtype.includes('?{Advantage')) {
const always = '{{always=1}} {{r2=[[@{d20}';
const query = '@{queryadvantage}';
setAttrs({rtype: always}, () => {
setAttrs({rtype: query}, () => {
resolve();
});
});
} else {
resolve();
}
});
});
}
),
new SheetUpdate(
'fix_attacks_spelllink',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
update_attacks("spells");
resolve();
}
});
}
),
new SheetUpdate(
'fix_pc_skills_when_jack_and_odd_prof',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
updateSkillRolls(globalAttributesByCategory.skills.all());
resolve();
}
});
}
),
new SheetUpdate(
'fix_armor_stealth_penalty',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
const armorsWithStealthDisadvantage = [
"chain mail",
"half plate",
"plate",
"ring mail",
"scale mail",
"spiked armor",
"splint"
];
const update = {};
const attrs_to_get = [];
getSectionIDs("repeating_inventory", function(idarray) {
_.each(idarray, function(currentID, i) {
attrs_to_get.push("repeating_inventory_" + currentID + "_itemname");
attrs_to_get.push("repeating_inventory_" + currentID + "_itemmodifiers");
});
getAttrs(attrs_to_get, function(v) {
Object.keys(v).filter(entry => { return entry.includes("itemname"); }).forEach(key => {
if(armorsWithStealthDisadvantage.indexOf(v[key].toLowerCase()) > 1) {
const modifiersKey = key.replace("_itemname", "_itemmodifiers");
let modifiers = (v[modifiersKey]) ? v[modifiersKey] : false;
if(!modifiers) {
update[modifiersKey] = "Stealth:Disadvantage";
} else if(!modifiers.includes("Stealth:Disadvantage")) {
update[modifiersKey] = modifiers + ", Stealth:Disadvantage";
}
}
});
setAttrs(update, () => {
update_skills(["stealth"]);
resolve();
});
});
});
}
});
}
),
new SheetUpdate(
'fix_pc_printedskills',
(resolve, reject) => {
getAttrs(["npc"], function(values) {
if(values["npc"] && values["npc"] == "1") {
reject('This update is only valid for pcs sheets');
} else {
formatPrintedTools();
formatPrintedSkills();
resolve();
}
});
}
),
new SheetUpdate(
'fix_npc_charactersheet_type',
(resolve, reject) => {
getAttrs(["npc", "charactersheet_type"], function(values) {
if(values["npc"] && values["npc"] == "1") {
if(!values["charactersheet_type"] || values["charactersheet_type"] === "pc") {
setAttrs({charactersheet_type:"npc"});
}
resolve();
} else {
reject('This update is only valid for npcs sheets');
}
});
}
)
];
const errorMessage = (name, error) => console.error(`%c ${name}: ${error}`, "color: orange; font-weight:14px;");
const warningMessage = (name, error) => console.warn(`%c ${name}: ${error}`, "color: gold; font-weight:14px;");
const checkSourceForSheetworker = eventinfo => eventinfo.sourceType && eventinfo.sourceType != "sheetworker" ? true : false
// Example: -m2jzgfth07mvwp3hhng
const getReprowid = sourceAttribute => sourceAttribute.split('_')[2]
// Example: repeating_tool_-m2jzgfth07mvwp3hhng
const getRepeatingSectionPrefix = sourceAttribute => {
const split = sourceAttribute.split('_');
return `${split[0]}_${split[1]}_${split[2]}`
}
// Example: repeating_tool_-m2jzgfth07mvwp3hhng_attkflag returns attkflag
const getAttrNameAtEndRepeating = sourceAttribute => {
const repeatingSplit = sourceAttribute.split('_')
const repeatingSplice = repeatingSplit.splice(3)
const repeatingAttrName = repeatingSplice.length > 1 ? repeatingSplice.join('_') : repeatingSplice[0]
return repeatingAttrName
}
// Create a collective list of attributs to reduce redundancy
var abilityList = ["Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma"];
var abilityListEnabledOptionals = [];
var customACFormulas = [];
const globalAttributesByCategory = {
abilities: ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"],
abilitiesWithAllOptionals: ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma", "honor", "sanity"],
classes: characterClasses().get(true),
casterProgression: ["full", "half", "third", "pact"],
hitDieTypes: ["d4", "d6", "d8", "d10", "d12"],
spellLevels: ["cantrip", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
traitsTypes: ["Background", "Feats", "Racial", "Class", "Other"],
proficiencyTypes: ["LANGUAGE", "ARMOR", "WEAPON", "OTHER"],
skills: {
strength: ["athletics"],
constitution: [],
dexterity: ["acrobatics", "sleight_of_hand", "stealth"],
intelligence: ["arcana", "history", "investigation", "nature", "navigation", "religion"],
wisdom: ["animal_handling", "insight", "medicine", "perception", "survival"],
charisma: ["deception", "intimidation", "performance", "persuasion"],
honor: [],
sanity: [],
all: () => {
return [
...globalAttributesByCategory.skills.strength,
...globalAttributesByCategory.skills.dexterity,
...globalAttributesByCategory.skills.intelligence,
...globalAttributesByCategory.skills.wisdom,
...globalAttributesByCategory.skills.charisma,
];
},
getParentAbility: (skill) => {
let parent = "";
globalAttributesByCategory["abilities"].forEach((ability) => {
if (globalAttributesByCategory.skills[ability].indexOf(skill) > -1) {
parent = ability;
return;
}
});
return parent;
},
},
spellSections: ["spell-cantrip", "spell-1", "spell-2", "spell-3", "spell-4", "spell-5", "spell-6", "spell-7", "spell-8", "spell-9"],
};
Compendium.enableNotifications();
//Overwrites default defaultGetCharmancerData
var defaultGetCharmancerData = getCharmancerData;
var getCharmancerData = function () {
const data = defaultGetCharmancerData();
handleFlexInCharmancerData(data);
includeLPFeatsInCharmancerData(data);
includeLinkedChoicesInCharmancerData(data);
return data;
};
var defaultGetCompendiumPage = getCompendiumPage;
var getCompendiumPage = function () {
const handlePageData = (pageData, callback) => {
let handleFlex = (data) => {
if (
(SheetUtils.checkPath(data, '["data"]["Category"]') && data["data"]["Category"] === "Races") ||
data["data"]["Category"] === "Subraces"
) {
const type = data["data"]["Category"];
if (type === "Races") {
if (data.data["data-Ability Score Increase"] && isFlex(data.data["data-Ability Score Increase"])) {
data.data["data-Ability Score Choice"] = "3+1";
delete data.data["data-Ability Score Increase"];
}
} else {
const macerdata = getCharmancerData();
const flex = SheetUtils.checkPath(macerdata, '["l1-race"]["data"]["race"]["data-Flex"]');
if (flex) {
delete data.data["data-Ability Score Choice"];
delete data.data["data-Ability Score Increase"];
}
}
}
};
let handledLinkedChoice = (data) => {
if (!SheetUtils.checkPath(data, '["data"]["blobs"]')) return;
const linked = {};
Object.keys(data["data"]["blobs"]).forEach((key) => {
const blob = data["data"]["blobs"][key];
if (!blob["data-Linked Choice"]) return;
const linkedChoice = JSON.parse(blob["data-Linked Choice"]);
delete blob["data-Linked Choice"];
const group = `${key} Choices`.replace(/ /g, "_");
linkedChoice.forEach((option) => {
linked[`${key} (${option})`] = {};
linked[`${key} (${option})`]["Group"] = group;
Object.keys(blob).forEach((blobKey) => {
const forbiddenProperties = [];
if (forbiddenProperties.indexOf(blobKey) > -1) return;
linked[`${key} (${option})`][blobKey] = blob[blobKey].replace(/@{choice}/gi, option);
});
});
const allowedProperties = ["Level"];
Object.keys(blob).forEach((blobKey) => {
if (allowedProperties.indexOf(blobKey) === -1) delete blob[blobKey];
});
blob["Choice"] = `Blob:${group}`;
blob["Description"] = getTranslationByKey(`customize-trait`);
});
data["data"]["blobs"] = { ...data["data"]["blobs"], ...linked };
};
if (Array.isArray(pageData)) {
pageData.forEach((page) => {
handleFlex(page);
handledLinkedChoice(page);
});
} else {
handleFlex(pageData);
handledLinkedChoice(pageData);
}
if (typeof callback === "function") callback(pageData);
};
switch (arguments.length) {
case 0:
return [];
case 1:
defaultGetCompendiumPage(arguments[0], (pageData) => handlePageData(pageData));
break;
case 2:
defaultGetCompendiumPage(arguments[0], (pageData) => handlePageData(pageData, arguments[1]));
break;
default:
defaultGetCompendiumPage(arguments[0], arguments[1], (pageData) => handlePageData(pageData, arguments[2]));
}
};
var isFlex = function (data) {
return typeof data === "string" && data.toLocaleLowerCase().includes("flex");
};
var handleFlexInCharmancerData = function (data) {
let flex = false;
if (
SheetUtils.checkPath(data, '["l1-race"]["data"]["race"]["data-Ability Score Increase"]') &&
isFlex(data["l1-race"]["data"]["race"]["data-Ability Score Increase"])
) {
flex = true;
delete data["l1-race"]["data"]["race"]["data-Ability Score Increase"];
data["l1-race"]["data"]["race"]["data-Ability Score Choice"] = "3+1";
data["l1-race"]["data"]["race"]["data-Flex"] = true;
}
if (SheetUtils.checkPath(data, '["l1-subrace"]["data"]') && flex) {
delete data["l1-subrace"]["data"]["subrace"]["data-Ability Score Increase"];
delete data["l1-subrace"]["data"]["subrace"]["data-Ability Score Choice"];
}
};
var includeLPFeatsInCharmancerData = function (data) {
if (SheetUtils.checkPath(data, "['lp-asi']['values']")) {
var feats = {};
Object.keys(data["lp-asi"]["values"]).forEach((value) => {
if (value.indexOf("asi-row_switch") && data["lp-asi"]["values"][value] === "feat") {
const rowID = SheetUtils.getUID(value);
const rowName = `repeating_${rowID}_asi-row_feat`;
if (SheetUtils.checkPath(data, `['lp-asi']['values']['${rowName}_data']`)) {
//Safeguarding Compendium data:
const featData = Compendium.parseObject(data["lp-asi"]["values"][`${rowName}_data`], "E_COHL51NS:LP-Feats");
data["lp-asi"]["data"][rowName] = featData;
}
}
});
}
};
var includeLinkedChoicesInCharmancerData = function (data) {
const previousChoices = readPreviousChoices(data);
Object.values(data).forEach((page) => {
if (!page.data) return;
Object.values(page.data).forEach((section) => {
if (!section["blobs"]) return;
let blobs = section["blobs"];
const linked = {};
Object.keys(blobs).forEach((key) => {
const blob = blobs[key];
if (!blob["data-Linked Choice"]) return;
const linkedChoice = JSON.parse(blob["data-Linked Choice"]);
delete blob["data-Linked Choice"];
//Check if a previous choice exists for the blob:
const parsedName = key.replace(/\(.+\)/g, "").trim();
if (previousChoices[parsedName] && linkedChoice.indexOf(previousChoices[parsedName]) > -1) {
const option = previousChoices[parsedName];
Object.keys(blob).forEach((blobKey) => {
blob[blobKey] = blob[blobKey].replace(/@{choice}/gi, option);
});
} else {
const group = `${key} Choices`.replace(/ /g, "_");
linkedChoice.forEach((option) => {
linked[`${key} (${option})`] = {};
linked[`${key} (${option})`]["Group"] = group;
Object.keys(blob).forEach((blobKey) => {
const forbiddenProperties = [];
if (forbiddenProperties.indexOf(blobKey) > -1) return;
linked[`${key} (${option})`][blobKey] = blob[blobKey].replace(/@{choice}/gi, option);
});
});
const allowedProperties = ["Level"];
Object.keys(blob).forEach((blobKey) => {
if (allowedProperties.indexOf(blobKey) === -1) delete blob[blobKey];
});
blob["Choice"] = `Blob:${group}`;
blob["Description"] = getTranslationByKey(`customize-trait`);
}
});
section["blobs"] = { ...blobs, ...linked };
});
});
};
var readPreviousChoices = function (data) {
const previousChoices = {};
if (!SheetUtils.checkPath(data, '["lp-welcome"]["values"]["previous_repeating"]')) return previousChoices;
const previousRepeating = JSON.parse(data["lp-welcome"]["values"]["previous_repeating"]);
if (!previousRepeating.traits) return previousChoices;
previousRepeating.traits.forEach((trait) => {
const hasChoice = trait.name.match(/ \(.+\)/g);
if (!hasChoice) return;
const name = trait.name.replace(/\(.+\)/g, "").trim();
previousChoices[name] = hasChoice[0].replace(/\(|\)/g, "").trim();
});
return previousChoices;
};
if (typeof environment === "undefined") {
var sheetUpdater = new SheetUpdater(dndUpdates);
on("sheet:opened", function (eventinfo) {
sheetUpdater.checkUpdates((result) => {
var getInfo = function (sections, callback, results) {
results = results || {};
if (sections.length > 0) {
var section = sections.pop();
getSectionIDs(section, function (ids) {
results[section] = ids;
getInfo(sections, callback, results);
});
} else {
callback(results);
}
};
var spellSections = ["spell-cantrip"];
for (var x = 1; x <= 9; x++) {
spellSections.push("spell-" + x);
}
getInfo(spellSections, function (results) {
var getList = ["character_id"];
_.each(results, function (sectionids, sectionname) {
_.each(sectionids, function (spellid) {
getList.push("repeating_" + sectionname + "_" + spellid + "_rollcontent");
});
});
getAttrs(getList, function (attrs) {
var set = {};
_.each(attrs, function (data, name) {
if (data.length === 68 && data.split("|")[0].substr(2) !== attrs["character_id"]) {
set[name] = "%{" + attrs["character_id"] + data.substr(22);
}
});
setAttrs(set, function () {
if (eventinfo.sourceType === "sheetworker") {
setAttrs({ l1mancer_status: "completed" });
} else {
check_l1_mancer();
check_lp_mancer();
}
});
});
});
});
});
}
//Start of Compendium Drops
on("sheet:compendium-drop", function () {
getAttrs(
[
"npc",
"is_vehicle",
"vehicle_hp_max",
"hp_max",
"npc_senses",
"token_size",
"cd_bar1_v",
"cd_bar1_m",
"cd_bar1_l",
"cd_bar2_v",
"cd_bar2_m",
"cd_bar2_l",
"cd_bar3_v",
"cd_bar3_m",
"cd_bar3_l",
],
function (v) {
const isVehicle = v["npc"] == "1" && v["is_vehicle"] == "1";
var default_attr = {};
default_attr["width"] = 70;
default_attr["height"] = 70;
if (v["npc_senses"].toLowerCase().match(/(darkvision|blindsight|tremorsense|truesight)/)) {
const nightVisionRange = Math.max.apply(Math, v["npc_senses"].match(/\d+/g));
//LDL
default_attr["light_radius"] = nightVisionRange;
//UDL
default_attr["has_bright_light_vision"] = "on"; //Toggles Vision On
default_attr["has_low_light_vision"] = "on"; //Toggles Vision On
default_attr["has_night_vision"] = "on"; //Toggles Night Vision On
default_attr["night_vision_distance"] = nightVisionRange; //Set Night Vision Range
}
if (v["token_size"]) {
var squarelength = 70;
if (v["token_size"].toString().indexOf(",") > -1) {
var setwidth = !isNaN(v["token_size"].split(",")[0]) ? v["token_size"].split(",")[0] : 1;
var setheight = !isNaN(v["token_size"].split(",")[1]) ? v["token_size"].split(",")[1] : 1;
default_attr["width"] = setwidth * squarelength;
default_attr["height"] = setheight * squarelength;
} else {
default_attr["width"] = squarelength * v["token_size"];
default_attr["height"] = squarelength * v["token_size"];
}
}
var getList = {};
for (x = 1; x <= 3; x++) {
_.each(["v", "m"], function (letter) {
var keyname = "cd_bar" + x + "_" + letter;
if (v[keyname] != undefined && isNaN(v[keyname])) {
getList[keyname] = v[keyname];
}
});
}
getAttrs(_.values(getList), function (values) {
_.each(_.keys(getList), function (keyname) {
v[keyname] = values[getList[keyname]] == undefined ? "" : values[getList[keyname]];
});
if (v["cd_bar1_l"]) {
default_attr["bar1_link"] = v["cd_bar1_l"];
} else if (v["cd_bar1_v"] || v["cd_bar1_m"]) {
if (v["cd_bar1_v"]) {
default_attr["bar1_value"] = v["cd_bar1_v"];
}
if (v["cd_bar1_m"]) {
default_attr["bar1_max"] = v["cd_bar1_m"];
}
} else {
default_attr["bar1_value"] = v["hp_max"];
default_attr["bar1_max"] = v["hp_max"];
}
if (v["cd_bar2_l"]) {
default_attr["bar2_link"] = v["cd_bar2_l"];
} else if (v["cd_bar2_v"] || v["cd_bar2_m"]) {
if (v["cd_bar2_v"]) {
default_attr["bar2_value"] = v["cd_bar2_v"];
}
if (v["cd_bar2_m"]) {
default_attr["bar2_max"] = v["cd_bar2_m"];
}
} else {
default_attr["bar2_link"] = "npc_ac";
}
if (v["cd_bar3_l"]) {
default_attr["bar3_link"] = v["cd_bar3_l"];
} else if (v["cd_bar3_v"] || v["cd_bar3_m"]) {
if (v["cd_bar3_v"]) {
default_attr["bar3_value"] = v["cd_bar3_v"];
}
if (v["cd_bar3_m"]) {
default_attr["bar3_max"] = v["cd_bar3_m"];
}
}
if (isVehicle) {
default_attr["bar1_value"] = v["vehicle_hp_max"];
default_attr["bar1_max"] = v["vehicle_hp_max"];
default_attr["bar2_link"] = "vehicle_ac";
}
setDefaultToken(default_attr);
});
}
);
});
globalAttributesByCategory.abilitiesWithAllOptionals.forEach((attr) => {
on(`change:${attr}_base change:${attr}_bonus`, function () {
update_attr(`${attr}`);
});
});
globalAttributesByCategory.abilitiesWithAllOptionals.forEach((attr) => {
on(`change:${attr}`, function () {
update_mod(`${attr}`);
const cap = attr.charAt(0).toUpperCase() + attr.slice(1);
check_customac(cap);
attr === "strength" ? update_weight() : false;
attr === "dexterity" ? update_initiative() : false;
});
});
globalAttributesByCategory.abilitiesWithAllOptionals.forEach((attr) => {
on(`change:${attr}_mod`, function () {
update_save(`${attr}`);
update_attacks(`${attr}`);
update_tool(`${attr}`);
update_spell_info(`${attr}`);
switch (`${attr}`) {
case "strength":
update_skills(["athletics"]);
break;
case "dexterity":
update_skills(["acrobatics", "sleight_of_hand", "stealth"]);
update_ac();
update_initiative();
break;
case "intelligence":
update_skills(["arcana", "history", "investigation", "nature", "navigation", "religion"]);
break;
case "wisdom":
update_skills(["animal_handling", "insight", "medicine", "perception", "survival"]);
break;
case "charisma":
update_skills(["deception", "intimidation", "performance", "persuasion"]);
break;
default:
false;
}
});
});
globalAttributesByCategory.abilitiesWithAllOptionals.forEach((attr) => {
on(`change:${attr}_save_prof change:${attr}_save_mod`, function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_save(`${attr}`) : false;
});
});
on("change:globalsavemod", function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_all_saves() : false;
});
on("change:death_save_mod", function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_save("death") : false;
});
globalAttributesByCategory.skills.all().forEach((attr) => {
on(`change:${attr}_prof change:${attr}_type change:${attr}_flat`, function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_skills([`${attr}`]) : false;
});
});
on(
"change:repeating_tool:toolname change:repeating_tool:toolbonus_base change:repeating_tool:toolattr_base change:repeating_tool:tool_mod",
function (eventinfo) {
const repeatingRowID = getReprowid(eventinfo.sourceAttribute);
checkSourceForSheetworker(eventinfo) ? update_tool(repeatingRowID) : false;
}
);
on(
"change:repeating_attack:atkname change:repeating_attack:atkflag change:repeating_attack:atkattr_base change:repeating_attack:atkmod change:repeating_attack:atkmagic change:repeating_attack:atkcritrange change:repeating_attack:atkprofflag change:repeating_attack:dmgflag change:repeating_attack:dmgbase change:repeating_attack:dmgattr change:repeating_attack:dmgmod change:repeating_attack:dmgtype change:repeating_attack:dmg2flag change:repeating_attack:dmg2base change:repeating_attack:dmg2attr change:repeating_attack:dmg2mod change:repeating_attack:dmg2type change:repeating_attack:saveflag change:repeating_attack:savedc change:repeating_attack:saveflat change:repeating_attack:dmgcustcrit change:repeating_attack:dmg2custcrit change:repeating_attack:ammo change:repeating_attack:saveattr change:repeating_attack:atkrange",
function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
var source = getAttrNameAtEndRepeating(eventinfo.sourceAttribute);
var attackid = getReprowid(eventinfo.sourceAttribute);
if (source == "atkattr_base" || source == "savedc") {
getAttrs(["repeating_attack_spellid", "repeating_attack_spelllevel"], function (v) {
set = {};
if (
v.repeating_attack_spellid &&
v.repeating_attack_spellid != "" &&
v.repeating_attack_spelllevel &&
v.repeating_attack_spelllevel != ""
) {
var newVal = eventinfo.newValue == "spell" ? "spell" : _.last(eventinfo.newValue.split("_")[0].split("{"));
set["repeating_attack_atkattr_base"] = newVal == "spell" ? "spell" : "@{" + newVal + "_mod}";
set["repeating_attack_savedc"] = newVal == "spell" ? "spell" : "(@{" + newVal + "_mod}+8+@{pb})";
set["repeating_spell-" + v.repeating_attack_spelllevel + "_" + v.repeating_attack_spellid + "_spell_ability"] =
newVal == "spell" ? "spell" : "@{" + newVal + "_mod}+";
}
setAttrs(set, function () {
update_attacks(attackid);
});
});
} else {
update_attacks(attackid);
}
}
}
);
on("change:default_critical_range", function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
update_attacks("all");
}
});
on("change:repeating_damagemod remove:repeating_damagemod", function (eventinfo) {
update_globaldamage();
});
on("change:global_damage_mod_flag", function (eventinfo) {
getSectionIDs("damagemod", function (ids) {
var update = {};
if (eventinfo.newValue === "1") {
if (!ids || ids.length === 0) {
var rowid = generateRowID();
update[`repeating_damagemod_${rowid}_global_damage_active_flag`] = "1";
}
} else {
_.each(ids, function (rowid) {
update[`repeating_damagemod_${rowid}_global_damage_active_flag`] = "0";
});
}
setAttrs(update);
});
});
on("change:exhaustion_toggle", function (eventinfo) {
if (eventinfo.newValue !== "0") {
getAttrs(["exhaustion_level"], function (attrs) {
if (!attrs.exhaustion_level || attrs.exhaustion_level === "") {
var update = {};
update.exhaustion_level = "0";
setAttrs(update);
}
});
}
});
on("change:exhaustion_level", function (eventinfo) {
const newValue = parseInt(eventinfo.newValue) || 0,
previousValue = parseInt(eventinfo.previousValue) || 0;
let update = {};
if (newValue === 0) {
//If exhaustion is 0 the reset exhaustion_1 to "No Effect" and blank the other spans
for (let i = 2; i <= 6; i++) {
update[`exhaustion_${i}`] = "";
}
update[`exhaustion_1`] = "• " + getTranslationByKey(`exhaustion-0`);
} else if (newValue > previousValue) {
//If exhaustion increase then add text to the spans
for (let i = previousValue; i <= newValue; i++) {
update[`exhaustion_${i}`] = "• " + getTranslationByKey(`exhaustion-${i}`);
}
} else {
//If exhaustion decrease remove text from spans
for (let i = newValue + 1; i <= previousValue; i++) {
update[`exhaustion_${i}`] = "";
}
}
setAttrs(update, { silent: true });
});
on("change:race change:subrace", function (eventinfo) {
update_race_display();
});
on(`change:repeating_inventory:hasattack`, function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
const itemid = getReprowid(eventinfo.sourceAttribute);
getAttrs([`repeating_inventory_${itemid}_itemattackid`], function (v) {
const hasattack = eventinfo.newValue,
itemattackid = v[`repeating_inventory_${itemid}_itemattackid`];
hasattack == 1 ? create_attack_from_item(itemid) : remove_attack(itemattackid);
});
}
});
on(`change:repeating_inventory:useasresource`, function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
const itemid = getReprowid(eventinfo.sourceAttribute);
getAttrs([`repeating_inventory_${itemid}_itemresourceid`], function (v) {
const useasresource = eventinfo.newValue,
itemresourceid = v[`repeating_inventory_${itemid}_itemresourceid`];
useasresource == 1 ? create_resource_from_item(itemid) : remove_resource(itemresourceid);
});
}
});
on(
"change:repeating_inventory:itemname change:repeating_inventory:itemproperties change:repeating_inventory:itemmodifiers change:repeating_inventory:itemcount",
function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
var itemid = getReprowid(eventinfo.sourceAttribute);
getAttrs(["repeating_inventory_" + itemid + "_itemattackid", "repeating_inventory_" + itemid + "_itemresourceid"], function (v) {
var attackid = v["repeating_inventory_" + itemid + "_itemattackid"];
var resourceid = v["repeating_inventory_" + itemid + "_itemresourceid"];
if (attackid) {
update_attack_from_item(itemid, attackid);
}
if (resourceid) {
update_resource_from_item(itemid, resourceid);
}
});
}
}
);
["other_resource", "repeating_resource:resource_left", "repeating_resource:resource_right"].forEach((attr) => {
on(`change:${attr} change:${attr}_name`, (eventinfo) => {
if (checkSourceForSheetworker(eventinfo)) {
const resourceid = eventinfo.sourceAttribute.includes("_name") ? eventinfo.sourceAttribute.slice(0, -5) : eventinfo.sourceAttribute;
getAttrs([`${resourceid}_itemid`], (v) => {
const value = eventinfo.newValue;
//Update repeating_inventory if an itemid is associated with a resource
if (v[`${resourceid}_itemid`] && v[`${resourceid}_itemid`] != "") {
const itemid = v[`${resourceid}_itemid`];
let update = {};
if (eventinfo.sourceAttribute.includes("_name")) {
update[`repeating_inventory_${itemid}_itemname`] = eventinfo.newValue;
} else {
update[`repeating_inventory_${itemid}_itemcount`] = eventinfo.newValue;
}
setAttrs(update, { silent: true }, () => {
update_weight();
});
}
});
}
});
});
on(
"change:repeating_inventory:itemweight change:repeating_inventory:itemcount change:repeating_inventory:equipped change:cp change:sp change:ep change:gp change:pp change:encumberance_setting change:size change:carrying_capacity_mod change:ingore_non_equipped_weight",
function () {
update_weight();
}
);
on("change:repeating_inventory:itemmodifiers change:repeating_inventory:equipped", function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
var itemid = getReprowid(eventinfo.sourceAttribute);
getAttrs(["repeating_inventory_" + itemid + "_itemmodifiers"], function (v) {
if (v["repeating_inventory_" + itemid + "_itemmodifiers"]) {
check_itemmodifiers(v["repeating_inventory_" + itemid + "_itemmodifiers"], eventinfo.previousValue);
}
});
}
});
on(
"change:custom_ac_flag change:custom_ac_base change:custom_ac_part1 change:custom_ac_part2 change:custom_ac_shield",
function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_ac() : false;
}
);
["spell-cantrip", "spell-1", "spell-2", "spell-3", "spell-4", "spell-5", "spell-6", "spell-7", "spell-8", "spell-9"].forEach((attr) => {
on(
`change:repeating_${attr}:includedesc change:repeating_${attr}:innate change:repeating_${attr}:spell_ability change:repeating_${attr}:spell_updateflag change:repeating_${attr}:spellathigherlevels change:repeating_${attr}:spellattack change:repeating_${attr}:spelldamage change:repeating_${attr}:spelldamage2 change:repeating_${attr}:spelldamagetype change:repeating_${attr}:spelldamagetype2 change:repeating_${attr}:spelldescription change:repeating_${attr}:spelldmgmod change:repeating_${attr}:spellhealing change:repeating_${attr}:spellhlbonus change:repeating_${attr}:spellhldie change:repeating_${attr}:spellhldietype change:repeating_${attr}:spellname change:repeating_${attr}:spellprepared change:repeating_${attr}:spellrange change:repeating_${attr}:spellsave change:repeating_${attr}:spellsavesuccess change:repeating_${attr}:spelltarget change:repeating_${attr}:spell_damage_progression`,
(eventinfo) => {
if (checkSourceForSheetworker(eventinfo)) {
const spellid = eventinfo.sourceAttribute.split("_")[2],
repeating_source = `repeating_${attr}_${spellid}`;
getAttrs([`${repeating_source}_spellattackid`, "character_id"], (v) => {
var attackid = v[`${repeating_source}_spellattackid`];
var lvl = attr.split("spell-")[1];
if (attackid && lvl && spellid) {
update_attack_from_spell(lvl, spellid, attackid, false, v["character_id"]);
}
});
}
}
);
});
["spell-cantrip", "spell-1", "spell-2", "spell-3", "spell-4", "spell-5", "spell-6", "spell-7", "spell-8", "spell-9"].forEach((attr) => {
on(`change:repeating_${attr}:spelloutput`, (eventinfo) => {
if (checkSourceForSheetworker(eventinfo)) {
const spellid = eventinfo.sourceAttribute.split("_")[2],
repeating_source = `repeating_${attr}_${spellid}`;
getAttrs(
[`${repeating_source}_spellattackid`, `${repeating_source}_spelllevel`, `${repeating_source}_spellathigherlevels`, "character_id"],
function (v) {
const attackid = v[repeating_source + "_spellattackid"];
const higherlevels = v[repeating_source + "_spellathigherlevels"];
const spelloutput = eventinfo.newValue;
let lvl = v[repeating_source + "_spelllevel"];
if (spelloutput && spelloutput === "ATTACK") {
create_attack_from_spell(lvl, spellid, v["character_id"]);
} else if (spelloutput && spelloutput === "SPELLCARD" && attackid && attackid != "") {
let lvl = parseInt(v[repeating_source + "_spelllevel"], 10);
remove_attack(attackid);
update_spelloutput(higherlevels, lvl, repeating_source, spelloutput);
}
}
);
}
});
});
["spell-cantrip", "spell-1", "spell-2", "spell-3", "spell-4", "spell-5", "spell-6", "spell-7", "spell-8", "spell-9"].forEach((attr) => {
on(`change:repeating_${attr}:spellathigherlevels`, (eventinfo) => {
if (checkSourceForSheetworker(eventinfo)) {
const spellid = eventinfo.sourceAttribute.split("_")[2],
repeating_source = `repeating_${attr}_${spellid}`;
getAttrs([`${repeating_source}_spelllevel`, `${repeating_source}_spelloutput`], function (v) {
const higherlevels = eventinfo.newValue;
const lvl = parseInt(v[repeating_source + "_spelllevel"], 10);
const spelloutput = v[repeating_source + "_spelloutput"];
if (spelloutput && spelloutput === "SPELLCARD") {
update_spelloutput(higherlevels, lvl, repeating_source, spelloutput);
}
});
}
});
});
const update_spelloutput = (higherlevels, lvl, repeating_source, spelloutput) => {
let spelllevel = "@{spelllevel}";
let update = {};
if (higherlevels) {
for (i = 0; i < 10 - lvl; i++) {
let tot = parseInt(i, 10) + parseInt(lvl, 10);
spelllevel = spelllevel + "|Level " + tot + "," + tot;
}
spelllevel = `?{Cast at what level? ${spelllevel}}`;
}
update[
repeating_source + "_rollcontent"
] = `@{wtype}&{template:spell} {{level=@{spellschool} ${spelllevel}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} {{savedc=@{spell_save_dc}}} @{spellconcentration} @{charname_output}`;
setAttrs(update, { silent: true });
};
on(
"change:class change:custom_class change:cust_classname change:cust_hitdietype change:cust_spellcasting_ability change:cust_spellslots change:cust_strength_save_prof change:cust_dexterity_save_prof change:cust_constitution_save_prof change:cust_intelligence_save_prof change:cust_wisdom_save_prof change:cust_charisma_save_prof change:subclass change:multiclass1 change:multiclass1_subclass change:multiclass2 change:multiclass2_subclass change:multiclass3 change:multiclass3_subclass",
function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_class() : false;
}
);
on(
"change:base_level change:multiclass1_flag change:multiclass1 change:multiclass1_lvl change:multiclass2_flag change:multiclass2 change:multiclass2_lvl change:multiclass3_flag change:multiclass3 change:multiclass3_lvl change:arcane_fighter change:arcane_rogue change:third_caster",
function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? set_level() : false;
}
);
on(
"change:level_calculations change:caster_level change:lvl1_slots_mod change:lvl2_slots_mod change:lvl3_slots_mod change:lvl4_slots_mod change:lvl5_slots_mod change:lvl6_slots_mod change:lvl7_slots_mod change:lvl8_slots_mod change:lvl9_slots_mod change:arcane_fighter change:arcane_rogue change:third_caster",
function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
getAttrs(["level_calculations"], function (v) {
if (!v["level_calculations"] || v["level_calculations"] == "on") {
update_spell_slots();
}
});
}
}
);
on("change:caster_level", function (eventinfo) {
getAttrs(["caster_level", "npc"], function (v) {
var casterlvl = v["caster_level"] && !isNaN(parseInt(v["caster_level"], 10)) ? parseInt(v["caster_level"], 10) : 0;
if (v["npc"] && v["npc"] == 1 && casterlvl > 0) {
setAttrs({ level: casterlvl });
}
});
});
on("change:pb_type change:pb_custom", function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_pb() : false;
});
on("change:dtype", function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
update_attacks("all");
update_npc_action("all");
}
});
on("change:jack_of_all_trades", function (eventinfo) {
if (checkSourceForSheetworker(eventinfo)) {
update_jack_attr();
update_all_ability_checks();
}
});
on("change:initmod change:init_tiebreaker", function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_initiative() : false;
});
on("change:spellcasting_ability change:spell_dc_mod change:globalmagicmod", function (eventinfo) {
checkSourceForSheetworker(eventinfo) ? update_spell_info() : false;
});
on("change:npc_challenge", function () {
update_challenge();
});
on(
"change:npc_str_save_base change:npc_dex_save_base change:npc_con_save_base change:npc_int_save_base change:npc_wis_save_base change:npc_cha_save_base",
function (eventinfo) {
update_npc_saves();
}
);
on(
"change:npc_acrobatics_base change:npc_animal_handling_base change:npc_arcana_base change:npc_athletics_base change:npc_deception_base change:npc_history_base change:npc_insight_base change:npc_intimidation_base change:npc_investigation_base change:npc_medicine_base change:npc_nature_base change:npc_navigation_base change:npc_perception_base change:npc_performance_base change:npc_persuasion_base change:npc_religion_base change:npc_sleight_of_hand_base change:npc_stealth_base change:npc_survival_base",
function (eventinfo) {
update_npc_skills();
}
);
["npcaction", "vehicleactions", "npcbonusaction", "npcaction-l", "npcaction-m"].forEach((fieldset) => {
[
"attack_flag",
"attack_type",
"attack_range",
"attack_target",
"attack_tohit",
"attack_damage",
"attack_damagetype",
"attack_damage2",
"attack_damagetype2",
"show_desc",
"description",
].forEach((property) => {
on(`change:repeating_${fieldset}:${property}`, (eventInfo) => {
const actionid = eventInfo.sourceAttribute.split("_")[2];
update_npc_action(actionid, fieldset);
});
});
});
on("change:core_die change:halflingluck_flag", function () {
getAttrs(["core_die", "halflingluck_flag"], function (v) {
core = v.core_die && v.core_die != "" ? v.core_die : "1d20";
luck = v.halflingluck_flag && v.halflingluck_flag === "1" ? "ro<1" : "";
update = {};
update["d20"] = core + luck;
if (!v.core_die || v.core_die === "") {
update["core_die"] = "1d20";
}
setAttrs(update);
});
});
on("change:powerful_build", function () {
update_weight();
});
[`ac`, `attack`, "save", "skill"].forEach((attr) => {
on(`change:global_${attr}_mod_flag`, (eventinfo) => {
const mod = attr === "attack" ? "tohitmod" : `${attr}mod`;
if (eventinfo.newValue === "1") {
const firstAttr = attr === "ac" ? "val" : "roll";
const firstAttrValue = attr === "ac" ? 1 : "1d4";
const secondAttrValue = attr === "ac" ? "Defense" : attr === "skill" ? "GUIDANCE" : "BLESS";
getSectionIDs(mod, (ids) => {
if (!ids || ids.length === 0) {
var update = {};
var rowid = generateRowID();
update[`repeating_${mod}_${rowid}_global_${attr}_${firstAttr}`] = `${firstAttrValue}`;
update[`repeating_${mod}_${rowid}_global_${attr}_name`] = `${secondAttrValue}`;
update[`repeating_${mod}_${rowid}_global_${attr}_active_flag`] = "1";
setAttrs(update);
}
});
} else {
getSectionIDs(mod, function (ids) {
var update = {};
var rowid = generateRowID();
_.each(ids, function (rowid) {
update[`repeating_${mod}_${rowid}_global_${attr}_active_flag`] = "0";
});
setAttrs(update);
});
}
});
});
on("change:repeating_skillmod remove:repeating_skillmod", function (eventinfo) {
update_globalskills();
});
on("change:repeating_savemod remove:repeating_savemod", function (eventinfo) {
update_globalsaves();
});
on("change:repeating_tohitmod remove:repeating_tohitmod", function (eventinfo) {
update_globalattack();
});
on("change:repeating_acmod remove:repeating_acmod", function (eventinfo) {
update_ac();
});
on("change:confirm", function (eventinfo) {
setAttrs({ monster_confirm_flag: "" });
getAttrs(["drop_category"], function (v) {
if (v["drop_category"]) {
handle_drop(v["drop_category"]);
}
});
});
on("change:cancel", function (eventinfo) {
setAttrs({ monster_confirm_flag: "" });
var update = {};
update["drop_category"] = "";
update["drop_name"] = "";
update["drop_data"] = "";
update["drop_content"] = "";
setAttrs(update, { silent: true });
});
on("change:mancer_confirm", function (eventinfo) {
setAttrs({ mancer_confirm_flag: "", charactermancer_step: "l1-welcome" }, function () {
check_l1_mancer();
});
});
on("change:mancer_cancel", function (eventinfo) {
setAttrs({ mancer_confirm_flag: "", l1mancer_status: "completed" }, function () {
check_l1_mancer();
});
});
on("change:mancer_npc", function (eventinfo) {
setAttrs({ mancer_confirm_flag: "", l1mancer_status: "completed", npc: "1", charactersheet_type: "npc" }, function () {
check_l1_mancer();
});
});
on("change:passiveperceptionmod", function (eventinfo) {
update_passive_perception();
});
on("remove:repeating_inventory", function (eventinfo) {
var itemid = getReprowid(eventinfo.sourceAttribute);
var attackids =
eventinfo.removedInfo && eventinfo.removedInfo["repeating_inventory_" + itemid + "_itemattackid"]
? eventinfo.removedInfo["repeating_inventory_" + itemid + "_itemattackid"]
: undefined;
var resourceid =
eventinfo.removedInfo && eventinfo.removedInfo["repeating_inventory_" + itemid + "_itemresourceid"]
? eventinfo.removedInfo["repeating_inventory_" + itemid + "_itemresourceid"]
: undefined;
if (attackids) {
_.each(attackids.split(","), function (value) {
remove_attack(value);
});
}
if (resourceid) {
remove_resource(resourceid);
}
if (eventinfo.removedInfo && eventinfo.removedInfo["repeating_inventory_" + itemid + "_itemmodifiers"]) {
check_itemmodifiers(eventinfo.removedInfo["repeating_inventory_" + itemid + "_itemmodifiers"]);
}
update_weight();
});
on("remove:repeating_attack", function (eventinfo) {
var attackid = getReprowid(eventinfo.sourceAttribute);
var itemid = eventinfo.removedInfo["repeating_attack_" + attackid + "_itemid"];
var spellid = eventinfo.removedInfo["repeating_attack_" + attackid + "_spellid"];
var spelllvl = eventinfo.removedInfo["repeating_attack_" + attackid + "_spelllevel"];
if (itemid) {
getAttrs(["repeating_inventory_" + itemid + "_hasattack"], function (v) {
if (v["repeating_inventory_" + itemid + "_hasattack"] && v["repeating_inventory_" + itemid + "_hasattack"] == 1) {
var update = {};
update["repeating_inventory_" + itemid + "_itemattackid"] = "";
update["repeating_inventory_" + itemid + "_hasattack"] = 0;
setAttrs(update, { silent: true });
}
});
}
if (spellid && spelllvl) {
getAttrs(["repeating_spell-" + spelllvl + "_" + spellid + "_spelloutput"], function (v) {
if (
v["repeating_spell-" + spelllvl + "_" + spellid + "_spelloutput"] &&
v["repeating_spell-" + spelllvl + "_" + spellid + "_spelloutput"] == "ATTACK"
) {
var update = {};
update["repeating_spell-" + spelllvl + "_" + spellid + "_spellattackid"] = "";
update["repeating_spell-" + spelllvl + "_" + spellid + "_spelloutput"] = "SPELLCARD";
setAttrs(update, { silent: true });
}
});
}
});
on("remove:repeating_resource", function (eventinfo) {
const resourceid = getReprowid(eventinfo.sourceAttribute);
let update = {};
_.each(["left", "right"], (side) => {
const itemid = eventinfo.removedInfo[`repeating_resource_${resourceid}_resource_${side}_itemid`];
if (itemid) {
update[`repeating_inventory_${itemid}_useasresource`] = 0;
update[`repeating_inventory_${itemid}_itemresourceid`] = "";
}
});
setAttrs(update, { silent: true });
});
on(
"remove:repeating_spell-cantrip remove:repeating_spell-1 remove:repeating_spell-2 remove:repeating_spell-3 remove:repeating_spell-4 remove:repeating_spell-5 remove:repeating_spell-6 remove:repeating_spell-7 remove:repeating_spell-8 remove:repeating_spell-9",
function (eventinfo) {
var attackid = eventinfo.removedInfo[eventinfo.sourceAttribute + "_spellattackid"];
if (attackid) {
remove_attack(attackid);
}
}
);
on("clicked:relaunch_lvl1mancer", function (eventinfo) {
getAttrs(["l1mancer_status"], function (v) {
if (v["l1mancer_status"] === "completed") {
setAttrs({ l1mancer_status: "relaunch" });
}
check_l1_mancer();
});
});
on("clicked:launch_lvl+mancer", function (eventinfo) {
getAttrs(
[
"class",
"level",
"hp_max",
"custom_class",
"multiclass1_flag",
"multiclass2_flag",
"multiclass3_flag",
"multiclass1",
"multiclass2",
"multiclass3",
],
function (v) {
var throw_warning = false;
var class_array = [v["class"]];
if (
!v["class"] ||
!v["hp_max"] ||
isNaN(parseInt(v["hp_max"], 10)) ||
!v["level"] ||
isNaN(parseInt(v["level"], 10)) ||
parseInt(v["level"], 10) < 1 ||
(v["multiclass2_flag"] == 1 && v["multiclass1_flag"] == 0) ||
(v["multiclass3_flag"] == 1 && v["multiclass2_flag"] == 0)
) {
throw_warning = true;
}
if (v["multiclass1_flag"] == 1) {
class_array.push(v["multiclass1"]);
}
if (v["multiclass2_flag"] == 1) {
class_array.push(v["multiclass2"]);
}
if (v["multiclass3_flag"] == 1) {
class_array.push(v["multiclass3"]);
}
// Check to see if there are any duplicate multiclasses
if (new Set(class_array).size !== class_array.length) {
throw_warning = true;
}
if (throw_warning === true) {
setAttrs({ leveler_warningflag: "show" });
return;
}
setAttrs({ lpmancer_status: "active" }, function () {
startCharactermancer("lp-welcome");
});
}
);
});
on("clicked:launch_the_right_mancer", function (eventinfo) {
getAttrs(
[
"class",
"level",
"hp_max",
"custom_class",
"multiclass1_flag",
"multiclass2_flag",
"multiclass3_flag",
"multiclass1",
"multiclass2",
"multiclass3",
],
function (v) {
var throw_warning = false;
var class_array = [v["class"]];
if (
!v["class"] ||
!v["hp_max"] ||
isNaN(parseInt(v["hp_max"], 10)) ||
!v["level"] ||
isNaN(parseInt(v["level"], 10)) ||
parseInt(v["level"], 10) < 1 ||
(v["multiclass2_flag"] == 1 && v["multiclass1_flag"] == 0) ||
(v["multiclass3_flag"] == 1 && v["multiclass2_flag"] == 0)
) {
throw_warning = true;
}
if (v["multiclass1_flag"] == 1) {
class_array.push(v["multiclass1"]);
}
if (v["multiclass2_flag"] == 1) {
class_array.push(v["multiclass2"]);
}
if (v["multiclass3_flag"] == 1) {
class_array.push(v["multiclass3"]);
}
// Check to see if there are any duplicate multiclasses
if (new Set(class_array).size !== class_array.length) {
throw_warning = true;
}
if (throw_warning === true) {
//Fire LV1 Mancer
getAttrs(["l1mancer_status"], function (v) {
if (v["l1mancer_status"] === "completed") {
setAttrs({ l1mancer_status: "relaunch" });
}
check_l1_mancer();
});
} else {
//FIre LV+ Mancer
setAttrs({ lpmancer_status: "active" }, function () {
startCharactermancer("lp-welcome");
});
}
}
);
});
on("change:experience", function (eventinfo) {
update_leveler_display();
});
var update_attr = function (attr) {
var update = {};
var attr_fields = [attr + "_base", attr + "_bonus"];
getSectionIDs("repeating_inventory", function (idarray) {
_.each(idarray, function (currentID, i) {
attr_fields.push("repeating_inventory_" + currentID + "_equipped");
attr_fields.push("repeating_inventory_" + currentID + "_itemmodifiers");
});
getAttrs(attr_fields, function (v) {
var base = v[attr + "_base"] && !isNaN(parseInt(v[attr + "_base"], 10)) ? parseInt(v[attr + "_base"], 10) : 10;
var bonus = v[attr + "_bonus"] && !isNaN(parseInt(v[attr + "_bonus"], 10)) ? parseInt(v[attr + "_bonus"], 10) : 0;
var item_base = 0;
var item_bonus = 0;
_.each(idarray, function (currentID) {
if (
(!v["repeating_inventory_" + currentID + "_equipped"] || v["repeating_inventory_" + currentID + "_equipped"] === "1") &&
v["repeating_inventory_" + currentID + "_itemmodifiers"] &&
v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().indexOf(attr > -1)
) {
var mods = v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().split(",");
_.each(mods, function (mod) {
if (mod.indexOf(attr) > -1 && mod.indexOf("save") === -1) {
if (mod.indexOf(":") > -1) {
var new_base = !isNaN(parseInt(mod.replace(/[^0-9]/g, ""), 10)) ? parseInt(mod.replace(/[^0-9]/g, ""), 10) : false;
item_base = new_base && new_base > item_base ? new_base : item_base;
} else if (mod.indexOf("-") > -1) {
var new_mod = !isNaN(parseInt(mod.replace(/[^0-9]/g, ""), 10)) ? parseInt(mod.replace(/[^0-9]/g, ""), 10) : false;
item_bonus = new_mod ? item_bonus - new_mod : item_bonus;
} else {
var new_mod = !isNaN(parseInt(mod.replace(/[^0-9]/g, ""), 10)) ? parseInt(mod.replace(/[^0-9]/g, ""), 10) : false;
item_bonus = new_mod ? item_bonus + new_mod : item_bonus;
}
}
});
}
});
update[attr + "_flag"] = bonus > 0 || item_bonus > 0 || item_base > base ? 1 : 0;
base = base > item_base ? base : item_base;
const total = base + bonus + item_bonus;
update[attr] = total;
update[`${[attr]}_mod`] = Math.floor((total - 10) / 2);
setAttrs(update);
});
});
};
var update_mod = function (attr) {
getAttrs([attr], function (v) {
var attr_abr = attr.substring(0, 3);
var finalattr = v[attr] && isNaN(v[attr]) === false ? Math.floor((parseInt(v[attr], 10) - 10) / 2) : 0;
var update = {};
update[attr + "_mod"] = finalattr;
update["npc_" + attr_abr + "_negative"] = v[attr] && !isNaN(v[attr]) && parseInt(v[attr], 10) < 10 ? 1 : 0;
setAttrs(update);
});
};
var update_save = function (attr) {
var save_attrs = [attr + "_mod", attr + "_save_prof", attr + "_save_mod", "pb", "globalsavemod", "pb_type"];
getSectionIDs("repeating_inventory", function (idarray) {
_.each(idarray, function (currentID, i) {
save_attrs.push("repeating_inventory_" + currentID + "_equipped");
save_attrs.push("repeating_inventory_" + currentID + "_itemmodifiers");
});
getAttrs(save_attrs, function (v) {
var attr_mod = v[attr + "_mod"] ? parseInt(v[attr + "_mod"], 10) : 0;
var prof = v[attr + "_save_prof"] && v[attr + "_save_prof"] != 0 && !isNaN(v["pb"]) ? parseInt(v["pb"], 10) : 0;
var save_mod = v[attr + "_save_mod"] && !isNaN(parseInt(v[attr + "_save_mod"], 10)) ? parseInt(v[attr + "_save_mod"], 10) : 0;
var global = v["globalsavemod"] && !isNaN(v["globalsavemod"]) ? parseInt(v["globalsavemod"], 10) : 0;
var items = 0;
_.each(idarray, function (currentID) {
if (
v["repeating_inventory_" + currentID + "_equipped"] &&
v["repeating_inventory_" + currentID + "_equipped"] === "1" &&
v["repeating_inventory_" + currentID + "_itemmodifiers"] &&
(v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().indexOf("saving throws") > -1 ||
v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().indexOf(attr + " save") > -1)
) {
var mods = v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().split(",");
_.each(mods, function (mod) {
if (mod.indexOf(attr + " save") > -1) {
var substr = mod.slice(mod.lastIndexOf(attr + " save") + attr.length + " save".length);
var bonus = substr && substr.length > 0 && !isNaN(parseInt(substr, 10)) ? parseInt(substr, 10) : 0;
} else if (mod.indexOf("saving throws") > -1) {
var substr = mod.slice(mod.lastIndexOf("saving throws") + "saving throws".length);
var bonus = substr && substr.length > 0 && !isNaN(parseInt(substr, 10)) ? parseInt(substr, 10) : 0;
}
if (bonus && bonus != 0) {
items = items + bonus;
}
});
}
});
var total = attr_mod + prof + save_mod + global + items;
if (v["pb_type"] && v["pb_type"] === "die" && v[attr + "_save_prof"] != 0 && attr != "death") {
total = total + "+" + v["pb"];
}
var update = {};
update[attr + "_save_bonus"] = total;
setAttrs(update, { silent: true }, () => {
updateSavingRoll(attr);
});
});
});
};
var update_all_saves = function () {
update_save("strength");
update_save("dexterity");
update_save("constitution");
update_save("intelligence");
update_save("wisdom");
update_save("charisma");
update_save("honor");
update_save("sanity");
update_save("death");
};
var update_all_ability_checks = function () {
update_initiative();
update_skills([
"athletics",
"acrobatics",
"sleight_of_hand",
"stealth",
"arcana",
"history",
"investigation",
"nature",
"navigation",
"religion",
"animal_handling",
"insight",
"medicine",
"perception",
"survival",
"deception",
"intimidation",
"performance",
"persuasion",
]);
};
var update_skills = function (skills_array) {
var skill_parent = {
athletics: "strength",
acrobatics: "dexterity",
sleight_of_hand: "dexterity",
stealth: "dexterity",
arcana: "intelligence",
history: "intelligence",
investigation: "intelligence",
nature: "intelligence",
navigation: "intelligence",
religion: "intelligence",
animal_handling: "wisdom",
insight: "wisdom",
medicine: "wisdom",
perception: "wisdom",
survival: "wisdom",
deception: "charisma",
intimidation: "charisma",
performance: "charisma",
persuasion: "charisma",
};
var attrs_to_get = ["pb", "pb_type", "jack_of_all_trades", "jack"];
var update = {};
var callbacks = [];
if (skills_array.indexOf("perception") > -1) {
callbacks.push(function () {
update_passive_perception();
});
}
_.each(skills_array, function (s) {
if (skill_parent[s] && attrs_to_get.indexOf(skill_parent[s]) === -1) {
attrs_to_get.push(skill_parent[s] + "_mod");
}
attrs_to_get.push(s + "_prof");
attrs_to_get.push(s + "_type");
attrs_to_get.push(s + "_flat");
});
getSectionIDs("repeating_inventory", function (idarray) {
_.each(idarray, function (currentID, i) {
attrs_to_get.push("repeating_inventory_" + currentID + "_equipped");
attrs_to_get.push("repeating_inventory_" + currentID + "_itemmodifiers");
});
getAttrs(attrs_to_get, function (v) {
const imposeDisadvantage = [];
_.each(skills_array, function (s) {
var attr_mod = v[skill_parent[s] + "_mod"] ? parseInt(v[skill_parent[s] + "_mod"], 10) : 0;
var prof = v[s + "_prof"] != 0 && !isNaN(v["pb"]) ? parseInt(v["pb"], 10) : 0;
var flat = v[s + "_flat"] && !isNaN(parseInt(v[s + "_flat"], 10)) ? parseInt(v[s + "_flat"], 10) : 0;
var type = v[s + "_type"] && !isNaN(parseInt(v[s + "_type"], 10)) ? parseInt(v[s + "_type"], 10) : 1;
var jack = v["jack_of_all_trades"] && v["jack_of_all_trades"] != 0 && v["jack"] ? v["jack"] : 0;
var item_bonus = 0;
_.each(idarray, function (currentID) {
if (
v["repeating_inventory_" + currentID + "_equipped"] &&
v["repeating_inventory_" + currentID + "_equipped"] === "1" &&
v["repeating_inventory_" + currentID + "_itemmodifiers"] &&
(v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().replace(/ /g, "_").indexOf(s) > -1 ||
v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().indexOf("ability checks") > -1)
) {
var mods = v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().split(",");
_.each(mods, function (mod) {
if (mod.replace(/ /g, "_").indexOf(s) > -1 || mod.indexOf("ability checks") > -1) {
if (mod.indexOf("-") > -1) {
var new_mod = !isNaN(parseInt(mod.replace(/[^0-9]/g, ""), 10)) ? parseInt(mod.replace(/[^0-9]/g, ""), 10) : false;
item_bonus = new_mod ? item_bonus - new_mod : item_bonus;
} else {
var new_mod = !isNaN(parseInt(mod.replace(/[^0-9]/g, ""), 10)) ? parseInt(mod.replace(/[^0-9]/g, ""), 10) : false;
item_bonus = new_mod ? item_bonus + new_mod : item_bonus;
}
}
if (mod.includes(":") && mod.trim().split(":")[0].replace(/ /g, "_") === s && mod.trim().split(":")[1] === "disadvantage") {
imposeDisadvantage.push(s);
}
});
}
});
var total = attr_mod + flat + item_bonus;
if (v["pb_type"] && v["pb_type"] === "die") {
if (v[s + "_prof"] != 0) {
type === 1 ? "" : "2";
total = total + "+" + type + v["pb"];
} else if (v[s + "_prof"] == 0 && jack != 0) {
total = total + "+" + jack;
}
} else {
if (v[s + "_prof"] != 0) {
total = total + prof * type;
} else if (v[s + "_prof"] == 0 && jack != 0) {
total = total + parseInt(jack, 10);
}
}
update[s + "_bonus"] = total;
});
setAttrs(update, { silent: true }, function () {
callbacks.forEach(function (callback) {
callback();
});
updateSkillRolls(skills_array, imposeDisadvantage);
});
});
});
};
var update_tool = function (tool_id) {
//D&D 5e Mancer: Land Vehicles proficiency does not drop with Marine background (UC748)
//Added a test to check for an undefined tool_id so to prevent similar errors.
//By Miguel Peres
if (typeof tool_id == "undefined") return;
if (tool_id.substring(0, 1) === "-" && tool_id.length === 20) {
do_update_tool([tool_id]);
} else if (tool_id === "all") {
getSectionIDs("repeating_tool", function (idarray) {
do_update_tool(idarray);
});
} else {
getSectionIDs("repeating_tool", function (idarray) {
var tool_attribs = [];
_.each(idarray, function (id) {
tool_attribs.push("repeating_tool_" + id + "_toolattr_base");
});
getAttrs(tool_attribs, function (v) {
var attr_tool_ids = [];
_.each(idarray, function (id) {
if (v["repeating_tool_" + id + "_toolattr_base"] && v["repeating_tool_" + id + "_toolattr_base"].indexOf(tool_id) > -1) {
attr_tool_ids.push(id);
}
});
if (attr_tool_ids.length > 0) {
do_update_tool(attr_tool_ids);
}
});
});
}
};
var do_update_tool = function (tool_array) {
var tool_attribs = [
"pb",
"pb_type",
"jack",
"strength_mod",
"dexterity_mod",
"constitution_mod",
"intelligence_mod",
"wisdom_mod",
"charisma_mod",
];
var update = {};
_.each(tool_array, function (tool_id) {
tool_attribs.push("repeating_tool_" + tool_id + "_toolbonus_base");
tool_attribs.push("repeating_tool_" + tool_id + "_tool_mod");
tool_attribs.push("repeating_tool_" + tool_id + "_toolattr_base");
});
getAttrs(tool_attribs, function (v) {
_.each(tool_array, function (tool_id) {
var query = false;
if (v["repeating_tool_" + tool_id + "_toolattr_base"] && v["repeating_tool_" + tool_id + "_toolattr_base"].substring(0, 2) === "?{") {
update["repeating_tool_" + tool_id + "_toolattr"] = "QUERY";
var mod =
"?{Attribute?|Strength,@{strength_mod}|Dexterity,@{dexterity_mod}|Constitution,@{constitution_mod}|Intelligence,@{intelligence_mod}|Wisdom,@{wisdom_mod}|Charisma,@{charisma_mod}}";
if (v["repeating_tool_" + tool_id + "_tool_mod"]) {
mod = mod + "+" + v["repeating_tool_" + tool_id + "_tool_mod"];
}
query = true;
} else {
var attr = v["repeating_tool_" + tool_id + "_toolattr_base"]
.substring(0, v["repeating_tool_" + tool_id + "_toolattr_base"].length - 5)
.substr(2);
var attr_mod = v[attr + "_mod"] ? parseInt(v[attr + "_mod"], 10) : 0;
var tool_mod =
v["repeating_tool_" + tool_id + "_tool_mod"] && !isNaN(parseInt(v["repeating_tool_" + tool_id + "_tool_mod"], 10))
? parseInt(v["repeating_tool_" + tool_id + "_tool_mod"], 10)
: 0;
var mod = attr_mod + tool_mod;
update["repeating_tool_" + tool_id + "_toolattr"] = attr.toUpperCase();
if (v["repeating_tool_" + tool_id + "_tool_mod"] && v["repeating_tool_" + tool_id + "_tool_mod"].indexOf("@{") > -1) {
update["repeating_tool_" + tool_id + "_toolbonus"] =
update["repeating_tool_" + tool_id + "_toolbonus"] + "+" + v["repeating_tool_" + tool_id + "_tool_mod"];
}
if (!v["repeating_tool_" + tool_id + "_tool_mod"]) {
update["repeating_tool_" + tool_id + "_tool_mod"] = 0;
}
}
if (v["pb_type"] && v["pb_type"] === "die") {
if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(@{pb})") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + "+" + v.pb;
} else if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(@{pb}*2)") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + "+2" + v.pb;
} else if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(floor(@{pb}/2))") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + "+" + v.jack;
}
} else if (
v["repeating_tool_" + tool_id + "_toolattr_base"] &&
v["repeating_tool_" + tool_id + "_toolattr_base"].substring(0, 2) === "?{"
) {
if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(@{pb})") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + "+" + parseInt(v.pb, 10);
} else if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(@{pb}*2)") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + "+" + parseInt(v.pb, 10) * 2;
} else if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(floor(@{pb}/2))") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + "+" + parseInt(v.jack, 10);
}
} else {
if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(@{pb})") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + parseInt(v.pb, 10);
} else if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(@{pb}*2)") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + parseInt(v.pb, 10) * 2;
} else if (v["repeating_tool_" + tool_id + "_toolbonus_base"] === "(floor(@{pb}/2))") {
update["repeating_tool_" + tool_id + "_toolbonus"] = mod + parseInt(v.jack, 10);
}
}
if (query) {
update["repeating_tool_" + tool_id + "_toolbonus_display"] = "?";
} else {
update["repeating_tool_" + tool_id + "_toolbonus_display"] = update["repeating_tool_" + tool_id + "_toolbonus"];
}
});
setAttrs(update, { silent: true }, () => {
updateToolRolls(tool_array);
});
});
};
var get_repeating_data = function (callback) {
var getallrepeating = function (getobj, thiscallback, attrlist) {
attrlist = attrlist || [];
var thisget = getobj.pop();
getSectionIDs(thisget.name, function (ids) {
_.each(ids, function (sectionId) {
_.each(thisget.list, function (attr) {
attrlist.push("repeating_" + thisget.name + "_" + sectionId + "_" + attr);
});
});
if (getobj.length > 0) {
getallrepeating(getobj, thiscallback, attrlist);
} else {
getAttrs(attrlist, function (vals) {
thiscallback(vals);
});
}
});
};
var getList = [
{ name: "proficiencies", list: ["name"] },
{ name: "tool", list: ["toolname", "toolattr_base"] },
{ name: "traits", list: ["name", "source", "source_type"] },
{ name: "resource", list: ["resource_left_name", "resource_right_name"] },
{ name: "spell-cantrip", list: ["spellname", "spellattackid", "spellsource", "innate", "spellattackid"] },
{ name: "savemod", list: ["global_save_name"] },
{ name: "tohitmod", list: ["global_attack_name"] },
{ name: "damagemod", list: ["global_damage_name"] },
{ name: "acmod", list: ["global_ac_name"] },
{ name: "skillmod", list: ["global_skill_name"] },
{ name: "attack", list: ["atkname", "spellid"] },
{ name: "hpmod", list: ["levels", "source", "mod"] },
{ name: "vehicleweapon-bow", list: ["name"] },
{ name: "vehicleweapon-port", list: ["name"] },
{ name: "vehicleweapon-starboard", list: ["name"] },
{ name: "vehicleweapon-stern", list: ["name"] },
{ name: "vehicleofficer", list: ["name"] },
{ name: "vehicleupgrade", list: ["name"] },
{ name: "vehicleammo", list: ["name"] },
{ name: "vehicleactions", list: ["name"] },
];
for (var x = 1; x <= 9; x++) {
getList.push({ name: "spell-" + x, list: ["spellname", "spellattackid", "spellsource", "innate", "spellattackid"] });
}
var repeating = { prof_names: [], traits: [] };
_.each(getList, function (section) {
if (!["proficiencies", "traits"].includes(section.name)) {
repeating[section.name] = {};
}
});
getallrepeating(getList, function (vals) {
var traitstemp = {};
_.each(vals, function (value, name) {
if (name.split("_")[1] == "proficiencies") {
repeating.prof_names.push(value.toLowerCase());
} else if (name.split("_")[1] == "tool") {
repeating.tool[name.split("_")[2]] = repeating.tool[name.split("_")[2]] || {};
let attr = _.last(name.split("_"));
repeating.tool[name.split("_")[2]][attr] = value.toLowerCase();
if (attr == "toolname") repeating.prof_names.push(value.toLowerCase());
} else if (name.split("_")[1] == "traits") {
traitstemp[name.split("_")[2]] = traitstemp[name.split("_")[2]] ? traitstemp[name.split("_")[2]] : {};
traitstemp[name.split("_")[2]][_.last(name.split("_"))] = value;
} else if (name.split("_")[1] == "resource") {
repeating.resource[name.split("_")[2]] = repeating.resource[name.split("_")[2]] || {};
repeating.resource[name.split("_")[2]][name.split("_")[4]] = value;
} else if (name.split("_")[1] == "hpmod") {
repeating.hpmod[name.split("_")[2]] = repeating.hpmod[name.split("_")[2]] || {};
repeating.hpmod[name.split("_")[2]][name.split("_")[3]] = value;
} else if (name.split("_")[1].split("-")[0] == "spell") {
repeating[name.split("_")[1]][name.split("_")[2]] = repeating[name.split("_")[1]][name.split("_")[2]] || {};
repeating[name.split("_")[1]][name.split("_")[2]][name.split("_")[3]] = value;
} else if (name.split("_")[1] == "attack") {
repeating[name.split("_")[1]][name.split("_")[2]] = repeating[name.split("_")[1]][name.split("_")[2]] || {};
repeating[name.split("_")[1]][name.split("_")[2]][name.split("_")[3]] = value;
} else {
repeating[name.split("_")[1]][name.split("_")[2]] = value;
}
});
_.each(traitstemp, function (v, k) {
var trait = _.clone(v);
trait.id = k;
repeating.traits.push(trait);
});
callback(repeating);
});
};
var check_itemmodifiers = function (modifiers, previousValue) {
var mods = modifiers.toLowerCase().split(",");
if (previousValue) {
prevmods = previousValue.toLowerCase().split(",");
mods = _.union(mods, prevmods);
}
_.each(mods, function (mod) {
if (mod.indexOf("ac:") > -1 || mod.indexOf("ac +") > -1 || mod.indexOf("ac -") > -1) {
update_ac();
}
if (mod.indexOf("stealth:") > -1) {
update_skills(["stealth"]);
}
if (mod.indexOf("spell") > -1) {
update_spell_info();
}
if (mod.indexOf("saving throws") > -1) {
update_all_saves();
}
if (mod.indexOf("strength save") > -1) {
update_save("strength");
} else if (mod.indexOf("strength") > -1) {
update_attr("strength");
}
if (mod.indexOf("dexterity save") > -1) {
update_save("dexterity");
} else if (mod.indexOf("dexterity") > -1) {
update_attr("dexterity");
}
if (mod.indexOf("constitution save") > -1) {
update_save("constitution");
} else if (mod.indexOf("constitution") > -1) {
update_attr("constitution");
}
if (mod.indexOf("intelligence save") > -1) {
update_save("intelligence");
} else if (mod.indexOf("intelligence") > -1) {
update_attr("intelligence");
}
if (mod.indexOf("wisdom save") > -1) {
update_save("wisdom");
} else if (mod.indexOf("wisdom") > -1) {
update_attr("wisdom");
}
if (mod.indexOf("charisma save") > -1) {
update_save("charisma");
} else if (mod.indexOf("charisma") > -1) {
update_attr("charisma");
}
if (mod.indexOf("honor save") > -1) {
update_save("honor");
} else if (mod.indexOf("honor") > -1) {
update_attr("honor");
}
if (mod.indexOf("sanity save") > -1) {
update_save("sanity");
} else if (mod.indexOf("sanity") > -1) {
update_attr("sanity");
}
if (mod.indexOf("ability checks") > -1) {
update_all_ability_checks();
}
if (mod.indexOf("acrobatics") > -1) {
update_skills(["acrobatics"]);
}
if (mod.indexOf("animal handling") > -1) {
update_skills(["animal_handling"]);
}
if (mod.indexOf("arcana") > -1) {
update_skills(["arcana"]);
}
if (mod.indexOf("athletics") > -1) {
update_skills(["athletics"]);
}
if (mod.indexOf("deception") > -1) {
update_skills(["deception"]);
}
if (mod.indexOf("history") > -1) {
update_skills(["history"]);
}
if (mod.indexOf("insight") > -1) {
update_skills(["insight"]);
}
if (mod.indexOf("intimidation") > -1) {
update_skills(["intimidation"]);
}
if (mod.indexOf("investigation") > -1) {
update_skills(["investigation"]);
}
if (mod.indexOf("medicine") > -1) {
update_skills(["medicine"]);
}
if (mod.indexOf("nature") > -1) {
update_skills(["nature"]);
}
if (mod.indexOf("navigation") > -1) {
update_skills(["navigation"]);
}
if (mod.indexOf("perception") > -1) {
update_skills(["perception"]);
}
if (mod.indexOf("performance") > -1) {
update_skills(["performance"]);
}
if (mod.indexOf("persuasion") > -1) {
update_skills(["persuasion"]);
}
if (mod.indexOf("religion") > -1) {
update_skills(["religion"]);
}
if (mod.indexOf("sleight of hand") > -1) {
update_skills(["sleight_of_hand"]);
}
if (mod.indexOf("stealth") > -1) {
update_skills(["stealth"]);
}
if (mod.indexOf("survival") > -1) {
update_skills(["survival"]);
}
});
};
var create_attack_from_item = function (itemid, options) {
var update = {};
var newrowid = generateRowID();
update["repeating_inventory_" + itemid + "_itemattackid"] = newrowid;
if (options && options.versatile) {
var newrowid2 = generateRowID();
update["repeating_inventory_" + itemid + "_itemattackid"] += "," + newrowid2;
setAttrs(update, {}, function () {
update_attack_from_item(itemid, newrowid, { newattack: true, versatile: "primary" });
update_attack_from_item(itemid, newrowid2, { newattack: true, versatile: "secondary" });
});
} else {
setAttrs(update, {}, update_attack_from_item(itemid, newrowid, { newattack: true }));
}
};
var update_attack_from_item = function (itemid, attackid, options) {
getAttrs(
[
"repeating_inventory_" + itemid + "_itemname",
"repeating_inventory_" + itemid + "_itemproperties",
"repeating_inventory_" + itemid + "_itemmodifiers",
"strength",
"dexterity",
"class",
"multiclass1",
"multiclass2",
"multiclass3",
],
function (v) {
var update = {};
var itemtype;
var damage;
var damagetype;
var damage2;
var damagetype2;
var attackmod;
var damagemod;
var range;
var atkcritrange;
var alt = {};
let atk_desc = "";
if (options && options.newattack) {
update["repeating_attack_" + attackid + "_options-flag"] = "0";
update["repeating_attack_" + attackid + "_itemid"] = itemid;
}
const isMonk = () => {
let monk = false;
["class", "multiclass1", "multiclass2", "multiclass3"].forEach((entry) => {
if (v[entry] && v[entry] === "Monk") monk = true;
});
return monk;
};
const isMonkWeapon = (weapon) => {
const monkWeapons = [
"club",
"dagger",
"dart",
"dorata",
"greatclub",
"handaxe",
"javelin",
"light crossbow",
"light hammer",
"mace",
"quarterstaff",
"shortbow",
"sickle",
"sling",
"spear",
"shortsword",
];
return monkWeapons.indexOf(weapon.toLowerCase().trim()) > -1;
};
if (v["repeating_inventory_" + itemid + "_itemmodifiers"] && v["repeating_inventory_" + itemid + "_itemmodifiers"] != "") {
var mods = v["repeating_inventory_" + itemid + "_itemmodifiers"].split(",");
_.each(mods, function (mod) {
if (mod.indexOf("Item Type:") > -1) {
itemtype = mod.split(":")[1].trim();
} else if (mod.indexOf("Alternate Secondary Damage Type:") > -1) {
alt.damagetype2 = mod.split(":")[1].trim();
} else if (mod.indexOf("Alternate Secondary Damage:") > -1) {
alt.damage2 = mod.split(":")[1].trim();
} else if (mod.indexOf("Alternate Damage Type:") > -1) {
alt.damagetype = mod.split(":")[1].trim();
} else if (mod.indexOf("Alternate Damage:") > -1) {
alt.damage = mod.split(":")[1].trim();
} else if (mod.indexOf("Secondary Damage Type:") > -1) {
damagetype2 = mod.split(":")[1].trim();
} else if (mod.indexOf("Secondary Damage:") > -1) {
damage2 = mod.split(":")[1].trim();
} else if (mod.indexOf("Damage Type:") > -1) {
damagetype = mod.split(":")[1].trim();
} else if (mod.indexOf("Damage:") > -1) {
damage = mod.split(":")[1].trim();
} else if (mod.indexOf("Critical Range:") > -1) {
atkcritrange = mod.split(":")[1].trim();
} else if (mod.indexOf("Range:") > -1) {
range = mod.split(":")[1].trim();
} else if (mod.indexOf(" Attacks ") > -1) {
attackmod = mod.split(" Attacks ")[1].trim();
} else if (mod.indexOf(" Damage ") > -1) {
damagemod = mod.split(" Damage ")[1].trim();
} else if (mod.indexOf("Attack Description:") > -1) {
atk_desc = mod.split(":")[1].trim().replace(/&#44/g, ",");
}
});
}
if (v["repeating_inventory_" + itemid + "_itemname"] && v["repeating_inventory_" + itemid + "_itemname"] != "") {
update["repeating_attack_" + attackid + "_atkname"] = v["repeating_inventory_" + itemid + "_itemname"];
if (options && options.versatile === "primary") {
update["repeating_attack_" + attackid + "_atkname"] += " (One-Handed)";
} else if (options && options.versatile === "secondary") {
update["repeating_attack_" + attackid + "_atkname"] += " (Two-Handed)";
}
}
if (options && options.versatile === "secondary") {
if (alt.damage) {
update["repeating_attack_" + attackid + "_dmgbase"] = alt.damage;
}
if (alt.damagetype) {
update["repeating_attack_" + attackid + "_dmgtype"] = alt.damagetype;
}
if (alt.damage2) {
update["repeating_attack_" + attackid + "_dmg2base"] = alt.damage2;
update["repeating_attack_" + attackid + "_dmg2attr"] = 0;
update["repeating_attack_" + attackid + "_dmg2flag"] = "{{damage=1}} {{dmg2flag=1}}";
}
if (alt.damagetype2) {
update["repeating_attack_" + attackid + "_dmg2type"] = alt.damagetype2;
}
update["repeating_attack_" + attackid + "_versatile_alt"] = "1";
} else {
if (damage) {
update["repeating_attack_" + attackid + "_dmgbase"] = damage;
}
if (damagetype) {
update["repeating_attack_" + attackid + "_dmgtype"] = damagetype;
}
if (damage2) {
update["repeating_attack_" + attackid + "_dmg2base"] = damage2;
update["repeating_attack_" + attackid + "_dmg2attr"] = 0;
update["repeating_attack_" + attackid + "_dmg2flag"] = "{{damage=1}} {{dmg2flag=1}}";
}
if (damagetype2) {
update["repeating_attack_" + attackid + "_dmg2type"] = damagetype2;
}
}
if (atkcritrange) {
update["repeating_attack_" + attackid + "_atkcritrange"] = atkcritrange;
}
if (range) {
update["repeating_attack_" + attackid + "_atkrange"] = range;
}
if (atk_desc) {
update["repeating_attack_" + attackid + "_atk_desc"] = atk_desc;
}
const monkWeapon = isMonk() && isMonkWeapon(v["repeating_inventory_" + itemid + "_itemname"]);
var finesse =
v["repeating_inventory_" + itemid + "_itemproperties"] && /finesse/i.test(v["repeating_inventory_" + itemid + "_itemproperties"]);
if ((itemtype && itemtype.indexOf("Ranged") > -1) || ((finesse || monkWeapon) && +v.dexterity > +v.strength)) {
update["repeating_attack_" + attackid + "_atkattr_base"] = "@{dexterity_mod}";
update["repeating_attack_" + attackid + "_dmgattr"] = "@{dexterity_mod}";
} else {
update["repeating_attack_" + attackid + "_atkattr_base"] = "@{strength_mod}";
update["repeating_attack_" + attackid + "_dmgattr"] = "@{strength_mod}";
}
if (attackmod && damagemod && attackmod == damagemod) {
update["repeating_attack_" + attackid + "_atkmagic"] = parseInt(attackmod, 10);
update["repeating_attack_" + attackid + "_atkmod"] = "";
update["repeating_attack_" + attackid + "_dmgmod"] = "";
} else {
if (attackmod) {
update["repeating_attack_" + attackid + "_atkmod"] = parseInt(attackmod, 10);
}
if (damagemod) {
update["repeating_attack_" + attackid + "_dmgmod"] = parseInt(damagemod, 10);
}
update["repeating_attack_" + attackid + "_atkmagic"] = "";
}
var callback = function () {
update_attacks(attackid, "item");
};
setAttrs(update, { silent: true }, callback);
}
);
};
const create_resource_from_item = (itemid) => {
const newrowid = generateRowID();
let update = {};
getAttrs(["other_resource_name"], (v) => {
//Use other_resource if it is empty
if (!v.other_resource_name || v.other_resource_name == "") {
update[`repeating_inventory_${itemid}_itemresourceid`] = "other_resource";
setAttrs(update, {}, update_resource_from_item(itemid, "other_resource", true));
//If other_resource is populated look through the repeating sections for an empty spot
} else {
getSectionIDs(`repeating_resource`, (idarray) => {
if (idarray.length == 0) {
update[`repeating_inventory_${itemid}_itemresourceid`] = `${newrowid}_resource_left`;
setAttrs(update, {}, update_resource_from_item(itemid, `${newrowid}_resource_left`, true));
} else {
let array = [];
_.each(idarray, (currentID, i) => {
["left", "right"].forEach((side) => {
array.push(`repeating_resource_${currentID}_resource_${side}`);
array.push(`repeating_resource_${currentID}_resource_${side}_name`);
array.push(`repeating_resource_${currentID}_resource_${side}_max`);
});
});
getAttrs(array, (y) => {
let existing = false;
_.each(idarray, (currentID, i) => {
["left", "right"].forEach((side) => {
const Name = y[`repeating_resource_${currentID}_resource_${side}_name`] || false;
const Value = y[`repeating_resource_${currentID}_resource_${side}`] || false;
const Max = y[`repeating_resource_${currentID}_resource_${side}_max`] || false;
//If Name, Value, & Max are empty and existing === false then populate a resource there
if (!Name && !Value && !Max && existing === false) {
update[`repeating_inventory_${itemid}_itemresourceid`] = `${currentID}_resource_${side}`;
setAttrs(update, {}, update_resource_from_item(itemid, `${currentID}_resource_${side}`, true));
existing = true;
}
});
});
//If nothing is empty then generatae a new row
if (!existing) {
update[`repeating_inventory_${itemid}_itemresourceid`] = `${newrowid}_resource_left`;
setAttrs(update, {}, update_resource_from_item(itemid, `${newrowid}_resource_left`, true));
}
});
}
});
}
});
};
var update_resource_from_item = function (itemid, resourceid, newresource) {
getAttrs(["repeating_inventory_" + itemid + "_itemname", "repeating_inventory_" + itemid + "_itemcount"], function (v) {
var update = {};
var id;
if (resourceid && resourceid == "other_resource") {
id = resourceid;
} else {
id = "repeating_resource_" + resourceid;
}
if (newresource) {
update[id + "_itemid"] = itemid;
}
if (!v["repeating_inventory_" + itemid + "_itemname"]) {
update["repeating_inventory_" + itemid + "_useasresource"] = 0;
update["repeating_inventory_" + itemid + "_itemresourceid"] = "";
remove_resource(resourceid);
}
if (v["repeating_inventory_" + itemid + "_itemname"] && v["repeating_inventory_" + itemid + "_itemname"] != "") {
update[id + "_name"] = v["repeating_inventory_" + itemid + "_itemname"];
}
if (v["repeating_inventory_" + itemid + "_itemcount"] && v["repeating_inventory_" + itemid + "_itemcount"] != "") {
update[id] = v["repeating_inventory_" + itemid + "_itemcount"];
}
setAttrs(update, { silent: true });
});
};
const create_attack_from_spell = function (lvl, spellid, character_id, existing_id) {
let update = {};
const newrowid = existing_id ? existing_id : generateRowID();
update[`repeating_spell-${lvl}_${spellid}_spellattackid`] = newrowid;
update[`repeating_spell-${lvl}_${spellid}_rollcontent`] = `%{${character_id}|repeating_attack_${newrowid}_attack}`;
if (typeof environment === "undefined") {
setAttrs(update, {}, update_attack_from_spell(lvl, spellid, newrowid, true, character_id));
}
};
const update_attack_from_spell = (lvl, spellid, attackid, newattack, character_id) => {
const repeating_spell = `repeating_spell-${lvl}_${spellid}`;
const repeating_attack = `repeating_attack_${attackid}`;
getAttrs(
[
`${repeating_spell}_spellname`,
`${repeating_spell}_spellrange`,
`${repeating_spell}_spelltarget`,
`${repeating_spell}_spellattack`,
`${repeating_spell}_spelldamage`,
`${repeating_spell}_spelldamage2`,
`${repeating_spell}_spelldamagetype`,
`${repeating_spell}_spelldamagetype2`,
`${repeating_spell}_spellhealing`,
`${repeating_spell}_spelldmgmod`,
`${repeating_spell}_spellsave`,
`${repeating_spell}_spellsavesuccess`,
`${repeating_spell}_spellhldie`,
`${repeating_spell}_spellhldietype`,
`${repeating_spell}_spellhlbonus`,
`${repeating_spell}_spelllevel`,
`${repeating_spell}_includedesc`,
`${repeating_spell}_spelldescription`,
`${repeating_spell}_spellathigherlevels`,
`${repeating_spell}_spell_damage_progression`,
`${repeating_spell}_innate`,
`${repeating_spell}_spell_ability`,
"spellcasting_ability",
],
(v) => {
let update = {};
let description = "";
const spellAbility = v[`${repeating_spell}_spell_ability`] != "spell" ? v[`${repeating_spell}_spell_ability`].slice(0, -1) : "spell";
update[`${repeating_attack}_atkattr_base`] = spellAbility;
if (newattack) {
update[`${repeating_attack}_options-flag`] = "0";
update[`${repeating_attack}_spellid`] = spellid;
update[`${repeating_attack}_spelllevel`] = lvl;
}
if (v[`${repeating_spell}_spell_ability`] == "spell") {
update[`${repeating_attack}_savedc`] = "(@{spell_save_dc})";
} else if (v[`${repeating_spell}_spell_ability`]) {
update[`${repeating_attack}_savedc`] = `(${spellAbility}+8+@{spell_dc_mod}+@{pb})`;
}
if (v[`${repeating_spell}_spellname`] && v[`${repeating_spell}_spellname`] != "") {
update[`${repeating_attack}_atkname`] = v[`${repeating_spell}_spellname`];
}
update[`${repeating_attack}_atkflag`] =
!v[`${repeating_spell}_spellattack`] || v[`${repeating_spell}_spellattack`] === "None" ? "0" : "{{attack=1}}";
if (v[`${repeating_spell}_spellattack`] || !v[`${repeating_spell}_spellattack`] === "None") {
description = description + v[`${repeating_spell}_spellattack`] + " Spell Attack. ";
}
if (v[`${repeating_spell}_spelldamage`] && v[`${repeating_spell}_spelldamage`] != "") {
update[`${repeating_attack}_dmgbase`] =
v[`${repeating_spell}_spell_damage_progression`] && v[`${repeating_spell}_spell_damage_progression`] === "Cantrip Dice"
? "[[round((@{level} + 1) / 6 + 0.5)]]" + v[`${repeating_spell}_spelldamage`].substring(1)
: v[`${repeating_spell}_spelldamage`];
}
update[`${repeating_attack}_dmgflag`] =
v[`${repeating_spell}_spelldamage`] && v[`${repeating_spell}_spelldamage`] != "" ? "{{damage=1}} {{dmg1flag=1}}" : "0";
update[`${repeating_attack}_dmgattr`] =
v[`${repeating_spell}_spelldmgmod`] && v[`${repeating_spell}_spelldmgmod`] === "Yes" ? spellAbility : "0";
update[`${repeating_attack}_dmgtype`] = v[`${repeating_spell}_spelldamagetype`] ? v[`${repeating_spell}_spelldamagetype`] : "";
update[`${repeating_attack}_dmg2base`] = v[`${repeating_spell}_spelldamage2`] ? v[`${repeating_spell}_spelldamage2`] : "";
update[`${repeating_attack}_dmg2attr`] = "0";
update[`${repeating_attack}_dmg2flag`] = v[`${repeating_spell}_spelldamage2`] ? "{{damage=1}} {{dmg2flag=1}}" : 0;
update[`${repeating_attack}_dmg2type`] = v[`${repeating_spell}_spelldamagetype2`] ? v[`${repeating_spell}_spelldamagetype2`] : "";
update[`${repeating_attack}_atkrange`] = v[`${repeating_spell}_spellrange`] ? v[`${repeating_spell}_spellrange`] : "";
update[`${repeating_attack}_saveflag`] = v[`${repeating_spell}_spellsave`]
? "{{save=1}} {{saveattr=@{saveattr}}} {{savedesc=@{saveeffect}}} {{savedc=[[[[@{savedc}]][SAVE]]]}}"
: "0";
update[`${repeating_attack}_saveeffect`] = v[`${repeating_spell}_spellsavesuccess`] ? v[`${repeating_spell}_spellsavesuccess`] : "";
if (v[`${repeating_spell}_spellsave`]) {
update[`${repeating_attack}_saveattr`] = v[`${repeating_spell}_spellsave`];
}
if (
v[`${repeating_spell}_spellhldie`] &&
v[`${repeating_spell}_spellhldie`] != "" &&
v[`${repeating_spell}_spellhldietype`] &&
v[`${repeating_spell}_spellhldietype`] != ""
) {
let bonus = "";
const spelllevel = v[`${repeating_spell}_spelllevel`];
let query = "?{Cast at what level?";
for (i = 0; i < 10 - spelllevel; i++) {
query = query + "|Level " + (parseInt(i, 10) + parseInt(spelllevel, 10)) + "," + i;
}
query = query + "}";
if (v[`${repeating_spell}_spellhlbonus`] && v[`${repeating_spell}_spellhlbonus`] != "") {
bonus = "+(" + v[`${repeating_spell}_spellhlbonus`] + "*" + query + ")";
}
update[`${repeating_attack}_hldmg`] = `{{hldmg=[[(${v[`${repeating_spell}_spellhldie`]}*${query})${
v[`${repeating_spell}_spellhldietype`]
}${bonus}]]}}`;
} else {
update[`${repeating_attack}_hldmg`] = "";
}
if (v[`${repeating_spell}_spellhealing`] && v[`${repeating_spell}_spellhealing`] != "") {
if (!v[`${repeating_spell}_spelldamage`] || v[`${repeating_spell}_spelldamage`] === "") {
update[`${repeating_attack}_dmgbase`] = v[`${repeating_spell}_spellhealing`];
update[`${repeating_attack}_dmgflag`] = "{{damage=1}} {{dmg1flag=1}}";
update[`${repeating_attack}_dmgtype`] = "Healing";
} else if (!v[`${repeating_spell}_spelldamage2`] || v[`${repeating_spell}_spelldamage2`] === "") {
update[`${repeating_attack}_dmg2base`] = v[`${repeating_spell}_spellhealing`];
update[`${repeating_attack}_dmg2flag`] = "{{damage=1}} {{dmg2flag=1}}";
update[`${repeating_attack}_dmg2type`] = "Healing";
}
}
update[`${repeating_attack}_spell_innate`] = v[`${repeating_spell}_innate`] ? v[`${repeating_spell}_innate`] : "";
if (v[`${repeating_spell}_spelltarget`]) {
description = description + v[`${repeating_spell}_spelltarget`] + ". ";
}
if (v[`${repeating_spell}_includedesc`] && v[`${repeating_spell}_includedesc`] === "on") {
description = v[`${repeating_spell}_spelldescription`];
if (v[`${repeating_spell}_spellathigherlevels`] && v[`${repeating_spell}_spellathigherlevels`] != "") {
description = description + "\n\nAt Higher Levels: " + v[`${repeating_spell}_spellathigherlevels`];
}
} else if (v[`${repeating_spell}_includedesc`] && v[`${repeating_spell}_includedesc`] === "off") {
description = "";
}
update[`${repeating_attack}_atk_desc`] = description;
update[`${repeating_attack}_spelldesc_link`] = `%{${character_id}|${repeating_spell}_output}`;
var callback = () => {
update_attacks(attackid, "spell");
};
setAttrs(update, { silent: true }, callback);
}
);
};
const update_attacks = (update_id, source) => {
if (update_id.substring(0, 1) === "-" && update_id.length === 20) {
do_update_attack([update_id], source);
} else if (["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma", "spells", "all"].indexOf(update_id) > -1) {
getSectionIDs("repeating_attack", (idarray) => {
if (update_id === "all") {
do_update_attack(idarray);
} else if (update_id === "spells") {
let attack_attribs = [];
_.each(idarray, (id) => {
attack_attribs.push(`repeating_attack_${id}_spellid`, `repeating_attack_${id}_spelllevel`);
});
getAttrs([...attack_attribs, "character_id"], (v) => {
const filteredIds = _.filter(idarray, (id) => {
return v[`repeating_attack_${id}_spellid`] && v[`repeating_attack_${id}_spellid`] != "";
});
let spell_attacks = {};
_.each(filteredIds, (attack_id) => {
spell_attacks[attack_id] = {
spell_id: v[`repeating_attack_${attack_id}_spellid`],
spell_lvl: v[`repeating_attack_${attack_id}_spelllevel`],
};
});
_.each(spell_attacks, (data, attack_id) => {
update_attack_from_spell(data.spell_lvl, data.spell_id, attack_id, false, v["character_id"]);
});
});
} else {
let attack_attribs = ["spellcasting_ability"];
_.each(idarray, (id) => {
attack_attribs.push(`repeating_attack_${id}_atkattr_base`);
attack_attribs.push(`repeating_attack_${id}_dmgattr`);
attack_attribs.push(`repeating_attack_${id}_dmg2attr`);
attack_attribs.push(`repeating_attack_${id}_savedc`);
});
getAttrs(attack_attribs, (v) => {
let attr_attack_ids = [];
_.each(idarray, (id) => {
if (
(v[`repeating_attack_${id}_atkattr_base`] && v[`repeating_attack_${id}_atkattr_base`].indexOf(update_id) > -1) ||
(v[`repeating_attack_${id}_dmgattr`] && v[`repeating_attack_${id}_dmgattr`].indexOf(update_id) > -1) ||
(v[`repeating_attack_${id}_dmg2attr`] && v[`repeating_attack_${id}_dmg2attr`].indexOf(update_id) > -1) ||
(v[`repeating_attack_${id}_savedc`] && v[`repeating_attack_${id}_savedc`].indexOf(update_id) > -1) ||
(v[`repeating_attack_${id}_savedc`] &&
v[`repeating_attack_${id}_savedc`] === "(@{spell_save_dc})" &&
v["spellcasting_ability"] &&
v["spellcasting_ability"].indexOf(update_id) > -1)
) {
attr_attack_ids.push(id);
}
});
if (attr_attack_ids.length > 0) {
do_update_attack(attr_attack_ids);
}
});
}
});
}
};
const do_update_attack = (attack_array, source) => {
if (typeof environment !== "undefined") return;
let attack_attribs = [
"level",
"d20",
"pb",
"pb_type",
"pbd_safe",
"dtype",
"globalmagicmod",
"strength_mod",
"dexterity_mod",
"constitution_mod",
"intelligence_mod",
"wisdom_mod",
"charisma_mod",
"spellcasting_ability",
"spell_save_dc",
"spell_attack_mod",
"spell_dc_mod",
"global_damage_mod_roll",
"global_damage_mod_crit",
"default_critical_range",
];
_.each(attack_array, (attackid) => {
[
"atkflag",
"atkname",
"atkattr_base",
"atkmod",
"atkprofflag",
"atkmagic",
"atkcritrange",
"dmgflag",
"dmgbase",
"dmgattr",
"dmgmod",
"dmgtype",
"dmg2flag",
"dmg2base",
"dmg2attr",
"dmg2mod",
"dmg2type",
"dmgcustcrit",
"dmg2custcrit",
"saveflag",
"savedc",
"saveeffect",
"saveflat",
"hldmg",
"spellid",
"spelllevel",
"atkrange",
"itemid",
"ammo",
"spelldesc_link",
].forEach((attr) => {
attack_attribs.push(`repeating_attack_${attackid}_${attr}`);
});
});
getAttrs(attack_attribs, (v) => {
_.each(attack_array, (attackid) => {
const repeating_attack = `repeating_attack_${attackid}`;
let callbacks = [];
let update = {};
let hbonus = "";
let hdmg1 = "";
let hdmg2 = "";
let dmg = "";
let dmg2 = "";
let rollbase = "";
let spellattack = false;
let magicattackmod = 0;
let magicsavemod = 0;
let atkattr_abrev = "";
let dmgattr_abrev = "";
let dmg2attr_abrev = "";
//PROFICIENCY BONUS select in Settings.
const pbd_safe = v["pbd_safe"] || "";
//HIGHER LVL CAST DMG found in Spells for spells like Fireball
const hldmgcrit =
v[`${repeating_attack}_hldmg`] && v[`${repeating_attack}_hldmg`] != ""
? v[`${repeating_attack}_hldmg`].slice(0, 7) + "crit" + v[`${repeating_attack}_hldmg`].slice(7)
: "";
if (v[`${repeating_attack}_spellid`] && v[`${repeating_attack}_spellid`] != "") {
spellattack = true;
//GLOBAL MAGIC ATTACK MODIFIER in Settings
magicattackmod = parseInt(v["spell_attack_mod"], 10) || 0;
//SPELL SAVE DC MOD in Settings
magicsavemod = parseInt(v["spell_dc_mod"], 10) || 0;
}
//ATTACK select inside settings for the repeating attack
if (!v[`${repeating_attack}_atkattr_base`] || v[`${repeating_attack}_atkattr_base`] === "0") {
atkattr_base = 0;
} else if (v[`${repeating_attack}_atkattr_base`] && v[`${repeating_attack}_atkattr_base`] === "spell") {
atkattr_base = parseInt(v[v["spellcasting_ability"].substring(2, v["spellcasting_ability"].length - 2)], 10);
atkattr_abrev = v["spellcasting_ability"].substring(2, 5).toUpperCase();
} else {
atkattr_base = parseInt(
v[v[`${repeating_attack}_atkattr_base`].substring(2, v[`${repeating_attack}_atkattr_base`].length - 1)],
10
);
atkattr_abrev = v[`${repeating_attack}_atkattr_base`].substring(2, 5).toUpperCase();
}
//DAMAGE 1 ability select inside settings for the repeating attack
if (!v[`${repeating_attack}_dmgattr`] || v[`${repeating_attack}_dmgattr`] === "0") {
dmgattr = 0;
} else if (v[`${repeating_attack}_dmgattr`] && v[`${repeating_attack}_dmgattr`] === "spell") {
dmgattr = parseInt(v[v["spellcasting_ability"].substring(2, v["spellcasting_ability"].length - 2)], 10);
dmgattr_abrev = v["spellcasting_ability"].substring(2, 5).toUpperCase();
} else {
dmgattr = parseInt(v[v[`${repeating_attack}_dmgattr`].substring(2, v[`${repeating_attack}_dmgattr`].length - 1)], 10);
dmgattr_abrev = v[`${repeating_attack}_dmgattr`].substring(2, 5).toUpperCase();
}
//DAMAGE 2 ability select inside settings for the repeating attack
if (!v[`${repeating_attack}_dmg2attr`] || v[`${repeating_attack}_dmg2attr`] === "0") {
dmg2attr = 0;
} else if (v[`${repeating_attack}_dmg2attr`] && v[`${repeating_attack}_dmg2attr`] === "spell") {
dmg2attr = parseInt(v[v["spellcasting_ability"].substring(2, v["spellcasting_ability"].length - 2)], 10);
dmg2attr_abrev = v["spellcasting_ability"].substring(2, 5).toUpperCase();
} else {
dmg2attr = parseInt(v[v[`${repeating_attack}_dmg2attr`].substring(2, v[`${repeating_attack}_dmg2attr`].length - 1)], 10);
dmg2attr_abrev = v[`${repeating_attack}_dmg2attr`].substring(2, 5).toUpperCase();
}
//DAMAGE first input inside settings for the repeating attack
const dmgbase = v[`${repeating_attack}_dmgbase`] || 0;
const dmg2base = v[`${repeating_attack}_dmg2base`] || 0;
//DAMAGE 1 second input inside settings for the repeating attack
const dmgmod = parseInt(v[`${repeating_attack}_dmgmod`], 10) || 0;
//DAMAGE 2 second input inside settings for the repeating attack
const dmg2mod = parseInt(v[`${repeating_attack}_dmg2mod`], 10) || 0;
//DAMAGE 1 TYPE input inside settings for the repeating attack
const dmgtype = v[`${repeating_attack}_dmgtype`] || "";
//DAMAGE 2 TYPE input inside settings for the repeating attack
const dmg2type = v[`${repeating_attack}_dmg2type`] || "";
//PROFICIENT flag inside settings for the repeating attack && PROFICIENCY BONUS from Settings
const atkprofflag = v[`${repeating_attack}_atkprofflag`] || 0;
const pb = atkprofflag != 0 && v.pb ? v.pb : 0;
//ATTACK input inside settings for the repeating attack
const atkmod = parseInt(v[`${repeating_attack}_atkmod`], 10) || 0;
//MAGIC BONUS input inside settings for the repeating attack
const atkmag = parseInt(v[`${repeating_attack}_atkmagic`], 10) || 0;
//used in _atkdmgtype display
const dmgmag =
isNaN(atkmag) === false &&
atkmag != 0 &&
((v[`${repeating_attack}_dmgflag`] && v[`${repeating_attack}_dmgflag`] != 0) ||
(v[`${repeating_attack}_dmg2flag`] && v[`${repeating_attack}_dmg2flag`] != 0))
? `+ ${atkmag} Magic Bonus`
: "";
//Spell description link
const spelldesc_link = v[`${repeating_attack}_spelldesc_link`]
? `{{spelldesc_link=[Show Spell Description](~repeating_attack_spelldesc_link)}} `
: "";
//ATTACK checkbox inside settings for the repeating attack
const atkflag = v[`${repeating_attack}_atkflag`] || 0;
if (atkflag != 0) {
bonus_mod = atkattr_base + atkmod + atkmag + magicattackmod;
plus_minus = bonus_mod > -1 ? "+" : "";
//pb_type is PROFICIENCY BONUS select in Settings
if (v["pb_type"] && v["pb_type"] === "die") {
bonus = `${bonus_mod}+${pb}`;
} else {
bonus_mod = bonus_mod + parseInt(pb, 10);
bonus = plus_minus + bonus_mod;
}
//SAVING THROW checkbox inside settings for the repeating attack
} else if (v[`${repeating_attack}_saveflag`] && v[`${repeating_attack}_saveflag`] != 0) {
const saveDC = v[`${repeating_attack}_savedc`] || "";
//SAVING THROW VS DC select "Spell" inside settings for the repeating attack
if (!saveDC || saveDC === "(@{spell_save_dc})") {
const tempdc = v["spell_save_dc"];
bonus = "DC" + tempdc;
//SAVING THROW VS DC select "FLAT" inside settings for the repeating attack
} else if (saveDC === "(@{saveflat})") {
const tempdc = parseInt(v[`${repeating_attack}_saveflat`]) || 0;
bonus = "DC" + tempdc;
//SAVING THROW VS DC select ability score inside settings for the repeating attack
} else {
const savedcattr = v[`${repeating_attack}_savedc`].split("_mod")[0].slice(3);
const safe_pb = v["pb_type"] && v["pb_type"] === "die" ? parseInt(pb.substring(1), 10) / 2 : parseInt(pb, 10);
const safe_attr = v[`${savedcattr}_mod`] ? parseInt(v[`${savedcattr}_mod`], 10) : 0;
const tempdc = 8 + safe_attr + safe_pb + magicsavemod;
bonus = "DC" + tempdc;
}
} else {
bonus = "-";
}
//DAMAGE checkbox inside settings for the repeating attack
const dmgflag1 = v[`${repeating_attack}_dmgflag`] || 0;
const dmgflag2 = v[`${repeating_attack}_dmg2flag`] || 0;
if (dmgflag1 != 0) {
if (spellattack === true && dmgbase.indexOf("[[round((@{level} + 1) / 6 + 0.5)]]") > -1) {
// SPECIAL CANTRIP DAMAGE
dmgdiestring = Math.round((parseInt(v["level"], 10) + 1) / 6 + 0.5).toString();
dmg = dmgdiestring + dmgbase.substring(dmgbase.lastIndexOf("d"));
//select & input to the right of damage
if (dmgattr + dmgmod != 0) {
dmg += `+${dmgattr + dmgmod}`;
}
dmg += ` ${dmgtype}`;
} else {
if (dmgbase === 0 && dmgattr + dmgmod === 0) {
dmg = 0;
}
if (dmgbase != 0) {
dmg = dmgbase;
}
if (dmgbase != 0 && dmgattr + dmgmod != 0) {
dmg = dmgattr + dmgmod > 0 ? dmg + "+" : dmg;
}
if (dmgattr + dmgmod != 0) {
dmg = dmg + (dmgattr + dmgmod);
}
dmg = dmg + " " + dmgtype;
}
}
if (dmgflag2 != 0) {
if (dmg2base === 0 && dmg2attr + dmg2mod === 0) {
dmg2 = 0;
}
if (dmg2base != 0) {
dmg2 = dmg2base;
}
if (dmg2base != 0 && dmg2attr + dmg2mod != 0) {
dmg2 = dmg2attr + dmg2mod > 0 ? dmg2 + "+" : dmg2;
}
if (dmg2attr + dmg2mod != 0) {
dmg2 = dmg2 + (dmg2attr + dmg2mod);
}
dmg2 = dmg2 + " " + dmg2type;
}
update[`${repeating_attack}_atkdmgtype`] = dmgflag1 != 0 && dmgflag2 != 0 ? `${dmg} + ${dmg2}${dmgmag} ` : `${dmg}${dmg2}${dmgmag} `;
//Build ROLL TEMPLATE with below variables
//ATTACK checkbox inside settings for the repeating attack
if (atkflag != 0) {
if (atkattr_base != 0) {
hbonus += ` + ${atkattr_base}[${atkattr_abrev}]`;
}
if (atkmod != 0) {
hbonus += ` + ${atkmod}[MOD]`;
}
if (pb != 0) {
hbonus += ` + ${pb}${pbd_safe}[PROF]`;
}
if (atkmag != 0) {
hbonus += ` + ${atkmag}[MAGIC]`;
}
if (magicattackmod != 0) {
hbonus += ` + ${magicattackmod}[SPELLATK]`;
}
}
//DAMAGE 1 checkbox inside settings for the repeating attack
if (dmgflag1 != 0) {
hdmg1 += dmgbase;
if (dmgattr != 0) {
hdmg1 += ` + ${dmgattr}[${dmgattr_abrev}]`;
}
if (dmgmod != 0) {
hdmg1 += ` + ${dmgmod}[MOD]`;
}
if (atkmag != 0) {
hdmg1 += ` + ${atkmag}[MAGIC]`;
}
} else {
hdmg1 += "0";
}
//DAMAGE 2 checkbox inside settings for the repeating attack
if (dmgflag2 != 0) {
hdmg2 += dmg2base;
if (dmg2attr != 0) {
hdmg2 += ` + ${dmg2attr}[${dmg2attr_abrev}]`;
}
if (dmg2mod != 0) {
hdmg2 += ` + ${dmg2mod}[MOD]`;
}
} else {
hdmg2 += "0";
}
//CRIT input inside settings for the repeating attack
let crit1 = v[`${repeating_attack}_dmgcustcrit`] || 0;
let crit2 = v[`${repeating_attack}_dmg2custcrit`] || 0;
crit1 = crit1 != 0 ? v[`${repeating_attack}_dmgcustcrit`] : dmgbase;
crit2 = crit2 != 0 ? v[`${repeating_attack}_dmg2custcrit`] : dmg2base;
r1 = atkflag != 0 ? "@{d20}" : "0d20";
r2 = atkflag != 0 ? "@{rtype}" : "{{r2=[[0d20";
//set in the GLOBAL DAMAGE MODIFIER section
let globaldamage = `[[${v.global_damage_mod_roll || 0}]]`;
let globaldamagecrit = `[[${v.global_damage_mod_crit || 0}]]`;
//Assamble Roll Templates
const criticalrange =
v[`${repeating_attack}_atkcritrange`] &&
v["default_critical_range"] &&
parseInt(v["default_critical_range"]) < parseInt(v[`${repeating_attack}_atkcritrange`])
? "@{default_critical_range}"
: "@{atkcritrange}";
if (v.dtype === "full") {
rollbase = `@{wtype}&{template:atkdmg} {{mod=@{atkbonus}}} {{rname=@{atkname}}} {{r1=[[${r1}cs>${criticalrange}${hbonus}]]}} ${r2}cs>${criticalrange}${hbonus}]]}} @{atkflag} {{range=@{atkrange}}} @{dmgflag} {{dmg1=[[${hdmg1}]]}} {{dmg1type=${dmgtype}}} @{dmg2flag} {{dmg2=[[${hdmg2}]]}} {{dmg2type=${dmg2type}}} {{crit1=[[${crit1}[CRIT]]]}} {{crit2=[[${crit2}[CRIT]]]}} @{saveflag} {{desc=@{atk_desc}}} @{hldmg} ${hldmgcrit} {{spelllevel=@{spelllevel}}} {{innate=@{spell_innate}}} {{globalattack=@{global_attack_mod}}} {{globaldamage=${globaldamage}}} {{globaldamagecrit=${globaldamagecrit}}} {{globaldamagetype=@{global_damage_mod_type}}} ammo=@{ammo} ${spelldesc_link}@{charname_output}`;
} else if (atkflag != 0) {
rollbase = `@{wtype}&{template:atk} {{mod=@{atkbonus}}} {{rname=[@{atkname}](~repeating_attack_attack_dmg)}} {{rnamec=[@{atkname}](~repeating_attack_attack_crit)}} {{r1=[[${r1}cs>${criticalrange}${hbonus}]]}} ${r2}cs>${criticalrange}${hbonus}]]}} {{range=@{atkrange}}} {{desc=@{atk_desc}}} {{spelllevel=@{spelllevel}}} {{innate=@{spell_innate}}} {{globalattack=@{global_attack_mod}}} ammo=@{ammo} ${spelldesc_link}@{charname_output}`;
} else if (dmgflag1 != 0) {
rollbase = `@{wtype}&{template:dmg} {{rname=@{atkname}}} @{atkflag} {{range=@{atkrange}}} @{dmgflag} {{dmg1=[[${hdmg1}]]}} {{dmg1type=${dmgtype}}} @{dmg2flag} {{dmg2=[[${hdmg2}]]}} {{dmg2type=${dmg2type}}} @{saveflag} {{desc=@{atk_desc}}} @{hldmg} {{spelllevel=@{spelllevel}}} {{innate=@{spell_innate}}} {{globaldamage=${globaldamage}}} {{globaldamagetype=@{global_damage_mod_type}}} ammo=@{ammo} ${spelldesc_link}@{charname_output}`;
} else {
rollbase = `@{wtype}&{template:dmg} {{rname=@{atkname}}} @{atkflag} {{range=@{atkrange}}} @{saveflag} {{desc=@{atk_desc}}} {{spelllevel=@{spelllevel}}} {{innate=@{spell_innate}}} ammo=@{ammo} ${spelldesc_link}@{charname_output}`;
}
update[
`${repeating_attack}_rollbase_dmg`
] = `@{wtype}&{template:dmg} {{rname=@{atkname}}} @{atkflag} {{range=@{atkrange}}} @{dmgflag} {{dmg1=[[${hdmg1}]]}} {{dmg1type=${dmgtype}}} @{dmg2flag} {{dmg2=[[${hdmg2}]]}} {{dmg2type=${dmg2type}}} @{saveflag} {{desc=@{atk_desc}}} @{hldmg} {{spelllevel=@{spelllevel}}} {{innate=@{spell_innate}}} {{globaldamage=${globaldamage}}} {{globaldamagetype=@{global_damage_mod_type}}} ${spelldesc_link}@{charname_output}`;
update[
`${repeating_attack}_rollbase_crit`
] = `@{wtype}&{template:dmg} {{crit=1}} {{rname=@{atkname}}} @{atkflag} {{range=@{atkrange}}} @{dmgflag} {{dmg1=[[${hdmg1}]]}} {{dmg1type=${dmgtype}}} @{dmg2flag} {{dmg2=[[${hdmg2}]]}} {{dmg2type=${dmg2type}}} {{crit1=[[${crit1}]]}} {{crit2=[[${crit2}]]}} @{saveflag} {{desc=@{atk_desc}}} @{hldmg} ${hldmgcrit} {{spelllevel=@{spelllevel}}} {{innate=@{spell_innate}}} {{globaldamage=${globaldamage}}} {{globaldamagecrit=${globaldamagecrit}}} {{globaldamagetype=@{global_damage_mod_type}}} ${spelldesc_link}@{charname_output}`;
update[`${repeating_attack}_atkbonus`] = bonus;
update[`${repeating_attack}_rollbase`] = rollbase;
if (
v[`${repeating_attack}_spellid`] &&
v[`${repeating_attack}_spellid`] != "" &&
(!source || (source && source != "spell")) &&
v[`${repeating_attack}_spellid`].length == 20
) {
const spellid = v[`${repeating_attack}_spellid`];
const lvl = v[`${repeating_attack}_spelllevel`];
callbacks.push(() => {
update_spell_from_attack(lvl, spellid, attackid);
});
}
if (v[`${repeating_attack}_itemid`] && v[`${repeating_attack}_itemid`] != "" && (!source || (source && source != "item"))) {
const itemid = v[`${repeating_attack}_itemid`];
callbacks.push(() => {
update_item_from_attack(itemid, attackid);
});
}
setAttrs(update, { silent: true }, () => {
callbacks.forEach((callback) => {
callback();
});
});
});
});
};
//Update spells in the attack section
const update_spell_from_attack = (lvl, spellid, attackid) => {
const repeating_attack = `repeating_attack_${attackid}`;
const repeating_spell = `repeating_spell-${lvl}_${spellid}`;
let update = {};
getAttrs(
[
`${repeating_attack}_atkname`,
`${repeating_attack}_atkrange`,
`${repeating_attack}_atkflag`,
`${repeating_attack}_atkattr_base`,
`${repeating_attack}_dmgbase`,
`${repeating_attack}_dmgtype`,
`${repeating_attack}_dmg2base`,
`${repeating_attack}_dmg2type`,
`${repeating_attack}_saveflag`,
`${repeating_attack}_saveattr`,
`${repeating_attack}_saveeffect`,
],
(v) => {
update[`${repeating_spell}_spellname`] = v[`${repeating_attack}_atkname`];
update[`${repeating_spell}_spellrange`] = v[`${repeating_attack}_atkrange`] || "";
update[`${repeating_spell}_spellsavesuccess`] = v[`${repeating_attack}_saveeffect`] || "";
if (v[`${repeating_attack}_dmgtype`] && v[`${repeating_attack}_dmgtype`].toLowerCase() == "healing") {
if (v[`${repeating_attack}_dmgbase`] && v[`${repeating_attack}_dmgbase`] != "") {
update[`${repeating_spell}_spellhealing`] = v[`${repeating_attack}_dmgbase`];
}
} else {
if (
v[`${repeating_attack}_dmgbase`] &&
v[`${repeating_attack}_dmgbase`] != "" &&
v[`${repeating_attack}_dmgbase`].indexOf("[[round((@{level} + 1) / 6 + 0.5)]]") === -1
) {
update[`${repeating_spell}_spelldamage`] = v[`${repeating_attack}_dmgbase`];
} else if (!v[`${repeating_attack}_dmgbase`] || v[`${repeating_attack}_dmgbase`] === "") {
update[`${repeating_spell}_spelldamage`] = "";
}
update[`${repeating_spell}_spelldamagetype`] = v[`${repeating_attack}_dmgtype`] || "";
}
if (v[`${repeating_attack}_dmg2type`] && v[`${repeating_attack}_dmg2type`].toLowerCase() === "healing") {
if (v[`${repeating_attack}_dmgbase`] && v[`${repeating_attack}_dmgbase`] != "") {
update[`${repeating_spell}_spellhealing`] = v[`${repeating_attack}_dmgbase`];
}
} else {
update[`${repeating_spell}_spelldamage2`] = v[`${repeating_attack}_dmg2base`] || "";
update[`${repeating_spell}_spelldamagetype2`] = v[`${repeating_attack}_dmg2type`] || "";
}
//SAVING THROW checkbox inside settings for the repeating attack
update[`${repeating_spell}_spellsave`] =
v[`${repeating_attack}_saveflag`] && v[`${repeating_attack}_saveflag`] != "0" ? v[`${repeating_attack}_saveattr`] : "";
setAttrs(update, { silent: true });
}
);
};
var update_item_from_attack = function (itemid, attackid) {
getAttrs(
[
"repeating_attack_" + attackid + "_atkname",
"repeating_attack_" + attackid + "_dmgbase",
"repeating_attack_" + attackid + "_dmg2base",
"repeating_attack_" + attackid + "_dmgtype",
"repeating_attack_" + attackid + "_dmg2type",
"repeating_attack_" + attackid + "_atkrange",
"repeating_attack_" + attackid + "_atkmod",
"repeating_attack_" + attackid + "_dmgmod",
"repeating_inventory_" + itemid + "_itemmodifiers",
"repeating_attack_" + attackid + "_versatile_alt",
"repeating_inventory_" + itemid + "_itemproperties",
"repeating_attack_" + attackid + "_atkmagic",
],
function (v) {
var update = {};
var mods = v["repeating_inventory_" + itemid + "_itemmodifiers"];
var damage = v["repeating_attack_" + attackid + "_dmgbase"] ? v["repeating_attack_" + attackid + "_dmgbase"] : 0;
var damage2 = v["repeating_attack_" + attackid + "_dmg2base"] ? v["repeating_attack_" + attackid + "_dmg2base"] : 0;
var damagetype = v["repeating_attack_" + attackid + "_dmgtype"] ? v["repeating_attack_" + attackid + "_dmgtype"] : 0;
var damagetype2 = v["repeating_attack_" + attackid + "_dmg2type"] ? v["repeating_attack_" + attackid + "_dmg2type"] : 0;
var atkcritrange = v["repeating_attack_" + attackid + "_atkcritrange"] ? v["repeating_attack_" + attackid + "_atkcritrange"] : 20;
var range = v["repeating_attack_" + attackid + "_atkrange"] ? v["repeating_attack_" + attackid + "_atkrange"] : 0;
var attackmod = v["repeating_attack_" + attackid + "_atkmod"] ? v["repeating_attack_" + attackid + "_atkmod"] : 0;
var damagemod = v["repeating_attack_" + attackid + "_dmgmod"] ? v["repeating_attack_" + attackid + "_dmgmod"] : 0;
var magicmod = v["repeating_attack_" + attackid + "_atkmagic"] ? v["repeating_attack_" + attackid + "_atkmagic"] : 0;
var atktype = "";
var altprefix = v["repeating_attack_" + attackid + "_versatile_alt"] === "1" ? "Alternate " : "";
if (/Alternate Damage:/i.test(v["repeating_inventory_" + itemid + "_itemmodifiers"])) {
update["repeating_inventory_" + itemid + "_itemname"] = v["repeating_attack_" + attackid + "_atkname"].replace(
/\s*(?:\(One-Handed\)|\(Two-Handed\))/,
""
);
} else {
update["repeating_inventory_" + itemid + "_itemname"] = v["repeating_attack_" + attackid + "_atkname"];
}
var attack_type_regex = /(?:^|,)\s*Item Type: (Melee|Ranged) Weapon(?:,|$)/i;
var attack_type_results = attack_type_regex.exec(v["repeating_inventory_" + itemid + "_itemmodifiers"]);
atktype = attack_type_results ? attack_type_results[1] : "";
if (mods) {
mods = mods.split(",");
} else {
mods = [];
}
var damage_regex = new RegExp("^\\s*" + altprefix + "Damage:\\s*(.+)$", "i");
var damage_found = false;
for (var i = 0; i < mods.length; i++) {
if ((damage_found = damage_regex.exec(mods[i]))) {
if (damage !== 0) {
mods[i] = mods[i].replace(damage_found[1], damage);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!damage_found && damage !== 0) {
mods.push(altprefix + "Damage: " + damage);
}
var damage2_regex = new RegExp("^\\s*" + altprefix + "Secondary Damage:\\s*(.+)$", "i");
var damage2_found = false;
for (var i = 0; i < mods.length; i++) {
if ((damage2_found = damage2_regex.exec(mods[i]))) {
if (damage2 !== 0) {
mods[i] = mods[i].replace(damage2_found[1], damage2);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!damage2_found && damage2 !== 0) {
mods.push(altprefix + "Secondary Damage: " + damage2);
}
var dmgtype_regex = new RegExp("^\\s*" + altprefix + "Damage Type:\\s*(.+)$", "i");
var dmgtype_found = false;
for (var i = 0; i < mods.length; i++) {
if ((dmgtype_found = dmgtype_regex.exec(mods[i]))) {
if (damagetype !== 0) {
mods[i] = mods[i].replace(dmgtype_found[1], damagetype);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!dmgtype_found && damagetype !== 0) {
mods.push(altprefix + "Damage Type: " + damagetype);
}
var dmgtype2_regex = new RegExp("^\\s*" + altprefix + "Secondary Damage Type:\\s*(.+)$", "i");
var dmgtype2_found = false;
for (var i = 0; i < mods.length; i++) {
if ((dmgtype2_found = dmgtype2_regex.exec(mods[i]))) {
if (damagetype2 !== 0) {
mods[i] = mods[i].replace(dmgtype_found[1], damagetype);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!dmgtype2_found && damagetype2 !== 0) {
mods.push(altprefix + "Secondary Damage Type: " + damagetype2);
}
var atkcritrange_found = false;
for (var i = 0; i < mods.length; i++) {
if ((atkcritrange_found = /^\s*Critical Range:\s*(.+)$/i.exec(mods[i]))) {
if (atkcritrange !== 0) {
mods[i] = mods[i].replace(atkcritrange_found[1], atkcritrange);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!range_found && range !== 0) {
mods.push("Critical Range: " + atkcritrange);
}
var range_found = false;
for (var i = 0; i < mods.length; i++) {
if ((range_found = /^\s*Range:\s*(.+)$/i.exec(mods[i]))) {
if (range !== 0) {
mods[i] = mods[i].replace(range_found[1], range);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!range_found && range !== 0) {
mods.push("Range: " + range);
}
var attackmod_regex = new RegExp("^\\s*(?:" + (atktype !== "" ? atktype + "|" : "") + "Weapon) Attacks \\+?(.+)$", "i");
var attackmod_found = false;
for (var i = 0; i < mods.length; i++) {
if ((attackmod_found = attackmod_regex.exec(mods[i]))) {
if (magicmod !== 0 || attackmod !== 0) {
mods[i] = mods[i].replace(attackmod_found[1], magicmod !== 0 ? magicmod : attackmod);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!attackmod_found && (magicmod !== 0 || attackmod !== 0)) {
var properties = v["repeating_inventory_" + itemid + "_itemproperties"];
if (properties && /Thrown/i.test(properties)) {
mods.push("Weapon Attacks: " + (magicmod !== 0 ? magicmod : attackmod));
} else {
mods.push(atktype + " Attacks: " + (magicmod !== 0 ? magicmod : attackmod));
}
}
var damagemod_regex = new RegExp("^\\s*(?:" + (atktype !== "" ? atktype + "|" : "") + "Weapon) Damage \\+?(.+)$", "i");
var damagemod_found = false;
for (var i = 0; i < mods.length; i++) {
if ((damagemod_found = damagemod_regex.exec(mods[i]))) {
if (magicmod !== 0 || damagemod !== 0) {
mods[i] = mods[i].replace(damagemod_found[1], magicmod !== 0 ? magicmod : attackmod);
} else {
mods.splice(i, 1);
}
break;
}
}
if (!damagemod_found && (magicmod !== 0 || damagemod !== 0)) {
var properties = v["repeating_inventory_" + itemid + "_itemproperties"];
if (properties && /Thrown/i.test(properties)) {
mods.push("Weapon Damage: " + (magicmod !== 0 ? magicmod : damagemod));
} else {
mods.push(atktype + " Damage: " + (magicmod !== 0 ? magicmod : damagemod));
}
}
update["repeating_inventory_" + itemid + "_itemmodifiers"] = mods.join(",");
setAttrs(update, { silent: true });
}
);
};
var remove_attack = function (attackid) {
removeRepeatingRow("repeating_attack_" + attackid);
};
const createResource = (name, max, current) => {
const updateResource = (name, max, current, side, id) => {
const newrowid = id || generateRowID();
let update = {};
update[`repeating_resource_${newrowid}_resource_${side}_name`] = name;
update[`repeating_resource_${newrowid}_resource_${side}_max`] = max;
update[`repeating_resource_${newrowid}_resource_${side}`] = current;
setAttrs(update);
};
getAttrs(["other_resource_name"], (v) => {
//Use other_resource if it is empty or same name:
if (!v.other_resource_name || v.other_resource_name == "" || v.other_resource_name == name) {
let update = {};
update[`other_resource_name`] = name;
update[`other_resource_max`] = max;
update[`other_resource`] = current;
setAttrs(update);
//If other_resource is populated look through the repeating sections:
} else {
getSectionIDs(`repeating_resource`, (idarray) => {
//No resources founds, create one:
if (idarray.length == 0) {
updateResource(name, max, current);
} else {
const namesAttributes = idarray
.map((id) => {
return `repeating_resource_${id}_resource_left_name`;
})
.concat(
idarray.map((id) => {
return `repeating_resource_${id}_resource_right_name`;
})
);
getAttrs(namesAttributes, (v) => {
let existingID = false;
let side = "left";
for (const [key, value] of Object.entries(v)) {
if (value == name || value == "") {
existingID = SheetUtils.getUID(key);
if (key.includes("_right_")) side = "right";
break;
}
}
//Existing resource found, updated it:
if (existingID) {
updateResource(name, max, current, side, existingID);
//No matching resource found, create one:
} else {
updateResource(name, max, current, side);
}
});
}
});
}
});
};
const deleteResource = (name) => {
const updateResource = (name, max, current, side, id) => {
const newrowid = id || generateRowID();
let update = {};
update[`repeating_resource_${newrowid}_resource_${side}_name`] = name;
update[`repeating_resource_${newrowid}_resource_${side}_max`] = max;
update[`repeating_resource_${newrowid}_resource_${side}`] = current;
setAttrs(update);
};
getAttrs(["other_resource_name"], (v) => {
if (v.other_resource_name && v.other_resource_name == name) {
let update = {};
update[`other_resource_name`] = "";
update[`other_resource_max`] = "";
update[`other_resource`] = "";
setAttrs(update);
//If other_resource is populated look through the repeating sections:
} else {
getSectionIDs(`repeating_resource`, (idarray) => {
//No resources founds, create one:
if (idarray.length > 0) {
const namesAttributes = idarray
.map((id) => {
return `repeating_resource_${id}_resource_left_name`;
})
.concat(
idarray.map((id) => {
return `repeating_resource_${id}_resource_right_name`;
})
);
getAttrs(namesAttributes, (v) => {
let existingID = false;
let side = "left";
for (const [key, value] of Object.entries(v)) {
if (value == name) existingID = SheetUtils.getUID(key);
if (key.includes("_right_")) side = "right";
break;
}
if (existingID) {
if (side == "right" && !v[`repeating_resource_${existingID}_resource_left_name`]) {
removeRepeatingRow(`repeating_resource_${existingID}`);
} else if (side == "left" && !v[`repeating_resource_${existingID}_resource_right_name`]) {
removeRepeatingRow(`repeating_resource_${existingID}`);
} else {
let update = {};
update[`repeating_resource_${existingID}_resource_${side}_name`] = "";
update[`repeating_resource_${existingID}_resource_${side}_max`] = "";
update[`repeating_resource_${existingID}_resource_${side}`] = "";
update[`repeating_resource_${existingID}_resource_${side}_itemid`] = "";
setAttrs(update);
}
}
});
}
});
}
});
};
const remove_resource = (id) => {
const attr = id === "other_resource" ? id : `repeating_resource_${id}_itemid`;
let update = {};
getAttrs([`${attr}`], (v) => {
const itemid = v[`${attr}`];
if (id == "other_resource") {
update["other_resource"] = "";
update["other_resource_name"] = "";
update["other_resource_itemid"] = "";
setAttrs(update, { silent: true });
} else {
const currentID = id.replace("repeating_resource_", "").substring(0, 20);
const side = id.includes("left") ? `right` : `left`;
//Get the inputs for the opposite side to see if they are empty.
//If they are empty delete the entire row. Otherwise set the side that was the source of this event to empty strings
getAttrs(
[
`repeating_resource_${currentID}_resource_${side}`,
`repeating_resource_${currentID}_resource_${side}_name`,
`repeating_resource_${currentID}_resource_${side}_max`,
],
(v) => {
const Name = v[`repeating_resource_${currentID}_resource_${side}_name`] || false;
const Value = v[`repeating_resource_${currentID}_resource_${side}`] || false;
const Max = v[`repeating_resource_${currentID}_resource_${side}_max`] || false;
if (!Name && !Value && !Max) {
if (currentID) removeRepeatingRow(`repeating_resource_${currentID}`);
} else {
update[`repeating_resource_${id}`] = "";
update[`repeating_resource_${id}_name`] = "";
update[`repeating_resource_${id}_max`] = "";
update[`repeating_resource_${id}_itemid`] = "";
}
setAttrs(update, { silent: true });
}
);
}
});
};
const update_weight = function () {
let update = {};
let wtotal = 0;
let weight_attrs = [
"cp",
"sp",
"ep",
"gp",
"pp",
"encumberance_setting",
"strength",
"size",
"carrying_capacity_mod",
"powerful_build",
"ingore_non_equipped_weight",
];
getSectionIDs("repeating_inventory", (idarray) => {
_.each(idarray, (currentID, i) => {
weight_attrs.push(`repeating_inventory_${currentID}_itemweight`);
weight_attrs.push(`repeating_inventory_${currentID}_itemcount`);
weight_attrs.push(`repeating_inventory_${currentID}_equipped`);
});
getAttrs(weight_attrs, (v) => {
let coinWeight = 0;
["cp", "sp", "ep", "gp", "pp"].forEach((type) => {
coinWeight += isNaN(parseInt(v[`${type}`], 10)) === false ? parseInt(v[`${type}`], 10) : 0;
});
wtotal = wtotal + coinWeight / 50;
_.each(idarray, (currentID, i) => {
if (
v["ingore_non_equipped_weight"] &&
v["ingore_non_equipped_weight"] == 1 &&
(!v[`repeating_inventory_${currentID}_equipped`] || v[`repeating_inventory_${currentID}_equipped`] != 1)
)
return;
if (
v[`repeating_inventory_${currentID}_itemweight`] &&
isNaN(parseInt(v[`repeating_inventory_${currentID}_itemweight`], 10)) === false
) {
count =
v[`repeating_inventory_${currentID}_itemcount`] && isNaN(parseFloat(v[`repeating_inventory_${currentID}_itemcount`])) === false
? parseFloat(v[`repeating_inventory_${currentID}_itemcount`])
: 1;
wtotal = wtotal + parseFloat(v[`repeating_inventory_${currentID}_itemweight`]) * count;
}
});
update["weighttotal"] = wtotal % 1 === 0 ? wtotal : parseFloat(wtotal.toFixed(2));
const str_base = parseInt(v.strength, 10);
let size_multiplier = 1;
if (v["size"]) {
const size = v["size"].toLowerCase().trim();
const sizeLookup = {
tiny: 0.5,
medium: 1,
large: 2,
huge: 4,
gargantuan: 8,
};
const sizeIndex = Object.keys(sizeLookup).indexOf(size);
size_multiplier = sizeIndex > -1 ? sizeLookup[size] : size_multiplier;
if (v["powerful_build"] && v["powerful_build"] == 1 && sizeIndex > -1) {
size_multiplier *= 2;
}
}
let str = str_base * size_multiplier;
// Parse the carrying capacitiy modificator if any
if (v.carrying_capacity_mod) {
const operator = v.carrying_capacity_mod.substring(0, 1);
const value = v.carrying_capacity_mod.substring(1);
if (["*", "x", "+", "-"].indexOf(operator) > -1 && isNaN(parseFloat(value, 10)) === false) {
str =
operator === "*" || operator == "x"
? str * parseFloat(value, 10)
: operator === "+"
? str + parseFloat(value, 10)
: operator === "-"
? str - parseFloat(value, 10)
: str;
}
}
if (!v.encumberance_setting || v.encumberance_setting === "off") {
update["encumberance"] = wtotal > str * 15 ? "OVER CARRYING CAPACITY" : " ";
} else if (v.encumberance_setting === "on") {
update["encumberance"] =
wtotal > str * 15 ? "IMMOBILE" : wtotal > str * 10 ? "HEAVILY ENCUMBERED" : wtotal > str * 5 ? "ENCUMBERED" : " ";
} else {
update["encumberance"] = " ";
}
setAttrs(update, { silent: true });
});
});
};
var update_ac = function () {
// //update["custom_ac_base"] = parsedKey.Base;
//update["custom_ac_part1"] = parsedKey["Attribute 1"];
//update["custom_ac_part2"] = parsedKey["Attribute 2"] ? parsedKey["Attribute 2"] : "";
//update["custom_ac_shield"] = parsedKey.Shields;
getAttrs(["custom_ac_flag"], function (v) {
if (v.custom_ac_flag === "2") {
return;
} else {
var update = {};
var ac_attrs = [
"simpleinventory",
"custom_ac_base",
"custom_ac_part1",
"custom_ac_part2",
"strength_mod",
"dexterity_mod",
"constitution_mod",
"intelligence_mod",
"wisdom_mod",
"charisma_mod",
"custom_ac_shield",
];
getSectionIDs("repeating_acmod", function (acidarray) {
_.each(acidarray, function (currentID, i) {
ac_attrs.push("repeating_acmod_" + currentID + "_global_ac_val");
ac_attrs.push("repeating_acmod_" + currentID + "_global_ac_active_flag");
});
getSectionIDs("repeating_inventory", function (idarray) {
_.each(idarray, function (currentID, i) {
ac_attrs.push("repeating_inventory_" + currentID + "_equipped");
ac_attrs.push("repeating_inventory_" + currentID + "_itemmodifiers");
});
getAttrs(ac_attrs, function (b) {
var custom_total = 0;
if (v.custom_ac_flag === "1") {
if (customACFormulas.length > 1) {
const ac_array = customACFormulas;
let bestValue = 0;
let bestIndex = 0;
ac_array.forEach((ac_formula, index) => {
const base = isNaN(parseInt(ac_formula["Base"], 10)) === false ? parseInt(ac_formula["Base"], 10) : 10;
const part1attr = ac_formula["Attribute 1"] ? ac_formula["Attribute 1"].toLowerCase() : "none";
const part2attr = ac_formula["Attribute 2"] ? ac_formula["Attribute 2"].toLowerCase() : "none";
const part1 = part1attr === "none" ? 0 : parseInt(b[part1attr + "_mod"], 10);
const part2 = part2attr === "none" ? 0 : parseInt(b[part2attr + "_mod"], 10);
formula_total = base + part1 + part2;
if (formula_total > bestValue) {
bestValue = formula_total;
bestIndex = index;
}
});
//Updates selectors in the sheet;
const overwrite = {};
overwrite["custom_ac_base"] = customACFormulas[bestIndex].Base;
overwrite["custom_ac_part1"] = customACFormulas[bestIndex]["Attribute 1"];
overwrite["custom_ac_part2"] = customACFormulas[bestIndex]["Attribute 2"] ? customACFormulas[bestIndex]["Attribute 2"] : "";
overwrite["custom_ac_shield"] = customACFormulas[bestIndex].Shields;
setAttrs(overwrite, { silent: true });
customACFormulas = [];
custom_total = bestValue;
} else {
var base = isNaN(parseInt(b.custom_ac_base, 10)) === false ? parseInt(b.custom_ac_base, 10) : 10;
var part1attr = b.custom_ac_part1.toLowerCase();
var part2attr = b.custom_ac_part2.toLowerCase();
var part1 = part1attr === "none" ? 0 : parseInt(b[part1attr + "_mod"], 10);
var part2 = part2attr === "none" ? 0 : parseInt(b[part2attr + "_mod"], 10);
custom_total = base + part1 + part2;
}
}
var globalacmod = 0;
_.each(acidarray, function (currentID, i) {
if (b["repeating_acmod_" + currentID + "_global_ac_active_flag"] == "1") {
globalacmod += parseInt(b["repeating_acmod_" + currentID + "_global_ac_val"], 10);
}
});
var dexmod = +b["dexterity_mod"];
var total = 10 + dexmod;
var armorcount = 0;
var shieldcount = 0;
var armoritems = [];
if (b.simpleinventory === "complex") {
_.each(idarray, function (currentID, i) {
if (
b["repeating_inventory_" + currentID + "_equipped"] &&
b["repeating_inventory_" + currentID + "_equipped"] === "1" &&
b["repeating_inventory_" + currentID + "_itemmodifiers"] &&
b["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().indexOf("ac") > -1
) {
var mods = b["repeating_inventory_" + currentID + "_itemmodifiers"].split(",");
var ac = 0;
var type = "mod";
_.each(mods, function (mod) {
if (mod.substring(0, 10) === "Item Type:") {
type = mod.substring(11, mod.length).trim().toLowerCase();
}
if (
mod.toLowerCase().indexOf("ac:") > -1 ||
mod.toLowerCase().indexOf("ac +") > -1 ||
mod.toLowerCase().indexOf("ac+") > -1
) {
var regex = mod.replace(/[^0-9]/g, "");
var bonus = regex && regex.length > 0 && isNaN(parseInt(regex, 10)) === false ? parseInt(regex, 10) : 0;
ac = ac + bonus;
}
if (mod.toLowerCase().indexOf("ac -") > -1 || mod.toLowerCase().indexOf("ac-") > -1) {
var regex = mod.replace(/[^0-9]/g, "");
var bonus = regex && regex.length > 0 && isNaN(parseInt(regex, 10)) === false ? parseInt(regex, 10) : 0;
ac = ac - bonus;
}
});
armoritems.push({ type: type, ac: ac });
}
});
armorcount = armoritems.filter(function (item) {
return item["type"].indexOf("armor") > -1;
}).length;
shieldcount = armoritems.filter(function (item) {
return item["type"].indexOf("shield") > -1;
}).length;
var base = dexmod;
var armorac = 10;
var shieldac = 0;
var modac = 0;
_.each(armoritems, function (item) {
if (item["type"].indexOf("light armor") > -1) {
armorac = item["ac"];
base = dexmod;
} else if (item["type"].indexOf("medium armor") > -1) {
armorac = item["ac"];
base = Math.min(dexmod, 2);
} else if (item["type"].indexOf("heavy armor") > -1) {
armorac = item["ac"];
base = 0;
} else if (item["type"].indexOf("shield") > -1) {
shieldac = item["ac"];
} else {
modac = modac + item["ac"];
}
});
total = base + armorac + shieldac + modac;
}
update["armorwarningflag"] = "hide";
update["customacwarningflag"] = "hide";
if (armorcount > 1 || shieldcount > 1) {
update["armorwarningflag"] = "show";
}
update["ac"] = total + globalacmod;
if (custom_total > 0) {
if (armorcount > 0 || (shieldcount > 0 && b.custom_ac_shield != "yes")) {
update["customacwarningflag"] = "show";
} else {
update["ac"] =
b.custom_ac_shield == "yes" ? custom_total + shieldac + globalacmod + modac : custom_total + globalacmod + modac;
}
}
setAttrs(update, { silent: true });
});
});
});
}
});
};
var check_customac = function (attr) {
getAttrs(["custom_ac_flag", "custom_ac_part1", "custom_ac_part2"], function (v) {
if (
v["custom_ac_flag"] &&
v["custom_ac_flag"] === "1" &&
((v["custom_ac_part1"] && v["custom_ac_part1"] === attr) || (v["custom_ac_part2"] && v["custom_ac_part2"] === attr))
) {
update_ac();
}
});
};
const update_initiative = () => {
const attrs_to_get = ["dexterity", "dexterity_mod", "initmod", "jack_of_all_trades", "jack", "init_tiebreaker", "pb_type"];
getSectionIDs("repeating_inventory", (idarray) => {
_.each(idarray, (currentID) => {
attrs_to_get.push(`repeating_inventory_${currentID}_equipped`);
attrs_to_get.push(`repeating_inventory_${currentID}_itemmodifiers`);
});
getAttrs(attrs_to_get, (v) => {
let update = {};
let final_init = parseInt(v["dexterity_mod"], 10);
if (v["initmod"] && !isNaN(parseInt(v["initmod"], 10))) {
final_init = final_init + parseInt(v["initmod"], 10);
}
if (v["init_tiebreaker"] && v["init_tiebreaker"] != 0) {
final_init = final_init + parseInt(v["dexterity"], 10) / 100;
}
if (v["jack_of_all_trades"] && v["jack_of_all_trades"] != 0) {
if (v["pb_type"] && v["pb_type"] === "die" && v["jack"]) {
final_init = final_init + "+" + v["jack"];
} else if (v["jack"] && !isNaN(parseInt(v["jack"], 10))) {
final_init = final_init + parseInt(v["jack"], 10);
}
}
_.each(idarray, (currentID) => {
if (
v[`repeating_inventory_${currentID}_equipped`] &&
v[`repeating_inventory_${currentID}_equipped`] === "1" &&
v[`repeating_inventory_${currentID}_itemmodifiers`] &&
v[`repeating_inventory_${currentID}_itemmodifiers`].toLowerCase().indexOf("ability checks") > -1
) {
const mods = v[`repeating_inventory_${currentID}_itemmodifiers`].toLowerCase().split(",");
_.each(mods, (mod) => {
if (mod.indexOf("ability checks") > -1) {
const new_mod = !isNaN(parseInt(mod.replace(/[^0-9]/g, ""), 10)) ? parseInt(mod.replace(/[^0-9]/g, ""), 10) : false;
final_init = mod.indexOf("-") > -1 && new_mod ? final_init - new_mod : new_mod ? final_init + new_mod : final_init;
}
});
}
});
if (final_init % 1 != 0) {
final_init = parseFloat(final_init.toPrecision(12)); // ROUNDING ERROR BUGFIX
}
update["initiative_bonus"] = final_init;
setAttrs(update, { silent: true });
});
});
};
const update_class = () => {
getAttrs(
[
"class",
"base_level",
"custom_class",
"cust_hitdietype",
"cust_spellcasting_ability",
"cust_strength_save_prof",
"cust_dexterity_save_prof",
"cust_constitution_save_prof",
"cust_intelligence_save_prof",
"cust_wisdom_save_prof",
"cust_charisma_save_prof",
"npc",
],
(v) => {
if (v.npc && v.npc == "1") {
return;
}
const abilites = ["Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma"];
//Custom Classes
if (v.custom_class && v.custom_class != "0") {
update = {};
update["hitdietype"] = v.cust_hitdietype;
update["hitdieroll"] = v.cust_hitdietype;
update["spellcasting_ability"] = v.cust_spellcasting_ability;
abilites.forEach((save) => {
const ability = save.toLowerCase();
update[`${ability}_save_prof`] = v[`cust_${ability}_save_prof`];
});
setAttrs(update, { silent: true });
//If "Choose" has been selected in the select
} else if (v.class === "") {
update = {};
update["hitdietype"] = 4;
update["hitdieroll"] = 4;
update["spellcasting_ability"] = "0*";
abilites.forEach((save) => {
const ability = save.toLowerCase();
update[`${ability}_save_prof`] = 0;
});
setAttrs(update, { silent: true });
//Standard classes can pull their data from the Comependium
} else {
getCompendiumPage(v.class, (pages) => {
pages = removeDuplicatedPageData(pages);
update = {};
const classPages = Array.isArray(pages) ? pages : [pages];
classPages.forEach((classData) => {
if (classData.data && "Category" in classData.data && classData.data[`Category`] === "Classes") {
update["hitdietype"] = classData.data["Hit Die"] ? classData.data["Hit Die"].slice(1) : "6";
update["hitdieroll"] = classData.data["Hit Die"] ? classData.data["Hit Die"].slice(1) : "6";
if(classData?.data?.["Spellcasting Ability"]) {
if(classData.data["Spellcasting Ability"].toLowerCase() !== "choice") {
update["spellcasting_ability"] = `@{${classData.data["Spellcasting Ability"].toLowerCase()}_mod}+`;
}
} else {
update["spellcasting_ability"] = "0*";
}
abilites.forEach((save) => {
const ability = save.toLowerCase();
update[`${ability}_save_prof`] =
classData.data["data-Saving Throws"] && classData.data["data-Saving Throws"].indexOf(`${save}`) > -1 ? "(@{pb})" : 0;
update_save(ability);
});
}
setAttrs(update, { silent: true });
//Update Spell info to change or remove casting attributes in Spells
update_spell_info();
});
});
}
}
);
set_level();
};
const update_hds = (source) => {
var newPool = {};
Object.keys(source).forEach((dieType) => {
newPool[`d${dieType}`] = {
size: source[dieType],
};
});
getHitDicePool((allPool) => {
let hd = 0;
let hdMax = 0;
const existingPool = Object.keys(allPool).filter((die) => {
return Object.keys(newPool).indexOf(die) > -1;
});
Object.keys(newPool).forEach((die) => {
if (existingPool.indexOf(die) > -1) {
const pool = allPool[die];
const poolUpdate = newPool[die];
const previousValue = pool.size;
const newValue = poolUpdate.size;
const gained = poolUpdate.size < pool.available ? poolUpdate.size - pool.available : Math.max(newValue - previousValue, 0);
const available = pool.available + gained;
newPool[die]["available"] = available;
} else {
newPool[die]["available"] = newPool[die]["size"];
}
hd += newPool[die]["available"];
hdMax += newPool[die]["size"];
});
setAttrs({ hit_dice: hd, hit_dice_max: hdMax }, { silent: true }, () => {
updateHitDicePool(newPool, true);
});
});
};
const set_level = function () {
getAttrs(
[
"base_level",
"multiclass1_flag",
"multiclass2_flag",
"multiclass3_flag",
"multiclass1_lvl",
"multiclass2_lvl",
"multiclass3_lvl",
"class",
"multiclass1",
"multiclass2",
"multiclass3",
"arcane_fighter",
"arcane_rogue",
"third_caster",
"custom_class",
"cust_spellslots",
"cust_classname",
"level_calculations",
"class",
"subclass",
"multiclass1_subclass",
"multiclass2_subclass",
"multiclass3_subclass",
"hitdietype",
"use_per_class_hit_dice",
],
(v) => {
let update = {};
let callbacks = [];
const multiclass =
(v[`multiclass1_flag`] && v[`multiclass1_flag`] === "1") ||
(v.multiclass2_flag && v.multiclass2_flag === "1") ||
(v.multiclass3_flag && v.multiclass3_flag === "1")
? true
: false;
const finalclass = v[`custom_class`] && v[`custom_class`] != "0" ? v[`cust_spellslots`] : v[`class`];
let finallevel = v[`base_level`] && v[`base_level`] > 0 ? parseInt(v.base_level, 10) : 1;
const charclass = v[`custom_class`] && v[`custom_class`] != "0" ? v[`cust_classname`] : v[`class`];
let hitdie_final = multiclass ? `?{Hit Die Class|${charclass},@{hitdietype}` : "@{hitdietype}";
const subclass = v[`subclass`] ? v[`subclass`] + " " : "";
let class_display = `${subclass}${charclass} ${finallevel}`;
let hitDicePool = {};
hitDicePool[v["hitdietype"]] = finallevel;
// This nested array is used to determine the overall spellcasting level for the character.
var classes = [[finalclass.toLowerCase(), v[`base_level`]]];
["multiclass1", "multiclass2", "multiclass3"].forEach((multiclass) => {
if (v[`${multiclass}_flag`] && v[`${multiclass}_flag`] === "1") {
const multiclasslevel = v[`${multiclass}_lvl`] && v[`${multiclass}_lvl`] > 0 ? parseInt(v[`${multiclass}_lvl`], 10) : 1;
const subclass = v[`${multiclass}_subclass`] ? v[`${multiclass}_subclass`] + " " : "";
finallevel = finallevel + multiclasslevel;
hitdie_final =
`${hitdie_final}|` +
v[`${multiclass}`].charAt(0).toUpperCase() +
v[`${multiclass}`].slice(1) +
"," +
checkHitDie(v[`${multiclass}`]);
if (hitDicePool[checkHitDie(v[`${multiclass}`])]) {
hitDicePool[checkHitDie(v[`${multiclass}`])] += multiclasslevel;
} else {
hitDicePool[checkHitDie(v[`${multiclass}`])] = multiclasslevel;
}
classes.push([v[`${multiclass}`], multiclasslevel]);
class_display = `${class_display}, ${subclass}` + v[`${multiclass}`] + ` ${multiclasslevel}`;
}
});
const casterlevel = checkCasterLevel(classes, v[`arcane_fighter`], v[`arcane_rogue`], v[`third_caster`]);
update["hitdie_final"] = multiclass ? `${hitdie_final}}` : hitdie_final;
update["level"] = finallevel;
update["caster_level"] = casterlevel;
update["class_display"] = class_display;
if (!v["level_calculations"] || v["level_calculations"] === "on") {
if (v["use_per_class_hit_dice"] && v["use_per_class_hit_dice"] == 1) {
callbacks.push(() => {
update_hds(hitDicePool);
});
} else {
update["hit_dice_max"] = finallevel;
}
callbacks.push(() => {
update_spell_slots();
});
}
callbacks.push(() => {
update_pb();
});
callbacks.push(() => {
update_leveler_display();
});
setAttrs(update, { silent: true }, () => {
callbacks.forEach((callback) => {
callback();
});
});
}
);
};
var isMultiCaster = function (classes, arcane_fighter, arcane_rogue, third_caster) {
var singlecaster = false;
var multicaster = false;
_.each(classes, function (multiclass) {
var caster = getCasterType(multiclass[0], multiclass[1], arcane_fighter, arcane_rogue, third_caster) > 0;
if (caster && singlecaster) {
multicaster = true;
} else if (caster) {
singlecaster = true;
}
});
return multicaster;
};
var getCasterType = function (class_string, levels, arcane_fighter, arcane_rogue, third_caster) {
var full = [...characterClasses().filter({ caster: "full" }).get(true), "full"];
var half = [...characterClasses().filter({ caster: "half" }).get(true), "half"];
class_string = class_string.toLowerCase();
if (full.indexOf(class_string) != -1) {
return 1;
} else if (half.indexOf(class_string) != -1) {
if (class_string === "artificer" && levels == 1) return 1;
return levels == 1 ? 0 : 1 / 2;
} else if (
class_string === "third" ||
third_caster == 1 ||
(class_string === "fighter" && arcane_fighter == 1) ||
(class_string === "rogue" && arcane_rogue == 1)
) {
return levels == 1 || levels == 2 ? 0 : 1 / 3;
} else {
return 0;
}
};
var checkCasterLevel = function (classes, arcane_fighter, arcane_rogue, third_caster) {
var multicaster = isMultiCaster(classes, arcane_fighter, arcane_rogue, third_caster);
var totalcasterlevel = 0;
_.each(classes, function (multiclass) {
var casterlevel = parseInt(multiclass[1], 10) * getCasterType(multiclass[0], multiclass[1], arcane_fighter, arcane_rogue, third_caster);
// Characters with multiple spellcasting classes round down the casting level for that class
// Character with a single spellcasting class round up the casting level
totalcasterlevel = totalcasterlevel + (multicaster ? Math.floor(casterlevel) : Math.ceil(casterlevel));
});
return totalcasterlevel;
};
var checkHitDie = function (class_string) {
const d12class = characterClasses().filter({ hitdie: "d12" }).get(true);
const d10class = characterClasses().filter({ hitdie: "d10" }).get(true);
const d8class = characterClasses().filter({ hitdie: "d8" }).get(true);
const d6class = characterClasses().filter({ hitdie: "d6" }).get(true);
class_string = class_string.toLowerCase();
if (d12class.indexOf(class_string) != -1) {
return "12";
} else if (d10class.indexOf(class_string) != -1) {
return "10";
} else if (d8class.indexOf(class_string) != -1) {
return "8";
} else if (d6class.indexOf(class_string) != -1) {
return "6";
} else {
return "0";
}
};
var update_leveler_display = function () {
getAttrs(["experience", "level"], function (v) {
let lvl = 0;
let exp = 0;
let update = {};
const xpLookup = [
0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, 85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000, 305000,
355000,
];
update["showleveler"] = 0;
update["invalidXP"] = 0;
if (v["level"] && !isNaN(parseInt(v["level"], 10)) && parseInt(v["level"], 10) > 0) {
lvl = parseInt(v["level"], 10);
}
if (v["experience"]) {
let sanitizedXP = SheetUtils.sanitizeXP(v["experience"]);
if (!sanitizedXP) {
update["invalidXP"] = 1;
} else if (Array.isArray(sanitizedXP) && sanitizedXP.length > 0) {
exp = sanitizedXP[0];
}
}
if (lvl > 0 && lvl < xpLookup.length && exp > 0) {
if (exp >= xpLookup[lvl]) update["showleveler"] = 1;
}
setAttrs(update, { silent: true });
});
};
const get_spells_slots = (casterLvl, pactMagicLvl) => {
const casterSlots = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 0, 0, 0, 0],
[3, 0, 0, 0, 0, 0, 0, 0, 0],
[4, 2, 0, 0, 0, 0, 0, 0, 0],
[4, 3, 0, 0, 0, 0, 0, 0, 0],
[4, 3, 2, 0, 0, 0, 0, 0, 0],
[4, 3, 3, 0, 0, 0, 0, 0, 0],
[4, 3, 3, 1, 0, 0, 0, 0, 0],
[4, 3, 3, 2, 0, 0, 0, 0, 0],
[4, 3, 3, 3, 1, 0, 0, 0, 0],
[4, 3, 3, 3, 2, 0, 0, 0, 0],
[4, 3, 3, 3, 2, 1, 0, 0, 0],
[4, 3, 3, 3, 2, 1, 0, 0, 0],
[4, 3, 3, 3, 2, 1, 1, 0, 0],
[4, 3, 3, 3, 2, 1, 1, 0, 0],
[4, 3, 3, 3, 2, 1, 1, 1, 0],
[4, 3, 3, 3, 2, 1, 1, 1, 0],
[4, 3, 3, 3, 2, 1, 1, 1, 1],
[4, 3, 3, 3, 3, 1, 1, 1, 1],
[4, 3, 3, 3, 3, 2, 1, 1, 1],
[4, 3, 3, 3, 3, 2, 2, 1, 1],
];
const pactMagicSlots = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 0, 0, 0, 0],
[0, 0, 0, 0, 4, 0, 0, 0, 0],
[0, 0, 0, 0, 4, 0, 0, 0, 0],
[0, 0, 0, 0, 4, 0, 0, 0, 0],
[0, 0, 0, 0, 4, 0, 0, 0, 0],
];
return casterSlots[casterLvl].map((slots, level) => {
return slots + pactMagicSlots[pactMagicLvl][level];
});
};
const update_spell_slots = () => {
getAttrs(
[
"use_spell_points",
"lvl1_slots_mod",
"lvl2_slots_mod",
"lvl3_slots_mod",
"lvl4_slots_mod",
"lvl5_slots_mod",
"lvl6_slots_mod",
"lvl7_slots_mod",
"lvl8_slots_mod",
"lvl9_slots_mod",
"caster_level",
],
(casterInfo) => {
get_class_level(characterClasses().filter({ caster: "pact" }).get(true), (pactMagicLvl) => {
const usingSPellPoints = casterInfo.use_spell_points && casterInfo.use_spell_points == 1 ? true : false;
const casterLvl = usingSPellPoints
? 0
: casterInfo["caster_level"] && !isNaN(parseInt(casterInfo["caster_level"], 10))
? parseInt(casterInfo["caster_level"], 10)
: 0;
const totalSlots = get_spells_slots(casterLvl, pactMagicLvl);
let update = {};
if (casterLvl > 0 || pactMagicLvl > 0) {
for (let i = 1; i <= 9; i++) {
update[`lvl${i}_slots_total`] = totalSlots[i - 1];
}
} else {
for (let i = 1; i <= 9; i++) {
update[`lvl${i}_slots_total`] = 0;
}
}
for (let [key, value] of Object.entries(update)) {
const mod = parseInt(casterInfo[`${key.split("_total")[0]}_mod`], 10) || 0;
update[key] = value + mod;
}
setAttrs(update, { silent: true });
});
}
);
};
var update_pb = function () {
callbacks = [];
getAttrs(["level", "pb_type", "pb_custom"], function (v) {
var update = {};
var pb = 2;
var lvl = parseInt(v["level"], 10);
if (lvl < 5) {
pb = "2";
} else if (lvl < 9) {
pb = "3";
} else if (lvl < 13) {
pb = "4";
} else if (lvl < 17) {
pb = "5";
} else {
pb = "6";
}
var jack = Math.floor(pb / 2);
if (v["pb_type"] === "die") {
update["jack"] = "d" + pb;
update["pb"] = "d" + pb * 2;
update["pbd_safe"] = "cs0cf0";
} else if (v["pb_type"] === "custom" && v["pb_custom"] && v["pb_custom"] != "") {
update["pb"] = v["pb_custom"];
update["jack"] = !isNaN(parseInt(v["pb_custom"], 10)) ? Math.floor(parseInt(v["pb_custom"], 10) / 2) : jack;
update["pbd_safe"] = "";
} else {
update["pb"] = pb;
update["jack"] = jack;
update["pbd_safe"] = "";
}
callbacks.push(function () {
update_attacks("all");
});
callbacks.push(function () {
update_spell_info();
});
callbacks.push(function () {
update_jack_attr();
});
callbacks.push(function () {
update_initiative();
});
callbacks.push(function () {
update_tool("all");
});
callbacks.push(function () {
update_all_saves();
});
callbacks.push(function () {
update_skills([
"athletics",
"acrobatics",
"sleight_of_hand",
"stealth",
"arcana",
"history",
"investigation",
"nature",
"navigation",
"religion",
"animal_handling",
"insight",
"medicine",
"perception",
"survival",
"deception",
"intimidation",
"performance",
"persuasion",
]);
});
setAttrs(update, { silent: true }, function () {
callbacks.forEach(function (callback) {
callback();
});
});
});
};
var update_jack_attr = function () {
var update = {};
getAttrs(["jack_of_all_trades", "jack"], function (v) {
if (v["jack_of_all_trades"] && v["jack_of_all_trades"] != 0) {
update["jack_bonus"] = "+" + v["jack"];
update["jack_attr"] = "+" + v["jack"] + "@{pbd_safe}";
} else {
update["jack_bonus"] = "";
update["jack_attr"] = "";
}
setAttrs(update, { silent: true });
});
};
var update_spell_info = function (attr) {
var update = {};
getAttrs(
[
"spellcasting_ability",
"spell_dc_mod",
"globalmagicmod",
"strength_mod",
"dexterity_mod",
"constitution_mod",
"intelligence_mod",
"wisdom_mod",
"charisma_mod",
],
function (v) {
if (attr && v["spellcasting_ability"] && v["spellcasting_ability"].indexOf(attr) === -1) {
return;
}
if (!v["spellcasting_ability"] || (v["spellcasting_ability"] && v["spellcasting_ability"] === "0*")) {
update["spell_attack_bonus"] = "0";
update["spell_save_dc"] = "0";
var callback = function () {
update_attacks("spells");
};
setAttrs(update, { silent: true }, callback);
return;
}
var attr = attr ? attr : "";
var ability = parseInt(v[v["spellcasting_ability"].substring(2, v["spellcasting_ability"].length - 2)], 10);
var spell_mod = v["globalmagicmod"] && !isNaN(parseInt(v["globalmagicmod"], 10)) ? parseInt(v["globalmagicmod"], 10) : 0;
var atk = v["globalmagicmod"] && !isNaN(parseInt(v["globalmagicmod"], 10)) ? ability + parseInt(v["globalmagicmod"], 10) : ability;
var dc = v["spell_dc_mod"] && !isNaN(parseInt(v["spell_dc_mod"], 10)) ? 8 + ability + parseInt(v["spell_dc_mod"], 10) : 8 + ability;
var itemfields = ["pb_type", "pb"];
getSectionIDs("repeating_inventory", function (idarray) {
_.each(idarray, function (currentID, i) {
itemfields.push("repeating_inventory_" + currentID + "_equipped");
itemfields.push("repeating_inventory_" + currentID + "_itemmodifiers");
});
getAttrs(itemfields, function (v) {
_.each(idarray, function (currentID) {
if (
(!v["repeating_inventory_" + currentID + "_equipped"] || v["repeating_inventory_" + currentID + "_equipped"] === "1") &&
v["repeating_inventory_" + currentID + "_itemmodifiers"] &&
v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().indexOf("spell" > -1)
) {
var mods = v["repeating_inventory_" + currentID + "_itemmodifiers"].toLowerCase().split(",");
_.each(mods, function (mod) {
if (mod.indexOf("spell attack") > -1) {
var substr = mod.slice(mod.lastIndexOf("spell attack") + "spell attack".length);
atk = substr && substr.length > 0 && !isNaN(parseInt(substr, 10)) ? atk + parseInt(substr, 10) : atk;
spell_mod = substr && substr.length > 0 && !isNaN(parseInt(substr, 10)) ? spell_mod + parseInt(substr, 10) : spell_mod;
}
if (mod.indexOf("spell dc") > -1) {
var substr = mod.slice(mod.lastIndexOf("spell dc") + "spell dc".length);
dc = substr && substr.length > 0 && !isNaN(parseInt(substr, 10)) ? dc + parseInt(substr, 10) : dc;
}
});
}
});
if (v["pb_type"] && v["pb_type"] === "die") {
atk = atk + "+" + v["pb"];
//Fixing Spell DC when using type Die
//By Miguel
//Used to be equals to: dc + parseInt(v["pb"].substring(1), 10) / 2
dc = dc + "+" + v["pb"];
} else {
atk = parseInt(atk, 10) + parseInt(v["pb"], 10);
dc = parseInt(dc, 10) + parseInt(v["pb"], 10);
}
update["spell_attack_mod"] = spell_mod;
update["spell_attack_bonus"] = atk;
update["spell_save_dc"] = dc;
var callback = function () {
update_attacks("spells");
};
setAttrs(update, { silent: true }, callback);
});
});
}
);
};
var update_passive_perception = function () {
getAttrs(["pb_type", "passiveperceptionmod", "perception_bonus"], function (v) {
var passive_perception = 10;
var mod = !isNaN(parseInt(v["passiveperceptionmod"], 10)) ? parseInt(v["passiveperceptionmod"], 10) : 0;
var bonus = !isNaN(parseInt(v["perception_bonus"], 10)) ? parseInt(v["perception_bonus"], 10) : 0;
if (
v["pb_type"] &&
v["pb_type"] === "die" &&
v["perception_bonus"] &&
isNaN(v["perception_bonus"]) &&
v["perception_bonus"].indexOf("+") > -1
) {
var pieces = v["perception_bonus"].split(/\+|d/);
var base = !isNaN(parseInt(pieces[0], 10)) ? parseInt(pieces[0], 10) : 0;
var num_dice = !isNaN(parseInt(pieces[1], 10)) ? parseInt(pieces[1], 10) : 1;
var half_pb_die = !isNaN(parseInt(pieces[2], 10)) ? parseInt(pieces[2], 10) / 2 : 2;
bonus = base + num_dice * half_pb_die;
}
passive_perception = passive_perception + bonus + mod;
setAttrs({ passive_wisdom: passive_perception });
});
};
var update_race_display = function () {
getAttrs(["race", "subrace"], function (v) {
var final_race = "";
final_race = v.subrace ? v.subrace : v.race;
if (v.race.toLowerCase() === "dragonborn") {
final_race = v.race;
}
setAttrs({ race_display: final_race });
});
};
var organize_section_proficiencies = function () {
getSectionIDs("proficiencies", function (ids) {
var attribs = ["_reporder_repeating_proficiencies"];
_.each(ids, function (id) {
attribs.push("repeating_proficiencies_" + id + "_prof_type");
attribs.push("repeating_proficiencies_" + id + "_name");
});
getAttrs(attribs, function (v) {
var final_array = _(ids)
.chain()
.sortBy(function (id) {
return v["repeating_proficiencies_" + id + "_name"];
})
.sortBy(function (id) {
return v["repeating_proficiencies_" + id + "_prof_type"];
})
.value();
_.each(final_array, function (id) {});
if (final_array && final_array.length > 0) {
setSectionOrder("proficiencies", final_array);
}
});
});
};
const update_challenge = () => {
getAttrs(["npc_challenge"], (v) => {
let update = {};
const challengeRatingsXP = {
0: "10",
"1/8": "25",
"1/4": "50",
"1/2": "100",
1: "200",
2: "450",
3: "700",
4: "1100",
5: "1800",
6: "2300",
7: "2900",
8: "3900",
9: "5000",
10: "5900",
11: "7200",
12: "8400",
13: "10000",
14: "11500",
15: "13000",
16: "15000",
17: "18000",
18: "20000",
19: "22000",
20: "25000",
21: "33000",
22: "41000",
23: "50000",
24: "62000",
25: "75000",
26: "90000",
27: "105000",
28: "120000",
29: "135000",
30: "155000",
};
const xp = parseInt(challengeRatingsXP[v.npc_challenge]) || 0;
const pb =
xp <= 1100
? 2
: xp <= 3900
? 3
: xp <= 8400
? 4
: xp <= 15000
? 5
: xp <= 25000
? 6
: xp <= 62000
? 7
: xp <= 120000
? 8
: xp <= 155000
? 9
: 0;
update["npc_xp"] = xp;
update["pb_custom"] = pb;
update["pb_type"] = "custom";
setAttrs(update, { silent: true }, function () {
update_pb();
});
});
};
const update_linked_vehicle_actions = () => {
const weapons = {};
getSectionIDs("vehicleweapon", (ids) => {
const nameAttrs = ids.map((id) => {
return `repeating_vehicleweapon_${id}_name`;
});
getAttrs(nameAttrs, (values) => {
Object.keys(values).forEach((attr) => {
if (values[attr]) {
const weaponID = SheetUtils.getUID(attr);
weapons[values[attr]] = weaponID;
}
});
getSectionIDs("vehicleactions", (ids) => {
const idAttrs = ids.map((id) => {
return `repeating_vehicleactions_${id}_weaponid`;
});
getAttrs(idAttrs, (values) => {
let update = {};
Object.keys(values).forEach((attr) => {
if (values[attr]) {
const actionID = SheetUtils.getUID(attr);
if (Object.values(weapons).indexOf(values[attr]) > -1) {
//Valid linked weapon, do nothing.
} else if (weapons.hasOwnProperty(values[attr])) {
update[`repeating_vehicleactions_${actionID}_weaponid`] = weapons[values[attr]];
} else {
update[`repeating_vehicleactions_${actionID}_weaponid`] = "";
update[`repeating_vehicleactions_${actionID}_weaponname`] = "";
update[`repeating_vehicleactions_${actionID}_color`] = "";
}
}
});
setAttrs(update, { silent: true });
});
});
});
});
};
const update_npc_saves = () => {
const list = [
"npc_str_save_base",
"npc_dex_save_base",
"npc_con_save_base",
"npc_int_save_base",
"npc_wis_save_base",
"npc_cha_save_base",
];
const type = "save";
update_npc_lists(list, type);
};
const update_npc_skills = () => {
const list = [
"npc_acrobatics_base",
"npc_animal_handling_base",
"npc_arcana_base",
"npc_athletics_base",
"npc_deception_base",
"npc_history_base",
"npc_insight_base",
"npc_intimidation_base",
"npc_investigation_base",
"npc_medicine_base",
"npc_nature_base",
"npc_navigation_base",
"npc_perception_base",
"npc_performance_base",
"npc_persuasion_base",
"npc_religion_base",
"npc_sleight_of_hand_base",
"npc_stealth_base",
"npc_survival_base",
];
const type = "skills";
update_npc_lists(list, type);
};
const update_npc_lists = (list, type) => {
getAttrs(list, function (v) {
let update = {};
let last_save = 0;
let npc_flag = 0;
_.each(list.reverse(), (base) => {
const attr = base.slice(4, -5); //Remove npc_ and _base
let item = parseInt(v[`${base}`], 10);
// CSS will add comma :after 2 & 4 and +/- :before
// 1 = Positive Number, 2 = Last Number, 3 = Negative Number, 4 = Last Negative Number
if ((v[`${base}`] && !isNaN(item)) || v[`${base}`] === 0) {
if (last_save === 0) {
last_save = 1;
item_flag = item < 0 ? 4 : 2;
} else {
item_flag = item < 0 ? 3 : 1;
}
} else {
item_flag = 0;
item = "";
}
update[`npc_${attr}_flag`] = item_flag;
update[`npc_${attr}`] = item;
npc_flag += item_flag;
});
const flagAttr = type === "save" ? "saving" : type;
update[`npc_${flagAttr}_flag`] = npc_flag === 0 ? "" : npc_flag;
setAttrs(update, { silent: true });
});
};
var update_npc_action = function (update_id, fieldset) {
if (update_id.substring(0, 1) === "-" && update_id.length === 20) {
do_update_npc_action([update_id], fieldset);
} else if (update_id === "all") {
var mythic_array = [];
var legendary_array = [];
var bonus_array = [];
var actions_array = [];
getSectionIDs("repeating_npcaction-l", function (idarray) {
legendary_array = idarray;
if (legendary_array.length > 0) {
do_update_npc_action(legendary_array, "npcaction-l");
}
getSectionIDs("repeating_npcaction-m", function (idarray) {
mythic_array = idarray;
if (mythic_array.length > 0) {
do_update_npc_action(mythic_array, "npcaction-m");
}
getSectionIDs("repeating_vehicleactions", function (idarray) {
vehicle_array = idarray;
if (vehicle_array.length > 0) {
do_update_npc_action(vehicle_array, "vehicleactions");
}
getSectionIDs("repeating_npcbonusaction", function (idarray) {
bonus_array = idarray;
if (bonus_array.length > 0) {
do_update_npc_action(bonus_array, "npcbonusaction");
}
getSectionIDs("repeating_npcaction", function (idarray) {
actions_array = idarray.filter(function (i) {
return legendary_array.indexOf(i) < 0 && mythic_array.indexOf(i) < 0 && bonus_array.indexOf(i) < 0;
});
if (actions_array.length > 0) {
do_update_npc_action(actions_array, "npcaction");
}
});
});
});
});
});
}
};
var do_update_npc_action = function (action_array, fieldset) {
var repvar = `repeating_${fieldset}_`;
var action_attribs = ["dtype"];
_.each(action_array, function (actionid) {
action_attribs.push(repvar + actionid + "_attack_flag");
action_attribs.push(repvar + actionid + "_attack_type");
action_attribs.push(repvar + actionid + "_attack_range");
action_attribs.push(repvar + actionid + "_attack_target");
action_attribs.push(repvar + actionid + "_attack_tohit");
action_attribs.push(repvar + actionid + "_attack_damage");
action_attribs.push(repvar + actionid + "_attack_damagetype");
action_attribs.push(repvar + actionid + "_attack_damage2");
action_attribs.push(repvar + actionid + "_attack_damagetype2");
});
getAttrs(action_attribs, function (v) {
_.each(action_array, function (actionid) {
var callbacks = [];
var update = {};
var onhit = "";
var damage_flag = "";
var range = "";
var attack_flag = v[repvar + actionid + "_attack_flag"] && v[repvar + actionid + "_attack_flag"] != "0" ? "{{attack=1}}" : "";
var tohit =
v[repvar + actionid + "_attack_tohit"] && isNaN(parseInt(v[repvar + actionid + "_attack_tohit"], 10)) === false
? parseInt(v[repvar + actionid + "_attack_tohit"], 10)
: 0;
if (v[repvar + actionid + "_attack_type"] && v[repvar + actionid + "_attack_range"]) {
if (v[repvar + actionid + "_attack_type"] === "Melee") {
var rangetype = "Reach";
} else {
var rangetype = "Range";
}
range = ", " + rangetype + " " + v[repvar + actionid + "_attack_range"];
}
var target =
v[repvar + actionid + "_attack_target"] && v[repvar + actionid + "_attack_target"] != ""
? ", " + v[repvar + actionid + "_attack_target"]
: "";
var attack_tohitrange = "+" + tohit + range + target;
var dmg1 =
v[repvar + actionid + "_attack_damage"] && v[repvar + actionid + "_attack_damage"] != ""
? v[repvar + actionid + "_attack_damage"]
: "";
var dmg1type =
v[repvar + actionid + "_attack_damagetype"] && v[repvar + actionid + "_attack_damagetype"] != ""
? " " + v[repvar + actionid + "_attack_damagetype"]
: "";
var dmg2 =
v[repvar + actionid + "_attack_damage2"] && v[repvar + actionid + "_attack_damage2"] != ""
? v[repvar + actionid + "_attack_damage2"]
: "";
var dmg2type =
v[repvar + actionid + "_attack_damagetype2"] && v[repvar + actionid + "_attack_damagetype2"] != ""
? " " + v[repvar + actionid + "_attack_damagetype2"]
: "";
var dmgspacer = dmg1 != "" && dmg2 != "" ? " plus " : "";
var parsed_dmg1 = parse_roll_string(dmg1);
var parsed_dmg2 = parse_roll_string(dmg2);
if (dmg1 != "") {
onhit = onhit + parsed_dmg1.avg + " (" + dmg1 + ")" + dmg1type + " damage";
}
dmgspacer = dmg1 != "" && dmg2 != "" ? " plus " : "";
onhit = onhit + dmgspacer;
if (dmg2 != "") {
onhit = onhit + parsed_dmg2.avg + " (" + dmg2 + ")" + dmg2type + " damage";
}
if (dmg1 != "" || dmg2 != "") {
damage_flag = damage_flag + "{{damage=1}} ";
}
if (dmg1 != "") {
damage_flag = damage_flag + "{{dmg1flag=1}} ";
}
if (dmg2 != "") {
damage_flag = damage_flag + "{{dmg2flag=1}} ";
}
var crit1 = "";
if (parsed_dmg1.rolls.length > 0) {
parsed_dmg1.rolls.forEach(function (value) {
crit1 += parsed_dmg1.array[value] + "+";
});
crit1 = crit1.substring(0, crit1.length - 1);
}
var crit2 = "";
if (parsed_dmg2.rolls.length > 0) {
parsed_dmg2.rolls.forEach(function (value) {
crit2 += parsed_dmg2.array[value] + "+";
});
crit2 = crit2.substring(0, crit2.length - 1);
}
var rollbase = "";
if (v.dtype === "full") {
if (attack_flag) {
rollbase =
"@{wtype}&{template:npcfullatk} " +
attack_flag +
" @{damage_flag} @{npc_name_flag} {{rname=@{name}}} {{r1=[[@{d20}+(@{attack_tohit}+0)]]}} @{rtype}+(@{attack_tohit}+0)]]}} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}} {{description=@{show_desc}}} @{charname_output}";
} else {
if (dmg1) {
rollbase =
"@{wtype}&{template:dmgaction} @{damage_flag} @{npc_name_flag} {{rname=@{name}}} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}} {{description=@{show_desc}}} @{charname_output}";
} else {
rollbase = "@{wtype}&{template:npcaction} @{npc_name_flag} {{rname=@{name}}} {{description=@{show_desc}}} @{charname_output}";
}
}
} else if (v[repvar + actionid + "_attack_flag"] && v[repvar + actionid + "_attack_flag"] != "0") {
rollbase = `@{wtype}&{template:npcatk} ${attack_flag} @{damage_flag} @{npc_name_flag} {{rname=[@{name}](~repeating_${fieldset}_npc_dmg)}} {{rnamec=[@{name}](~repeating_${fieldset}_npc_crit)}} {{type=[^{attack-u}](~repeating_${fieldset}_npc_dmg)}} {{typec=[^{attack-u}](~repeating_${fieldset}_npc_crit)}} {{r1=[[@{d20}+(@{attack_tohit}+0)]]}} @{rtype}+(@{attack_tohit}+0)]]}} @{charname_output}`;
} else if (dmg1 || dmg2) {
if (attack_flag) {
rollbase =
"@{wtype}&{template:npcdmg} @{damage_flag} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}} {{description=@{show_desc}}} @{charname_output}";
} else {
rollbase =
"@{wtype}&{template:dmgaction} @{damage_flag} @{npc_name_flag} {{rname=@{name}}} {{dmg1=[[@{attack_damage}+0]]}} {{dmg1type=@{attack_damagetype}}} {{dmg2=[[@{attack_damage2}+0]]}} {{dmg2type=@{attack_damagetype2}}} {{crit1=[[@{attack_crit}+0]]}} {{crit2=[[@{attack_crit2}+0]]}} {{description=@{show_desc}}} @{charname_output}";
}
} else {
rollbase = "@{wtype}&{template:npcaction} @{npc_name_flag} {{rname=@{name}}} {{description=@{show_desc}}} @{charname_output}";
}
update[repvar + actionid + "_attack_tohitrange"] = attack_tohitrange;
update[repvar + actionid + "_attack_onhit"] = onhit;
update[repvar + actionid + "_damage_flag"] = damage_flag;
update[repvar + actionid + "_attack_crit"] = crit1;
update[repvar + actionid + "_attack_crit2"] = crit2;
update[repvar + actionid + "_rollbase"] = rollbase;
setAttrs(update, { silent: true });
});
});
};
var parse_roll_string = function (rollstring) {
var out = { array: [], avg: 0, rolls: [] };
if (!rollstring || rollstring === "") {
return out;
}
var rs_regex_initial = /(\-?\d+(?:d\d+)?)/gi;
var rs_regex_repeating = /([\+\-])(\-?\d+(?:d\d+)?)/gi;
var rs_nospace = rollstring.replace(/\s/g, "");
var rs_initial = rs_regex_initial.exec(rs_nospace);
if (rs_initial) {
out.array.push(rs_initial[1]);
rs_regex_repeating.lastIndex = rs_regex_initial.lastIndex;
var rs_repeating;
while ((rs_repeating = rs_regex_repeating.exec(rs_nospace))) {
out.array.push(rs_repeating[1], rs_repeating[2]);
}
}
var add = true;
var dice_regex = /(\d+)d(\d+)/i;
var dice;
out.array.forEach(function (value, index, array) {
if (value === "+") {
add = true;
} else if (value === "-") {
add = false;
} else if ((dice = dice_regex.exec(value))) {
// this value is a die roll
var dice_avg = Math.floor(+dice[1] * (+dice[2] / 2 + 0.5));
out.avg += add ? dice_avg : -dice_avg;
out.rolls.push(index);
} else {
// this value is a number
out.avg += add ? +value : -+value;
}
});
return out;
};
var get_class_level = function (classNames, callback) {
getAttrs(
[
"class",
"base_level",
"multiclass1_flag",
"multiclass1",
"multiclass1_lvl",
"multiclass2_flag",
"multiclass2",
"multiclass2_lvl",
"multiclass3_flag",
"multiclass3",
"multiclass3_lvl",
],
function (attrs) {
const classList = Array.isArray(classNames)
? classNames.map((entry) => {
return entry.toLowerCase();
})
: [classNames.toLowerCase()];
const isInList = (attr) => {
return classList.includes(attr.toLowerCase());
};
if (isInList(attrs["class"])) {
callback(attrs.base_level);
} else if (attrs.multiclass1_flag && attrs.multiclass1_flag !== "0" && isInList(attrs["multiclass1"])) {
callback(attrs.multiclass1_lvl);
} else if (attrs.multiclass2_flag && attrs.multiclass2_flag !== "0" && isInList(attrs["multiclass2"])) {
callback(attrs.multiclass2_lvl);
} else if (attrs.multiclass3_flag && attrs.multiclass3_flag !== "0" && isInList(attrs["multiclass3"])) {
callback(attrs.multiclass3_lvl);
} else {
callback("0");
}
}
);
};
var update_globaldamage = function (callback) {
getSectionIDs("damagemod", function (ids) {
if (ids) {
var fields = {};
var attr_name_list = [];
ids.forEach(function (id) {
fields[id] = {};
attr_name_list.push(
`repeating_damagemod_${id}_global_damage_active_flag`,
`repeating_damagemod_${id}_global_damage_name`,
`repeating_damagemod_${id}_global_damage_damage`,
`repeating_damagemod_${id}_global_damage_critical_damage`,
`repeating_damagemod_${id}_global_damage_type`
);
});
getAttrs(attr_name_list, function (attrs) {
var regex = /^repeating_damagemod_(.+)_global_damage_(active_flag|name|critical_damage|damage|type)$/;
_.each(attrs, function (obj, name) {
var r = regex.exec(name);
if (r) {
fields[r[1]][r[2]] = obj;
}
});
var update = { global_damage_mod_roll: "", global_damage_mod_crit: "", global_damage_mod_type: "" };
const simpleDamageRegex = new RegExp(/([0-9]*d[0-9]+)|(\[.+?\])|\+|\-|\/|\*| /gi);
_.each(fields, function (element) {
if (element.active_flag != "0") {
if (element.name && element.damage) {
update["global_damage_mod_roll"] += element.damage + "[" + element.name + "]" + "+";
}
if (element.name && element.critical_damage) {
const criticalDamage = element.critical_damage;
const criticalSource = element.name;
update["global_damage_mod_crit"] = update["global_damage_mod_crit"]
? `${update["global_damage_mod_crit"]} + ${criticalDamage}[${criticalSource}]`
: `${criticalDamage}[${criticalSource}]`;
update["global_damage_mod_crit"] = update["global_damage_mod_crit"].replace(/\+$/gi, "").trim();
} else if (element.name && element.damage) {
const criticalDamage = element.damage;
const criticalSource = element.name;
const simpleDamage = element.damage.replace(simpleDamageRegex, "").length == 0;
if (simpleDamage) {
update["global_damage_mod_crit"] = update["global_damage_mod_crit"]
? `${update["global_damage_mod_crit"]} + ${criticalDamage}[${criticalSource}]`
: `${criticalDamage}[${criticalSource}]`;
update["global_damage_mod_crit"] = update["global_damage_mod_crit"].replace(/\+$/gi, "").trim();
}
//removing this regex
// Remove any non-roll damage modifiers from the global damage modifier for the crit rolls
// Will also remove any labels attached to these non-roll damage modifiers
//.replace(/(?:[+\-*\/%]|\*\*|^)\s*\d+(?:\[.*?])?(?!\d?d\d+)/gi, '')
// If what was just replace removed the first damage modifier, remove any now precending plus signs
//.replace(/(?:^\+)/i, '');
}
if (element.type) {
update["global_damage_mod_type"] += element.type + "/";
}
}
});
update["global_damage_mod_roll"] = update["global_damage_mod_roll"].replace(/\+(?=$)/, "");
update["global_damage_mod_type"] = update["global_damage_mod_type"].replace(/\/(?=$)/, "");
setAttrs(update, { silent: true }, function () {
update_attacks("all");
if (typeof callback == "function") callback();
});
});
}
});
};
var update_globalattack = function (callback) {
getSectionIDs("tohitmod", function (ids) {
if (ids) {
var fields = {};
var attr_name_list = [];
ids.forEach(function (id) {
fields[id] = {};
attr_name_list.push(
`repeating_tohitmod_${id}_global_attack_active_flag`,
`repeating_tohitmod_${id}_global_attack_roll`,
`repeating_tohitmod_${id}_global_attack_name`
);
});
getAttrs(attr_name_list, function (attrs) {
var regex = /^repeating_tohitmod_(.+)_global_attack_(active_flag|roll|name)$/;
_.each(attrs, function (obj, name) {
var r = regex.exec(name);
if (r) {
fields[r[1]][r[2]] = obj;
}
});
var update = { global_attack_mod: "" };
_.each(fields, function (element) {
if (element.active_flag != "0") {
if (element.roll && element.roll !== "") {
update["global_attack_mod"] += element.roll + "[" + element.name + "]" + "+";
}
}
});
if (update["global_attack_mod"] !== "") {
update["global_attack_mod"] = "[[" + update["global_attack_mod"].replace(/\+(?=$)/, "") + "]]";
}
setAttrs(update, { silent: true }, function () {
if (typeof callback == "function") callback();
});
});
}
});
};
var update_globalsaves = function (callback) {
getSectionIDs("savemod", function (ids) {
if (ids) {
var fields = {};
var attr_name_list = [];
ids.forEach(function (id) {
fields[id] = {};
attr_name_list.push(
`repeating_savemod_${id}_global_save_active_flag`,
`repeating_savemod_${id}_global_save_roll`,
`repeating_savemod_${id}_global_save_name`
);
});
getAttrs(attr_name_list, function (attrs) {
var regex = /^repeating_savemod_(.+)_global_save_(active_flag|roll|name)$/;
_.each(attrs, function (obj, name) {
var r = regex.exec(name);
if (r) {
fields[r[1]][r[2]] = obj;
}
});
var update = { global_save_mod: "" };
_.each(fields, function (element) {
if (element.active_flag != "0") {
if (element.roll && element.roll !== "") {
update["global_save_mod"] += element.roll + "[" + element.name + "]" + "+";
}
}
});
if (update["global_save_mod"] !== "") {
update["global_save_mod"] = "[[" + update["global_save_mod"].replace(/\+(?=$)/, "") + "]]";
}
setAttrs(update, { silent: true }, function () {
if (typeof callback == "function") callback();
});
});
}
});
};
var update_globalskills = function (callback) {
getSectionIDs("skillmod", function (ids) {
if (ids) {
var fields = {};
var attr_name_list = [];
ids.forEach(function (id) {
fields[id] = {};
attr_name_list.push(
`repeating_skillmod_${id}_global_skill_active_flag`,
`repeating_skillmod_${id}_global_skill_roll`,
`repeating_skillmod_${id}_global_skill_name`
);
});
getAttrs(attr_name_list, function (attrs) {
var regex = /^repeating_skillmod_(.+)_global_skill_(active_flag|roll|name)$/;
_.each(attrs, function (obj, name) {
var r = regex.exec(name);
if (r) {
fields[r[1]][r[2]] = obj;
}
});
var update = { global_skill_mod: "" };
_.each(fields, function (element) {
if (element.active_flag != "0") {
if (element.roll && element.roll !== "") {
update["global_skill_mod"] += element.roll + "[" + element.name + "]" + "+";
}
}
});
if (update["global_skill_mod"] !== "") {
update["global_skill_mod"] = "[[" + update["global_skill_mod"].replace(/\+(?=$)/, "") + "]]";
}
setAttrs(update, { silent: true }, function () {
if (typeof callback == "function") callback();
});
});
}
});
};
var check_l1_mancer = function () {
getAttrs(
[
"class",
"base_level",
"strength_base",
"dexterity_base",
"constitution_base",
"intelligence_base",
"wisdom_base",
"charisma_base",
"l1mancer_status",
"version",
"charactermancer_step",
],
function (v) {
if (!v["version"] || parseFloat(v["version"]) < 2.2) {
return;
}
if (v["l1mancer_status"] && v["l1mancer_status"] === "completed") {
return;
}
if (v["charactermancer_step"] && v["charactermancer_step"].split("-")[0] == "l1") {
startCharactermancer(v["charactermancer_step"]);
} else {
if (v["l1mancer_status"] && v["l1mancer_status"] === "relaunch") {
startCharactermancer("l1-welcome");
} else {
setAttrs({ mancer_confirm_flag: 1 });
}
}
}
);
};
let LPMancerStep = false;
var check_lp_mancer = function () {
getAttrs(
[
"class",
"base_level",
"strength_base",
"dexterity_base",
"constitution_base",
"intelligence_base",
"wisdom_base",
"charisma_base",
"l1mancer_status",
"lpmancer_status",
"version",
"charactermancer_step",
],
function (v) {
if (v["lpmancer_status"] === "active" && v["charactermancer_step"] && v["charactermancer_step"].split("-")[0] == "lp") {
LPMancerStep = v["charactermancer_step"];
startCharactermancer("lp-dataintegrity");
}
}
);
};
on("page:lp-dataintegrity", (eventinfo) => {
if (!LPMancerStep) return;
verifyMancerIntegrity(() => {
changeCharmancerPage(LPMancerStep, () => {
LPMancerStep = false;
});
});
});
const verifyMancerIntegrity = (callback) => {
const data = getCharmancerData();
const invalidSlides = [];
Object.keys(data).forEach((slide) => {
if (!data[slide].values) return;
const invalid = Object.keys(data[slide].values).reduce((accumulator, currentValue) => {
return accumulator === true ? true : currentValue.includes(" ") ? true : false;
}, false);
if (invalid) {
console.log(`${slide} has invalid data, deleting it!`);
invalidSlides.push(slide);
}
});
if (invalidSlides.length > 0) {
deleteCharmancerData(invalidSlides, () => {
callback();
});
} else {
callback();
}
};
on("mancer:cancel", function (eventinfo) {
if (!eventinfo["value"]) {
return;
}
var update = {};
if (eventinfo["value"] === "l1-welcome" || eventinfo["value"] === "l1-cancel") {
update["l1mancer_status"] = "completed";
update["charactermancer_step"] = "l1-welcome";
deleteCharmancerData(["l1-welcome", "l1-race", "l1-class", "l1-abilities", "l1-background", "l1-equipment", "l1-spells", "l1-summary"]);
} else if (eventinfo["value"].substring(0, 3) === "l1-") {
update["l1mancer_status"] = eventinfo["value"];
} else if (eventinfo["value"] === "lp-welcome" || eventinfo["value"] === "lp-cancel") {
update["lpmancer_status"] = "";
update["charactermancer_step"] = "";
deleteCharmancerData(["lp-welcome", "lp-levels", "lp-choices", "lp-asi", "lp-spells", "lp-summary"]);
} else if (eventinfo["value"].substring(0, 3) === "lp-") {
update["charactermancer_step"] = "";
update["lpmancer_status"] = "";
}
setAttrs(update);
});
on("change:hitdie_final", function (eventinfo) {
setHitdieroll();
});
on("sheet:opened", function (eventinfo) {
setHitdieroll();
update_leveler_display();
});
var setHitdieroll = function () {
getAttrs(["npc", "hitdietype"], function (v) {
if ((v["npc"] && v["npc"] == 1) || !v["hitdietype"]) return;
v["hitdietype"] = globalAttributesByCategory.hitDieTypes.indexOf(`d${v["hitdietype"]}`) > -1 ? v["hitdietype"] : "4";
setAttrs({ hitdieroll: v["hitdietype"] });
});
};
var flagManager = new FlagManager("ui_flags");
[
{ section: "npcaction", button: "edit" },
{ section: "npcbonusaction", button: "edit" },
{ section: "vehicleactions", button: "edit" },
{ section: "vehicleweapon-bow", button: "edit" },
{ section: "vehicleweapon-port", button: "edit" },
{ section: "vehicleweapon-starboard", button: "edit" },
{ section: "vehicleweapon-stern", button: "edit" },
{ section: "vehicleofficer", button: "edit" },
{ section: "vehicleupgrade", button: "edit" },
{ section: "vehicleammo", button: "edit" },
{ section: "npctrait", button: "edit" },
{ section: "npcreaction", button: "edit" },
{ section: "npcaction-l", button: "edit" },
{ section: "npcaction-m", button: "edit" },
].forEach((flag) => {
on(`clicked:repeating_${flag.section}:${flag.button}`, function (eventInfo) {
SheetUtils.getSectionOrder(flag.section, (ids) => {
const uid = SheetUtils.getUID(eventInfo.sourceAttribute).toLowerCase();
const index = ids.indexOf(uid);
const flagName = `repeating_${flag.section}_${flag.button}_${index}_${uid}`;
console.log(flagName);
flagManager.toggleFlag(flagName);
});
});
on(`remove:repeating_${flag.section}`, function (eventInfo) {
flagManager.syncFlagsWithRepeatingSection(flag.section);
});
on(`change:_reporder:${flag.section}`, function (eventInfo) {
flagManager.syncFlagsWithRepeatingSection(flag.section);
});
on(`change:repeating_${flag.section}`, function (eventInfo) {
if(eventInfo.sourceAttribute.includes('repeating_vehicleammo') && (eventInfo.sourceAttribute.includes('quantity') || eventInfo.sourceAttribute.includes('loaded'))) return;
if (
!eventInfo.sourceType ||
eventInfo.sourceType !== "player" ||
(eventInfo.sourceAttribute && eventInfo.sourceAttribute.indexOf("_rollbase") > -1)
)
return;
if (/--silent$/g.test(eventInfo.sourceAttribute)) return;
SheetUtils.getSectionOrder(flag.section, (ids) => {
const uid = SheetUtils.getUID(eventInfo.sourceAttribute.toLowerCase());
const index = ids.indexOf(uid);
if (index === ids.length - 1) {
const flagName = `repeating_${flag.section}_${flag.button}_${index}_${uid}`;
flagManager.addFlag(flagName);
}
});
});
});
var showOptionalAttributeFields = function () {
const optionalAttributesClasses = SheetUtils.toLowerCase(abilityListEnabledOptionals).map((key) => `sheet-${key}_toggle`);
optionalAttributesClasses.forEach((attribute) => {
showChoices([attribute]);
});
};
var getAllSpellsIDS = function (callback) {
const levels = ["cantrip", "1", "2", "3", "4", "5", "6", "6", "8", "9"];
let currentLevel = 0;
let spells = [];
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 1;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 2;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 3;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 4;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 5;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 6;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 7;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 8;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
currentLevel = 9;
getSectionIDs(`spell-${levels[currentLevel]}`, (ids) => {
ids.forEach((id) => {
spells.push(`repeating_spell-${levels[currentLevel]}_${id}`);
});
callback(spells);
});
});
});
});
});
});
});
});
});
});
};
on("sheet:opened change:honor_toggle change:sanity_toggle", (eventInfo) => {
getAttrs(["honor_toggle", "sanity_toggle"], (values) => {
abilityListEnabledOptionals = SheetUtils.toCapitalCase(
Object.keys(values)
.filter((key) => {
return values[key] === "1";
})
.map((key) => key.replace("_toggle", ""))
);
abilityList = SheetUtils.toCapitalCase(globalAttributesByCategory.abilities).concat(abilityListEnabledOptionals);
});
});
globalAttributesByCategory.skills.all().forEach((skill) => {
on(`change:${skill}_bonus`, () => {
updateSkillRoll(skill);
});
});
globalAttributesByCategory.abilitiesWithAllOptionals.forEach((ability) => {
on(`change:${ability}_save_bonus`, () => {
updateSavingRoll(ability);
});
});
on("change:rtype change:reliable_talent change:pb", (eventInfo) => {
getSectionIDs("repeating_tool", (idarray) => {
updateSavingRolls(globalAttributesByCategory.abilitiesWithAllOptionals);
updateSkillRolls(globalAttributesByCategory.skills.all());
do_update_tool(idarray);
});
});
var updateSkillRoll = function (skill, imposeDisadvantage = [], callback) {
updateSkillRolls([skill], (imposeDisadvantage = []), callback);
};
var updateSavingRoll = function (saving, callback) {
updateSavingRolls([saving], callback);
};
const checkProficiency = function (values, prof) {
return values[prof] && values[prof].indexOf("@{pb}") > -1;
};
var updateSavingRolls = function (savings, callback) {
const attrs_prof = savings.map((saving) => {
return `${saving}_save_prof`;
});
const attrs_bonus = savings.map((saving) => {
return `${saving}_save_bonus`;
});
getAttrs([...attrs_prof, ...attrs_bonus, "reliable_talent", "rtype", "advantagetoggle", "queryadvantage", "pb"], (values) => {
const usingProficiencyDie = values.pb && String(values.pb).includes("d") ? true : false;
let rtype = values["rtype"].includes("@{advantagetoggle}") ? values["advantagetoggle"] : values["rtype"];
rtype = values["rtype"].includes("@{queryadvantage}") ? values["queryadvantage"] : values["rtype"];
let update = {};
savings.forEach((saving) => {
let roll;
const isProficient = checkProficiency(values, `${saving}_save_prof`);
if (usingProficiencyDie && isProficient) {
const proficiencyDie = values.pb.match(/^[0-9]/) ? values.pb : `${values.pb}`;
const savingBonus = String(values[`${saving}_save_bonus`])
.replace(proficiencyDie, "")
.replace(/\+{2,}|-{2,}|^[\+-]|[\+-]$/, "");
const rtypeString = rtype.includes("{{normal=1}}") ? "{{normal=1}} " : `${rtype}+${savingBonus}]]}} `;
const r3 = ` + [[${proficiencyDie}@{pbd_safe}[Proficiency]]]`;
roll = `@{wtype}&{template:simple3D} {{rname=^{${saving}-save-u}}} {{mod=${
values[`${saving}_save_bonus`]
}}} {{r1=[[@{d20}+${savingBonus}]]}} {{r3=${r3}}} ${rtypeString}{{global=@{global_save_mod}}} @{charname_output}`;
} else {
const rtypeString = rtype.includes("{{normal=1}}") ? "{{normal=1}} " : `${rtype}+@{${saving}_save_bonus}@{pbd_safe}]]}} `;
roll = `@{wtype}&{template:simple} {{rname=^{${saving}-save-u}}} {{mod=@{${saving}_save_bonus}}} {{r1=[[@{d20}+@{${saving}_save_bonus}@{pbd_safe}]]}} ${rtypeString}{{global=@{global_save_mod}}} @{charname_output}`;
}
update[`${saving}_save_roll`] = roll;
});
setAttrs(update, () => {
if (callback) callback();
});
});
};
var updateSkillRolls = function (skills, imposeDisadvantage, callback) {
const attrs_prof = skills.map((skill) => {
return `${skill}_prof`;
});
const attrs_bonus = skills.map((skill) => {
return `${skill}_bonus`;
});
const attrs_type = skills.map((skill) => {
return `${skill}_type`;
});
const attrs_currentroll = skills.map((skill) => {
return `${skill}_roll`;
});
const ability_mods = globalAttributesByCategory.abilities.map((ability) => {
return `${ability}_mod`;
});
getAttrs(
[
...attrs_prof,
...attrs_bonus,
...attrs_type,
...attrs_currentroll,
...ability_mods,
"reliable_talent",
"jack_of_all_trades",
"jack",
"rtype",
"advantagetoggle",
"queryadvantage",
"pb",
],
(values) => {
const usingProficiencyDie = values.pb && String(values.pb).includes("d") ? true : false;
let rtype = values["rtype"].includes("@{advantagetoggle}") ? values["advantagetoggle"] : values["rtype"];
rtype = values["rtype"].includes("@{queryadvantage}") ? values["queryadvantage"] : values["rtype"];
const query = rtype.indexOf("queryadvantage") > -1;
const isJack = values["jack_of_all_trades"] && values["jack_of_all_trades"] == "@{jack}";
let update = {};
const originalRtype = rtype;
skills.forEach((skill) => {
if (skill === "stealth" && imposeDisadvantage && imposeDisadvantage.indexOf(skill) > -1)
rtype = "{{imposed=(Heavy Armor)}} {{query=1}} {{disadvantage=1}} {{r2=[[@{d20}";
else if (skill === "stealth" && !imposeDisadvantage && values[`${skill}_roll`].includes("imposed="))
rtype = "{{imposed=(Heavy Armor)}} {{query=1}} {{disadvantage=1}} {{r2=[[@{d20}";
else rtype = originalRtype;
let roll;
const isProficient = checkProficiency(values, `${skill}_prof`);
if ((usingProficiencyDie && isProficient) || (usingProficiencyDie && isJack)) {
const die = !isProficient && isJack ? `${values["jack"]}` : `${values[`${skill}_type`]}${values.pb}`;
const proficiencyDie = die;
const jackSafeDie = !isProficient && isJack ? `floor(${values.pb}/2)` : proficiencyDie;
const skillBonus = String(values[`${skill}_bonus`])
.replace(proficiencyDie, "")
.replace(/\+{2,}|-{2,}|^[\+-]|[\+-]$/, "");
const rtypeString = rtype.includes("{{normal=1}}") ? "{{normal=1}} " : `${rtype}+${skillBonus}]]}} `;
const r3 = ` + [[${jackSafeDie}@{pbd_safe}[Proficiency]]]`;
roll = `@{wtype}&{template:simple3D} {{rname=^{${skill}-u}}} {{mod=${
values[`${skill}_bonus`]
}}} {{r1=[[@{d20}+${skillBonus}[Mods]]]}} {{r3=${r3}}} ${rtypeString}{{global=@{global_skill_mod}}} @{charname_output}`;
} else {
let profBonus = values["pb"] && isProficient ? parseInt(values["pb"]) : 0;
profBonus = !isProficient && isJack && values["pb"] ? Math.floor(parseInt(values["pb"]) / 2) : profBonus;
let rollBonuses = `@{${skill}_bonus}[Mods]`;
if (values[`${skill}_bonus`] && values[`${skill}_bonus`].toString().toLowerCase().indexOf("d") === -1) {
const parentAbility = globalAttributesByCategory.skills.getParentAbility(skill);
const parentMod = parseInt(values[`${parentAbility}_mod`]);
const otherBonuses = parseInt(values[`${skill}_bonus`]) - profBonus;
const label = otherBonuses > parentMod ? "Mods" : parentAbility;
if (profBonus != 0 && otherBonuses != 0) {
rollBonuses = `${profBonus}[Proficiency]+${otherBonuses}[${label}]`.replace(/\+-/g, "-");
} else if (profBonus == 0 && otherBonuses != 0) {
rollBonuses = `${otherBonuses}[${label}]`.replace(/\+-/g, "-");
} else if (profBonus != 0 && otherBonuses == 0) {
rollBonuses = `${profBonus}[Proficiency]`.replace(/\+-/g, "-");
} else {
let rollBonuses = `@{${skill}_bonus}[Mods]`;
}
}
const rtypeString = rtype.includes("{{normal=1}}") ? "{{normal=1}} " : `${rtype}+${rollBonuses}@{pbd_safe}]]}} `;
roll = `@{wtype}&{template:simple} {{rname=^{${skill}-u}}} {{mod=@{${skill}_bonus}}} {{r1=[[@{d20}+${rollBonuses}@{pbd_safe}]]}} ${rtypeString}{{global=@{global_skill_mod}}} @{charname_output}`;
}
if (values["reliable_talent"] && values["reliable_talent"] == 10 && values[`${skill}_prof`] && values[`${skill}_prof`] != 0) {
if (query) {
const queryReplace = "r2=[[{1d20,0d20+10}k1".replace("{", "&#123;").replace("}", "&#125;").replace(",", "&#44;");
roll = roll.replace(/r2=\[\[@{d20}/g, queryReplace);
roll = roll.replace(/@{d20}/g, "{@{d20},0d20+10}k1");
} else {
roll = roll.replace(/@{d20}/g, "{@{d20},0d20+10}k1");
}
}
update[`${skill}_roll`] = roll;
});
setAttrs(update, { silent: true }, () => {
if (callback) callback();
});
}
);
};
var updateToolRolls = function (toolsIDs, callback) {
const attrs_prof = toolsIDs.map((id) => {
return `repeating_tool_${id}_toolbonus_base`;
});
const attrs_bonus = toolsIDs.map((id) => {
return `repeating_tool_${id}_toolbonus`;
});
const attrs_bonus_base = toolsIDs.map((id) => {
return `repeating_tool_${id}_toolbonus_base`;
});
getAttrs(
[
...attrs_prof,
...attrs_bonus,
...attrs_bonus_base,
"reliable_talent",
"jack_of_all_trades",
"jack",
"rtype",
"advantagetoggle",
"queryadvantage",
"pb",
],
(values) => {
const usingProficiencyDie = values.pb && String(values.pb).includes("d") ? true : false;
let rtype = values["rtype"].includes("@{advantagetoggle}") ? values["advantagetoggle"] : values["rtype"];
rtype = values["rtype"].includes("@{queryadvantage}") ? values["queryadvantage"] : values["rtype"];
const query = rtype.indexOf("queryadvantage") > -1;
let update = {};
const isJack = values["jack_of_all_trades"] && values["jack_of_all_trades"] == "@{jack}";
toolsIDs.forEach((id) => {
let roll;
if (usingProficiencyDie) {
const isProficient = values[`repeating_tool_${id}_toolbonus_base`] !== "(floor(@{pb}/2))";
const proficiencyType =
values[`repeating_tool_${id}_toolbonus_base`] && values[`repeating_tool_${id}_toolbonus_base`] === "(@{pb}*2)" ? "2" : "";
const die = isJack && !isProficient ? `${proficiencyType}${values["jack"]}` : `${proficiencyType}${values.pb}`;
const proficiencyDie = die;
const jackSafeDie = !isProficient && isJack ? `floor(${values.pb}/2)` : proficiencyDie;
const skillBonus = String(values[`repeating_tool_${id}_toolbonus`])
.replace(proficiencyDie, "")
.replace(/\+{2,}|-{2,}|^[\+-]|[\+-]$/, "")
.replace(/\+0|-0/, "");
const rtypeString = rtype.includes("{{normal=1}}") ? "{{normal=1}} " : `${rtype}+${skillBonus}]]}} `;
const r3 = ` + [[${jackSafeDie}@{pbd_safe}[Proficiency]]]`;
roll = `@{wtype}&{template:simple3D} {{rname=@{toolname}}} {{mod=${
values[`repeating_tool_${id}_toolbonus`]
}}} {{r1=[[@{d20}+${skillBonus}[Mods]]]}} {{r3=${r3}}} ${rtypeString}{{global=@{global_skill_mod}}} @{charname_output}`;
} else {
const rtypeString = rtype.includes("{{normal=1}}") ? "{{normal=1}} " : `${rtype}+@{toolbonus}[Mods]@{pbd_safe}]]}} `;
roll = `@{wtype}&{template:simple} {{rname=@{toolname}}} {{mod=@{toolbonus}}} {{r1=[[@{d20}+@{toolbonus}[Mods]@{pbd_safe}]]}} ${rtypeString}{{global=@{global_skill_mod}}} @{charname_output}`;
}
if (
values["reliable_talent"] &&
values["reliable_talent"] == 10 &&
values[`repeating_tool_${id}_toolbonus_base`] &&
values[`repeating_tool_${id}_toolbonus_base`] != "(floor(@{pb}/2))"
) {
if (query) {
const queryReplace = "r2=[[{1d20,0d20+10}k1".replace("{", "&#123;").replace("}", "&#125;").replace(",", "&#44;");
roll = roll.replace(/r2=\[\[@{d20}/g, queryReplace);
roll = roll.replace(/@{d20}/g, "{@{d20},0d20+10}k1");
} else {
roll = roll.replace(/@{d20}/g, "{@{d20},0d20+10}k1");
}
}
update[`repeating_tool_${id}_toolroll`] = roll;
});
setAttrs(update, () => {
if (callback) callback();
});
}
);
};
var updateCharnameOutput = function (npcCallback, pcCallback) {
getAttrs(["npc", "npc_name", "npc_name_flag"], (values) => {
if (values["npc"] && values["npc"] == "1" && values["npc_name"] && values["npc_name_flag"]) {
const charname = values["npc_name_flag"].replace("name", "charname");
setAttrs({ charname_output: charname }, () => {
if (npcCallback) npcCallback();
});
} else {
if (pcCallback) pcCallback();
else if (npcCallback) npcCallback();
}
});
};
on("change:npc_name change:npc_name_flag", (eventInfo) => {
updateCharnameOutput();
});
var getHitDiceAttributeSet = function (id) {
let attributes = [];
attributes.push(`repeating_hd_${id}_type`);
attributes.push(`repeating_hd_${id}_size`);
attributes.push(`repeating_hd_${id}_available`);
return attributes;
};
var getHitDicePool = function (callback, single) {
getSectionIDs("repeating_hd", (ids) => {
if (single) ids = [single];
let attributes = [];
ids.forEach((id) => {
attributes = attributes.concat(getHitDiceAttributeSet(id));
});
getAttrs(attributes, (values) => {
let pool = {};
if (single) {
(pool["id"] = ids[0]),
(pool["size"] = parseInt(values[`repeating_hd_${ids[0]}_size`] || 0)),
(pool["available"] = parseInt(values[`repeating_hd_${ids[0]}_available`]) || 0);
pool["type"] = parseInt(values[`repeating_hd_${ids[0]}_type`]) || 0;
} else {
ids.forEach((id) => {
pool[`d${values[`repeating_hd_${id}_type`]}`] = {
id: id,
size: parseInt(values[`repeating_hd_${id}_size`] || 0),
available: parseInt(values[`repeating_hd_${id}_available`]) || 0,
};
});
}
if (typeof callback === "function") callback(pool);
});
});
};
var isValidEvent = function (eventInfo, blockWorker) {
if (blockWorker && (!eventInfo.sourceType || eventInfo.sourceType != "player")) return false;
if (eventInfo.newValue && eventInfo.previousValue && eventInfo.newValue != eventInfo.previousValue) return true;
if (eventInfo.newValue && !eventInfo.previousValue) return true;
return false;
};
var updateHitDicePool = function (newPool, silent) {
if (Object.keys(newPool).length === 0) return;
silent = silent || false;
getHitDicePool((availablePool) => {
let update = {};
Object.keys(newPool).forEach((die) => {
if (availablePool[die]) {
const id = availablePool[die].id;
Object.keys(newPool[die]).forEach((property) => {
update[`repeating_hd_${id}_${property}`] = newPool[die][property];
});
} else {
const id = generateRowID();
update[`repeating_hd_${id}_type`] = die.replace(/[^0-9]/g, "");
Object.keys(newPool[die]).forEach((property) => {
update[`repeating_hd_${id}_${property}`] = newPool[die][property];
});
}
});
if (silent) {
setAttrs(update, { silent: true });
} else {
setAttrs(update);
}
});
};
on("change:repeating_hd:size", (eventInfo) => {
if (isValidEvent(eventInfo, true)) {
const id = SheetUtils.getUID(eventInfo.sourceAttribute);
const newValue = eventInfo.newValue || 0;
const previousValue = eventInfo.previousValue || 0;
let hitDiceMax = 0;
getHitDicePool((allPool) => {
const selectedPoolKey = Object.keys(allPool).filter((die) => {
return allPool[die]["id"] == id;
});
const pool = allPool[selectedPoolKey];
const gained = pool.size < pool.available ? pool.size - pool.available : Math.max(newValue - previousValue, 0);
const available = pool.available + gained;
const update = {
[`repeating_hd_${pool.id}_available`]: available,
};
Object.keys(allPool).forEach((die) => {
hitDiceMax += allPool[die]["size"];
});
setAttrs(update, () => {
setAttrs({ hit_dice_max: hitDiceMax }, { silent: true });
});
});
}
});
on("change:repeating_hd:available", (eventInfo) => {
if (isValidEvent(eventInfo, true)) {
getHitDicePool((pool) => {
let dicePoolSize = 0;
Object.keys(pool).forEach((die) => {
dicePoolSize += pool[die]["available"];
});
let update = { hit_dice: dicePoolSize };
setAttrs(update, { silent: true });
});
}
});
on("change:repeating_hd:consumed", (eventInfo) => {
const id = SheetUtils.getUID(eventInfo.sourceAttribute);
const poolAttribute = `repeating_hd_${id}_available`;
getAttrs([poolAttribute], (values) => {
const update = { [poolAttribute]: Math.max(parseInt(values[poolAttribute]) - 1, 0) };
setAttrs(update, { silent: true }, () => {
getHitDicePool((pool) => {
let dicePoolSize = 0;
Object.keys(pool).forEach((die) => {
dicePoolSize += pool[die]["available"];
});
let update = { hit_dice: dicePoolSize };
setAttrs(update, { silent: true });
});
});
});
});
on("change:use_per_class_hit_dice", (eventInfo) => {
set_level();
});
on("change:hit_dice", (eventInfo) => {
//For compatibility with rest script
getAttrs(["use_per_class_hit_dice", "hit_dice"], (values) => {
if (values["use_per_class_hit_dice"] && values["use_per_class_hit_dice"] == 1) {
let increaseLeft = 0;
let updatePool = {};
getHitDicePool((pool) => {
let dicePoolSize = 0;
Object.keys(pool).forEach((die) => {
dicePoolSize += pool[die]["available"];
});
increaseLeft = parseInt(values["hit_dice"]) - dicePoolSize;
["d12", "d10", "d8", "d6", "d4"].forEach((die) => {
if (pool[die] && pool[die]["available"] < pool[die]["size"]) {
const diff = Math.min(pool[die]["size"] - pool[die]["available"], increaseLeft);
updatePool[die] = { available: pool[die]["available"] + diff };
increaseLeft -= diff;
return;
}
});
updateHitDicePool(updatePool, true);
});
}
});
});
var showHD = function () {
getSectionIDs("repeating_hd", (ids) => {
let attributes = [];
ids.forEach((id) => {
attributes.push(`repeating_hd_${id}_type`);
attributes.push(`repeating_hd_${id}_size`);
attributes.push(`repeating_hd_${id}_available`);
});
getAttrs(attributes, (values) => {
const types = Object.keys(values).filter((entry) => {
return entry.indexOf("_type") > -1;
});
let dice = [];
let update = {
current_d4: 0,
current_d6: 0,
current_d8: 0,
current_d10: 0,
current_d12: 0,
current_d4_max: 0,
current_d6_max: 0,
current_d8_max: 0,
current_d10_max: 0,
current_d12_max: 0,
show_hit_dice_roll: "[[0]]",
};
types.forEach((type) => {
dice.push(values[type]);
const id = SheetUtils.getUID(type);
update[`current_d${values[type]}_max`] = values[`repeating_hd_${id}_available`];
});
update["show_hit_dice"] = dice.join(" ");
setAttrs(update);
});
});
};
on("clicked:show_hit_dice", (eventInfo) => {
showHD();
});
on("change:current_d4 change:current_d6 change:current_d4 change:current_d8 change:current_d10 change:current_d12", (eventInfo) => {
if (eventInfo.newValue || eventInfo.previousValue) {
getAttrs(["current_d4", "current_d6", "current_d8", "current_d10", "current_d12", "constitution_mod"], (values) => {
let query = [];
let totalDice = 0;
Object.keys(values)
.filter((entry) => {
return entry.includes("current_");
})
.forEach((entry) => {
if (parseInt(values[entry]) > 0) {
const splited = entry.split("_");
const dice = splited[splited.length - 1];
query.push(`${values[entry]}${dice}`);
totalDice += parseInt(values[entry]);
}
});
if (totalDice > 0 && values["constitution_mod"] && values["constitution_mod"] != 0)
query.push(parseInt(values["constitution_mod"]) * totalDice);
setAttrs({ show_hit_dice_roll: query.join(" + ") });
});
}
});
on("change:hit_dice_rolled", (eventInfo) => {
getSectionIDs("repeating_hd", (ids) => {
let attributes = [];
ids.forEach((id) => {
attributes.push(`repeating_hd_${id}_type`);
attributes.push(`repeating_hd_${id}_size`);
attributes.push(`repeating_hd_${id}_available`);
});
getAttrs([...attributes, "current_d4", "current_d6", "current_d8", "current_d10", "current_d12"], (values) => {
const types = Object.keys(values).filter((entry) => {
return entry.indexOf("_type") > -1;
});
let update = {
current_d4: 0,
current_d6: 0,
current_d8: 0,
current_d10: 0,
current_d12: 0,
current_d4_max: 0,
current_d6_max: 0,
current_d8_max: 0,
current_d10_max: 0,
current_d12_max: 0,
show_hit_dice: "",
show_hit_dice_roll: "[[0]]",
};
types.forEach((type) => {
const die = values[type];
if (values[`current_d${die}`] && parseInt(values[`current_d${die}`]) > 0) {
const id = SheetUtils.getUID(type);
const result = parseInt(values[`repeating_hd_${id}_available`] - values[`current_d${die}`]);
update[`repeating_hd_${id}_available`] = Math.max(result, 0);
}
});
setAttrs(update);
});
});
});
on("clicked:hide_hit_dice", (eventInfo) => {
let update = {
current_d4: 0,
current_d6: 0,
current_d8: 0,
current_d10: 0,
current_d12: 0,
current_d4_max: 0,
current_d6_max: 0,
current_d8_max: 0,
current_d10_max: 0,
current_d12_max: 0,
show_hit_dice: "",
show_hit_dice_roll: "[[0]]",
};
setAttrs(update);
});
on("change:consumed_hero_points", (eventInfo) => {
getAttrs(["hero_points"], (values) => {
const hero_points = values["hero_points"] ? parseInt(values["hero_points"]) : 0;
const update = { hero_points: Math.max(hero_points - 1, 0) };
setAttrs(update, { silent: true }, () => {});
});
});
on("change:hero_points", (eventInfo) => {
const hero_points = eventInfo.newValue ? Math.max(eventInfo.newValue, 0) : 0;
const update = { hero_points: hero_points };
setAttrs(update, { silent: true }, () => {});
});
on("change:level", (eventInfo) => {
updateHeroPoints(eventInfo.newValue);
});
on("change:use_hero_points", (eventInfo) => {
if (eventInfo.newValue && eventInfo.newValue == 1) updateHeroPoints();
});
var updateHeroPoints = function (level) {
if (level) {
const hero_points = level ? Math.floor(parseInt(level) / 2) + 5 : 0;
const update = { hero_points: hero_points };
setAttrs(update, { silent: true }, () => {});
} else {
getAttrs(["level"], (values) => {
const level = values.level ? values.level : 0;
updateHeroPoints(level);
});
}
};
const classRelatedAttrs = [
"class",
"subclass",
"base_level",
"multiclass1_flag",
"multiclass1",
"multiclass1_subclass",
"multiclass1_lvl",
"multiclass2_flag",
"multiclass2",
"multiclass2_subclass",
"multiclass2_lvl",
"multiclass3_flag",
"multiclass3",
"multiclass3_subclass",
"multiclass3_lvl",
];
const getSpellPoints = (callback) => {
getAttrs(classRelatedAttrs, (values) => {
let level = 0;
let characterClassAttributes = {
mainclass: {
class: "class",
subclass: "subclass",
level: "base_level",
},
};
for (let i = 1; i <= 3; i++) {
characterClassAttributes[`multiclass_${i}`] = {
enabled: `multiclass${i}_flag`,
class: `multiclass${i}`,
subclass: `multiclass${i}_subclass`,
level: `multiclass${i}_lvl`,
};
}
const pointsPerLevel = ["", 4, 6, 14, 17, 27, 32, 38, 44, 57, 64, 73, 78, 83, 88, 94, 100, 107, 114, 123, 133];
const getLevelInfo = (charClass, values) => {
let charLevel = 0;
if (!characterClassAttributes[charClass]) return charLevel;
if (
!characterClassAttributes[charClass]["enabled"] ||
(values[characterClassAttributes[charClass]["enabled"]] && values[characterClassAttributes[charClass]["enabled"]] == 1)
) {
const subclass = values[characterClassAttributes[charClass]["subclass"]] || "";
const level = values[characterClassAttributes[charClass]["level"]]
? parseInt(values[characterClassAttributes[charClass]["level"]])
: 0;
charClass =
subclass.toLowerCase().includes("arcane trickster") || subclass.toLowerCase().includes("eldritch knight")
? "third"
: values[characterClassAttributes[charClass]["class"]];
const casterType = getCasterType(charClass.toLowerCase());
if (casterType > 0) {
charLevel = Math.floor(level * casterType);
}
}
return charLevel;
};
Object.keys(characterClassAttributes).forEach((charClass) => {
const levelInfo = getLevelInfo(charClass, values);
level += levelInfo;
});
callback(pointsPerLevel[level]);
});
};
const setSpellPoints = () => {
getSpellPoints((spellPoints) => {
console.log("CALCULATING SPELL POINTS");
if (spellPoints > 0) createResource("Spell Points", spellPoints, spellPoints);
});
};
classRelatedAttrs.forEach((attr) => {
on(`change:${attr}`, (evenInfo) => {
getAttrs(["use_spell_points"], (v) => {
if (v["use_spell_points"] && v["use_spell_points"] == 1) {
setSpellPoints();
}
});
});
});
on("change:use_spell_points", (eventInfo) => {
if (eventInfo.newValue && eventInfo.newValue == 1) {
setSpellPoints();
} else {
deleteResource("Spell Points");
}
update_spell_slots();
});
var updateSpellOutputDC = function (eventinfo) {
if (!eventinfo.newValue) return;
getAttrs(
["strength_mod", "dexterity_mod", "constitution_mod", "intelligence_mod", "wisdom_mod", "charisma_mod", "spell_save_dc", "pb"],
(values) => {
let dc = 0;
const spellLevel = eventinfo.sourceAttribute.split("_")[1].replace("spell-", "");
const pb = values.pb ? parseInt(values.pb) : 0;
if (eventinfo.newValue === "spell") {
const spellSaveDc = values["spell_save_dc"] ? parseInt(values["spell_save_dc"]) : 8 + pb;
dc = spellSaveDc;
} else if (eventinfo.newValue === "0*") {
dc = 0;
} else {
const attr = eventinfo.newValue.replace(/@{|}\+/g, "");
const mod = values[attr] ? 8 + pb + parseInt(values[attr]) : 8 + pb;
dc = mod;
}
const id = SheetUtils.getUID(eventinfo.sourceAttribute);
const update = {};
update[`repeating_spell-${spellLevel}_${id}_roll_output_dc`] = dc;
setAttrs(update);
}
);
};
globalAttributesByCategory.spellLevels.forEach((level) => {
on(`change:repeating_spell-${level}:spell_ability`, (eventinfo) => {
updateSpellOutputDC(eventinfo);
});
});
on("clicked:short_rest", () => {
console.log("Taking a nap...");
Rest.short();
});
on("clicked:long_rest", () => {
console.log("ZzzZZZZzzzzZ...");
Rest.long();
});
on("change:charactersheet_type", (eventInfo) => {
if (eventInfo.newValue == "pc") {
setAttrs({ npc: "0", is_vehicle: "0", kingdom: "0" });
} else if (eventInfo.newValue == "npc") {
setAttrs({ npc: "1", is_vehicle: "0", kingdom: "0" });
} else if (eventInfo.newValue == "vehicle") {
console.log("tarrrrr");
setAttrs({ npc: "1", is_vehicle: "1", kingdom: "0" });
} else if (eventInfo.newValue == "kingdom") {
setAttrs({ npc: "0", is_vehicle: "0", kingdom: "1" });
}
});
const formatPrintedTools = () => {
getSectionIDs("tool", (ids) => {
const attrs = [
...ids.map((id) => {
return `repeating_tool_${id}_toolbonus_base`;
}),
...ids.map((id) => {
return `repeating_tool_${id}_toolname`;
}),
];
getAttrs(attrs, (values) => {
const tools = [];
ids.forEach((id) => {
const expert =
values[`repeating_tool_${id}_toolbonus_base`] && values[`repeating_tool_${id}_toolbonus_base`] == "(@{pb}*2)" ? "⁺" : "";
const name = values[`repeating_tool_${id}_toolname`];
if (name) {
tools.push(name + expert);
}
});
setAttrs({
other_tool: tools.sort().join(", "),
});
});
});
};
const formatPrintedSkills = () => {
getSectionIDs("proficiencies", (ids) => {
const attrs = [
...ids.map((id) => {
return `repeating_proficiencies_${id}_prof_type`;
}),
...ids.map((id) => {
return `repeating_proficiencies_${id}_name`;
}),
];
getAttrs(attrs, (values) => {
const otherSkills = {
language: [],
weapon: [],
armor: [],
other: [],
};
ids.forEach((id) => {
const type = values[`repeating_proficiencies_${id}_prof_type`];
const name = values[`repeating_proficiencies_${id}_name`];
if (type && name) {
otherSkills[type.toLowerCase()].push(name);
}
});
setAttrs({
other_languages: otherSkills.language.sort().join(", "),
other_armor: otherSkills.armor.sort().join(", "),
other_weapon: otherSkills.weapon.sort().join(", "),
other_other: otherSkills.other.sort().join(", "),
});
});
});
};
on("change:repeating_tool remove:repeating_tool", function (eventinfo) {
formatPrintedTools();
});
on("change:repeating_proficiencies remove:repeating_proficiencies", function (eventinfo) {
formatPrintedSkills();
});
var resetTraitFilters = function () {
setAttrs({ filter_traits_by: "" });
};
on("sheet:opened", function (eventinfo) {
resetTraitFilters();
});
on("sheet:opened", () => {
const selects = [
{ selector: ".header-info select[name=attr_class]", attr: "class" },
{ selector: ".class_options select[name=attr_multiclass1]", attr: "multiclass1" },
{ selector: ".class_options select[name=attr_multiclass2]", attr: "multiclass2" },
{ selector: ".class_options select[name=attr_multiclass3]", attr: "multiclass3" },
];
selects.forEach((select) => {
populateListOptions({
elemSelector: select.selector,
optionsArray: [{ label: "Loading..." }],
});
});
getCompendiumQuery("Category:Classes", (data) => {
getAttrs(["class", "multiclass1", "multiclass2", "multiclass3"], (values) => {
const existingClasses = characterClassList.map((entry) => {
return entry.name.toLowerCase();
});
const compendiumClassList = [];
data.forEach((entry) => {
if (existingClasses.includes(entry.name.toLowerCase())) return;
const newClass = {
name: entry.name,
hitdie: entry.data["Hit Die"] ? entry.data["Hit Die"].toLowerCase() : "d6",
};
if (
entry.data["Caster Progression"] &&
globalAttributesByCategory.casterProgression.includes(entry.data["Caster Progression"].toLowerCase())
)
newClass.caster = entry.data["Caster Progression"].toLowerCase();
compendiumClassList.push(newClass);
});
const getOptions = (selected) => {
const optionsArray = [
{
i18n: "choose",
label: "Choose",
},
...characterClassList.map((entry) => {
const option = {
value: entry.name,
};
if (selected && selected === entry.name) option.selected = true;
if (getTranslationByKey(entry.name.toLowerCase())) option.i18n = entry.name.toLowerCase();
else option.label = entry.name;
return option;
}),
...compendiumClassList.map((entry) => {
const option = {
value: entry.name,
};
if (selected && selected === entry.name) option.selected = true;
else option.label = entry.name;
return option;
}),
];
return optionsArray;
};
selects.forEach((select) => {
const options = values[select.attr] ? getOptions(values[select.attr]) : getOptions();
populateListOptions({
elemSelector: select.selector,
optionsArray: options,
});
});
compendiumClassList.forEach((compendiumClass) => characterClassList.push(compendiumClass));
});
});
});
on(
"change:repeating_vehicleammo:loaded",
eventinfo => {
const update = {};
getSectionIDs('vehicleammo', ids => {
const uid = SheetUtils.getUID(eventinfo.sourceAttribute);
const ammoName = eventinfo.sourceAttribute.replace('loaded', 'name');
const ammoDesc = eventinfo.sourceAttribute.replace('loaded', 'description');
getAttrs([ammoName, ammoDesc], values => {
if(eventinfo.newValue && eventinfo.newValue == 1) {
update['vehicle_ammo_name'] = values[ammoName];
update['vehicle_ammo_desc'] = values[ammoDesc];
} else {
update['vehicle_ammo_name'] = '';
update['vehicle_ammo_desc'] = '';
}
ids.filter(id => { return id.toLowerCase() !== uid.toLowerCase()}).forEach(id => {
update[`repeating_vehicleammo_${id}_loaded`] = '';
});
setAttrs(update, {silent: true});
});
});
}
);
on(
"change:repeating_vehicleammo:name",
eventinfo => {
const update = {};
const uid = SheetUtils.getUID(eventinfo.sourceAttribute);
const ammoLoaded = eventinfo.sourceAttribute.replace('name', 'loaded');
getAttrs([ammoLoaded], values => {
if(values[ammoLoaded] && values[ammoLoaded] == 1) {
update['vehicle_ammo_name'] = eventinfo.newValue || "";
}
setAttrs(update, {silent: true});
});
}
);
on(
"change:repeating_vehicleammo:attr_description",
eventinfo => {
const update = {};
const uid = SheetUtils.getUID(eventinfo.sourceAttribute);
const ammoLoaded = eventinfo.sourceAttribute.replace('name', 'loaded');
getAttrs([ammoLoaded], values => {
if(values[ammoLoaded] && values[ammoLoaded] == 1) {
update['vehicle_ammo_desc'] = eventinfo.newValue || "";
}
setAttrs(update, {silent: true});
});
}
);
const resetWeaponSlot = () => {
const reset = {
drop_category: "",
drop_name: "",
drop_data: "",
drop_content: "",
vehicle_weapon_confirmation_toggle: "",
vehicle_dropped_weapon_slot: ""
};
setAttrs(reset, {silent: true});
};
on('clicked:cancelweaponslot', eventinfo => {
resetWeaponSlot();
});
on('clicked:acceptweaponslot', eventinfo => {
getAttrs(['vehicle_dropped_weapon_slot'], v => {
/*const mockedweapon = {
Category: 'Vehicle Weapons',
Expansion: '16643',
'data-Weapons': JSON.stringify([
{
Name: 'Carronade',
'Target Die': 'd12', //Sring Die
Range: 'Short', //Short, Standard or Long
Weight: 'Light', //Light or Heavy
'Additional Effects': 'Hits do no ship damage but still produce casualties.', //String
'Slot': 'Bow'
}
]),
blobs: {},
};
const pagedata = JSON.parse(v['drop_data']);
const weapons = JSON.parse(pagedata['data-Weapons']);
if(v['vehicle_dropped_weapon_slot']) {
weapons[0]['Slot'] = v['vehicle_dropped_weapon_slot'];
pagedata['data-Weapons'] = JSON.stringify(weapons);
const update = { drop_data: JSON.stringify(mockedweapon), vehicle_weapon_confirmation_toggle: '', vehicle_dropped_weapon_slot: '' };
setAttrs({ update, silent: true }, eventinfo => {
handle_drop('Vehicle Weapons');
});
} else {
resetWeaponSlot();
}*/
if(v['vehicle_dropped_weapon_slot']) {
setAttrs({vehicle_weapon_confirmation_toggle: ''}, () => { handle_drop('Vehicle Weapons'); });
} else {
resetWeaponSlot();
}
});
});
on("change:repeating_vehicleweapon:color change:repeating_vehicleweapon:name", (eventinfo) => {
const weaponID = SheetUtils.getUID(eventinfo.sourceAttribute);
const newColor = (eventinfo.newValue) ? eventinfo.newValue : "";
const nameAttribute = (eventinfo.sourceAttribute.includes("color")) ?
eventinfo.sourceAttribute.replace("color", "name")
:
eventinfo.sourceAttribute
;
const colorAttribute = (eventinfo.sourceAttribute.includes("name")) ?
eventinfo.sourceAttribute.replace("name", "color")
:
eventinfo.sourceAttribute
;
getAttrs([nameAttribute, colorAttribute], values => {
const weaponName = (values[nameAttribute]) ? values[nameAttribute] : "";
const weaponColor = (values[colorAttribute]) ? values[colorAttribute] : "";
getSectionIDs("vehicleactions", ids => {
getAttrs(ids.map(id => { return `repeating_vehicleactions_${id}_weaponid`}), values => {
let update = {};
const match = Object.keys(values).filter(attribute => {
return values[attribute].toLowerCase() === weaponID.toLowerCase();
}).map(entry => { return SheetUtils.getUID(entry); });
match.forEach(actionID => {
update[`repeating_vehicleactions_${actionID}_weaponname`] = weaponName;
update[`repeating_vehicleactions_${actionID}_color`] = weaponColor;
setAttrs(update);
});
});
});
});
});
on("change:repeating_vehicle-cargo-items:quantity change:repeating_vehicle-cargo-items:weight", (eventinfo) => {
getSectionIDs("vehicle-cargo-items", ids => {
const attributeList = ["vehicle_cargo"];
ids.forEach(id => {
['quantity', 'weight'].forEach(attr => {
attributeList.push(`repeating_vehicle-cargo-items_${id}_${attr}`);
});
});
getAttrs(attributeList, values => {
const maxCargo = (values["vehicle_cargo"] && !isNaN(values["vehicle_cargo"])) ? parseFloat(values["vehicle_cargo"]) : 0;
const quantities = Object.keys(values).filter(key => key.includes('quantity'));
let totalWeight = 0;
quantities.forEach(quantity => {
const weightAttr = quantity.replace('quantity', 'weight')
if(values[quantity] && !isNaN(values[quantity]) && values[weightAttr] && !isNaN(values[weightAttr]))
totalWeight += parseInt(values[quantity]) * parseFloat(values[weightAttr])
});
const totalInTons = totalWeight / 2000;
const parsedWeight = (totalInTons > 0) ? String(totalInTons.toFixed(2)) : "0";
const isOverweight = (totalInTons > maxCargo) ? "1" : "0";
setAttrs({'vehicle_total_weight': parsedWeight, vehicle_is_overweight:isOverweight});
});
});
});
on("change:repeating_vehicleweapon:actions", eventinfo => {
if(!eventinfo.sourceType || eventinfo.sourceType !== "player") return;
if(!eventinfo.newValue || isNaN(!eventinfo.newValue)) return;
const value = parseInt(eventinfo.newValue);
if(value > 10) {
setAttrs({[eventinfo.sourceAttribute]: "10"}, {silent: true});
}
else if(eventinfo.newValue.includes(".") || eventinfo.newValue.includes(",")) {
setAttrs({[eventinfo.sourceAttribute]: String(value)}, {silent: true});
}
});
on("remove:repeating_vehicleweapon", function(eventinfo) {
const weaponID = SheetUtils.getUID(eventinfo.sourceAttribute);
getSectionIDs("vehicleactions", ids => {
getAttrs(ids.map(id => { return `repeating_vehicleactions_${id}_weaponid`}), values => {
const match = Object.keys(values).filter(attribute => {
return values[attribute].toLowerCase() === weaponID.toLowerCase();
}).map(entry => { return SheetUtils.getUID(entry); });
match.forEach(actionID => {
removeRepeatingRow(`repeating_vehicleactions_${actionID}`);
});
});
});
});
class RepeatingFieldset {
constructor(groupname, id) {
this.groupname = groupname;
this.reprowid = id || generateRowID();
}
buildRowName() {
return `repeating_${this.groupname}_${this.reprowid}`;
}
addAttribute(attributesibuteName) {
return `${this.buildRowName()}_${attributesibuteName}`;
}
}
class Attack extends RepeatingFieldset {
constructor(name, id) {
super("attack", id);
this.attributes = {
atkname: name,
atkbonus: "", //Part of button display
atkdmgtype: "", //Part of button display
atkflag: "{{attack=1}}",
atkattr_base: "@{strength_mod}",
atkrange: "",
atkmagic: "",
atkcritrange: 20,
dmgflag: "{{damage=1}} {{dmg1flag=1}}",
dmgbase: "",
dmgattr: "@{strength_mod}",
dmgmod: "",
dmgtype: "",
dmgcustcrit: "",
dmg2flag: 0,
dmg2base: "",
dmg2attr: 0,
dmg2mod: "",
dmg2type: "",
dmg2custcrit: "",
saveflag: 0,
saveattr: "Strength",
savedc: "(@{spell_save_dc})",
saveflat: "",
saveeffect: "",
atk_desc: "",
"options-flag": 0,
};
}
addSaveFlag() {
this.attributes.saveflag = "{{save=1}} {{saveattr=@{saveattr}}} {{savedesc=@{saveeffect}}} {{savedc=[[[[@{savedc}]][SAVE]]]}}";
}
checkFinesse(strenght, dexterity) {
const strengthGreaterThanDex = strenght > dexterity;
this.attributes.dmgattr = strengthGreaterThanDex ? "@{strength_mod}" : "@{dexterity_mod}";
this.attributes.atkattr_base = strengthGreaterThanDex ? "@{strength_mod}" : "@{dexterity_mod}";
}
}
class GlobalACMod extends RepeatingFieldset {
constructor(name, id) {
super("acmod", id);
name,
(this.attributes = {
global_ac_name: name,
global_ac_val: "",
global_ac_active_flag: 0,
"options-flag": 0,
});
}
}
class GlobalDamage extends RepeatingFieldset {
constructor(name, id) {
super("damagemod", id);
name,
(this.attributes = {
global_damage_name: name,
global_damage_damage: "",
global_damage_type: "",
global_damage_active_flag: 0,
"options-flag": 0,
});
}
}
class GlobalSave extends RepeatingFieldset {
constructor(name, id) {
super("savemod", id);
name,
(this.attributes = {
global_save_name: name,
global_save_roll: "",
global_save_active_flag: 0,
"options-flag": 0,
});
}
}
class GlobalAttack extends RepeatingFieldset {
constructor(name, id) {
super("tohitmod", id);
name,
(this.attributes = {
global_attack_name: name,
global_attack_roll: "",
global_attack_rollstring: "",
global_attack_active_flag: 0,
"options-flag": 0,
});
}
addRoll(roll) {
this.attributes.global_attack_roll = roll;
}
}
class HPMod extends RepeatingFieldset {
constructor(name, id) {
super("hpmod", id);
name,
(this.attributes = {
mod: "",
source: "",
levels: "total",
});
}
}
class Item extends RepeatingFieldset {
constructor(name, id) {
super("inventory", id);
name,
(this.attributes = {
itemname: name,
itemcount: 1,
itemproperties: "",
itemweight: 0,
itemcontent: "",
itemmodifiers: "",
hasattack: 0,
useasresource: 0,
});
}
}
class Proficiency extends RepeatingFieldset {
constructor(name, id) {
super("proficiencies", id);
this.attributes = {
name,
prof_type: "OTHER",
"options-flag": 0,
};
}
}
class Resource extends RepeatingFieldset {
constructor(name, id, side) {
super("resource", id);
(this.side = side || "left"),
(this.attributes = {
[`resource_${this.side}`]: "",
[`resource_${this.side}_name`]: name,
[`resource_${this.side}_max`]: "",
[`resource_${this.side}_use_pb`]: "",
[`resource_${this.side}_use_pb`]: "",
[`resource_${this.side}_reset`]: "",
});
}
}
class Spell extends RepeatingFieldset {
constructor(name, section, id) {
super(section, id);
this.attributes = {
"details-flag": 0,
"options-flag": 0,
rollcontent: "",
spell_ability: "spell",
innate: "",
spell_damage_progression: "",
spellathigherlevels: "",
spellattack: "",
spellcastingtime: "",
spellclass: "",
spellcomp_m: 0,
spellcomp_materials: "",
spellcomp_s: 0,
spellcomp_v: 0,
spellconcentration: "",
spelldamage2: "",
spelldamage: "",
spelldamagetype2: "",
spelldamagetype: "",
spelldescription: "",
spelldmgmod: "",
spellduration: "",
spellhealing: "",
spellhlbonus: "",
spellhldie: "",
spellhldietype: "",
spelllevel: "cantrip",
spellname: name,
spelloutput: "SPELLCARD",
spellrange: "",
spellritual: "",
spellsave: "",
spellsavesuccess: "",
spellschool: "abjuration",
spellsource: "",
spelltarget: "",
};
}
}
class Trait extends RepeatingFieldset {
constructor(name, id) {
super("traits", id);
this.attributes = {
name,
description: "",
source_type: "",
source: "",
["options-flag"]: "0",
display_flag: "on",
};
}
determineSource(category) {
switch (category) {
case "Backgrounds":
case 'Traits':
case "Feats":
return category.slice(0, -1);
break;
case "Races":
case "Subraces":
return "Racial";
break;
case "Classes":
case "Subclasses":
return "Class";
break;
default:
return "Other";
}
}
}
class ToolProficiency extends RepeatingFieldset {
constructor(name, id) {
super("tool", id);
this.attributes = {
toolname: name,
toolbonus_base: "(@{pb})",
toolattr_base:
"?{Attribute?|Strength,@{strength_mod}|Dexterity,@{dexterity_mod}|Constitution,@{constitution_mod}|Intelligence,@{intelligence_mod}|Wisdom,@{wisdom_mod}|Charisma,@{charisma_mod}}",
"options-flag": 0,
};
}
}
//========== Non-Repeating
class Class {
constructor(name) {
name,
(this.attributes = {
class: this.validateClassName(name) ? name : "",
base_level: 1,
hit_dice_max: 1,
hit_dice: 1,
hitdietype: 4,
spellcasting_ability: "0*",
});
}
validateClassName(name) {
const existingClasses = characterClassList.map((entry) => entry.name.toLowerCase());
return existingClasses.includes(name.toLowerCase()) ? true : false;
}
buildSpellcastingAbility(attributeName) {
return `@{${attributeName}_mod}+`;
}
}
class CustomAC {
constructor(name) {
name,
(this.attributes = {
custom_ac_flag: 1,
custom_ac_base: 10,
custom_ac_part1: "none",
custom_ac_part2: "none",
custom_ac_shield: "yes",
});
}
}
class Race {
constructor(name) {
name,
(this.attributes = {
race: name,
speed: 30,
size: "Medium",
creaturetype: "Humanoid",
halflingluck_flag: this.checkHalfling(name) ? 1 : 0,
});
}
checkHalfling(name) {
return name === "Halfling" ? true : false;
}
}
class Subrace {
constructor(name) {
name,
(this.attributes = {
subrace: name,
});
}
}
class SavingThrow {
constructor(name) {
name,
(this.attributes = {
[`${this.attributesName(name)}_save_prof`]: `(@{pb})`,
});
}
attributesName(name) {
return name.toLowerCase();
}
}
class Skill {
constructor(name) {
name,
(this.attributes = {
[`${this.attributesName(name)}_prof`]: `(@{pb}*@{${this.attributesName(name)}_type})`,
});
}
attributesName(name) {
return name.toLowerCase().replace(/ /g, "_");
}
}
class Subclass {
constructor(name) {
name,
(this.attributes = {
subclass: name,
arcane_fighter: 0,
arcane_rogue: 0,
});
}
checkArcane(className) {
return className === "fighter" || className === "rogue" ? true : false;
}
buildSpellcastingAbility(attributeName) {
return `@{${attributeName}_mod}+`;
}
}
//========== NPC AKA MONSTERS
class Npc {
constructor(name) {
name,
(this.attributes = {
npc_name: name,
strength_base: 10,
dexterity_base: 10,
constitution_base: 10,
intelligence_base: 10,
wisdom_base: 10,
charisma_base: 10,
npc_condition_immunities: "",
npc_immunities: "",
npc_resistances: "",
npc_languages: "",
npc_speed: "",
token_size: 1,
npc_vulnerabilities: "",
npc_challenge: "",
npc_senses: "",
npc_type: "",
npc_ac: "",
npc_actype: "",
hp_max: "",
npc_hpformula: "",
npc_xp: "",
npc_legendary_actions: 0,
npc_legendary_actions_desc: "",
npc_mythic_actions: 0,
npc_mythic_actions_desc: "",
npc: 1,
charactersheet_type: "npc",
"npc_options-flag": 0,
npcreactionsflag: 0,
npcspellcastingflag: 0,
spellcasting_ability: "0*",
globalmagicmod: 0,
caster_level: 0,
spell_dc_mod: 0,
npc_pb: 0,
});
}
addSkills(list) {
list.forEach((value) => (this.attributes[`npc_${value.replace(/ /g, "_")}_base`] = ""));
}
addSaves(list) {
list.forEach((value) => (this.attributes[`npc_${value.slice(0, 3)}_save_base`] = ""));
}
addSpellcaster(ability, casterLevels) {
const spellcastrAttributes = {
npcspellcastingflag: 1,
base_level: casterLevels || 1,
caster_level: casterLevels || 1,
class: "Wizard",
level: casterLevels || 1,
spellcasting_ability: ability,
};
Object.assign(this.attributes, spellcastrAttributes);
}
addInnateCaster() {
this.attributes.npcspellcastingflag = 1;
}
}
class NpcAction extends RepeatingFieldset {
constructor(name, section, id) {
super(section, id);
name,
(this.attributes = {
name,
description: "",
"npc_options-flag": 0,
});
}
addAttack() {
const attackAttributes = {
attack_flag: "on",
attack_display_flag: "{{attack=1}}",
attack_options: "{{attack=1}}",
attack_type: "",
attack_target: "one target",
attack_range: "5 ft",
attack_tohit: "",
attack_damage: "",
attack_damagetype: "",
attack_damage2: "",
attack_damagetype2: "",
};
Object.assign(this.attributes, attackAttributes);
}
}
class NpcReaction extends RepeatingFieldset {
constructor(name, id) {
super("npcreaction", id);
name,
(this.attributes = {
name,
"npc_options-flag": 0,
description: "",
});
}
}
class NpcTraits extends RepeatingFieldset {
constructor(name, id) {
super("npctrait", id);
name,
(this.attributes = {
name,
"npc_options-flag": 0,
description: "",
});
}
}
class VehicleWeapon extends RepeatingFieldset {
constructor(name, id, slot) {
super(`vehicleweapon-${slot}`, id);
name,
(this.attributes = {
name,
quantity: 1,
target_die: "",
range: "",
weight: "",
additional_effects: ""
});
}
}
class VehicleAmmo extends RepeatingFieldset {
constructor(name, id) {
super("vehicleammo", id);
name,
(this.attributes = {
name,
quantity: 1,
description: "",
loaded: 0
});
}
}
class VehicleUpgrade extends RepeatingFieldset {
constructor(name, id) {
super("vehicleupgrade", id);
name,
(this.attributes = {
name,
description: ""
});
}
}
class VehicleOfficer extends RepeatingFieldset {
constructor(name, id) {
super("vehicleofficer", id);
name,
(this.attributes = {
name,
description: ""
});
}
}
class VehicleCargo extends RepeatingFieldset {
constructor(name, id) {
super("vehicle-cargo-items", id);
name,
(this.attributes = {
name,
quantity: 1,
weight: ""
});
}
}
class VehicleAction extends NpcAction {
constructor(name, parent, id) {
super(name, "vehicleactions", id);
this.attributes["weaponid"] = parent ? parent : "";
this.attributes["weaponname"] = parent ? parent : "";
this.attributes["color"] = "";
}
}
class Vehicle extends Npc {
constructor(name) {
super(name);
this.attributes["charactersheet_type"] = "vehicle";
this.attributes["token_size"] = 4;
this.attributes["vehicle_type"] = "";
this.attributes["vehicle_size"] = "";
this.attributes["vehicle_base_speed"] = "";
this.attributes["vehicle_mobility"] = "";
this.attributes["vehicle_fuel_items"] = "";
this.attributes["vehicle_hp"] = "";
this.attributes["vehicle_hp_max"] = "";
this.attributes["vehicle_explosion_cap"] = "";
this.attributes["vehicle_bow_slots"] = "0";
this.attributes["vehicle_port_slots"] = "0";
this.attributes["vehicle_starboard_slots"] = "0";
this.attributes["vehicle_stern_slots"] = "0";
this.attributes["vehicle_show_ammunition"] = "0";
this.attributes["vehicle_show_officers"] = "0";
this.attributes["vehicle_unranked_crew"] = "";
this.attributes["vehicle_unranked_crew_max"] = "";
this.attributes["vehicle_maximum_crew"] = "";
this.attributes["vehicle_maximum_crew_bonus"] = "";
this.attributes["vehicle_skeleton_crew"] = "0";
this.attributes["vehicle_mettle_pool"] = "0";
this.attributes["vehicle_crew_boons"] = "";
this.attributes["vehicle_ammo_name"] = "";
this.attributes["vehicle_ammo_desc"] = "";
}
}
const dropFunctions = {
validateCantripScaling: scaling => scaling === 'dice' || scaling === 'beam' ? true : false,
buildCantripScaling: scaling => `Cantrip ${scaling.charAt(0).toUpperCase()}${scaling.slice(1)}`,
buildDamage: (page, currentData, alternateDamage) => {
try {
const modifiers = dropFunctions.inferAbilityModifier(page.data["Item Type"], page.data["Properties"], currentData)
let damage = alternateDamage ? alternateDamage : page.data[`Damage`];
damage += modifiers && modifiers != 0 ? `+${modifiers}` : ""
damage += page.data["Modifiers"] ? dropFunctions.inferModifier(page.data["Modifiers"], 'damage') : ""
return damage
} catch (error) {
console.error(error)
return ''
}
},
buildItemModString: page => {
try {
let mods = `Item Type: ${page.data["Item Type"] || page.data["Category"]}`;
//Items with ac or attack related bonuses
["AC", "Damage", "Damage Type", "Secondary Damage", "Secondary Damage Type", "Alternate Damage", "Alternate Damage Type", "Alternate Secondary Damage", "Alternate Secondary Damage Type", "Critical Range", "Range"].forEach(attr => {
mods += page.data[`${attr}`] && page.data[`${attr}`] != "" ? `, ${attr}: ${page.data[`${attr}`]}` : "";
});
//Items with Attack Description such as Sun Sword
mods += page.data["data-Attack Description"] && page.data["data-Attack Description"] != "" ? `, Attack Description: ${page.data["data-Attack Description"].replace(/,/g,'&#44')}` : "";
//Items with modifiers such as Cloak of Protection
mods += page.data["Modifiers"] && page.data["Modifiers"] != "" ? `, ${page.data["Modifiers"]}` : "";
//Armors with Stealth Disadvantage
mods += page.data["Stealth"] && page.data["Stealth"].toLowerCase() === "disadvantage" ? `, Stealth:Disadvantage` : "";
return mods
} catch (error) {
console.error(error)
return ''
}
},
buildSpellQuery: spellLevel => {
try {
let spellQuery = ""
for(i = 0; i < 10-spellLevel; i++) {
const sum = (parseInt(i, 10) + parseInt(spellLevel, 10))
spellQuery += `|Level ${sum},${sum}`
};
return `@{wtype}&{template:spell} {{level=@{spellschool} ?{Cast at what level?${spellQuery}}}} {{name=@{spellname}}} {{castingtime=@{spellcastingtime}}} {{range=@{spellrange}}} {{target=@{spelltarget}}} @{spellcomp_v} @{spellcomp_s} @{spellcomp_m} {{material=@{spellcomp_materials}}} {{duration=@{spellduration}}} {{description=@{spelldescription}}} {{athigherlevels=@{spellathigherlevels}}} @{spellritual} {{innate=@{innate}}} @{spellconcentration} @{charname_output}`
} catch (error) {
console.error(error)
}
},
buildToHit: (page, currentData) => {
try {
let hit = dropFunctions.inferAbilityModifier(page.data["Item Type"], page.data["Properties"], currentData);
hit += page.data["Modifiers"] ? dropFunctions.inferModifier(page.data["Modifiers"], 'attacks') : "";
return hit
} catch (error) {
console.error(error)
return ''
}
},
buildSenses: (senses, passivePerception) => {
try {
return senses ? `${senses}, passive Perception ${passivePerception}` : `passive Perception ${passivePerception}`;
} catch (error) {
console.log(error)
return ''
}
},
buildSpellList: spells => {
try {
let spellList = []
//Create an array of spells to get from the Comepndium
const spellTypes = ["spells", "innate"]
spellTypes.forEach(type => {
const spellObject = dropFunctions.jsonParse(spells)[type]
if (spellObject) {
for (let [key, value] of Object.entries(spellObject)) {
value.forEach(spell => spellList.push(spell))
}
}
})
return spellList
} catch (error) {
console.log(error)
}
},
buildType: (size, type, aligment) => {
let string = size || "";
type ? string += ` ${type}` : false;
aligment ? string += `, ${aligment}`: false;
return string
},
confirmFinesse: properties => properties.toLowerCase().includes("finesse") ? true : false,
confirmRanged: itemType => itemType.toLowerCase().includes("ranged") ? true : false,
confirmDexGreaterThanStr: (dex, str) => dex > str ? true : false,
convertAbbreviationToAttribute: threeLetterString => {
switch(threeLetterString.toLowerCase()) {
case "str":
return "@{strength_mod}";
break;
case "dex":
return "@{dexterity_mod}";
break;
case "con":
return "@{constitution_mod}";
break;
case "wis":
return "@{wisdom_mod}";
break;
case "int":
return "@{intelligence_mod}";
break;
case "cha":
return "@{charisma_mod}";
break;
default:
errorMessage("convertAbbreviationToAttribute", `${threeLetterString} did not match one of the expected types str, dex, con, wis, int, cha`)
return false
}
},
determineComponent: (componentList, component) => componentList.toLowerCase().includes(component) ? `{{${component}=1}}` : false,
inferAbilityModifier: (type, properties, currentData) => {
try {
const ranged = type ? dropFunctions.confirmRanged(type) : false;
const finesse = properties ? dropFunctions.confirmFinesse(properties) : false;
const dexGreaterThanStr = dropFunctions.confirmDexGreaterThanStr(currentData.dexterity_mod, currentData.strength_mod);
return ranged || (finesse && dexGreaterThanStr) ? currentData.dexterity_mod : currentData.strength_mod
} catch (error) {
console.log(error)
}
},
inferBaseAttribute: attribute => attribute.includes('(') ? attribute.split(" (")[0] : attribute,
inferCasterLevel: description => parseInt(description.substring(description.indexOf("-level")-4, description.indexOf("-level")-2).trim(), 10) || 1,
inferFormulaAttribute: attribute => attribute.includes('(') ? attribute.split(" (")[1].slice(0, -1) : "",
inferModifier: (modifiers, type) => {
try {
let modifier = "";
modifiers = modifiers.split(',');
modifiers.forEach(value => {
modifier += value.toLowerCase().includes(type) ? value.split(' ').pop() : ""
});
return modifier
} catch (error) {
console.log(error)
return ''
}
},
inferSpellcastingAbility: description => {
let ability = description.match(/casting ability is (.*?) /);
ability = ability && ability.length > 1 ? ability[1] : false;
ability = ability ? `@{${ability.toLowerCase()}_mod}+` : "0*";
return ability
},
inferSavingThrows: saves => {
try {
let object = {};
if (saves) {
saves = saves.includes(', ') ? saves.split(", ") : [saves]
saves.forEach(saveValue => {
const split = saveValue.includes(' ') ? saveValue.split(' ') : errorMessage(`inferSavingThrows`, `${saveValue} does no include a space`)
const abbreviation = split[0].toLowerCase();
object[`npc_${abbreviation}_save_base`] = split.pop();
});
}
return object
} catch (error) {
errorMessage('inferSavingThrows', error)
}
},
inferSkills: skills => {
try {
let object = {};
if (skills) {
skills = skills.includes(', ') ? skills.split(", ") : [skills]
skills.forEach(skillValue => {
const split = skillValue.includes(' ') ? skillValue.split(' ') : errorMessage(`inferSkills`, `${skillValue} does no include a space`)
const value = split.pop();
const name = split.length > 1 ? split.join("_").toLowerCase() : split.shift().toLowerCase();
object[`npc_${name}_base`] = value;
});
}
return object
} catch (error) {
errorMessage('inferSkills', error)
}
},
jsonParse: (data, defaultValue) => {
let result = null;
try {
result = JSON.parse(data);
} catch (error) {
switch(typeof defaultValue) {
case 'boolean':
case 'number':
case 'string':
case 'function':
result = (typeof data === typeof defaultValue) ? data : defaultValue;
break;
case 'object':
result = (typeof data === typeof defaultValue && data.constructor.name === defaultValue.constructor.name) ? data : defaultValue;
break;
default:
errorMessage('Invalid JSON', error);
}
}
return result;
},
updateSheetAttributes: newObjectForSheet => {
try {
let update = {};
for (let [key, value] of Object.entries(newObjectForSheet.attributes)) {
//If repeating section, update key needs the repeating information
const updateKey = newObjectForSheet.reprowid ? newObjectForSheet.addAttribute(key) : key;
//This 'update' will be handed to a setAttrs function
update[updateKey] = newObjectForSheet.attributes[key];
}
return update
} catch (error) {
errorMessage(`updateSheetAttributes`, error);
}
}
}
//These functions handle 'repeating' parameter of processDrop
const dropHelpers = {
findToolId: (repeatingTools, toolName) => {
let id = false
for(let [key, value] of Object.entries(repeatingTools)) {
value.toolname === toolName.toLowerCase() ? id = key : false;
}
return id
},
updateRepeatingTools: (newTool, existingTool) => {
existingTool.id = newTool.reprowid
existingTool.toolname = newTool.attributes.toolname.toLowerCase()
existingTool.base = newTool.attributes.toolbonus_base
return existingTool
},
updateRepeatingTraits: (newTrait, existingTrait) => {
existingTrait.id = newTrait.reprowid;
existingTrait.name = newTrait.attributes.name;
existingTrait.source = newTrait.attributes.source;
existingTrait.type = newTrait.attributes.source_type;
return existingTrait
}
}
on("change:drop_category", function (eventinfo) {
if (eventinfo.newValue === "Monsters" || eventinfo.newValue === "Vehicles") {
getAttrs(["class", "race", "speed", "hp"], function (v) {
if (v["class"] != "" || v["race"] != "" || v["speed"] != "" || v["hp"] != "") {
setAttrs({ monster_confirm_flag: 1 });
} else {
handle_drop(eventinfo.newValue);
}
});
} else if(eventinfo.newValue === "Vehicle Weapons") {
setAttrs({vehicle_weapon_confirmation_toggle: "1", vehicle_dropped_weapon_slot: ''});
} else {
handle_drop(eventinfo.newValue);
}
});
var handle_drop = function (category, eventinfo) {
getAttrs(
[
"speed",
"hp_max",
"hp",
"drop_name",
"drop_data",
"drop_content",
"character_id",
"npc_legendary_actions",
"strength_mod",
"dexterity_mod",
"npc",
"is_vehicle",
"base_level",
"strength_base",
"dexterity_base",
"constitution_base",
"wisdom_base",
"intelligence_base",
"charisma_base",
"class_resource_name",
"other_resource_name",
"multiclass1_lvl",
"multiclass2_lvl",
"multiclass3_lvl",
"vehicle_dropped_weapon_slot",
],
function (v) {
var pagedata = {};
try {
pagedata = JSON.parse(v.drop_data);
} catch (e) {
pagedata = v.drop_data;
}
var page = {
name: v.drop_name,
data: pagedata,
content: v.drop_content,
slot: v['vehicle_dropped_weapon_slot'] || false
};
var category = page.data["Category"];
if (category === "Races" && /Dhampir|Hexblood|Reborn/gi.test(page.name)) {
const expansion = "(VRGR)";
const raceName = page.name.replace(expansion, "").trim();
const raceSelection = `Races:${raceName}?expansion=10535`;
const reset = {
drop_category: "",
drop_name: "",
drop_data: "",
drop_content: "",
};
setAttrs({ utilitymancer_options: raceSelection, ...reset }, () => {
startCharactermancer("utility-welcome");
});
} else {
get_repeating_data(function (repeating) {
var results = processDrop(page, v, repeating);
setAttrs(results.update, { silent: true }, function () {
results.callbacks.forEach(function (callback) {
callback();
});
});
});
}
}
);
};
var processDrop = function (page, currentData, repeating, looped) {
var numUses = function (uses_string) {
uses_string = parseValues(uses_string);
var terms = uses_string.split("+");
var total = 0;
_.each(terms, function (term) {
total += parseInt(term);
});
return uses_string === "" || uses_string === "-" ? uses_string : total;
};
var parseValues = function (uses_string) {
var attribs = ["strength", "dexterity", "constitution", "wisdom", "intelligence", "charisma"];
uses_string = uses_string ? uses_string.toLowerCase() : "";
_.each(attribs, function (attrib) {
var attribMod = Math.floor((parseInt(currentData[attrib + "_base"]) - 10) / 2);
if (attribMod < 0 || isNaN(attribMod)) attribMod = 0;
uses_string = uses_string.replace(attrib, attribMod);
});
uses_string = uses_string.replace(/half_level/g, Math.floor(classlevel / 2));
return uses_string.replace(/level/g, classlevel);
};
const category = page.data["Category"];
let callbacks = [];
let update = {};
let blobs = {};
let classlevel = currentData.base_level ? parseInt(currentData.base_level) : 1;
repeating.traits = repeating.traits ? repeating.traits : [];
["drop_category", "drop_name", "drop_data", "drop_content"].forEach((attr) => (update[attr] = ""));
const coreDrops = ["Classes", "Backgrounds", "Feats", "Items", "Proficiencies", "Races", "Subraces", "Other Options and Features"];
if (coreDrops.includes(category)) {
update["tab"] = "core";
}
const assignUpdate = (newData) => Object.assign(update, newData);
const processItems = () => {
try {
const itemType = page.data["Item Type"] ? page.data["Item Type"] : category;
const npc = currentData.npc === "0" ? false : true;
const isVehicle = currentData.npc == "1" && currentData.is_vehicle == "1";
if (isVehicle) {
let newObject = new VehicleCargo(page.name);
if (page.data["Weight"]) newObject.attributes.weight = page.data["Weight"];
if (page.data["itemcount"]) newObject.attributes.quantity = page.data["itemcount"];
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
} else if (!npc) {
let newObject = new Item(page.name);
page.data["itemcount"] ? (newObject.attributes.itemcount = page.data["itemcount"]) : false;
["Properties", "Weight"].forEach((attribute) => {
const attributeLowercase = attribute.toLowerCase();
page.data[`${attribute}`] ? (newObject.attributes[`item${attributeLowercase}`] = page.data[`${attribute}`]) : false;
});
page.content ? (newObject.attributes.itemcontent = page.content) : false;
newObject.attributes.itemmodifiers = dropFunctions.buildItemModString(page);
//ATTACK items such as Weapons
itemType.toLowerCase().includes("weapon") ? (newObject.attributes.hasattack = 1) : false;
if (page.data["Stealth"]) {
callbacks.push(() => update_skills(["stealth"]));
}
if (page.data.AC && !looped) {
callbacks.push(() => update_ac());
}
if (page.data.Modifiers && page.data.Modifiers != "") {
callbacks.push(() => {
check_itemmodifiers(page.data.Modifiers);
});
}
//Create attacks for weapons. Create two attacks for versatile weapons such as Longsword.
if (newObject.attributes.hasattack === 1) {
callbacks.push(() => {
page.data["Alternate Damage"] && page.data["Alternate Damage"] !== ""
? create_attack_from_item(newObject.reprowid, { versatile: true })
: create_attack_from_item(newObject.reprowid);
});
}
if (itemType.toLowerCase() === "ammunition") {
newObject.attributes.useasresource = 1;
callbacks.push(() => {
create_resource_from_item(newObject.reprowid);
});
}
if (!looped) {
callbacks.push(() => {
update_weight();
});
}
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
} else if (npc && itemType.toLowerCase().includes("weapon")) {
const properties = page.data["Properties"] ? page.data["Properties"].toLowerCase() : "none";
let newObject = new NpcAction(page.name, "npcaction");
newObject.addAttack();
page.data["Item Type"] ? (newObject.attributes.attack_type = page.data["Item Type"]) : false;
page.content ? (newObject.attributes.description = page.content) : false;
newObject.attributes.attack_tohit = dropFunctions.buildToHit(page, currentData);
const damageModifiers = dropFunctions.inferAbilityModifier(page.data["Item Type"], page.data["Properties"], currentData);
newObject.attributes.attack_damage = dropFunctions.buildDamage(page, damageModifiers);
page.data["Damage Type"] ? (newObject.attributes.attack_damagetype = page.data["Damage Type"]) : false;
page.data["Secondary Damage"] && !page.name.includes("+")
? (newObject.attributes.attack_damage2 = page.data["Secondary Damage"])
: false;
page.data["Secondary Damage Type"] && !page.name.includes("+")
? (newObject.attributes.attack_damagetype2 = page.data["Secondary Damage Type"])
: false;
properties.includes("versatile") ? (newObject.attributes.name = `${newObject.attributes.name} (One-Handed)`) : false;
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
//Versatile creates two attacks
if (properties.includes("versatile") && (page.data["Alternate Damage"] || page.data["Secondary Damage"])) {
let alternateAttack = new NpcAction(`${page.name} (Two-Handed)`, "npcaction");
alternateAttack.addAttack();
alternateAttack.attributes.attack_type = newObject.attributes.attack_type;
alternateAttack.attributes.description = newObject.attributes.description;
if (page.data["Alternate Damage"]) {
alternateAttack.attributes.attack_damage = dropFunctions.buildDamage(page, damageModifiers, page.data["Alternate Damage"]);
page.data["Alternate Damage Type"]
? (alternateAttack.attributes.attack_damagetype = page.data["Alternate Damage Type"])
: false;
page.data["Alternate Secondary Damage"]
? (alternateAttack.attributes.attack_damage2 = page.data["Alternate Secondary Damage"])
: false;
page.data["Alternate Secondary Damage Type"]
? (alternateAttack.attributes.attack_damagetype2 = page.data["Alternate Secondary Damage Type"])
: false;
} else {
alternateAttack.attributes.attack_damage = dropFunctions.buildDamage(page, damageModifiers, page.data["Secondary Damage"]);
}
assignUpdate(dropFunctions.updateSheetAttributes(alternateAttack));
}
if (properties.includes("thrown")) {
let thrownAttack = new NpcAction(`${page.name} (Thrown)`, "npcaction");
thrownAttack.addAttack();
page.data["Range"] ? (thrownAttack.attributes.attack_range = page.data["Range"]) : false;
thrownAttack.attributes.attack_type = "Range Weapon";
["description", "attack_tohit", "attack_damage", "attack_damagetype", "attack_damage2", "attack_damagetype2"].forEach((attr) => {
thrownAttack.attributes[`${attr}`] = newObject.attributes[`${attr}`];
});
assignUpdate(dropFunctions.updateSheetAttributes(thrownAttack));
}
if (page.data.Modifiers) {
callbacks.push(
() => {
check_itemmodifiers(page.data.Modifiers);
},
() => {
update_npc_action("all");
}
);
} else {
callbacks.push(() => {
update_npc_action("all");
});
}
} else {
warningMessage(
"processItems",
`Only weapon drops are supported for npcs. For npcs, "Item Type" in the Compendium must include the word "weapon".`
);
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processSpells = () => {
try {
if (page.name) {
const spellLevel = page.data["Level"] && page.data["Level"] > 0 ? page.data["Level"] : "cantrip";
const section = `spell-${spellLevel}`;
let existing = { id: false };
for (let [key, value] of Object.entries(repeating[`${section}`])) {
value.spellname === page.name ? (existing.spellattackid = value.spellattackid) : false;
value.spellname === page.name ? (existing.id = key) : false;
if (value.spellname === page.name) {
["spellsource", "innate", "spellcasting_ability"].forEach((attributeToKeep) => {
if (repeating[`${section}`][key][attributeToKeep]) existing[attributeToKeep] = repeating[`${section}`][key][attributeToKeep];
});
}
}
let newObject = new Spell(page.name, section, existing.id);
newObject.attributes.spelllevel = spellLevel;
const innateString = page.data["innate"] ? page.data["innate"] : "";
const dropSheetAssocitation = {
spelldescription: page.data["data-description"],
spellschool: page.data["School"].toLowerCase(),
spellclass: page.data["spellclass"],
spellsource: existing.spellsource ? existing.spellsource : page.data["spellsource"],
spellritual: page.data["Ritual"],
spelldmgmod: page.data["Add Casting Modifier"],
spellcomp_materials: page.data["Material"],
spelldamage2: page.data["Secondary Damage"],
spellcastingtime: page.data["Casting Time"],
spelldamagetype: page.data["Damage Type"],
spelldamage: page.data["Damage"],
spellduration: page.data["Duration"],
spellhealing: page.data["Healing"],
spellrange: page.data["Range"],
spellsave: page.data["Save"],
spellsavesuccess: page.data["Save Success"],
spelldamagetype2: page.data["Secondary Damage Type"],
spelltarget: page.data["Target"],
spellhlbonus: page.data["Higher Spell Slot Bonus"],
spellathigherlevels: page.data["Higher Spell Slot Desc"],
spellhldie: page.data["Higher Spell Slot Dice"],
spellhldietype: page.data["Higher Spell Slot Die"],
spellattack: page.data["Spell Attack"],
innate: existing.innate ? existing.innate : innateString,
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newObject.attributes[`${key}`] = value) : false;
}
if (existing.spellcasting_ability) {
newObject.attributes.spell_ability = existing.spellcasting_ability;
} else {
//newObject.attributes.spell_ability = false;
if (page.data["Spellcasting Ability"])
newObject.attributes.spell_ability = `@{${page.data["Spellcasting Ability"].toLowerCase()}_mod}+`;
else if (page.data["spellcasting_ability"])
newObject.attributes.spell_ability = `@{${page.data["spellcasting_ability"].toLowerCase()}_mod}+`;
else if (page.data["Ability"]) newObject.attributes.spell_ability = `@{${page.data["Ability"].toLowerCase()}_mod}+`;
}
page.data["Concentration"] ? (newObject.attributes.spellconcentration = "{{concentration=1}}") : false;
if (existing.innate) {
newObject.attributes.innate = existing.innate;
} else {
page.data["Innate"] ? (newObject.attributes.innate = page.data["Innate"]) : false;
}
if (page.data["Components"]) {
["v", "s", "m"].forEach((component) => {
const componentCheck = dropFunctions.determineComponent(page.data["Components"], component);
newObject.attributes[`spellcomp_${component}`] = componentCheck ? componentCheck : 0;
});
}
if (page.data["Damage"] || page.data["Healing"]) {
newObject.attributes.spelloutput = "ATTACK";
callbacks.push(() => {
create_attack_from_spell(spellLevel, newObject.reprowid, currentData.character_id, existing.spellattackid);
});
}
if (newObject.attributes.spelloutput != "ATTACK" && page.data["Higher Spell Slot Desc"]) {
newObject.attributes.rollcontent = dropFunctions.buildSpellQuery(page.data["Level"]);
}
//Character sheet accepts 'dice', 'beam', or none
if (page.data["data-Cantrip Scaling"]) {
const validateCantripScaling = dropFunctions.validateCantripScaling(page.data["data-Cantrip Scaling"]);
if (validateCantripScaling) {
newObject.attributes.spell_damage_progression = dropFunctions.buildCantripScaling(page.data["data-Cantrip Scaling"]);
} else {
newObject.attributes.spell_damage_progression = "";
}
}
const assignedRowID = newObject.reprowid ? newObject.reprowid : generateRowID();
repeating[`${section}`][assignedRowID] = { spellname: page.name };
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processMonsters = () => {
try {
let newObject = new Npc(page.name);
newObject.addSkills(globalAttributesByCategory.skills.all());
newObject.addSaves(globalAttributesByCategory.abilities);
globalAttributesByCategory.abilities.forEach((ability) => {
const abbreviation = ability.slice(0, 3).toUpperCase();
page.data[`${abbreviation}`] ? (newObject.attributes[`${ability}_base`] = page.data[`${abbreviation}`]) : false;
callbacks.push(() => {
update_attr(`${ability}`);
});
});
const dropSheetAssocitation = {
token_size: page.data["Token Size"],
npc_condition_immunities: page.data["Condition Immunities"],
npc_immunities: page.data["Immunities"],
npc_languages: page.data["Languages"],
npc_vulnerabilities: page.data["Vulnerabilities"],
npc_resistances: page.data["Resistances"],
npc_speed: page.data["Speed"],
npc_challenge: page.data["Challenge Rating"],
npc_pb: page.data["PB"],
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newObject.attributes[`${key}`] = value) : false;
}
page.data["data-XP"] ? (newObject.attributes.npc_xp = page.data["data-XP"].toString().replace(",", "")) : false;
Object.assign(newObject.attributes, dropFunctions.inferSavingThrows(page.data["Saving Throws"]));
Object.assign(newObject.attributes, dropFunctions.inferSkills(page.data["Skills"]));
newObject.attributes.npc_type = dropFunctions.buildType(page.data["Size"], page.data["Type"], page.data["Alignment"]);
newObject.attributes.npc_senses = dropFunctions.buildSenses(page.data["Senses"], page.data["Passive Perception"]);
page.data["AC"] ? (newObject.attributes.npc_ac = dropFunctions.inferBaseAttribute(page.data["AC"])) : false;
page.data["AC"] ? (newObject.attributes.npc_actype = dropFunctions.inferFormulaAttribute(page.data["AC"])) : false;
page.data["HP"] ? (newObject.attributes.hp_max = dropFunctions.inferBaseAttribute(page.data["HP"])) : false;
page.data["HP"] ? (newObject.attributes.npc_hpformula = dropFunctions.inferFormulaAttribute(page.data["HP"])) : false;
const npcRepeating = [
"vehicleweapon",
"vehicleactions",
"vehicle-cargo-items",
"npcaction-l",
"npcreaction",
"npcaction",
"npcbonusaction",
"npctrait",
];
_.each(npcRepeating, (section) => {
getSectionIDs(`repeating_${section}`, (idarray) => {
_.each(idarray, (currentID, i) => {
removeRepeatingRow(`repeating_${section}_${currentID}`);
});
});
});
if (page.data["data-Legendary Actions"]) {
const actionCount = page.data["data-LANum"] ? page.data["data-LANum"] : 3;
newObject.attributes.npc_legendary_actions = actionCount;
if (page.data["Legendary Actions Desc"]) {
newObject.attributes.npc_legendary_actions_desc = page.data["Legendary Actions Desc"];
} else {
newObject.attributes.npc_legendary_actions_desc = `The ${page.name} can take ${actionCount}, choosing from the options below. Only one legendary option can be used at a time and only at the end of another creature's turn. The ${page.name} regains spent legendary actions at the start of its turn.`;
}
}
if (page.data["data-Mythic Actions"]) {
newObject.attributes.npc_mythic_actions = 1;
if (page.data["data-MAdesc"]) {
newObject.attributes.npc_mythic_actions_desc = page.data["data-MAdesc"];
} else {
//TODO UC1098
newObject.attributes.npc_legendary_actions_desc = ``;
}
}
if (page.data["data-Bonus Actions"]) {
newObject.attributes.npcbonusactionsflag = 1;
}
["Actions", "Bonus Actions", "Legendary Actions", "Mythic Actions"].forEach((attr) => {
if (page.data[`data-${attr}`]) {
const fieldsetLookup = {
Actions: "npcaction",
"Bonus Actions": "npcbonusaction",
"Legendary Actions": "npcaction-l",
"Mythic Actions": "npcaction-m",
};
const repeatingFieldset = fieldsetLookup[attr];
const actions = dropFunctions.jsonParse(page.data[`data-${attr}`]);
actions.forEach((action) => {
let newAction = new NpcAction(action["Name"], repeatingFieldset);
action["Desc"] ? (newAction.attributes.description = action["Desc"]) : false;
if (action["Type Attack"]) {
newAction.addAttack();
const dropSheetAttackAssocitation = {
attack_damagetype: action["Damage Type"],
attack_damage: action["Damage"],
attack_tohit: action["Hit Bonus"],
attack_range: action["Reach"],
attack_target: action["Target"],
attack_type: action["Type"],
attack_damage2: action["Damage 2"],
attack_damagetype2: action["Damage 2 Type"],
};
for (let [key, value] of Object.entries(dropSheetAttackAssocitation)) {
value ? (newAction.attributes[`${key}`] = value) : false;
}
} else {
if (action["Damage Type"]) newAction.attributes.attack_damagetype = action["Damage Type"];
if (action["Damage"]) newAction.attributes.attack_damage = action["Damage"];
}
assignUpdate(dropFunctions.updateSheetAttributes(newAction));
});
}
});
if (page.data["data-Reactions"]) {
newObject.attributes.npcreactionsflag = 1;
const reactions = dropFunctions.jsonParse(page.data[`data-Reactions`]);
reactions.forEach((reaction) => {
let newReaction = new NpcReaction(reaction["Name"]);
reaction["Desc"] ? (newReaction.attributes.description = reaction["Desc"]) : false;
assignUpdate(dropFunctions.updateSheetAttributes(newReaction));
});
}
if (page.data["data-Traits"]) {
const traits = dropFunctions.jsonParse(page.data[`data-Traits`]);
traits.forEach((trait) => {
let newTrait = new NpcTraits(trait["Name"]);
trait["Desc"] ? (newTrait.attributes.description = trait["Desc"]) : false;
if (trait.Name.match(/(?!innate )(?:^.{0,6}|.{7})(spellcasting)/i)) {
let spellCastingAbility = page.data["Spellcasting Ability"];
if (spellCastingAbility) {
spellCastingAbility = `@{${spellCastingAbility.toLowerCase()}_mod}+`;
} else {
spellCastingAbility = dropFunctions.inferSpellcastingAbility(newTrait.attributes.description);
}
const casterLevels = dropFunctions.inferCasterLevel(trait["Desc"]);
newObject.addSpellcaster(spellCastingAbility, casterLevels);
callbacks.push(() => update_pb());
callbacks.push(() => update_spell_slots());
} else if (trait.Name.match(/innate spellcasting/i)) {
let spellCastingAbility = page.data["Spellcasting Ability"];
newObject.addInnateCaster();
spellCastingAbility ? (newObject.attributes.spellcasting_ability = `@{${spellCastingAbility.toLowerCase()}_mod}+`) : false;
}
assignUpdate(dropFunctions.updateSheetAttributes(newTrait));
});
}
if (page.data["data-Spells"]) {
const spellList = dropFunctions.buildSpellList(page.data["data-Spells"]);
getCompendiumPage(spellList, (compendiumPages) => {
compendiumPages = removeDuplicatedPageData(compendiumPages);
compendiumPages = Array.isArray(compendiumPages) ? compendiumPages : [compendiumPages];
const innateSpellLists = dropFunctions.jsonParse(page.data["data-Spells"])[`innate`] || false;
let spellUpdate = {},
spellCallbacks = [];
compendiumPages.forEach((spellPage) => {
const processedSpell = processDrop(spellPage, currentData, repeating);
Object.assign(spellUpdate, processedSpell.update);
processedSpell.callbacks.forEach((callback) => spellCallbacks.push(callback));
});
if (innateSpellLists) {
//Search each keys in INNATE to see if spell name === one of the Values
for (let [innateKey, innateValue] of Object.entries(innateSpellLists)) {
innateValue.forEach((spellName) => {
for (let [key, value] of Object.entries(spellUpdate)) {
if (key.includes("_spellname") && (value === spellName || value.toLowerCase() === spellName)) {
const repeatingRow = key.split("_spellname")[0];
spellUpdate[`${repeatingRow}_innate`] = innateKey;
}
}
});
}
}
setAttrs(spellUpdate, { silent: true }, () => {
spellCallbacks.forEach((callback) => {
callback();
});
});
});
}
//Call backs
if (page.data["Saving Throws"]) {
callbacks.push(() => update_npc_saves());
}
if (page.data["Skills"]) {
callbacks.push(() => update_npc_skills());
}
if (page.data[`data-Actions`]) {
callbacks.push(() => update_npc_action("all"));
}
callbacks.push(() => update_challenge());
globalAttributesByCategory.abilities.forEach((ability) => {
callbacks.push(() => update_attr(`${ability}`));
});
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
} catch (error) {
errorMessage(page.name, error);
}
};
const processVehicles = () => {
try {
let newObject = new Vehicle(page.name);
const dropSheetAssocitation = {
token_size: page.data["Token Size"],
vehicle_type: page.data["Ship Type"],
vehicle_size: page.data["Size"].toLowerCase(),
vehicle_base_speed: page.data["Base Speed"],
vehicle_mobility: page.data["Mobility"].toLowerCase(),
vehicle_fuel_items: page.data["Fuel Items"],
vehicle_hp: page.data["HP"],
vehicle_hp_max: page.data["HP"],
vehicle_explosion_cap: page.data["Explosion Cap"],
vehicle_bow_slots: page.data["Bow Slots"],
vehicle_port_slots: page.data["Port Slots"],
vehicle_starboard_slots: page.data["Starboard Slots"],
vehicle_stern_slots: page.data["Stern Slots"],
vehicle_unranked_crew: page.data["Unranked Crew"],
vehicle_unranked_crew_max: page.data["Unranked Crew"],
vehicle_maximum_crew: page.data["Maximum Crew"],
vehicle_maximum_crew_bonus: page.data["Maximum Crew Bonus"],
vehicle_skeleton_crew: page.data["Skeleton Crew"],
vehicle_mettle_pool: page.data["Mettle Pool"],
vehicle_crew_boons: page.data["Relevant Crew Boons"]
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newObject.attributes[`${key}`] = value) : false;
}
const npcRepeating = [
"vehicleweapon-bow",
"vehicleweapon-port",
"vehicleweapon-starboard",
"vehicleweapon-stern",
"vehicleactions",
"vehicle-cargo-items",
"vehicleofficer",
"vehicleupgrade",
"vehicleammo",
"npcaction-l",
"npcreaction",
"npcaction",
"npcbonusaction",
"npctrait"
];
_.each(npcRepeating, (section) => {
getSectionIDs(`repeating_${section}`, (idarray) => {
_.each(idarray, (currentID, i) => {
removeRepeatingRow(`repeating_${section}_${currentID}`);
});
});
});
if (page.data["data-Weapons"]) {
const weapons = dropFunctions.jsonParse(page.data[`data-Weapons`]);
weapons.forEach((weapon) => {
if(!weapon["Slot"]) return
let newWeapon = new VehicleWeapon(weapon["Name"], null, weapon["Slot"]);
const dropSheetAssocitation = {
quantity: weapon["Quantity"],
target_die: weapon["Target Die"],
range: weapon["Range"].toLowerCase(),
weight: weapon["Weight"].toLowerCase(),
additional_effects: weapon["Additional Effects"],
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newWeapon.attributes[`${key}`] = value) : false;
}
assignUpdate(dropFunctions.updateSheetAttributes(newWeapon));
});
}
if (page.data["data-Ammo"]) {
const ammos = dropFunctions.jsonParse(page.data[`data-Ammo`]);
ammos.forEach((ammo) => {
let newammo = new VehicleCargo(ammo["Name"]);
const dropSheetAssocitation = {
quantity: ammo["Quantity"],
description: ammo["Description"],
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newammo.attributes[`${key}`] = value) : false;
}
assignUpdate(dropFunctions.updateSheetAttributes(newammo));
});
}
if (page.data["data-Upgrades"]) {
const upgrades = dropFunctions.jsonParse(page.data[`data-Upgrades`]);
upgrades.forEach((upgrade) => {
let newupgrade = new VehicleUpgrade(upgrade["Name"]);
const dropSheetAssocitation = {
description: upgrade["Description"],
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newupgrade.attributes[`${key}`] = value) : false;
}
assignUpdate(dropFunctions.updateSheetAttributes(newupgrade));
});
}
if (page.data["data-Prizes"]) {
const prizes = dropFunctions.jsonParse(page.data[`data-Prizes`]);
prizes.forEach((prize) => {
let newprize = new VehicleCargo(prize["Name"]);
const dropSheetAssocitation = {
quantity: prize["Quantity"],
weight: prize["Weight"]
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newprize.attributes[`${key}`] = value) : false;
}
assignUpdate(dropFunctions.updateSheetAttributes(newprize));
});
}
//Call backs
if (page.data["Saving Throws"]) {
callbacks.push(() => update_npc_saves());
}
if (page.data["Skills"]) {
callbacks.push(() => update_npc_skills());
}
if (page.data[`data-Actions`]) {
callbacks.push(() => update_npc_action("all"));
}
if (update["npc_challenge"]) {
callbacks.push(() => update_challenge());
}
globalAttributesByCategory.abilities.forEach((ability) => {
callbacks.push(() => update_attr(`${ability}`));
});
callbacks.push(() => update_linked_vehicle_actions());
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
} catch (error) {
errorMessage(page.name, error);
}
};
const processVehicleUpgrades = () => {
const isVehicle = currentData.npc == "1" && currentData.is_vehicle == "1";
if (!isVehicle) return;
try {
const upgradeNames = [];
if (page.data["data-Upgrades"]) {
const upgrades = dropFunctions.jsonParse(page.data[`data-Upgrades`]);
upgrades.forEach((upgrade) => {
const existingIndex = -1; //Object.values(repeating[`vehicleupgrade-${page.slot.toLowerCase()}`]).indexOf(upgrade["Name"]);
const existingID = existingIndex > -1 ? Object.keys(repeating[`vehicleupgrade-${page.slot.toLowerCase()}`])[existingIndex] : "";
if (existingID) {
return;
} else {
upgradeNames.push(upgrade["Name"]);
let newUpgrade = new VehicleUpgrade(upgrade["Name"], null, page.slot);
const dropSheetAssocitation = {
description: upgrade["Description"],
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newUpgrade.attributes[`${key}`] = value) : false;
}
assignUpdate(dropFunctions.updateSheetAttributes(newUpgrade));
}
});
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processVehicleWeapons = () => {
const isVehicle = currentData.npc == "1" && currentData.is_vehicle == "1";
if (!isVehicle) return;
try {
const weaponNames = [];
if (page.data["data-Weapons"]) {
const weapons = dropFunctions.jsonParse(page.data[`data-Weapons`]);
weapons.forEach((weapon) => {
if(!page.slot) return
const existingIndex = Object.values(repeating[`vehicleweapon-${page.slot.toLowerCase()}`]).indexOf(weapon["Name"]);
const existingID = existingIndex > -1 ? Object.keys(repeating[`vehicleweapon-${page.slot.toLowerCase()}`])[existingIndex] : "";
if (existingID) {
const quantityAttr = `repeating_vehicleweapon-${page.slot.toLowerCase()}_${existingID}_quantity`;
getAttrs([quantityAttr], (values) => {
const newQuantity = values[quantityAttr] && !isNaN(values[quantityAttr]) ? String(parseInt(values[quantityAttr]) + 1) : "2";
setAttrs({ [quantityAttr]: newQuantity });
});
} else {
weaponNames.push(weapon["Name"]);
let newWeapon = new VehicleWeapon(weapon["Name"], null, page.slot);
const dropSheetAssocitation = {
quantity: '1',
target_die: weapon["Target Die"],
range: weapon["Range"].toLowerCase(),
weight: weapon["Weight"].toLowerCase(),
additional_effects: weapon["Additional Effects"],
};
for (let [key, value] of Object.entries(dropSheetAssocitation)) {
value ? (newWeapon.attributes[`${key}`] = value) : false;
}
assignUpdate(dropFunctions.updateSheetAttributes(newWeapon));
}
});
}
["Actions"].forEach((attr) => {
if (page.data[`data-${attr}`]) {
const actions = dropFunctions.jsonParse(page.data[`data-${attr}`]);
actions.forEach((action, index) => {
const existingIndex = Object.values(repeating.vehicleactions).indexOf(action["Name"]);
if (existingIndex > -1) return;
const defaultParent = weaponNames.length >= index + 1 ? weaponNames[index] : "";
const parent = action["Weapon"] ? action["Weapon"] : defaultParent;
let newAction = new VehicleAction(action["Name"], parent);
action["Desc"] ? (newAction.attributes.description = action["Desc"]) : false;
if (action["Type Attack"]) {
newAction.addAttack();
const dropSheetAttackAssocitation = {
attack_damagetype: action["Damage Type"],
attack_damage: action["Damage"],
attack_tohit: action["Hit Bonus"],
attack_range: action["Reach"],
attack_target: action["Target"],
attack_type: action["Type"],
attack_damage2: action["Damage 2"],
attack_damagetype2: action["Damage 2 Type"],
};
for (let [key, value] of Object.entries(dropSheetAttackAssocitation)) {
value ? (newAction.attributes[`${key}`] = value) : false;
}
}
assignUpdate(dropFunctions.updateSheetAttributes(newAction));
});
}
});
//Call backs
if (page.data[`data-Actions`]) {
callbacks.push(() => update_npc_action("all"));
}
callbacks.push(() => update_linked_vehicle_actions());
} catch (error) {
errorMessage(page.name, error);
}
};
const processFeats = () => {
try {
const match = { name: page.name };
let existing = _.findWhere(repeating.traits, match);
existing = existing ? existing : {};
let newObject = new Trait(page.name, existing.id || false);
newObject.attributes.description = page.data["data-Description"] || page.content;
newObject.attributes.source = newObject.determineSource(category);
newObject.attributes.source_type = page.data["Properties"] || "";
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
existing = dropHelpers.updateRepeatingTraits(newObject, existing);
existing ? false : repeating.traits.push(existing);
resetTraitFilters();
} catch (error) {
errorMessage(page.name, error);
}
};
const processProficiencies = () => {
try {
const proficiencies = globalAttributesByCategory.proficiencyTypes;
const tools = ["tool", "skillcustom"];
let type = page.data["Type"] || "";
type = proficiencies.includes(type.toUpperCase())
? "proficiencies"
: tools.includes(type.toLowerCase())
? "tool"
: type.toLowerCase() === "skill"
? "skill"
: false;
if (type === "proficiencies") {
if (repeating.prof_names.indexOf(page.name.toLowerCase()) == -1) {
let newObject = new Proficiency(page.name);
proficiencies.includes(page.data["Type"].toUpperCase())
? (newObject.attributes.prof_type = page.data["Type"].toUpperCase())
: false;
repeating.prof_names.push(page.name.toLowerCase());
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
}
} else if (type === "tool") {
let existing = { id: false };
existing.id = dropHelpers.findToolId(repeating.tool, page.name);
let newObject = new ToolProficiency(page.name, existing.id);
page.data["toolbonus_base"] ? (newObject.attributes.toolbonus_base = "(@{pb}*2)") : false;
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
existing = dropHelpers.updateRepeatingTools(newObject, existing);
repeating.tool[newObject.reprowid] = existing;
repeating.prof_names.push(page.name.toLowerCase());
//Updates the displays for the tool...
callbacks.push(() => {
update_tool(newObject.reprowid);
});
} else if (type === "skill") {
const skillAttribute = proficiency.toLowerCase().replace(/ /g, "_");
if(globalAttributesByCategory.skills.all().includes(skillAttribute)) {
let newObject = new Skill(proficiency);
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
} else {
let newObject = new ToolProficiency(page.name, existing.id);
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
}
} else {
warningMessage(
`${page.data["Type"]}`,
`No an approved type. Accepted types are skill, ${proficiencies.concat(tools).join(" ,")}. Update compendium "Type".`
);
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processClass = () => {
try {
if (page.data.multiclass) {
if (page.name && page.name !== "") {
update[page.data.multiclass] = page.name;
}
update[page.data.multiclass + "_flag"] = "1";
classlevel = parseInt(currentData[page.data.multiclass + "_lvl"]);
} else {
let newObject = new Class(page.name);
if (page.data["Hit Die"] && page.data["Hit Die"] !== "") {
currentData.base_level ? (newObject.attributes.base_level = currentData.base_level) : false;
newObject.attributes.hit_dice_max = newObject.attributes.base_level + page.data["Hit Die"];
newObject.attributes.hitdietype = page.data["Hit Die"].replace(/[^0-9]/g, "");
newObject.attributes.hit_dice = newObject.attributes.base_level;
}
const spellcastingAbility = page.data["Spellcasting Ability"] ? page.data["Spellcasting Ability"].toLowerCase() : false;
if (spellcastingAbility && globalAttributesByCategory.abilities.includes(spellcastingAbility)) {
newObject.attributes.spellcasting_ability = newObject.buildSpellcastingAbility(spellcastingAbility);
}
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
}
if (page.data["data-Saving Throws"] && !page.data.multiclass) {
const saves = Array.isArray(page.data["data-Saving Throws"])
? page.data["data-Saving Throws"]
: dropFunctions.jsonParse(page.data["data-Saving Throws"]);
saves.forEach((value) => {
const save = new SavingThrow(value);
assignUpdate(dropFunctions.updateSheetAttributes(save));
});
}
if (!looped) {
callbacks.push(update_class);
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processSubclasses = () => {
try {
const isThirdCaster = typeof page.data["data-Third Caster"] !== "undefined";
const lowercaseClass = page.data.Class.toLowerCase();
if (page.data.multiclass) {
update[`${page.data.multiclass}_subclass`] = page.name;
classlevel = parseInt(currentData[`${page.data.multiclass}_lvl`]);
if (isThirdCaster) {
update[`third_caster`] = "1";
}
if (page.data["Spellcasting Ability"] && (lowercaseClass === "fighter" || lowercaseClass === "rogue")) {
update[`arcane_${lowercaseClass}`] = "1";
}
} else {
let newObject = new Subclass(page.name);
const spellcastingAbility = page.data["Spellcasting Ability"] ? page.data["Spellcasting Ability"].toLowerCase() : false;
if (spellcastingAbility && globalAttributesByCategory.abilities.includes(spellcastingAbility)) {
newObject.attributes.spellcasting_ability = newObject.buildSpellcastingAbility(spellcastingAbility);
if (newObject.checkArcane(lowercaseClass)) newObject.attributes[`arcane_${lowercaseClass}`] = 1;
if (isThirdCaster) {
newObject.attributes[`third_caster`] = "1";
}
}
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
}
if (!looped) {
callbacks.push(update_class);
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processRaces = () => {
try {
let newObject = new Race(page.name);
page.data["Size"] && page.data["Size"] != "Medium" ? (newObject.attributes.size = page.data["Size"]) : false;
page.data["Speed"] && page.data["Speed"] != 30 ? (newObject.attributes.speed = page.data["Speed"]) : false;
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
if (!looped) {
callbacks.push(update_race_display);
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processSubraces = () => {
try {
let newObject = new Subrace(page.name);
page.data["Size"] && page.data["Size"] ? (newObject.attributes.size = page.data["Size"]) : false;
page.data["Speed"] && page.data["Speed"] ? (newObject.attributes.speed = page.data["Speed"]) : false;
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
if (!looped) {
callbacks.push(update_race_display);
}
} catch (error) {
errorMessage(page.name, error);
}
};
const processOtherOptionsAndFeatures = () => {
//Do nothing...
};
const processBackgrounds = () => {
try {
update["background"] = page.name;
} catch (errorMessage) {
errorMessage(page.name, error);
}
};
switch (category) {
case "Backgrounds":
processBackgrounds();
break;
case "Classes":
processClass();
break;
case 'Traits':
case "Feats":
processFeats();
break;
case "Items":
processItems();
break;
case "Monsters":
processMonsters();
break;
case "Vehicles":
processVehicles();
break;
case "Vehicle Upgrades":
processVehicleUpgrades();
break;
case "Vehicle Weapons":
processVehicleWeapons();
break;
case "Proficiencies":
processProficiencies();
break;
case "Spells":
processSpells();
break;
case "Subclasses":
processSubclasses();
break;
case "Races":
processRaces();
break;
case "Subraces":
processSubraces();
break;
case "Other Options and Features":
processOtherOptionsAndFeatures();
break;
default:
errorMessage(category, `category does not match one of the drop functions conditions.`);
}
if (page.data.theseblobs) {
_.each(page.data.theseblobs, function (blobname) {
if (page.data.blobs[blobname]) blobs[blobname] = page.data.blobs[blobname];
});
} else {
if (category === "Other Options and Features") {
blobs = page.data.blobs;
} else {
blobs = filterBlobs(page.data.blobs, { Level: "1" });
}
}
for (let [blobName, blob] of Object.entries(blobs)) {
if (blob["Traits"]) {
try {
const traitArray = dropFunctions.jsonParse(blob["Traits"]);
if (traitArray && traitArray.length) {
traitArray.forEach((trait) => {
if (!trait.Input) {
let match = { name: trait["Name"], type: page.name };
if (trait["Replace"]) {
match = { name: trait["Replace"] };
}
let existing = _.findWhere(repeating.traits, match);
existing = existing ? existing : {};
let newObject = new Trait(trait["Name"].replace(/{{Input}}/g, ""), existing.id || false);
newObject.attributes.source = newObject.determineSource(category);
trait["Desc"] ? (newObject.attributes.description = trait["Desc"].replace(/{{Input}}/g, "")) : false;
page.name ? (newObject.attributes.source_type = removeExpansionInfo(page.name)) : false;
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
existing = dropHelpers.updateRepeatingTraits(newObject, existing);
existing ? false : repeating.traits.push(existing);
resetTraitFilters();
if (trait["Name"] === "Reliable Talent") {
update["reliable_talent"] = "10";
} else if (trait["Name"] === "Powerful Build") {
update["powerful_build"] = "1";
}
}
});
}
} catch (error) {
errorMessage(trait.Name, error);
}
}
["Language Proficiency", "Weapon Proficiency", "Armor Proficiency"].forEach((proficiencyType) => {
try {
const proficiencyArray = blob[`${proficiencyType}`] ? dropFunctions.jsonParse(blob[`${proficiencyType}`]) : false;
if (proficiencyArray && proficiencyArray["Proficiencies"]) {
proficiencyArray["Proficiencies"].forEach((proficiency) => {
if (repeating.prof_names.indexOf(proficiency.toLowerCase()) == -1) {
let newObject = new Proficiency(proficiency);
newObject.attributes.prof_type = proficiencyType.split(" Proficiency")[0].toUpperCase();
repeating.prof_names.push(proficiency.toLowerCase());
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
}
});
}
} catch (error) {
errorMessage(proficiencyType, error);
}
});
if (blob["Tool Proficiency"]) {
try {
const toolArray = dropFunctions.jsonParse(blob["Tool Proficiency"]);
if (toolArray["Proficiencies"] && toolArray["Proficiencies"].length) {
toolArray["Proficiencies"].forEach((proficiency) => {
let existing = { id: false };
existing.id = dropHelpers.findToolId(repeating.tool, proficiency);
let newObject = new ToolProficiency(proficiency, existing.id);
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
existing = dropHelpers.updateRepeatingTools(newObject, existing);
repeating.tool[newObject.reprowid] = existing;
repeating.prof_names.push(page.name.toLowerCase());
//Updates the displays for the tool...
callbacks.push(() => {
update_tool(newObject.reprowid);
});
});
}
} catch (error) {
errorMessage("Tool Proficiency", error);
}
}
if (blob["Skill Proficiency"]) {
try {
const skillArray = dropFunctions.jsonParse(blob["Skill Proficiency"]);
if (skillArray["Proficiencies"] && skillArray["Proficiencies"].length) {
skillArray["Proficiencies"].forEach((proficiency) => {
const skillAttribute = proficiency.toLowerCase().replace(/ /g, "_");
if(globalAttributesByCategory.skills.all().includes(skillAttribute)) {
let newObject = new Skill(proficiency);
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
} else {
let newObject = new ToolProficiency(page.name, existing.id);
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
}
});
callbacks.push(() => {
update_skills([
"athletics",
"acrobatics",
"sleight_of_hand",
"stealth",
"arcana",
"history",
"investigation",
"nature",
"navigation",
"religion",
"animal_handling",
"insight",
"medicine",
"perception",
"survival",
"deception",
"intimidation",
"performance",
"persuasion",
]);
});
}
} catch (error) {
errorMessage("Skill Proficiency", error);
}
}
if (blob["Global Damage"]) {
try {
const dmgmod = dropFunctions.jsonParse(blob["Global Damage"]);
let existing = {};
for (let [key, value] of Object.entries(repeating.damagemod)) {
value.toLowerCase() === dmgmod["Name"].toLowerCase() ? (existing.id = key) : false;
}
let newObject = new GlobalDamage(dmgmod["Name"], existing.id || false);
newObject.attributes.global_damage_damage = `${parseValues(dmgmod["Damage"])}`;
dmgmod["Active"] == "true" ? (newObject.attributes.global_damage_active_flag = 1) : false;
newObject.attributes.global_damage_type = dmgmod["Type"] ? dmgmod["Type"] : dmgmod["Name"];
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
update["global_damage_mod_flag"] = "1";
repeating.damagemod[newObject.reprowid] = dmgmod["Name"];
} catch (error) {
errorMessage("Global Damage", error);
}
}
if (blob["Actions"]) {
try {
let actionsObject = {};
dropFunctions.jsonParse(blob["Actions"]).forEach((value) => (actionsObject[value.Name] = value));
for (let [actionName, action] of Object.entries(actionsObject)) {
let existing = {};
for (let [key, value] of Object.entries(repeating.attack)) {
value.atkname === actionName ? (existing.id = key) : false;
}
let newObject = new Attack(actionName, existing.id || false);
action["Desc"] ? (newObject.attributes.atk_desc = action["Desc"]) : false;
if (action["Type Attack"]) {
if (action["Type"] === "Spell") {
newObject.attributes.atkflag = 0;
newObject.addSaveFlag();
}
action["Reach"] ? (newObject.attributes.atkrange = action["Reach"]) : false;
action["Damage"] ? (newObject.attributes.dmgbase = action["Damage"]) : false;
!action["Damage"] ? (newObject.attributes.dmgflag = 0) : false;
action["Damage Type"] ? (newObject.attributes.dmgtype = action["Damage Type"]) : false;
if (action["Modifier"]) {
if (action["Modifier"] === "FIN") {
newObject.checkFinesse(parseInt(currentData.strength_base), parseInt(currentData.dexterity_base));
} else {
const actionModifier = dropFunctions.convertAbbreviationToAttribute(action["Modifier"]);
actionModifier && actionModifier != newObject.attributes.dmgattr ? (newObject.attributes.dmgattr = actionModifier) : false;
actionModifier && actionModifier != newObject.attributes.atkattr_base
? (newObject.attributes.atkattr_base = actionModifier)
: false;
}
} else {
newObject.attributes.dmgattr = 0;
}
action["Save"] ? (newObject.attributes.saveattr = action["Save"]) : false;
action["Save Effect"] ? (newObject.attributes.saveeffect = action["Save Effect"]) : false;
if (action["Save DC"]) {
const saveDCModifier = dropFunctions.convertAbbreviationToAttribute(action["Save DC"]);
newObject.attributes.savedc = `(@{saveflat})`;
newObject.attributes.saveflat = `(8+@{pb}+${saveDCModifier})`;
}
if (action["Damage 2"] && action["Damage 2 Type"]) {
newObject.attributes.dmg2flag = "{{damage=1}} {{dmg2flag=1}}";
newObject.attributes.atk_dmg2base = action["Damage 2"];
newObject.attributes.attack_damagetype2 = action["Damage 2 Type"];
if (action["Modifier 2"]) {
newObject.attributes.dmg2attr = dropFunctions.convertAbbreviationToAttribute(action["Modifier 2"]);
} else {
newObject.attributes.dmgattr = 0;
}
}
}
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
repeating.attack[`${newObject.reprowid}`] = { atkname: newObject.attributes.atkname };
callbacks.push(() => {
do_update_attack([`${newObject.reprowid}`]);
});
}
} catch (error) {
errorMessage("Actions", error);
}
}
if (blob["Resources"]) {
try {
const resourcesArray = dropFunctions.jsonParse(blob["Resources"]);
resourcesArray.forEach((resource) => {
let section = undefined;
const findCurrentResource = (resourceName) => {
if (currentData["class_resource_name"] === resourceName) {
return "class_resource";
} else if (currentData["other_resource_name"] === resourceName) {
return "other_resource";
} else {
for (let [repeatingIDKey, repeatingValue] of Object.entries(repeating.resource)) {
return Object.values(repeatingValue).includes(resourceName) ? repeatingIDKey : false;
}
}
};
//Testing this funtion to find an empty resource
const findEmptyResource = () => {
if (currentData["class_resource_name"].length === 0) {
return "class_resource";
} else if (currentData["other_resource_name"].length === 0) {
return "other_resource";
} else {
let foundEmptyString = false;
for (let [repeatingIDKey, repeatingValue] of Object.entries(repeating.resource)) {
if (repeatingValue.left.length === 0) {
return `repeating_resource_${repeatingIDKey}_resource_left`;
} else if (repeatingValue.right.length === 0) {
return `repeating_resource_${repeatingIDKey}_resource_right`;
} else {
foundEmptyString = false;
}
}
return foundEmptyString;
}
};
let matchResource = findCurrentResource(resource.Name);
if (matchResource) {
if (matchResource === "class_resource" || matchResource === "other_resource") {
section = matchResource;
} else {
const repeatingSide = repeating.resource[matchResource].left === resource.Name ? "left" : "right";
section = `repeating_resource_${matchResource}_resource_${repeatingSide}`;
}
} else {
section = findEmptyResource();
}
//Section will be true if a repeating entry matches resource.Name or is has an empty side ||
//class/other_resource match the resource.Name or are empty
const validResetOptions = { sr: "short", lr: "long" };
if (section) {
update[`${section}_name`] = resource.Name;
if (resource.Uses) update[section] = numUses(resource.Uses);
if (resource.Uses && resource.Uses.toLowerCase() === "pb") {
update[`${section}_max`] = "";
update[`${section}_use_pb`] = "1";
} else {
update[`${section}_max`] = resource.Max ? numUses(resource.Max) : numUses(resource.Uses);
}
if (resource.Reset && validResetOptions.hasOwnProperty(resource.Reset.toLowerCase())) {
update[`${section}_reset`] = validResetOptions[resource.Reset.toLowerCase()];
}
if (section === "class_resource" || section === "other_resource") {
currentData[`${section}_name`] = resource.Name;
} else {
const sectionRepeatingId = section.split("_resource_")[1];
const sectionSide = section.includes("left") ? "left" : "right";
repeating.resource[`${sectionRepeatingId}`][sectionSide] = resource.Name;
}
} else {
const newResource = new Resource(resource.Name);
newResource.attributes.resource_left = resource.Uses ? numUses(resource.Uses) : 0;
if (resource.Uses && resource.Uses.toLowerCase() === "pb") {
newResource.attributes.resource_left_use_pb = 1;
} else {
newResource.attributes.resource_left_max = resource.Max ? numUses(resource.Max) : newResource.attributes.resource_left;
}
if (resource.Reset && validResetOptions.hasOwnProperty(resource.Reset.toLowerCase())) {
newResource.attributes.resource_left_reset = validResetOptions[resource.Reset.toLowerCase()];
}
assignUpdate(dropFunctions.updateSheetAttributes(newResource));
const addRepeating = {
[`${newResource.reprowid}`]: {
left: resource.Name,
right: "",
},
};
Object.assign(repeating.resource, addRepeating);
}
});
} catch (error) {
errorMessage("Resources", error);
}
}
if (blob["Custom AC"]) {
try {
const parsedKey = dropFunctions.jsonParse(blob["Custom AC"]);
customACFormulas = [...customACFormulas, dropFunctions.jsonParse(blob["Custom AC"])];
update["custom_ac_flag"] = "1";
update["custom_ac_array"] = JSON.stringify(customACFormulas);
if (!looped) {
callbacks.push(function () {
update_ac();
});
}
} catch (error) {
errorMessage("Custom AC", error);
}
}
if (blob["Hit Points Per Level"]) {
try {
const parsedKey = dropFunctions.jsonParse(blob["Hit Points Per Level"]);
let existing = {};
for (let [key, value] of Object.entries(repeating.hpmod)) {
value.source === blobName ? (existing.id = key) : false;
}
let newObject = new HPMod(blobName, existing.id || false);
newObject.attributes.mod = blob["Hit Points Per Level"];
newObject.attributes.source = blobName === "Wildspace Adaptation" ? "Tough" : blobName;
newObject.attributes.levels =
category === "Races" || category === "Subraces" || newObject.attributes.source === "Tough" ? "total" : "base";
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
} catch (error) {
errorMessage("Hit Points Per Level", error);
}
}
if (blob["Hit Points Bonus"]) {
let prevHp = update["hp"] || currentData["hp"];
let prevHpMax = update["hp_max"] || currentData["hp_max"];
prevHp = prevHp && !isNaN(parseInt(prevHp)) ? parseInt(prevHp) : 0;
prevHpMax = prevHpMax && !isNaN(parseInt(prevHpMax)) ? parseInt(prevHpMax) : 0;
update["hp"] = prevHp + parseInt(blob["Hit Points Bonus"]);
update["hp_max"] = prevHpMax + parseInt(blob["Hit Points Bonus"]);
}
["Global AC Mod", "Global Save", "Global Attack"].forEach((globalType) => {
try {
if (blob[globalType]) {
const globalShortName = globalType.includes("Attack") ? "attack" : globalType.includes("Save") ? "save" : "ac";
const repeatingKey = globalShortName === "attack" ? "tohitmod" : globalShortName === "save" ? "savemod" : "acmod";
const parsedKey = dropFunctions.jsonParse(blob[`${globalType}`]);
let existing = { id: false };
for (let [key, value] of Object.entries(repeating[`${repeatingKey}`])) {
value.includes(blobName) ? (existing.id = key) : false;
}
let newObject = {};
if (globalType === "Global AC Mod") {
newObject = new GlobalACMod(parsedKey.Name, existing.id);
newObject.attributes.global_ac_val = parsedKey.Bonus;
} else if (globalType === "Global Save") {
newObject = new GlobalSave(parsedKey.Name, existing.id);
newObject.attributes.global_save_roll = parsedKey.Bonus;
} else {
newObject = new GlobalAttack(parsedKey.Name, existing.id);
newObject.addRoll(parsedKey.Bonus);
}
parsedKey.Active !== "false" ? (newObject.attributes[`global_${globalShortName}_active_flag`] = 1) : false;
assignUpdate(dropFunctions.updateSheetAttributes(newObject));
update[`global_${globalShortName}_mod_flag`] = "1";
}
} catch (error) {
errorMessage(`${globalType}`, error);
}
});
if (blob["Initiative"]) {
if (blob["Initiative"].toLowerCase() === "advantage") {
update["initiative_style"] = "{@{d20},@{d20}}kh1";
} else if (blob["Initiative"].toLowerCase() === "disadvantage") {
update["initiative_style"] = "{@{d20},@{d20}}kl1";
} else {
update.initmod = numUses(blob["Initiative"]);
}
}
if (blob["Carry Multiplier"]) {
update["carrying_capacity_mod"] = `*${blob["Carry Multiplier"]}`;
}
if (blob["Speed"]) {
try {
if (blob["Speed"][0] === "+") {
let prevspeed = update["speed"] || currentData["speed"];
prevspeed = prevspeed && !isNaN(parseInt(prevspeed)) ? parseInt(prevspeed) : 0;
update["speed"] = prevspeed + parseInt(blob["Speed"]);
} else {
update["speed"] = parseInt(blob["Speed"]);
}
} catch (error) {
errorMessage("Speed", error);
}
}
if (blob["Jack of All Trades"]) {
update["jack_of_all_trades"] = "@{jack}";
}
if (blob["Global Save Mod"]) {
update["globalsavemod"] = numUses(blob["Global Save Mod"]);
}
if (blob["Saving Throws"]) {
try {
const parsedArray = dropFunctions.jsonParse(blob["Saving Throws"]);
parsedArray.forEach((value) => {
const save = new SavingThrow(value);
assignUpdate(dropFunctions.updateSheetAttributes(save));
});
} catch (error) {
errorMessage("Saving Throws", error);
}
}
if (blob["Proficiency Bonus"]) {
try {
const parsedBonus = dropFunctions.jsonParse(blob["Proficiency Bonus"]);
for (let [key, value] of Object.entries(parsedBonus)) {
update[`${key.replace(/ /g, "_").toLowerCase()}_flat`] = numUses(value);
}
update_skills([
"athletics",
"acrobatics",
"sleight_of_hand",
"stealth",
"arcana",
"history",
"investigation",
"nature",
"navigation",
"religion",
"animal_handling",
"insight",
"medicine",
"perception",
"survival",
"deception",
"intimidation",
"performance",
"persuasion",
]);
} catch (error) {
errorMessage("Proficiency Bonus", error);
}
}
if (blob["Spells"]) {
try {
let spells = dropFunctions.jsonParse(blob["Spells"]);
let spellUpdate = {},
spellCallbacks = [];
let spellPages = [];
spells.forEach((spell) => {
if (spell.Name) spellPages.push(spell.Name);
if (spell.Known) spellPages = spellPages.concat(spell.Known);
});
spellPages = Array.from(new Set(spellPages));
getCompendiumPage(spellPages, (spellData) => {
var inSpellBook = function (name) {
let found = false;
const spellRepeating = ["cantrip", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
spellRepeating.forEach((spellLevel) => {
if (found) return;
if (repeating[`spell-${spellLevel}`]) {
Object.keys(repeating[`spell-${spellLevel}`]).forEach((entry) => {
if (repeating[`spell-${spellLevel}`][entry]["spellname"] === name) {
found = true;
return;
}
});
}
});
return found;
};
spellData = removeDuplicatedPageData(spellData);
spells.forEach((spellInformation) => {
if (spellInformation.Known) {
if (Array.isArray(spellInformation.Known)) {
spellInformation.Known.forEach((s) => {
if (!inSpellBook(s)) {
const mockPage = {
name: s,
data: {
Category: "Spells",
},
};
const pageData = Array.isArray(spellData)
? spellData.filter((entry) => {
return entry["name"].toLowerCase() === s.toLowerCase();
})[0]
: spellData;
if (pageData) {
Object.assign(mockPage.data, pageData.data);
Object.assign(mockPage.data, spellInformation);
const processedSpell = processDrop(mockPage, currentData, repeating);
Object.assign(spellUpdate, processedSpell.update);
processedSpell.callbacks.forEach((callback) => spellCallbacks.push(callback));
}
}
});
}
} else {
if (spellInformation.Name && !inSpellBook(spellInformation.Name)) {
const mockPage = {
name: spellInformation.Name,
data: {
Category: "Spells",
},
};
const pageData = Array.isArray(spellData)
? spellData.filter((entry) => {
return entry["name"] === spellInformation.Name;
})[0]
: spellData;
Object.assign(mockPage.data, pageData.data);
Object.assign(mockPage.data, spellInformation);
const processedSpell = processDrop(mockPage, currentData, repeating);
Object.assign(spellUpdate, processedSpell.update);
processedSpell.callbacks.forEach((callback) => spellCallbacks.push(callback));
}
}
});
if (Object.keys(spellUpdate).length > 0) {
setAttrs(spellUpdate, { silent: true }, () => {
spellCallbacks.forEach((callback) => {
callback();
});
});
}
});
} catch (error) {
errorMessage("Spells", error);
}
}
if (blob["Custom Spells"]) {
try {
let customSpells = dropFunctions.jsonParse(blob["Custom Spells"]);
let spellUpdate = {},
spellCallbacks = [];
customSpells.forEach((spellInformation) => {
const mockPage = {
name: spellInformation.Name,
data: {
Category: "Spells",
},
};
Object.assign(mockPage.data, spellInformation);
const processedSpell = processDrop(mockPage, currentData, repeating);
Object.assign(spellUpdate, processedSpell.update);
processedSpell.callbacks.forEach((callback) => spellCallbacks.push(callback));
});
setAttrs(spellUpdate, { silent: true }, () => {
spellCallbacks.forEach((callback) => {
callback();
});
});
} catch (error) {
errorMessage("Custom Spells", error);
}
}
}
return {
update: update,
repeating: repeating,
callbacks: callbacks,
};
};
/*********************************************************************/
/*********************************************************************/
/****** ******/
/****** CHARACTERMANCER WORKERS ******/
/****** ******/
/*********************************************************************/
/*********************************************************************/
var proficiencyList = ["Weapon", "Armor", "Skill", "Tool", "Language", "Other"];
var changeListeners =
"mancerchange:race_ability_choice1 mancerchange:race_ability_choice2 mancerchange:race_ability_choice3 mancerchange:subrace_ability_choice1 mancerchange:subrace_ability_choice2 mancerchange:subrace_ability_choice3 mancerchange:custom_hit_die mancerchange:feat_ability_choice";
var tashaListeners = [];
for (let i = 1; i <= 6; i++) {
tashaListeners.push(`mancerchange:tasha_race_ability_choice${i}`);
tashaListeners.push(`mancerchange:tasha_subrace_ability_choice${i}`);
}
changeListeners += " " + tashaListeners.join(" ");
var proficiencyNum = 4;
var customListeners = "";
_.each(abilityList, function (ability) {
changeListeners += " mancerchange:race_custom_" + ability.toLowerCase();
changeListeners += " mancerchange:subrace_custom_" + ability.toLowerCase();
});
/* HELPER FUNCTIONS */
var setValidExpertiseOptions = function (slide, selector, addOptions) {
addOptions = addOptions || [];
const selectedSkills = getProficiencies(getCharmancerData(), slide)["all"]["Skill"];
const newOptions = Array.from(new Set(selectedSkills.concat(addOptions)));
setCharmancerOptions(selector, newOptions, { category: "Expertise" });
};
var deleteSlideData = function (slide, callback) {
const mancer = slide.split("-")[0];
deleteCharmancerData([slide, `${mancer}-summary`], () => {
if (typeof callback === "function") callback();
});
};
var recalcData = function (data) {
//Sets all the text in the top bar, returns ability information
var update = { hit_points: "-" };
var allAbilities = { race: {}, subrace: {}, feat: {} };
var disableAbilities = { race: [], subrace: [], feat: [] };
var allProficiencies = {};
var toSet = {};
var mancerdata = data || getCharmancerData();
var additionalHP = 0;
const usingTasha = isTashaEnabled(mancerdata);
if (mancerdata["l1-class"] && mancerdata["l1-class"].data.class && mancerdata["l1-class"].data.class["Suggested Abilities"]) {
update["class_suggested_abilities"] = mancerdata["l1-class"].data.class["Suggested Abilities"];
showChoices(["class_suggested_abilities_container"]);
} else {
update["class_suggested_abilities"] = "";
hideChoices(["class_suggested_abilities_container"]);
}
if (mancerdata["l1-class"] && mancerdata["l1-class"].data.class && mancerdata["l1-class"].data.class["Spellcasting Ability"]) {
update["class_spellcasting_ability"] = mancerdata["l1-class"].data.class["Spellcasting Ability"];
showChoices(["class_spellcasting_ability_container"]);
} else {
update["class_spellcasting_ability"] = "";
hideChoices(["class_spellcasting_ability_container"]);
}
const setZeroOrIncrease = (section, ability, value) => {
if (!section[ability]) {
section[ability] = parseInt(value);
} else {
section[ability] += parseInt(value);
}
};
_.each(mancerdata, function (page) {
if (usingTasha) {
_.each(page.values, function (value, name) {
if (
name.search("ability") !== -1 &&
name.search("tasha") !== -1 &&
(name.length == 26 || name.length == 29) &&
page.values[`${name}_increase`]
) {
const choiceSection = name.split("_")[1];
const increase = parseInt(page.values[`${name}_increase`]);
allAbilities[choiceSection] = allAbilities[choiceSection] || {};
setZeroOrIncrease(allAbilities[choiceSection], value.toLowerCase(), increase);
disableAbilities[choiceSection] = disableAbilities[choiceSection] || [];
disableAbilities[choiceSection].push(value);
}
});
} else {
_.each(page.values, function (value, name) {
if (name.search("ability") !== -1) {
var choiceSection = name.split("_")[0];
if (page.data[choiceSection] && page.data[choiceSection]["data-Ability Score Choice"]) {
var increase = 1;
if (typeof page.data[choiceSection]["data-Ability Score Choice"] == "string") {
increase = parseInt(_.last(page.data[choiceSection]["data-Ability Score Choice"].split("+")));
}
allAbilities[choiceSection] = allAbilities[choiceSection] || {};
setZeroOrIncrease(allAbilities[choiceSection], value.toLowerCase(), increase);
disableAbilities[choiceSection] = disableAbilities[choiceSection] || [];
disableAbilities[choiceSection].push(value);
}
}
});
_.each(page.data, function (pagedata, increaseSection) {
if (!usingTasha)
if (pagedata["data-Ability Score Increase"] && !isFlex(pagedata["data-Ability Score Increase"])) {
_.each(pagedata["data-Ability Score Increase"], function (amount, ability) {
allAbilities[increaseSection] = allAbilities[increaseSection] || {};
setZeroOrIncrease(allAbilities[increaseSection], ability.toLowerCase(), amount);
disableAbilities[increaseSection] = disableAbilities[increaseSection] || [];
disableAbilities[increaseSection].push(ability.toLowerCase());
});
}
//TODO: Investigate if additional hitpoits at first level could be calculated and interted at this point
//By Miguel
if (pagedata["data-HP per level"]) {
additionalHP += pagedata["data-HP per level"];
}
});
}
});
if (mancerdata["l1-race"] && mancerdata["l1-race"].values.race == "Rules:Races") {
_.each(abilityList, function (ability) {
var custom = mancerdata["l1-race"].values["race_custom_" + ability.toLowerCase()] || false;
if (custom) {
setZeroOrIncrease(allAbilities.race, ability.toLowerCase(), parseInt(custom));
}
});
}
if (mancerdata["l1-race"] && mancerdata["l1-race"].values.subrace == "Rules:Races") {
_.each(abilityList, function (ability) {
var custom = mancerdata["l1-race"].values["subrace_custom_" + ability.toLowerCase()] || false;
if (custom) {
setZeroOrIncrease(allAbilities.subrace, ability.toLowerCase(), parseInt(custom));
}
});
}
var abilityTotals = {};
_.each(allAbilities, function (abilities) {
_.each(abilities, function (amount, ability) {
abilityTotals[ability] = abilityTotals[ability] || { bonus: 0 };
abilityTotals[ability].bonus += parseInt(amount);
});
});
_.each(abilityList, function (upperAbility) {
var ability = upperAbility.toLowerCase();
abilityTotals[ability] = abilityTotals[ability] || { bonus: 0, mod: 0 };
if (mancerdata["l1-abilities"] && mancerdata["l1-abilities"].values[ability]) {
const abilityValue = mancerdata["l1-abilities"].values[ability];
const score = typeof abilityValue === "String" && abilityValue.includes("~") ? abilityValue.split("~")[0] : abilityValue;
abilityTotals[ability].base = parseInt(score);
} else {
abilityTotals[ability].base = 0;
}
abilityTotals[ability].total = abilityTotals[ability].bonus + abilityTotals[ability].base;
abilityTotals[ability].mod = Math.floor((abilityTotals[ability].total - 10) / 2);
update[ability + "_total"] = abilityTotals[ability].total == 0 ? "-" : abilityTotals[ability].total;
});
allAbilities.totals = abilityTotals;
if (mancerdata["l1-class"] && (mancerdata["l1-class"].data.class || mancerdata["l1-class"].values["custom_hit_die"])) {
var basehp = mancerdata["l1-class"].data.class
? mancerdata["l1-class"].data.class["Hit Die"]
: mancerdata["l1-class"].values["custom_hit_die"];
var conmod = abilityTotals.constitution.base > 0 ? abilityTotals.constitution.mod : 0;
//Calculating additional hitpoints at first level from things like Draconic Bloodline ad Hill Dwarf
//By Miguel
let additionalHitPointsFromClassAndRaceAtFirstLevel = getAdditionalHitPointsFromClassAndRaceAtFirstLevel(
getRelevantBlobs(mancerdata, "1", "l1")
);
allAbilities.hp = parseInt(basehp.replace("d", "")) + additionalHitPointsFromClassAndRaceAtFirstLevel + additionalHP + conmod;
update["hit_points"] = allAbilities.hp;
}
if (!data) {
let tashaDisabled = [];
if (
!SheetUtils.checkPath(mancerdata, `["l1-race"]["data"]["race"]["data-Allow Same Choice"]`) &&
!SheetUtils.checkPath(mancerdata, `["l1-race"]["data"]["race"]["data-Flex"]`)
) {
setCharmancerText({ race_ability_choice_rule: "" });
_.each(disableAbilities, function (disable, disablesection) {
disableCharmancerOptions(disablesection + "_ability_choice1", disable);
disableCharmancerOptions(disablesection + "_ability_choice2", disable);
disableCharmancerOptions(disablesection + "_ability_choice3", disable);
tashaDisabled = tashaDisabled.concat(disable);
});
} else {
_.each(disableAbilities, function (disable, disablesection) {
disableCharmancerOptions(disablesection + "_ability_choice1", []);
disableCharmancerOptions(disablesection + "_ability_choice2", []);
disableCharmancerOptions(disablesection + "_ability_choice3", []);
});
setCharmancerText({ race_ability_choice_rule: getTranslationByKey("vr-ability-rules") });
}
if (isTashaEnabled()) {
for (let i = 1; i <= 6; i++) {
disableCharmancerOptions(`tasha_race_ability_choice${i}`, tashaDisabled);
disableCharmancerOptions(`tasha_subrace_ability_choice${i}`, tashaDisabled);
}
}
setCharmancerText(update);
showOptionalAttributeFields();
setAttrs(toSet);
}
return allAbilities;
};
var getProficiencies = function (data, currentSlide, blobs) {
//Disables taken proficiency choices, returns proficiency info
var auto = {};
var allProficiencies = { Weapon: [], Armor: [], Skill: [], Tool: [], Language: [], Other: [], Expertise: [] };
var toSet = {};
var mancerdata = data || getCharmancerData();
var thismancer = currentSlide && currentSlide != "finish" ? currentSlide.split("-")[0] : "l1";
if (!blobs) {
switch (thismancer) {
case "l1":
blobs = getRelevantBlobs(mancerdata, "1", "l1");
break;
case "lp":
blobs = getAllLpBlobs(data, true);
break;
case "utility":
blobs = getAllUtilityBlobs(data, true);
break;
default:
blobs = { all: [], sorted: {}, names: {} };
}
}
//Safeguarding Compendium data:
if (thismancer !== "l1")
allProficiencies = data[`${thismancer}-welcome`].values["previous_proficiencies"]
? Compendium.parseObject(data[`${thismancer}-welcome`].values["previous_proficiencies"], "E_OM1TMKFK:Previous Proficiencies")
: allProficiencies;
currentSlide = currentSlide == "lp-finish" ? "finish" : currentSlide;
//For this first part, we're only concerned with choices that have already been made
//Get all of the "automatic" proficiencies
_.each(blobs.sorted, function (blobarray, section) {
_.each(blobarray, function (blob, blobName) {
_.each(proficiencyList, function (prof) {
if (blob[prof + " Proficiency"]) {
//Safeguarding Compendium data:
const json = Compendium.parseObject(blob[prof + " Proficiency"], `E_4K8VBAUN:Blob:${blobName}:${blob[prof + " Proficiency"]}`);
if (json.Proficiencies) {
auto[section] = auto[section] ? auto[section].concat(json.Proficiencies) : json.Proficiencies;
allProficiencies[prof] = allProficiencies[prof].concat(json.Proficiencies);
}
}
});
if (blob.Expertise) {
//Safeguarding Compendium data:
const json = Compendium.parseObject(blob.Expertise, `E_MDANGF2R:Blob:${blobName}`);
if (json.Proficiencies) {
allProficiencies.Expertise = allProficiencies.Expertise.concat(json.Proficiencies);
}
}
});
});
//Gather all of the chosen proficiencies
_.each(mancerdata, function (slide, slidename) {
if (slidename.split("-")[0] == thismancer) {
_.each(proficiencyList, function (prof) {
_.each(slide.values, function (value, name) {
if (name.split("_")[3] == prof.toLowerCase() && name.split("_")[4] == "choice") {
if (value != "custom") allProficiencies[prof].push(value.split(":")[1]);
}
});
});
//Get a list of chosen expertise
_.each(slide.values, function (value, name) {
if (name.includes("expertise_choice")) {
allProficiencies.Expertise.push(value.split(":")[1]);
}
});
}
});
//Gather proficiencies from custom choices
_.each(mancerdata, function (slide, slidename) {
if (slidename.split("-")[0] == thismancer) {
_.each(slide.values, function (value, name) {
if (name.substr(-18) == "proficiency_choice") {
if (value != "custom") allProficiencies[slide.values[name.replace("choice", "type")]].push(value.split(":")[1]);
}
});
}
});
//If the expertise choice is from the "known" list, make sure the choice is still available
//Reset to "" if not available. Don't do this if we're applying changes
if (currentSlide != "finish") {
_.each(mancerdata, function (slide, slidename) {
if (slidename.split("-")[0] == thismancer) {
_.each(slide.values, function (value, name) {
if (name.substr(-16) == "expertise_choice" && slide.values[name.replace("choice", "info")]) {
if (!(allProficiencies.Skill.includes(value.split(":")[1]) || allProficiencies.Tool.includes(value.split(":")[1]))) {
toSet[name] = "";
}
}
});
}
});
setAttrs(toSet);
}
if (currentSlide && currentSlide != "finish") {
//Now we need to know all of the selects, even if a choice hasn't been made
//Getting a list of all the relevant selects
var repsecs = { Weapon: [], Armor: [], Skill: [], Tool: [], Language: [], Other: [] };
var repexp = [];
_.each(mancerdata[currentSlide].repeating, function (id) {
_.each(proficiencyList, function (prof) {
if (_.last(id.split("_")) == prof.toLowerCase()) repsecs[prof].push(id);
});
if (_.last(id.split("_")) == "expertise") repexp.push(id);
if (_.last(id.split("_")) == "proficiency") {
if (mancerdata[currentSlide].values[id + "_type"]) repsecs[mancerdata[currentSlide].values[id + "_type"]].push(id);
}
});
//Disable already chosen proficiencies
_.each(proficiencyList, function (prof) {
_.each(repsecs[prof], function (id) {
const unsetValues = [];
if (currentSlide && currentSlide.split("-")[0] === "utility" && mancerdata[currentSlide]) {
Object.entries(mancerdata[currentSlide].values).forEach(([key, value]) => {
if (/repeating_.+_unset[0-9]_selected/g.test(key)) unsetValues.push(value);
});
}
const disabledSkills = allProficiencies[prof].filter((skill) => {
return unsetValues.indexOf(skill) === -1;
});
disableCharmancerOptions(id + "_choice", disabledSkills, { category: "Proficiencies" });
});
});
//Disable already chosen expertise, rebuild options if choice is from "known" list
_.each(repexp, function (id) {
if (mancerdata[currentSlide].values[id + "_info"]) {
var choices = allProficiencies.Skill;
//Safeguarding Compendium data:
var options = Compendium.parseArray(mancerdata[currentSlide].values[id + "_info"], `E_56G1GW3M:currentSlide`);
var settings = { category: "Proficiencies", disable: allProficiencies.Expertise, silent: true };
options.shift();
settings.add = options;
setCharmancerOptions(id + "_choice", choices, settings);
} else {
disableCharmancerOptions(id + "_choice", allProficiencies.Expertise, { category: "Proficiencies" });
}
});
}
_.each(allProficiencies, function (section, name) {
allProficiencies[name] = _.without(section, "");
});
[auto, allProficiencies].forEach((collection) => {
Object.values(collection).forEach((group) => {
Object.keys(group).forEach((skill) => {
group[skill] = removeExpansionInfo(group[skill]);
});
});
});
return { auto: auto, all: allProficiencies };
};
var handleAbilities = function (data, section, usingTasha) {
usingTasha = usingTasha || false;
var showList = [];
if (usingTasha) {
let total = [];
if (data["data-Ability Score Increase"]) {
//Safeguarding Compendium data:
const increase = Compendium.parseObject(data["data-Ability Score Increase"], `E_5LSSNM5A:${section}:data-Ability Score Increase`);
Object.keys(increase).forEach((attr) => {
total.push(parseInt(increase[attr]));
});
}
if (data["data-Ability Score Choice"]) {
const attrs = parseInt(data["data-Ability Score Choice"][0]);
const bonus = parseInt(data["data-Ability Score Choice"][2]);
for (let i = 0; i < attrs; i++) {
total.push(bonus);
}
}
if (total.length > 0) {
showList.push(section + "_abilities");
showList.push(`tasha_${section}_enabled`);
let update = {};
for (let i = 1; i <= total.length; i++) {
showList.push(`tasha_${section}_ability_choice${i}`);
update[`tasha_${section}_ability_choice${i}_increase`] = total[i - 1];
}
setAttrs(update);
}
} else {
if (data["data-Ability Score Increase"]) {
showList.push(section + "_abilities");
showList.push(`tasha_${section}_disabled`);
}
if (data["data-Ability Score Choice"]) {
showList.push(section + "_abilities");
showList.push(`tasha_${section}_disabled`);
const amount = parseInt(data["data-Ability Score Choice"].split("+")[0]);
for (let i = 1; i <= amount; i++) {
showList.push(section + `_ability_choice${i}`);
}
}
}
return showList;
};
var knownSpells = function (data) {
var mancerdata = data ? data : getCharmancerData();
var allSpells = {
race: false,
class: false,
known: [],
all: [],
errors: { class: [], race: [] },
};
var stats = recalcData(data); //since this can get called in the finish step, make sure it doesn't call setAttrs()
var racename = getName("race", mancerdata, true);
var subracename = getName("subrace", mancerdata, true);
var classname = getName("class", mancerdata, true);
var subclassname = getName("subclass", mancerdata, true);
var classAbility =
mancerdata["l1-class"] && mancerdata["l1-class"].data.class && mancerdata["l1-class"].data.class["Spellcasting Ability"]
? mancerdata["l1-class"].data.class["Spellcasting Ability"]
: "";
var expandedList = {};
var additionalList = {};
var blobs = getRelevantBlobs(mancerdata, "1", "l1");
//First look through all Blobs for any blob.Spells
_.each(blobs.all, function (blob, blobName) {
if (blob.Spells) {
//Safeguarding Compendium data:
const spells = Compendium.parseArray(blob.Spells, `E_G27W86AS:${blobName}`);
//Examine each spell to determine if it is "Extended List". "Extended List" adds to the classes spell list.
//Ravnica Guilds backgrounds such as Dimir Operative are examples of this
_.each(spells, function (spell) {
if (spell["Expanded List"]) {
expandedList[spell.Level] = expandedList[spell.Level]
? expandedList[spell.Level].concat(spell["Expanded List"])
: spell["Expanded List"];
}
});
}
});
//Look through blobs.sorted.
_.each(blobs.sorted, function (sectionblobs, section) {
//Subclass needs to be changed to class.
var parentsection = section.replace("sub", "");
additionalList[parentsection] = additionalList[parentsection] || [];
//Examine eaach blob for "Additional Spell List"
_.each(sectionblobs, function (blob) {
if (blob["Additional Spell List"]) {
additionalList[parentsection].push(blob["Additional Spell List"]);
}
});
});
//Process spell data from blobs
_.each(blobs.sorted, function (sectionblobs, section) {
var parentsection = section.replace("sub", "");
_.each(sectionblobs, function (blob, blobName) {
//Looks through the blobs for Spells attribute
//class.Spells contains the information for how many spells are choosen at each level & Prepared spells to be selected based on ability modifer + 1
//[subclass, background, race}.Spells will contain KNOWN, CHOSEN, or EXPANDED LIST for extra spells granted by these sections
if (blob.Spells) {
//Safeguarding Compendium data:
const spells = Compendium.parseArray(blob.Spells, `E_1BPMEBQP:Blob:${blobName}`);
allSpells[parentsection] = allSpells[parentsection] || { known: [], choices: [] };
_.each(spells, function (spell) {
var numSpells = 0;
expandedList[spell.Level] = expandedList[spell.Level] || [];
//class.Spells will contain Prepared and Choose.
//These are used to populate the number of selects needed for each spell lists on the Spells slide
if (spell.Prepared) {
numSpells = Math.max(stats.totals[spell.Prepared.split("+")[0].toLowerCase()].mod, 0) + parseInt(spell.Prepared.split("+")[1]);
}
if (spell.Choose) {
numSpells = parseInt(spell.Choose);
}
//If Spells are to be Chosen or Prepared add them to a class list, expand the list, and set the Ability modifier & Level
if (numSpells > 0) {
_.times(numSpells, function (n) {
var thisspell = { Level: parseInt(spell.Level) };
thisspell.List = spell.List ? spell.List : classname;
thisspell.AddList = additionalList[parentsection];
thisspell.Ability = spell.Ability ? spell.Ability : classAbility;
if (parentsection == "class") thisspell["Expanded List"] = expandedList[spell.Level];
allSpells[parentsection].choices.push(thisspell);
});
}
//Add any spells that are already known from sections such as Forge Domain Cleric starts with identify & searing smite
_.each(spell.Known, function (known) {
var thisspell = { Name: known, Level: parseInt(spell.Level) };
thisspell.Ability = spell.Ability ? spell.Ability : classAbility;
thisspell.Source = parentsection;
if (spell.Innate) thisspell.Innate = spell.Innate;
allSpells[parentsection].known.push(thisspell);
allSpells.known.push(known);
allSpells.all.push(thisspell);
});
});
}
});
});
//Collect custom race/subrace spells
if (mancerdata["l1-race"] && mancerdata["l1-race"].values.custom_race_spell_ability) {
var raceAbility = mancerdata["l1-race"].values.custom_race_spell_ability;
var numSpells = mancerdata["l1-race"].values.custom_race_spell_number;
var spellList = mancerdata["l1-race"].values.custom_race_spell_list;
if (spellList) {
allSpells.race = allSpells.race || { known: [], choices: [] };
for (var x = 1; x <= numSpells; x++) {
var thisSpell = {
Ability: raceAbility,
Level: 0,
List: spellList,
};
allSpells.race.choices.push(thisSpell);
}
} else {
allSpells.errors.race.push("custom_race_spell_list");
}
}
//Collect custom class spells
if (mancerdata["l1-class"] && mancerdata["l1-class"].values.custom_class_spell_ability) {
var customAbility = mancerdata["l1-class"].values.custom_class_spell_ability;
var customList = mancerdata["l1-class"].values.custom_class_spell_list;
var customSpells = {
0: mancerdata["l1-class"].values.custom_class_cantrip_number,
1: mancerdata["l1-class"].values.custom_class_spell_number,
};
if (customList && (customSpells["0"] > 0 || customSpells["1"] > 0)) {
allSpells.class = allSpells.class || { known: [], choices: [] };
_.each(customSpells, function (number, level) {
for (var x = 1; x <= number; x++) {
var thisSpell = {
Ability: customAbility,
Level: level,
List: customList,
};
allSpells.class.choices.push(thisSpell);
}
});
} else {
allSpells.errors.class.push("custom_class_spell_list");
allSpells.errors.class.push("custom_class_spell_number");
}
}
//Collect custom subclass spells
if (mancerdata["l1-class"] && mancerdata["l1-class"].values.subclass == "Rules:Classes") {
var customSpells = {
0: mancerdata["l1-class"].values.custom_subclass_cantrip_number,
1: mancerdata["l1-class"].values.custom_subclass_spell_number,
};
if (customSpells["0"] > 0 || customSpells["1"] > 0) {
allSpells.class = allSpells.class || { known: [], choices: [] };
_.each(customSpells, function (number, level) {
var customList = classname.toLowerCase();
if (level == "0" && mancerdata["l1-class"].values.custom_subclass_cantrip_list)
customList = mancerdata["l1-class"].values.custom_subclass_cantrip_list;
if (level == "1" && mancerdata["l1-class"].values.custom_subclass_spell_list)
customList = mancerdata["l1-class"].values.custom_subclass_spell_list;
for (var x = 1; x <= number; x++) {
var thisSpell = {
Ability: classAbility,
Level: level,
List: customList,
};
allSpells.class.choices.push(thisSpell);
}
});
}
}
//Collect data from choices
if (mancerdata["l1-spells"]) {
_.each(mancerdata["l1-spells"].repeating, function (id) {
if (mancerdata["l1-spells"].values[id + "_choice"]) {
var thisspell = { Name: mancerdata["l1-spells"].values[id + "_choice"].substring(7) };
thisspell.Ability = mancerdata["l1-spells"].values[id + "_info"] ? mancerdata["l1-spells"].values[id + "_info"] : classAbility;
if (mancerdata["l1-spells"].values[id + "_custom"]) thisspell.Source = mancerdata["l1-spells"].values[id + "_custom"];
allSpells.known.push(thisspell.Name);
allSpells.all.push(thisspell);
}
});
}
//Collect data from spell picker
if (SheetUtils.checkPath(mancerdata, `["l1-class"]["values"]`)) {
Object.keys(mancerdata["l1-class"]["values"])
.filter((key) => {
return key.includes("utilityrow_result");
})
.forEach((picked) => {
const data = mancerdata["l1-class"]["values"][picked];
const spells = data.split(",").map((entry) => {
return `${entry.trim()}`;
});
spells.forEach((spell) => {
const thisspell = { Name: spell };
thisspell.Ability = classAbility;
allSpells.known.push(thisspell.Name);
allSpells.all.push(thisspell);
});
});
}
return allSpells;
};
var cleanEquipment = function (equipment) {
var current_string = "(";
var hasDouble = false;
var firstItem = true;
_.each(equipment, function (item) {
if (item.search("~DBL") != -1) {
hasDouble = true;
item.replace("~DBL", "");
}
item = item.replace(/"Item Type":(.|)Weapon.*\b/g, "Any Weapon");
item = item.replace(/Subtype:(.|)Martial.*\b("|)/g, "Any Martial Weapon");
item = item.replace(/Subtype:(.|)Simple.*\b("|)/g, "Any Simple Weapon");
item = item.replace(/Subtype:(.|)Artisan's Tools.*\b("|)/g, "A Set of Artisan's Tools");
item = item.replace(/Subtype:(.|)Gaming Set.*\b("|)/g, "A Gaming Set");
item = item.replace(/Subtype:(.|)Musical Instrument.*\b("|)/g, "A Musical Instrument");
if (!firstItem) current_string += " or ";
current_string += item;
firstItem = false;
});
current_string += ")";
if (hasDouble) {
current_string = "two of " + current_string;
}
return current_string.replace(/,/g, ", ");
};
const removeExpansionInfo = function (text) {
if (!text) return "";
return text.replace(/\??expansion\=([0-9]+)?/gi, "");
};
var removeDuplicatedPageData = function (data) {
if (!Array.isArray(data)) return data;
const getPageName = function (item) {
let category = "";
if ("data" in item && "Category" in item.data) category += item.data["Category"] + ": ";
return category + item.name;
};
let seen = {};
return data.filter(function (item) {
let pageName = getPageName(item);
return seen.hasOwnProperty(pageName) ? false : (seen[pageName] = true);
});
};
var getName = function (request, data, custom) {
data = data || getCharmancerData();
var section = request.replace("sub", "");
var result = "";
var customString = custom ? "custom" : "";
if (data["l1-" + section] && data["l1-" + section].values[request]) {
result = data["l1-" + section].values[request + "_name"] || data["l1-" + section].values[request];
}
result = result.split(":")[0] == "Rules" || result.split(":")[0] == "CategoryIndex" ? customString : result;
//Removing expansion name from result
result = removeExpansionInfo(result);
return _.last(result.split(":"));
};
var getGainedSpells = function () {
let gainedSpells = [];
const data = getCharmancerData();
if ("lp-spells" in data && "values" in data["lp-spells"]) {
Object.keys(data["lp-spells"].values).forEach((key) => {
let found = false;
if (key.indexOf("_checked") === -1) return;
let spellNameKey = key.replace("_checked", "_name");
let spellName = data["lp-spells"].values[spellNameKey];
if (data["lp-welcome"].values.spellinfo) {
//Safeguarding Compendium data:
let previousSpells = Compendium.parseObject(data["lp-welcome"].values.spellinfo, `E_GDO4QK17:Previous Spells`);
Object.keys(previousSpells).forEach((spell) => {
if (previousSpells[spell].spellname === spellName) found = true;
});
}
if (!found) gainedSpells.push(spellName);
});
}
return gainedSpells;
};
var getGainedFeatures = function () {
const data = getCharmancerData();
if (!"lp-levels" in data) return;
let gainedFeatures = [];
for (let charclass = 1; charclass <= 4; charclass++) {
let levelsGained = data["lp-levels"].values["class" + charclass + "_addlevel"] || 0;
if (levelsGained === 0) continue; //No leves gained in this class, just skip to the next one.
//Now lets get the features got from each class at LP;
levelsGained = parseInt(levelsGained);
let currentLevel = parseInt(data["lp-levels"].values["class" + charclass + "_currentlevel"]);
Object.keys(data["lp-levels"].data["class" + charclass].blobs).forEach((feature) => {
let featureLevel = parseInt(data["lp-levels"].data["class" + charclass].blobs[feature]["Level"]);
if (featureLevel > currentLevel && featureLevel <= currentLevel + levelsGained) {
let sanitizedFeatureName = feature.replace(/\(.+\)/g, "");
sanitizedFeatureName = sanitizedFeatureName.replace(/ - .+/g, "").trim();
if ("Group" in data["lp-levels"].data["class" + charclass].blobs[feature]) return;
if ("Choice" in data["lp-levels"].data["class" + charclass].blobs[feature]) {
Object.keys(data["lp-choices"].values).forEach((info) => {
if (data["lp-choices"].values[info] === feature) {
let choice = info.replace("_info", "_choice");
if (choice in data["lp-choices"].values) {
let choiceName = data["lp-choices"].values[choice].replace("Blob:", "");
if (gainedFeatures.indexOf(choiceName) === -1) gainedFeatures.push(choiceName);
}
}
});
} else {
if (gainedFeatures.indexOf(sanitizedFeatureName) === -1) gainedFeatures.push(sanitizedFeatureName);
}
}
});
}
return gainedFeatures;
};
var filterBlobs = function (blobs, filters) {
var remove = filters.multiclass ? "no" : "yes";
var results = {};
delete filters.multiclass;
delete filters.slide;
_.each(blobs, function (blob, name) {
var match = true;
if (blob.Group && !filters.Group && !filters.name) match = false;
_.each(filters, function (v, k) {
if (k == "name") {
if (name != v) match = false;
} else if (v[0] === "<" && !isNaN(parseInt(v.substring(1)))) {
let blobval = isNaN(parseInt(blob[k])) ? 0 : parseInt(blob[k]);
if (blobval > parseInt(v.substring(1))) match = false;
} else {
if (blob[k] != v) match = false;
}
});
if (match && name.split("(")[0].toLowerCase() != "spell slots") results[name] = blob;
});
_.each(results, function (blob, name) {
if (blob.Multiclass == remove) {
delete results[name];
}
});
return results;
};
var handleBlobs = function (blobs, options = {}) {
//Old parameters: (blobs, filters, section, element, thisblob)
//Function to get blob parameter for Spell Pickers like Magical Secrets
//By Miguel
const getBlobDescription = function (blob) {
if ("Description" in blob) {
return blob.Description;
} else if ("Traits" in blob) {
//Safeguarding Compendium data:
traits = Compendium.parseArray(blob.Traits, `E_AEPC9UEM`)[0];
if ("Desc" in traits) return traits.Desc;
}
return "";
};
const handleProficiencies = function (data, subsection) {
if (!data) {
return;
}
subsection = subsection ? subsection : element;
_.each(proficiencyList, function (prof) {
if (data[prof + " Proficiency"]) {
addRepeatingSection(subsection, "row", function (rowid) {
//Safeguarding Compendium data:
var json = Compendium.parseObject(data[prof + " Proficiency"], `E_VDYAWL44:${prof}`);
let extraChoices = 0;
if (!json.Hidden) {
if (prof === "Skill" && options.section && options.section === "background") {
let allAutoSkills = [];
Object.keys(profdata.auto).forEach((section) => {
if (options.section && section !== options.section) allAutoSkills = allAutoSkills.concat(profdata.auto[section]);
});
const duplicatedSkills = json.Proficiencies.filter((skill) => {
return allAutoSkills.indexOf(skill) > -1;
});
json.Proficiencies = json.Proficiencies.filter((skill) => {
return duplicatedSkills.indexOf(skill) === -1;
});
extraChoices = duplicatedSkills.length;
}
var repupdate = {};
repupdate[rowid + " label span"] = json.Title ? json.Title : getTranslationByKey(prof.toLowerCase() + "-proficiencies");
if (json.Desc) {
repupdate[rowid + " label p"] = json.Desc;
if (json.Proficiencies) {
repupdate[rowid + " label p"] += "<br>" + json.Proficiencies.join(", ");
}
} else if (json.Proficiencies) {
repupdate[rowid + " label p"] = json.Proficiencies.join(", ");
}
setCharmancerText(repupdate);
}
_.each(json.Choice, function (v) {
addRepeatingSection(rowid + " label", "choose", section + "_" + prof.toLowerCase(), function (id) {
var choices = "";
const unsetValues = [];
if (options && options.slide && options.slide.split("-")[0] === "utility" && mancerdata[options.slide]) {
Object.entries(mancerdata[options.slide].values).forEach(([key, value]) => {
if (/repeating_.+_unset[0-9]_selected/g.test(key)) unsetValues.push(value);
});
}
const disabledSkills = profdata.all[prof].filter((skill) => {
return unsetValues.indexOf(skill) === -1;
});
disableCharmancerOptions(id + "_choice", disabledSkills, { category: "Proficiencies" });
var settings = { category: "Proficiencies", disable: disabledSkills };
if (prof == "Language") {
var customupdate = {};
customupdate[id + " select"] =
'<option value="" data-i18n="choose"></option><option class="custom" value="custom" data-i18n="custom"></option>';
setCharmancerText(customupdate);
}
if (Array.isArray(v)) {
if (v[0].split(":").length > 1) {
choices = 'Category:Proficiencies "Type:' + prof + '" ' + v.shift();
settings.add = v;
} else if (v[0] == "all") {
v.shift();
choices = 'Category:Proficiencies "Type:' + prof + '"';
settings.add = v;
} else {
choices = v;
}
} else {
choices = 'Category:Proficiencies "Type:' + prof + '"';
if (v != "all" && v != "any") {
choices += " " + v;
}
}
setCharmancerOptions(id + "_choice", choices, settings);
});
});
for (let i = 0; i < extraChoices; i++) {
addRepeatingSection(rowid + " label", "choose", section + "_" + prof.toLowerCase(), function (id) {
var choices = "";
var settings = { category: "Proficiencies", disable: profdata.all[prof] };
choices = 'Category:Proficiencies "Type:Skill"';
setCharmancerOptions(id + "_choice", choices, settings);
});
}
});
}
});
if (data.Expertise) {
addRepeatingSection(subsection, "row", function (rowid) {
//Safeguarding Compendium data:
var json = Compendium.parseObject(data.Expertise, `E_VYASZ9HU`);
if (json.Title) {
var repupdate = {};
repupdate[rowid + " label span"] = json.Title;
setCharmancerText(repupdate);
}
_.each(json.Choice, function (v) {
addRepeatingSection(rowid + " label", "choose", section + "_expertise", function (id) {
var choices = "";
var settings = { category: "Proficiencies", disable: profdata.all.Expertise };
if (v[0].split(":").length > 1) {
choices = 'Category:Proficiencies "Type:' + prof + '" ' + v.shift();
settings.add = v;
} else if (v[0] == "KNOWN") {
var info = {};
info[id + "_info"] = JSON.stringify(v);
setAttrs(info);
v.shift();
choices = profdata.all.Skill;
settings.add = v;
} else {
choices = v;
}
setCharmancerOptions(id + "_choice", choices, settings);
});
});
});
}
};
const handleTraits = function (blob, subsection, name) {
//Safeguarding Compendium data:
const traits = Compendium.parseArray(blob.Traits, `E_8E7G4PID:${name}`);
subsection = subsection ? subsection : element;
_.each(traits, function (trait) {
if (trait["Input Spells"]) {
addRepeatingSection(subsection, "utilityrow", function (rowid) {
let repupdate = {};
let set = {};
repupdate[`${rowid} label span .title`] = trait.Name.split("{{")[0];
if (!trait.Hide) repupdate[`${rowid} label p`] = trait.Desc;
repupdate[`${rowid} button`] = trait["Input Spells"].ButtonText;
set[`${rowid}_info`] = JSON.stringify(trait["Input Spells"]);
set[`${rowid}_type`] = "trait";
set[`${rowid}_blob`] = name;
set[`${rowid}_parent`] = options.parent;
set[`${rowid}_title`] = trait.Name;
set[`${rowid}_desc`] = trait.Desc;
setCharmancerText(repupdate);
setAttrs(set);
});
} else if (!trait.Hide) {
addRepeatingSection(subsection, "row", function (rowid) {
let repupdate = {};
if (trait.Input) {
//Fixing custom data in inputs not being remembered when switching tabs (UC809)
//By Miguel
const data = getCharmancerData();
//The function below looks for valyes through all Mancer data that match a input rowid:
const getPreviousInputValue = function (data, rowid) {
let parsedId = rowid.match(/-.+_/g)[0];
for (let attribute in data) {
if (!("values" in data[attribute])) continue;
for (let key in data[attribute].values) {
if (data[attribute].values.hasOwnProperty(key) && key.indexOf("_input") > -1 && key.indexOf(parsedId) > -1) {
return data[attribute].values[key];
}
}
}
return "";
};
const previousValue = getPreviousInputValue(data, rowid);
repupdate[`${rowid} label span`] = `<input type="text" name="comp_${rowid}_trait_input" value="${previousValue}">`;
repupdate[`${rowid} label span`] += `<input type="hidden" name="comp_${rowid}_trait_name" value="${
trait.Name ? trait.Name : ""
}">`;
repupdate[`${rowid} label span`] += `<input type="hidden" name="comp_${rowid}_trait_desc" value="${
trait.Desc ? trait.Desc : ""
}">`;
repupdate[`${rowid} label span`] += `<input type="hidden" name="comp_${rowid}_trait_section" value="${section}">`;
} else {
repupdate[`${rowid} label span`] = trait.Name;
repupdate[`${rowid} label p`] = blob.Prerequisite
? '<p class="prereq"><span data-i18n="prerequisite:"></span>' + blob.Prerequisite + "</p>"
: "";
//Fixing undefined when selecting Ranger Favored Enemy and Land (UC809)
//By Miguel
repupdate[`${rowid} label p`] += trait.Desc ? trait.Desc : "";
}
setCharmancerText(repupdate);
handleProficiencies(blob, rowid);
});
}
});
};
const mancerdata = getCharmancerData();
let filters = options.filters || {};
let section = options.section || undefined;
let thisblob = options.thisblob || undefined;
let element = options.element ? options.element : section + "_holder";
let sorted = { swaps: {}, choices: {}, traits: {}, other: {}, spells: {} };
let profdata = getProficiencies(mancerdata, options.slide);
let filtered = {};
if (!options.context) options.context = "Charmancer";
if (options && options.context && blobs) {
const blobsInContext = {};
Object.entries(blobs).forEach(([name, blob]) => {
if (blob["Available At"] && blob["Available At"].toLowerCase() !== options.context.toLowerCase()) return;
blobsInContext[name] = blob;
});
blobs = blobsInContext;
}
if (thisblob) {
filtered[thisblob] = blobs[thisblob];
} else {
filtered = filterBlobs(blobs, filters);
}
_.each(filtered, function (blob, name) {
if (name.split(" ")[0] == "Proficiencies") {
sorted.proficiencies = blob;
} else if (blob["Pick Spells"]) {
sorted.spells[name] = blob;
} else if (blob.Swap) {
sorted.swaps[name] = blob;
} else if (blob.Choice) {
sorted.choices[name] = blob;
} else if (blob.Traits) {
sorted.traits[name] = blob;
} else {
sorted.other[name] = blob;
}
});
handleProficiencies(sorted.proficiencies);
_.each(sorted.swaps, function (blob, name) {
addRepeatingSection(element, "row", function (rowid) {
var title = blob.Title ? blob.Title : name;
var choice = blob.Change;
var settings = {};
let choicenum = blob["Choice Number"] ? parseInt(blob["Choice Number"]) : 1;
const getSwapable = function () {
let swapable = [];
if (!SheetUtils.checkPath(mancerdata, "['lp-welcome']['values']['previous_repeating']")) {
return swapable;
}
//Safeguarding Compendium data:
const previousRepeating = Compendium.parseObject(
mancerdata["lp-welcome"]["values"]["previous_repeating"],
`E_9TQSWFGF:Blob:${title}`
);
if (!previousRepeating["traits"]) return swapable;
previousRepeating["traits"].forEach((trait) => {
const blobtype = blob.Swap.split(":")[1].replace(/s$/gi, "");
if (trait.name.indexOf(`${blobtype}:`) > -1) swapable.push(trait);
});
return swapable;
};
const swapable = getSwapable();
if (blob.Swap.split(":")[0] == "Blob") {
choice = Object.keys(filterBlobs(blobs, { Group: blob.Swap.split(":")[1], Level: "<" + (options.maxlevel || blob.Level) })).sort();
settings.category = "Blob";
}
var repupdate = {};
repupdate[rowid + " label span"] = title;
if (blob.Description) {
repupdate[rowid + " label p"] = blob.Description;
}
setCharmancerText(repupdate);
if (section.indexOf("-") === -1) {
let match = /[0-9]+$/.exec(section);
if (match) {
section = [section.slice(0, match.index), "-", section.slice(match.index)].join("");
section += "--" + blob.Level;
}
}
for (let x = 1; x <= choicenum; x++) {
addRepeatingSection(rowid + " label", "swap", section + "_feature", function (id) {
const swapChoices = swapable.map((entry) => {
return entry.name.split(":")[1].trim();
});
const category = blob.Swap.split(":")[1].replace(/s$/gi, "");
setCharmancerOptions(id + "_swap", swapChoices, { category: category }, function () {
var info = {};
info[id + "_info"] = title;
setAttrs(info);
settings.disable = swapChoices;
setCharmancerOptions(id + "_for", choice, settings, function () {
if (mancerdata[options.slide].values[id + "_for"] && mancerdata[options.slide].values[id + "_for"].split(":")[0] == "Blob") {
handleBlobs(blobs, {
filters: { name: mancerdata[options.slide].values[id + "_for"].split(":")[1] },
section: section,
element: id + " span",
slide: options.slide,
parent: options.parent,
});
}
});
});
});
}
handleProficiencies(blob, rowid);
});
});
_.each(sorted.choices, function (blob, name) {
addRepeatingSection(element, "row", function (rowid) {
var title = blob.Title ? blob.Title : name;
var choice = blob.Choice;
var settings = {};
let choicenum = blob["Choice Number"] ? parseInt(blob["Choice Number"]) : 1;
if (blob.Choice.split(":")[0] == "Blob") {
choice = Object.keys(
filterBlobs(blobs, { Group: blob.Choice.split(":")[1], Level: "<" + (options.maxlevel || blob.Level) })
).sort();
settings.category = "Blob";
}
var repupdate = {};
repupdate[rowid + " label span"] = title;
if (blob.Description) {
repupdate[rowid + " label p"] = blob.Description;
}
setCharmancerText(repupdate);
if (section.indexOf("-") === -1) {
let match = /[0-9]+$/.exec(section);
if (match) {
section = [section.slice(0, match.index), "-", section.slice(match.index)].join("");
section += "--" + blob.Level;
}
}
for (let x = 1; x <= choicenum; x++) {
const isBroken = /class[0-9]_subclass/g.test(section);
if (isBroken) {
const classLevel = section.replace(/[^0-9]/g, "");
section = `subclass-${classLevel}--${blob.Level || 1}`;
}
addRepeatingSection(rowid + " label", "choose", section + "_feature", function (id) {
var info = {};
info[id + "_info"] = title;
setAttrs(info);
try {
const options = JSON.parse(blob.Choice);
const isValid = Array.isArray(options);
if (isValid) setCharmancerOptions(id + "_choice", options);
} catch (e) {
setCharmancerOptions(id + "_choice", choice, settings, function () {
if (
mancerdata[options.slide].values[id + "_choice"] &&
mancerdata[options.slide].values[id + "_choice"].split(":")[0] == "Blob"
) {
handleBlobs(blobs, {
filters: { name: mancerdata[options.slide].values[id + "_choice"].split(":")[1] },
section: section,
element: id + " span",
slide: options.slide,
parent: options.parent,
});
}
});
}
});
}
handleProficiencies(blob, rowid);
});
});
_.each(sorted.spells, function (blob, name) {
addRepeatingSection(element, "row", function (rowid) {
let title = blob.Title ? blob.Title : name;
//Safeguarding Compendium data:
let spells = Compendium.parseArray(blob["Pick Spells"], `E_ABUAKNQD:Blobo:${title}`);
let settings = {};
let repupdate = {};
repupdate[rowid + ">label>span"] = title;
//This used to check for blob.Description which does not exist for Spell Pickers
//In such case the description is located at blob.Traits.Desc
//By Miguel
let description = getBlobDescription(blob);
if (description.length > 0) {
repupdate[rowid + ">label>p"] = description;
}
_.each(spells, function (spell) {
addRepeatingSection(rowid, "utilityrow", function (rowid) {
let repupdate = {};
let set = {};
repupdate[`${rowid} button`] = spell.ButtonText;
set[`${rowid}_info`] = JSON.stringify(spell);
set[`${rowid}_type`] = "pick";
set[`${rowid}_parent`] = options.parent;
set[`${rowid}_blob`] = name;
setCharmancerText(repupdate);
setAttrs(set);
});
});
setCharmancerText(repupdate);
handleProficiencies(blob, rowid);
});
});
_.each(sorted.other, function (blob, name) {
if (blob.Title || blob.Description) {
addRepeatingSection(element, "row", function (rowid) {
var title = blob.Title ? blob.Title : name;
var repupdate = {};
repupdate[rowid + " label span"] = "" + title;
repupdate[`${rowid} label p`] = blob.Prerequisite ? '<p class="prereq">' + blob.Prerequisite + "</p>" : "";
if (blob.Description) repupdate[rowid + " label p"] += blob.Description;
setCharmancerText(repupdate);
handleProficiencies(blob, rowid);
});
} else {
handleProficiencies(blob);
}
});
_.each(sorted.traits, function (blob, name) {
try {
var title = blob.Title ? blob.Title : name;
//Safeguarding Compendium data:
var traitsObj = Compendium.parseArray(blob.Traits, `E_6RGKOCMH:Blob:${title}`);
if (blob.Description || traitsObj.length > 1) {
addRepeatingSection(element, "row", function (rowid) {
if (blob.Description || blob.Title) {
var repupdate = {};
repupdate[rowid + " label span"] = "" + title;
if (blob.Description) repupdate[rowid + " label p"] = "" + blob.Description;
setCharmancerText(repupdate);
}
handleTraits(blob, rowid, name);
handleProficiencies(blob, rowid);
});
} else {
handleTraits(blob, undefined, name);
}
} catch {
errorMessage("Invalid JSON: Blob Trait");
}
});
};
var addCustomSections = function (section) {
var lower = section.toLowerCase();
var page = "l1-" + lower.replace("sub", "");
var mancerdata = getCharmancerData()[page];
var repids = [];
var total = 0;
var current = 0;
var traits = [];
var profs = [];
_.each(mancerdata.repeating, function (repid) {
if (_.last(repid.split("_")) == "trait") {
if (mancerdata.values[repid + "_name"] || mancerdata.values[repid + "_desc"]) {
var thistrait = {};
thistrait.name = mancerdata.values[repid + "_name"] ? mancerdata.values[repid + "_name"] : "";
thistrait.desc = mancerdata.values[repid + "_desc"] ? mancerdata.values[repid + "_desc"] : "";
traits.push(thistrait);
total++;
}
}
if (_.last(repid.split("_")) == "proficiency") {
if (mancerdata.values[repid + "_type"]) {
var thisprof = {};
thisprof.type = mancerdata.values[repid + "_type"];
thisprof.choice = mancerdata.values[repid + "_choice"] ? mancerdata.values[repid + "_choice"] : "";
profs.push(thisprof);
total++;
}
}
});
if (traits.length == 0) {
traits.push({ name: "", desc: "" });
total++;
}
if (profs.length == 0) {
profs.push({ type: "", choice: "" });
total++;
}
addRepeatingSection(lower + "_holder", "row", "customrow", function (rowid) {
var toset = {};
toset[rowid + " label span"] = "Proficiencies";
repids.push(rowid);
setCharmancerText(toset);
_.each(profs, function (prof) {
addRepeatingSection(rowid, "custom-proficiency", "custom_" + lower + "_proficiency", function (id) {
var toset = {};
if (prof.type != "") toset[id + "_type"] = prof.type;
if (prof.choice != "") toset[id + "_choice"] = prof.choice;
setAttrs(toset);
current++;
repids.push(id);
if (current >= total) clearRepeating(repids, page, "customrow");
});
});
});
addRepeatingSection(lower + "_holder", "row", "customrow", function (rowid) {
var toset = {};
toset[rowid + " label span"] = "Custom " + section + " Features";
repids.push(rowid);
setCharmancerText(toset);
_.each(traits, function (trait) {
addRepeatingSection(rowid, "custom-trait", "custom_" + lower + "_trait", function (id) {
var toset = {};
if (trait.name != "") toset[id + "_name"] = trait.name;
if (trait.desc != "") toset[id + "_desc"] = trait.desc;
//update[id + " span"] = trait.name;
setAttrs(toset);
current++;
repids.push(id);
if (current >= total) clearRepeating(repids, page, "customrow");
});
});
});
};
var clearRepeating = function (repids, page, match) {
//This function removes all repeating sections that aren't in the repids array (except for the topbar).
//The optional third argument will only remove certain types of repeating sections
var mancerdata = getCharmancerData();
_.each(mancerdata[page].repeating, function (repid) {
if (_.last(repid.split("_")) != "topbar" && !repids.includes(repid) && (!match || (match && repid.includes(match))))
clearRepeatingSectionById(repid);
});
};
//This function should be used to read additional hit points gained at first level by things like Draconic Bloodline or Hill Dwarf. Can be possbily be used in the future for Tough feat(?)
//By Miguel
var getAdditionalHitPointsFromClassAndRaceAtFirstLevel = function (blobs) {
let additionalHP = 0;
blobs.all.map((blob) => {
if ("Hit Points Per Level" in blob) {
additionalHP += parseInt(blob["Hit Points Per Level"]);
}
});
return additionalHP;
};
var getRelevantBlobs = function (mancerdata, level, section) {
var results = [];
var sorted = {};
var names = {};
level = level ? "" + level : "1";
_.each(mancerdata, function (slide, slidename) {
if (slidename.split("-")[0] == section) {
_.each(slide.data, function (data, section) {
if (data.blobs) {
var levelblobs = _.extend(filterBlobs(data.blobs, { Level: level }), filterBlobs(data.blobs, { Level: "every" }));
sorted[section] = [];
names[section] = [];
_.each(levelblobs, function (blob, blobname) {
results.push(blob);
sorted[section].push(blob);
names[section].push(blobname);
});
}
});
_.each(slide.values, function (value, name) {
if ((value + "").split(":")[0] == "Blob") {
var section = name.split("_")[2];
var blobs = slide.data[section] && slide.data[section].blobs ? slide.data[section].blobs : [];
_.each(filterBlobs(blobs, { name: value.split(":")[1] }), function (blob, blobname) {
results.push(blob);
sorted[section] = sorted[section] ? sorted[section] : [];
sorted[section].push(blob);
names[section] = names[section] ? names[section] : [];
names[section].push(blobname);
});
}
});
}
});
return { all: results, sorted: sorted, names: names };
};
var costOfScore = function (score) {
if (isNaN(score) || score == 8) {
return 0;
}
var cost = 0;
if (score > 8) {
if (score < 14) {
cost = score - 8;
} else if (score == 14) {
cost = 7;
} else if (score == 15) {
cost = 9;
} else {
// Score should never be higher, but let's cover our bases.
cost = 11;
}
}
return cost;
};
var recalcPoints = function () {
const optionalAttributes = SheetUtils.toLowerCase(abilityListEnabledOptionals);
const optionalArray = optionalAttributes.map((key) => "11");
const pointbuyPoints = 3 * optionalAttributes.length + 27;
data = getCharmancerData();
var attribs = globalAttributesByCategory.abilities.concat(optionalAttributes);
var maxPoints = pointbuyPoints;
var scores = {};
_.each(attribs, function (attrib) {
scores[attrib] = parseInt(data["l1-abilities"].values[attrib]);
if (isNaN(scores[attrib])) {
scores[attrib] = 8;
}
});
var pointsAvailable = maxPoints;
// Decrement points based on selected attribs
_.each(scores, function (score) {
if (!isNaN(score)) {
var cost = costOfScore(score);
pointsAvailable -= cost;
}
});
// Disable options if points are below a threshold.
if (pointsAvailable < 9) {
var choicesToDisable = ["15", "14", "13", "12", "11", "10", "9"];
_.each(attribs, function (attrib) {
var toDisableForThisAttrib = [];
_.each(choicesToDisable, function (choice) {
if (scores[attrib] <= Number(choice) && costOfScore(scores[attrib]) + pointsAvailable < costOfScore(Number(choice))) {
toDisableForThisAttrib.push(choice);
}
});
disableCharmancerOptions(attrib, toDisableForThisAttrib);
});
} else {
_.each(attribs, function (attrib) {
disableCharmancerOptions(attrib, []);
});
}
_.each(attribs, function (attrib) {
setCharmancerText({ points_available_display: String(pointsAvailable) });
});
return String(pointsAvailable);
};
var setRollButton = function (name, data, title, hold) {
var set = {};
var holdstring = hold ? "r" + hold : "";
title = title || name.split("_")[1];
title = title[0].toUpperCase() + title.substring(1);
if (title == "Personality") title += " Trait";
var roll = "@{wtype}&{template:mancerroll} {{title=" + title + "}} {{c1=[[1d" + data.length + holdstring + "]]}}";
_.each(data, function (trait, x) {
roll += " {{option" + (x + 1) + "=" + trait + "}}";
});
set["roll_" + name + "_roll"] = roll;
if (!hold) {
set[name + "_array"] = JSON.stringify({ name: title, array: data });
}
setAttrs(set);
};
var isTashaEnabled = (data) => {
data = data || getCharmancerData();
let enabled = false;
const customizeOrigin =
SheetUtils.checkPath(data, "['l1-race']['values']['customize_origin']") && data["l1-race"]["values"]["customize_origin"] == 1;
if (
SheetUtils.checkPath(data, "['l1-welcome']['values']['has_tasha']") &&
data["l1-welcome"]["values"]["has_tasha"] == 1 &&
customizeOrigin
)
enabled = true;
return enabled;
};
var hasTasha = (callback) => {
getCompendiumPage("Customizing Your Origin", (eventInfo) => {
let enabled = false;
if (eventInfo.expansion && SheetUtils.checkPath(eventInfo, "['data']['Category']") && eventInfo["data"]["Category"] === "Rules")
enabled = true;
callback(enabled);
});
};
var clearTasha = (eventinfo, subraceOnly, callback) => {
if (isTashaEnabled() && eventinfo.sourceType && eventinfo.sourceType == "player") {
let update = {};
for (let i = 1; i <= 6; i++) {
update[`tasha_subrace_ability_choice${i}`] = "";
update[`tasha_subrace_ability_choice${i}_increase`] = "";
if (!subraceOnly) {
update[`tasha_race_ability_choice${i}`] = "";
update[`tasha_race_ability_choice${i}_increase`] = "";
}
}
setAttrs(update, () => {
if (callback) callback();
});
} else {
callback();
}
};
/* MAIN CHOICE HANDLING */
on("mancerchange:race", function (eventinfo) {
clearTasha(eventinfo, false, () => {
var mancerdata = getCharmancerData();
getProficiencies(mancerdata, eventinfo.currentStep);
changeCompendiumPage("race-info", eventinfo.newValue);
hideChoices();
var reset = {};
if (!(eventinfo.newValue === "" || eventinfo.newValue === undefined)) {
showChoices(["race_options"]);
}
recalcData();
if (eventinfo.sourceType == "player") {
reset = {
race_ability_choice1: "",
race_ability_choice2: "",
race_ability_choice3: "",
subrace_ability_choice1: "",
subrace_ability_choice2: "",
subrace_ability_choice3: "",
subrace: "",
race_name: "",
subrace_name: "",
};
_.each(abilityList, function (ability) {
reset["race_custom_" + ability.toLowerCase()] = 0;
reset["subrace_custom_" + ability.toLowerCase()] = 0;
reset[ability.toLowerCase() + "_save"] = "";
});
reset["custom_race_spell_ability"] = "";
reset["custom_race_spell_number"] = "1";
reset["custom_race_spell_list"] = "";
reset["race_name"] = "";
reset["subrace_name"] = "";
reset["has_subrace"] = "";
clearRepeatingSections("race_holder");
clearRepeatingSections("subrace_holder");
}
if (eventinfo.newValue === "Rules:Races") {
//Clears saved data for this field
getCompendiumPage("");
setAttrs(reset, function () {
var update = { race_text: "" };
var options = eventinfo.sourceType == "player" ? { selected: "" } : {};
showChoices(["custom_race"]);
setCharmancerText(update);
addCustomSections("Race");
deleteSlideData("l1-feat");
});
} else {
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
setAttrs(reset, function () {
const usingTasha = isTashaEnabled();
mancerdata = getCharmancerData();
var update = {};
var showList = ["race_always", "race_traits"];
var possibles = ["race_size", "race_speed", "race_ability_score", "race_traits"];
var data = p["data"];
var validRacialSizes = [];
_.each(proficiencyList, function (type) {
possibles.push("race_" + type.toLowerCase() + "s");
});
_.each(possibles, function (key) {
update[key] = "";
});
if (data["Creature Type"] && data["Creature Type"].toLowerCase().trim() != "humanoid") {
const type_desc = getTranslationByKey(`creaturetype-desc`).replace(/\%/g, data["Creature Type"]);
update["race_creature_type"] = `${data["Creature Type"]}<br/><span>${type_desc}</span>`;
} else {
update["race_creature_type"] = "Humanoid";
}
if (data["Size"]) {
if (data["Size"].includes(",") || data["Size"].includes(" or ") || data["Size"].includes(" Or ")) {
data["Size"] = data["Size"].replace(/ or /gi, ",");
let validSizes = data["Size"].split(",");
validSizes = validSizes.map((entry) => entry.trim());
const last = validSizes.pop();
const text = `You are ${validSizes.join(", ")} or ${last}. You choose the size when you select this race.`;
update["race_size"] = text;
showList.push("custom_size");
validSizes = [...validSizes, last].map((entry) => entry.toLowerCase());
validRacialSizes = validSizes;
//setCharmancerOptions("custom_size", validSizes);
} else {
update["race_size"] = data["Size"];
}
}
if (data["Speed"]) {
update["race_speed"] = data["Speed"];
}
showList = showList.concat(handleAbilities(data, "race", usingTasha));
if (data["data-Ability Score Increase"]) {
var abilityText = [];
//Safeguarding Compendium data:
const json = Compendium.parseObject(data["data-Ability Score Increase"], `E_P5TA4NSV:Page:${eventinfo.newValue}`);
_.each(json, function (increase, ability) {
abilityText.push(ability + " +" + increase);
});
update["race_ability_score"] = abilityText.join(", ");
}
handleBlobs(data.blobs, { filters: { Level: "1" }, section: "race", slide: "l1-race" });
setCharmancerText(update);
var race_name =
eventinfo.newValue && eventinfo.newValue.split(":").length > 1 && eventinfo.newValue.split(":")[0] === "Races"
? eventinfo.newValue.split(":")[1]
: false;
race_name = removeExpansionInfo(race_name);
if (race_name) {
var subOptions = { show_source: true };
if (eventinfo.sourceType != "player") {
subOptions.silent = true;
} else {
subOptions.selected = "";
}
setCharmancerOptions("subrace", "Category:Subraces data-Parent:" + race_name, subOptions, function (values) {
if (values.length) {
setAttrs({ has_subrace: "true" });
showChoices(["subrace"]);
}
});
} else {
hideChoices(["subrace"]);
}
showChoices(showList);
if (validRacialSizes.length > 0) setCharmancerOptions("comp_size_choice", validRacialSizes);
recalcData();
});
});
}
});
});
on("mancerchange:subrace", function (eventinfo) {
clearTasha(eventinfo, true, () => {
var mancerdata = getCharmancerData();
getProficiencies(mancerdata, eventinfo.currentStep);
changeCompendiumPage("race-info", eventinfo.newValue);
var initHide = ["subrace_possible", "custom_trait_2", "custom_trait_3", "custom_trait_4"];
var reset = {};
if (eventinfo.newValue === "" || eventinfo.newValue === undefined) {
initHide.push("subrace_options");
} else {
showChoices(["subrace_options"]);
}
hideChoices(initHide);
recalcData();
if (eventinfo.sourceType == "player") {
reset = { subrace_ability_choice1: "", subrace_ability_choice2: "", subrace_ability_choice3: "", subrace_name: "" };
_.each(abilityList, function (ability) {
reset["race_custom_" + ability.toLowerCase()] = 0;
reset["subrace_custom_" + ability.toLowerCase()] = 0;
});
reset["custom_race_spell_ability"] = "";
reset["custom_race_spell_number"] = "1";
reset["custom_race_spell_list"] = "";
reset["subrace_name"] = "";
clearRepeatingSections("subrace_holder");
}
if (eventinfo.newValue === "Rules:Races") {
//Clears saved data for this field
getCompendiumPage("");
setAttrs(reset, function () {
var update = { subrace_text: "" };
var options = eventinfo.sourceType == "player" ? { selected: "" } : {};
showChoices(["custom_subrace"]);
setCharmancerText(update);
addCustomSections("Subrace");
deleteSlideData("l1-feat");
});
} else {
const usingTasha = isTashaEnabled();
hideChoices(["custom_subrace"]);
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
var update = {};
var showList = ["subrace_choices"];
var possibles = ["subrace_speed", "subrace_ability_score"];
var data = p["data"];
_.each(possibles, function (key) {
update[key] = "";
});
if (!data["data-Feats"])
deleteSlideData("l1-feat", function () {
recalcData();
});
if (data["Speed"]) {
update["subrace_speed"] = data["Speed"];
showList.push("subrace_speed_row");
}
showList = showList.concat(handleAbilities(data, "subrace", usingTasha));
if (data["data-Ability Score Increase"]) {
var abilityText = [];
//Safeguarding Compendium data:
const json = Compendium.parseObject(
data["data-Ability Score Increase"],
`E_BGM435JD:Page:${eventinfo.newValue}:data-Ability Score Increase`
);
_.each(json, function (increase, ability) {
abilityText.push(ability + " +" + increase);
});
update["subrace_ability_score"] = abilityText.join(", ");
}
handleBlobs(data.blobs, { filters: { Level: "1" }, section: "subrace", slide: "l1-race" });
setCharmancerText(update);
showChoices(showList);
setAttrs(reset);
recalcData();
});
}
});
});
on("mancerchange:class", function (eventinfo) {
if (eventinfo.sourceType && eventinfo.sourceType === "player") {
deleteSlideData("l1-class", () => {
changeCharmancerPage("l1-class");
});
} else {
setCharmancerText({ "sheet-class_holder": "" });
var mancerdata = getCharmancerData();
getProficiencies(mancerdata, eventinfo.currentStep);
changeCompendiumPage("class-info", eventinfo.newValue);
hideChoices();
var initHide = ["class_possible", "subclass_choices"];
var reset = {};
if (!(eventinfo.newValue === "" || eventinfo.newValue === undefined)) {
showChoices(["classes_options"]);
}
var current = recalcData();
if (eventinfo.sourceType == "player") {
reset = { subclass: "" };
_.each(abilityList, function (ability) {
reset[ability.toLowerCase() + "_save"] = "";
});
reset["custom_class_spell_ability"] = "";
reset["custom_class_cantrip_number"] = "0";
reset["custom_class_spell_number"] = "0";
reset["custom_class_spell_list"] = "";
reset["custom_hit_die"] = "";
reset["class_name"] = "";
reset["subclass_name"] = "";
reset["class_equipment_choice1"] = "";
reset["class_equipment_choice2"] = "";
reset["class_equipment_choice3"] = "";
reset["class_equipment_choice4"] = "";
clearRepeatingSections("class_holder");
clearRepeatingSections("subclass_holder");
}
if (eventinfo.newValue === "Rules:Classes") {
//Clears saved data for this field
getCompendiumPage("");
setAttrs(reset, function () {
var update = { class_text: "", custom_feature_name: "Class Features" };
var options = eventinfo.sourceType == "player" ? { selected: "" } : {};
showChoices(["custom_class"]);
update["custom_feature_name"] = "Custom Class Features";
setCharmancerText(update);
addCustomSections("Class");
});
} else {
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
var update = {};
var showList = [];
var data = p["data"];
var hideList = [];
handleBlobs(data.blobs, { filters: { Level: "1" }, section: "class", slide: "l1-class" });
var class_name =
eventinfo.newValue && eventinfo.newValue.split(":").length > 1 && eventinfo.newValue.split(":")[0] === "Classes"
? eventinfo.newValue.split(":")[1]
: false;
class_name = removeExpansionInfo(class_name);
if (class_name && data["data-Subclass Level"] == 1) {
var subOptions = { show_source: true };
if (eventinfo.sourceType != "player") {
subOptions.silent = true;
} else {
subOptions.selected = "";
}
update["subclass_prompt"] = "Choose a " + data["Subclass Name"];
setCharmancerOptions("subclass", "Category:Subclasses data-Parent:" + class_name, subOptions, function (values) {
if (values.length) showChoices(["subclass"]);
});
} else {
hideList.push("subclass");
}
if (data["data-Equipment"]) {
showList.push("class_equipment_row");
var equipment_string = "";
//Safeguarding Compendium data:
const json = Compendium.parseObject(data["data-Equipment"], `E_XV7ZV898:Page:${eventinfo.newValue}:data-Equipment`);
if (json["default"]) {
equipment_string += json["default"].join(", ");
}
for (var i = 1; i < 5; i++) {
if (json[i]) {
if (!json["default"].length && i == 1) {
equipment_string += "";
} else {
equipment_string += " and ";
}
equipment_string += cleanEquipment(json[i]);
}
}
update["class_standard_equipment"] = equipment_string;
}
setCharmancerText(update);
showChoices(showList);
hideChoices(hideList);
recalcData();
setAttrs(reset);
});
}
}
});
on("mancerchange:subclass", function (eventinfo) {
var mancerdata = getCharmancerData();
getProficiencies(mancerdata, eventinfo.currentStep);
changeCompendiumPage("class-info", eventinfo.newValue);
var initHide = ["subclass_possible"];
var reset = {};
if (eventinfo.newValue === "" || eventinfo.newValue === undefined) {
initHide.push("subclass_options");
initHide.push("custom_subclass");
} else {
showChoices(["subclass_options"]);
if (eventinfo.newValue === "Rules:Classes") {
showChoices(["custom_subclass"]);
} else {
initHide.push("custom_subclass");
}
}
hideChoices(initHide);
var current = recalcData();
if (eventinfo.sourceType == "player") {
reset["custom_class_spell_ability"] = "";
reset["custom_class_cantrip_number"] = "0";
reset["custom_class_spell_number"] = "0";
reset["custom_class_spell_list"] = "";
reset["custom_subclass_cantrip_number"] = "0";
reset["custom_subclass_cantrip_list"] = "";
reset["custom_subclass_spell_number"] = "0";
reset["custom_subclass_spell_list"] = "";
reset["subclass_name"] = "";
clearRepeatingSections("subclass_holder");
}
if (eventinfo.newValue === "Rules:Classes") {
//Clears saved data for this field
getCompendiumPage("");
setAttrs(reset, function () {
var mancerdata = getCharmancerData();
var options = eventinfo.sourceType == "player" ? { selected: "" } : {};
var subclassname = mancerdata["l1-class"].data.class["Subclass Name"];
showChoices(["custom_subclass"]);
if (mancerdata["l1-class"].data.class && mancerdata["l1-class"].data.class["Spellcasting Ability"]) {
showChoices(["custom_additional_spells"]);
} else {
showChoices(["custom_class_spells"]);
}
addCustomSections("Subclass");
});
} else {
hideChoices(["custom_subclass"]);
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
var data = p["data"];
handleBlobs(data.blobs, { filters: { Level: "1" }, section: "subclass", slide: "l1-class" });
recalcData();
setAttrs(reset);
});
}
});
on("mancerchange:background", function (eventinfo) {
var mancerdata = getCharmancerData();
getProficiencies(mancerdata, eventinfo.currentStep);
changeCompendiumPage("background-info", eventinfo.newValue);
var initHide = ["background_possible", "custom_background"];
var reset = {};
if (eventinfo.newValue === "" || eventinfo.newValue === undefined) {
initHide.push("backgrounds_options");
} else {
showChoices(["backgrounds_options"]);
}
hideChoices(initHide);
var current = recalcData();
if (eventinfo.sourceType == "player") {
reset = {};
reset["background_detail_choice"] = "";
reset["background_personality_choice1"] = "";
reset["background_personality_choice2"] = "";
reset["background_ideal_choice"] = "";
reset["background_bond_choice"] = "";
reset["background_flaw_choice"] = "";
reset["custom_background_trait_name"] = "";
reset["custom_background_trait_desc"] = "";
reset["feat_bg"] = "";
clearRepeatingSections("background_holder");
//SD-1693
clearRepeatingSections("randomize-traits");
hideChoices(["randomize-traits"]);
}
if (eventinfo.newValue === "Rules:Backgrounds") {
//Clears saved data for this field
getCompendiumPage("");
setAttrs(reset, function () {
var update = { background_text: "" };
var options = eventinfo.sourceType == "player" ? { selected: "" } : {};
hideChoices(["standardbg"]);
showChoices(["custom_background"]);
setCharmancerText(update);
addCustomSections("Background");
});
} else {
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
var update = {};
var showList = ["standardbg"];
var possibles = ["background_feature"];
var data = p["data"];
var hideList = [];
_.each(proficiencyList, function (type) {
possibles.push("background_" + type.toLowerCase() + "s");
});
_.each(possibles, function (key) {
update[key] = "";
});
handleBlobs(data.blobs, { filters: { Level: "1" }, section: "background", slide: "l1-background" });
//SD-1693
const randomizedTraits = Object.keys(data).filter((entry) => {
return entry.includes("data-Randomize");
});
randomizedTraits.forEach((trait) => {
const traitData = JSON.parse(data[trait]);
const label = trait.replace("data-Randomize", "").trim();
addRepeatingSection("randomize-traits", "randomize-trait", (sectionid) => {
const id = SheetUtils.getUID(sectionid);
const selectSelector = `comp_${sectionid}_choice`;
const buttonSelector = `${sectionid}_choice`;
const parentClass = `repeating_${id}_randomize-trait`;
setAttrs({ [`${parentClass}_name`]: label }, () => {
setCharmancerOptions(selectSelector, traitData["Choices"], {}, function (opts) {
setRollButton(buttonSelector, opts, label);
});
setCharmancerText({
[`${parentClass} .trait-label`]: label,
[`${parentClass} .trait-description`]: traitData["Description"],
});
});
});
});
if (randomizedTraits.length > 0) showList.push("randomize-traits");
if (data["data-Background Choices"] && data["data-Background Choices"] != "") {
showList.push("background_detail_choice_row");
//Safeguarding Compendium data:
var choices = Compendium.parseArray(
data["data-Background Choices"],
`E_8CK37T8B:Page:${eventinfo.newValue}:data-Background Choices`
);
showList.push("background_detail_choice");
setCharmancerOptions("background_detail_choice", choices, {}, function (opts) {
setRollButton("background_detail_choice", opts, data["data-Background Choice Name"]);
});
update["background_detail_choice_name"] = data["data-Background Choice Name"];
}
if (eventinfo.sourceType == "player") {
reset["background_detail_choice"] = "";
}
if (data["data-Personality Traits"]) {
showList.push("background_personality_row");
//Safeguarding Compendium data:
var choices = Compendium.parseArray(
data["data-Personality Traits"],
`E_Y9VNN6F8:Page:${eventinfo.newValue}:data-Personality Traits`
);
setCharmancerOptions("background_personality_choice1", choices, {}, function (opts) {
setRollButton("background_personality_choice1", opts);
});
setCharmancerOptions("background_personality_choice2", choices, {}, function (opts) {
setRollButton("background_personality_choice2", opts);
});
showList.push("background_personality_choice1");
showList.push("background_personality_choice2");
setAttrs({ traitless: "0" });
} else {
setAttrs({ traitless: "1" });
}
if (data["data-Ideals"]) {
//Safeguarding Compendium data:
var choices = Compendium.parseArray(data["data-Ideals"], `E_34RZKZJG:Page:${eventinfo.newValue}:data-Ideals`);
setCharmancerOptions("background_ideal_choice", choices, {}, function (opts) {
setRollButton("background_ideal_choice", opts);
});
showList.push("background_ideal_choice");
}
if (data["data-Bonds"]) {
//Safeguarding Compendium data:
var choices = Compendium.parseArray(data["data-Bonds"], `E_Z67Y2HUR:Page:${eventinfo.newValue}:data-Bonds`);
setCharmancerOptions("background_bond_choice", choices, {}, function (opts) {
setRollButton("background_bond_choice", opts);
});
showList.push("background_bond_choice");
}
if (data["data-Flaws"]) {
//Safeguarding Compendium data:
var choices = Compendium.parseArray(data["data-Flaws"], `E_JQ4MU86P:Page:${eventinfo.newValue}:data-Flaws`);
setCharmancerOptions("background_flaw_choice", choices, {}, function (data) {
setRollButton("background_flaw_choice", data);
});
showList.push("background_flaw_choice");
}
if (eventinfo.sourceType == "player") {
reset = _.extend(reset, {
background_personality_choice1: "",
background_personality_choice2: "",
background_ideal_choice: "",
background_bond_choice: "",
background_flaw_choice: "",
});
}
if (data["data-Equipment"]) {
showList.push("background_equipment_row");
var equipment_string = "";
//Safeguarding Compendium data:
const json = Compendium.parseObject(data["data-Equipment"], `E_8RFXEBG8:Page:${eventinfo.newValue}:data-Equipment`);
if (json["default"]) {
equipment_string += json["default"].join(", ");
}
for (var i = 1; i < 5; i++) {
if (json[i]) {
if (!json["default"].length && i == 1) {
equipment_string += "";
} else {
equipment_string += " and ";
}
equipment_string += cleanEquipment(json[i]);
}
}
update["background_standard_equipment"] = equipment_string;
}
var background_name =
eventinfo.newValue && eventinfo.newValue.split(":").length > 1 && eventinfo.newValue.split(":")[0] === "Backgrounds"
? eventinfo.newValue.split(":")[1]
: false;
setCharmancerText(update);
showChoices(showList);
hideChoices(hideList);
recalcData();
setAttrs(reset);
});
}
});
/* ABILITY SCORE */
on("page:l1-abilities", function (eventinfo) {
var data = getCharmancerData();
getCompendiumPage("Rules:Ability%20Scores", function (p) {
p = removeDuplicatedPageData(p);
var pagedata = p["data"];
var allChoices = [];
if (pagedata["data-Generation Choices"]) {
//Safeguarding Compendium data:
const genChoices = Compendium.parseObject(
pagedata["data-Generation Choices"],
`E_AWQ7Y4YE:Page:Rules:Ability Scores:data-Generation Choices`
);
_.each(genChoices, function (value, choice) {
allChoices.push(choice);
});
}
setCharmancerOptions("abilities", allChoices);
if (data["l1-abilities"] && data["l1-abilities"].values && data["l1-abilities"].values.abilities) {
setAttrs({ abilities: data["l1-abilities"].values.abilities });
}
});
});
on("mancerchange:abilities", function (eventinfo) {
const optionalAttributes = SheetUtils.toLowerCase(abilityListEnabledOptionals);
const optionalArray = optionalAttributes.map((key) => "11");
const pointbuyPoints = (3 * optionalAttributes.length + 27).toString();
var data = getCharmancerData();
var initHide = ["abilities_possible"];
var attribs = globalAttributesByCategory.abilities.concat(optionalAttributes);
var standardArray = ["8", "10"]
.concat(optionalArray)
.concat(["12", "13", "14", "15"])
.map((value, index) => {
return `${value}~${index + 1}`;
});
var pointbuyChoices = ["8", "9", "10", "11", "12", "13", "14", "15"];
hideChoices(initHide);
if (data["l1-abilities"].values.abilities == "Standard Array") {
showChoices(["abilities_stdarray", "abilities_selects"]);
_.each(attribs, function (attrib) {
setCharmancerOptions(attrib, standardArray);
});
}
if (data["l1-abilities"].values.abilities == "Roll for Stats") {
showChoices(["abilities_rollstats"]);
if (data["l1-abilities"].values.roll_results && data["l1-abilities"].values.roll_results.length) {
showChoices(["abilities_rollstats_rolled", "abilities_selects"]);
var roll_results =
typeof data["l1-abilities"].values.roll_results == "string"
? data["l1-abilities"].values.roll_results.split(",")
: data["l1-abilities"].values.roll_results;
_.each(attribs, function (attrib) {
setCharmancerOptions(attrib, roll_results);
});
}
}
if (data["l1-abilities"].values.abilities == "Point Buy") {
showChoices(["abilities_pointbuy", "abilities_selects"]);
setCharmancerText({ points_available_display: data["l1-abilities"].values.pointbuy_points || pointbuyPoints });
_.each(attribs, function (attrib) {
setCharmancerOptions(attrib, pointbuyChoices);
});
}
if (data["l1-abilities"].values.abilities == "custom") {
showChoices(["abilities_custom"]);
}
reset = {};
if (eventinfo.sourceType == "player") {
_.each(attribs, function (attrib) {
reset[attrib] = "";
});
reset["pointbuy_points"] = pointbuyPoints;
}
recalcData();
setAttrs(reset);
});
on(
"mancerchange:strength mancerchange:dexterity mancerchange:constitution mancerchange:intelligence mancerchange:wisdom mancerchange:charisma mancerchange:honor mancerchange:sanity",
function (eventinfo) {
const optionalAttributes = SheetUtils.toLowerCase(abilityListEnabledOptionals);
data = getCharmancerData();
var attribs = globalAttributesByCategory.abilities.concat(optionalAttributes);
var clearvalue = eventinfo.newValue;
var clearObject = {
strength: data["l1-abilities"].values.strength,
dexterity: data["l1-abilities"].values.dexterity,
constitution: data["l1-abilities"].values.constitution,
intelligence: data["l1-abilities"].values.intelligence,
wisdom: data["l1-abilities"].values.wisdom,
charisma: data["l1-abilities"].values.charisma,
};
optionalAttributes.forEach((attribute) => {
clearObject[attribute] = data["l1-abilities"].values[attribute];
});
if (data["l1-abilities"].values.abilities != "custom" && data["l1-abilities"].values.abilities != "Point Buy") {
attribs = _.reject(attribs, function (item) {
return item == eventinfo.triggerName;
});
_.each(attribs, function (attrib) {
if (clearvalue == clearObject[attrib]) {
clearObject[attrib] = "";
}
});
}
if (data["l1-abilities"].values.abilities == "Point Buy") {
clearObject.pointbuy_points = recalcPoints();
}
if (eventinfo.sourceType == "player") {
setAttrs(clearObject);
}
recalcData();
}
);
on("mancerroll:rollstats", function (eventinfo) {
const optionalAttributes = SheetUtils.toLowerCase(abilityListEnabledOptionals);
var attribs = globalAttributesByCategory.abilities.concat(optionalAttributes);
var results = [];
var i = 1;
_.each(eventinfo.roll, function (roll) {
results.push(roll.result + "~" + i);
i++;
});
results.sort(function (a, b) {
return Number(a.split("~")[0]) - Number(b.split("~")[0]);
});
_.each(attribs, function (attrib) {
setCharmancerOptions(attrib, results);
});
let set = {
roll_results: results,
strength: "",
dexterity: "",
constitution: "",
intelligence: "",
wisdom: "",
charisma: "",
};
optionalAttributes.forEach((attribute) => {
set[attribute] = "";
});
setAttrs(set);
showChoices(["abilities_rollstats_rolled", "abilities_selects"]);
});
on("clicked:clearstats", function (eventinfo) {
const optionalAttributes = SheetUtils.toLowerCase(abilityListEnabledOptionals);
var data = getCharmancerData();
var attribs = globalAttributesByCategory.abilities.concat(optionalAttributes);
reset = {};
_.each(attribs, function (attrib) {
reset[attrib] = "";
disableCharmancerOptions(attrib, []);
});
setAttrs(reset);
});
/* EQUIPMENT */
on("page:l1-equipment", function (eventinfo) {
const data = getCharmancerData();
const class_string = getName("class", data, true);
const background_string = getName("background", data, true);
const class_equipment =
data && data["l1-equipment"] && data["l1-equipment"].values && data["l1-equipment"].values.equipment_class
? data["l1-equipment"].values.equipment_class
: undefined;
const background_equipment =
data && data["l1-equipment"] && data["l1-equipment"].values ? data["l1-class"].values.equipment_background : undefined;
let update = {};
let update_attr = {};
let showList = [];
let hideList = [];
let allBlobs = getRelevantBlobs(data, "1", "l1");
let equipmentType =
data["l1-equipment"] && data["l1-equipment"].values && data["l1-equipment"].values.equipment_type
? data["l1-equipment"].values.equipment_type
: "";
const handleChoices = (type) => {
let equipment = {};
if (data[`l1-${type}`] && data[`l1-${type}`].data[type] && data[`l1-${type}`].data[type]["data-Equipment"]) {
equipment = data[`l1-${type}`].data[type]["data-Equipment"];
}
if (allBlobs.sorted[type]) {
_.each(allBlobs.sorted[type], (blob, blobName) => {
if (blob.Equipment) {
let blobstuff = blob.Equipment;
try {
//Safeguarding Compendium data:
blobstuff = Compendium.parseArray(blob.Equipment, `E_GB5V42OV:Blob:${blobName}:Equipment`);
} catch (e) {}
equipment.default = equipment.default ? equipment.default.concat(blobstuff) : blobstuff;
}
});
}
_.each(equipment, function (feature, num) {
if (num === "default") {
update[`${type}_equipment`] = feature.join(", ");
} else {
let dynamic_items = "Category:None";
let static_items = [];
let hasDouble = false;
const doubleOffset = 4;
let equipmentTitle = cleanEquipment(feature)
.replace(/^\w/, (char) => char.toUpperCase())
.replace(/(^\(|\)$)/g, "");
if (equipmentTitle.search("Two of") == 0) {
equipmentTitle += ")";
}
update[`${type}_equipment_choice${num}_title`] = equipmentTitle;
_.each(feature, function (f) {
if (f.indexOf("Subtype:") > -1) {
dynamic_items = `Category:Items "Item Rarity":Standard ${f.split("~")[0]}`;
} else if (f.indexOf("*Weapon") > -1) {
dynamic_items = `Category:Items "Item Rarity":Standard ${f.split("~")[0]}`;
} else {
static_items.push(f);
}
if (f.split("~")[1] && f.split("~")[1] == "DBL") {
hasDouble = true;
}
});
if (hasDouble) {
showList.push(`${type}_equipment_choice${num}_double`);
const doubleChoiceNum = parseInt(num) + doubleOffset;
setCharmancerOptions(`${type}_equipment_choice${doubleChoiceNum}`, dynamic_items, { add: static_items, category: "Items" });
} else {
hideList.push(`${type}_equipment_choice${num}_double`);
}
setCharmancerOptions(`${type}_equipment_choice${num}`, dynamic_items, { add: static_items, category: "Items" });
showList.push(`${type}_equipment_choice${num}`);
}
});
};
recalcData();
if (class_string) {
showList.push("has_class");
update["class_label"] = class_string;
update_attr["equipment_class"] = class_string;
if (class_equipment && class_equipment != class_string) {
update["starting_gold_btn_label"] = "";
update["class_equipment"] = "";
update_attr["starting_gold"] = 0;
update_attr["equipment_type"] = "";
equipmentType = "";
for (i = 1; i <= 8; i++) {
update_attr[`class_equipment_choice${i}`] = "";
}
}
if (class_string === "custom") {
update_attr["equipment_type"] = "gold";
hideList.push("equipment_type", "random_gold_option");
showList.push("custom_class_gold", "gold_option", "eqp_custom_class");
} else {
handleChoices("class");
const startingGold =
data["l1-class"].data.class && data["l1-class"].data.class["Starting Gold"] ? data["l1-class"].data.class["Starting Gold"] : false;
if (startingGold) {
update["starting_gold_btn_label"] = startingGold;
update_attr["roll_starting_gold"] = `@{wtype}&{template:mancerroll} {{title=Starting Gold}} {{r1=[[${startingGold.replace(
"x",
"*"
)}]]}}`;
showList.push("random_gold_option");
} else {
update_attr["roll_starting_gold"] = "@{wtype}&{template:mancerroll} {{title=Starting Gold}} {{r1=[[0]]}}";
update_attr["equipment_type"] = "class";
equipmentType = "class";
hideList.push("equipment_type", "gold_option");
showList.push("class_option", "no_gold_option");
}
}
} else {
showList.push("no_class");
}
if (background_string) {
update["background_label"] = background_string;
update_attr["equipment_background"] = background_string;
background_string != "custom" ? handleChoices("background") : false;
if ((data["l1-equipment"] && data["l1-equipment"].values && data["l1-equipment"].values.equipment_type) || class_string == "custom") {
if ((data["l1-equipment"] && equipmentType == "class") || class_string == "custom") {
showList.push("has_background");
if (background_equipment && background_equipment != background_string) {
update["background_equipment"] = "";
for (i = 1; i <= 4; i++) {
update_attr[`background_equipment_choice${i}`] = "";
}
}
}
if (data["l1-equipment"] && equipmentType == "gold" && class_string != "custom") {
showList.push("background_chose_starting_gold");
}
}
try {
if (data["l1-background"] && data["l1-background"].data && data["l1-background"].data.background["data-Starting Gold"]) {
update_attr["background_starting_gold"] = data["l1-background"].data.background["data-Starting Gold"] || 0;
}
} catch (error) {
errorMessage("l1-background", error);
}
} else {
showList.push("no_background");
}
if (equipmentType === "" || equipmentType === undefined) {
hideList.push("gold_option", "class_option");
} else if (equipmentType === "gold") {
if (background_string) showList.push("background_gold_option");
showList.push("gold_option");
hideList.push("class_option");
} else if (equipmentType === "class") {
showList.push("class_option");
hideList.push("gold_option");
}
showChoices(showList);
hideChoices(hideList);
setCharmancerText(update);
setAttrs(update_attr);
});
on("mancerchange:equipment_type", function (eventinfo) {
var showList = [];
var hideList = [];
var update = {};
var reset = {};
var data = getCharmancerData();
var class_string = getName("class", data, true);
var background_string = getName("background", data, true);
if (eventinfo.sourceType == "player") {
if (eventinfo.newValue === "" || eventinfo.newValue === undefined) {
hideList.push("gold_option", "class_option", "background_gold_option");
} else if (eventinfo.newValue === "gold") {
if (background_string) showList.push("background_gold_option");
if (class_string != "custom") showList.push("random_gold_option");
showList.push("gold_option");
hideList.push("class_option");
} else if (eventinfo.newValue === "class") {
showList.push("class_option");
hideList.push("gold_option", "background_gold_option");
}
}
showChoices(showList);
hideChoices(hideList);
});
on("mancerroll:starting_gold", function (eventinfo) {
setAttrs({ starting_gold: eventinfo.roll[0].result });
});
/* REPEATING SECTION LISTENERS */
on("mancerchange:repeating_row", function (eventinfo) {
var mancerdata = getCharmancerData();
var proficiencies = getProficiencies(mancerdata, eventinfo.currentStep);
var section = eventinfo.sourceSection
.split("_")[2]
.split("-")[0]
.replace(/[0-9]+$/gm, "");
if (eventinfo.currentStep.split("-")[0] === "utility") section = `new${section}`;
var type = eventinfo.sourceSection.split("_")[3];
var sourcepage = eventinfo.currentStep;
if (eventinfo.currentStep.split("-")[0] == "lp") {
var sectioninfo = eventinfo.sourceSection.split("_")[2].split("--")[0];
if (sectioninfo.indexOf("-") === -1) {
let index = sectioninfo.indexOf(section) + section.length;
sectioninfo = [sectioninfo.slice(0, index), "-", sectioninfo.slice(index)].join("");
}
sectioninfo = sectioninfo.split("-");
if (sectioninfo.length > 1) {
sourcepage = "lp-levels";
section = "class" + sectioninfo[1];
if (sectioninfo[0] == "subclass") section += "_subclass";
} else {
sourcepage = "lp-welcome";
section = "lp-" + sectioninfo[0];
}
}
var pagedata = mancerdata[sourcepage].data[section];
if (_.last(eventinfo.sourceAttribute.split("_")) == "choice") {
if (type == "feature") {
if (eventinfo.previousValue !== eventinfo.newValue) {
clearRepeatingSections(eventinfo.sourceSection + " span");
if (eventinfo.newValue && eventinfo.newValue.split(":")[0] == "Blob") {
if (pagedata.blobs[eventinfo.newValue.split(":")[1]]["Pick Spells"] || pagedata.blobs[eventinfo.newValue.split(":")[1]].Spells) {
deleteSlideData("lp-spells");
}
handleBlobs(pagedata.blobs, {
filters: { name: eventinfo.newValue.split(":")[1] },
section: section,
element: eventinfo.sourceSection + " span",
slide: eventinfo.currentStep,
parent: section,
});
}
}
}
if (eventinfo.newValue == "custom") {
showChoices(["custom[name=comp_" + eventinfo.sourceSection + "_custom]"]);
} else {
var toset = {};
hideChoices(["custom[name=comp_" + eventinfo.sourceSection + "_custom]"]);
toset[eventinfo.sourceSection + "_custom"] = "";
setAttrs(toset);
}
}
});
on("mancerchange:repeating_row", function (eventinfo) {
if (eventinfo.currentStep !== "lp-choices") return;
if (eventinfo.sourceAttribute.indexOf("feature_choice") === -1 && eventinfo.sourceAttribute.indexOf("feature_info") === -1) return;
const data = getCharmancerData();
//Safeguarding Compendium data:
const traits = Compendium.parseObject(data["lp-welcome"].values.previous_repeating, `E_6QRBUARE`).traits || [];
const singleSelectionTraits = [{ group: "Maneuvers", single: "Maneuver" }]; //Types of traits that can be taken only once.
let traitsAlreadyTaken = [];
//Read the current type of a trait
const currentTrait = data["lp-choices"].values[eventinfo.sourceAttribute.replace("_choice", "_info")] || "";
const traitIndex = singleSelectionTraits
.map((trait) => {
return trait.group;
})
.indexOf(currentTrait);
//Return if the trait is not among the ones set for single selection.
if (traitIndex === -1) return;
//Get single item type
const traitType = singleSelectionTraits[traitIndex].single + ": ";
const getRelevantChoices = function (data, choiceType) {
const choices = data["lp-choices"].values || [];
let relevantChoices = [];
for (choice in choices) {
if (choice.indexOf("_info") === -1) continue;
if (choices[choice] === choiceType) relevantChoices.push(choice.replace("_info", ""));
}
return relevantChoices;
};
//Reading traits set in previous Levels
for (trait in traits) {
if (traits[trait].name.indexOf(traitType) > -1) {
const selectedTrait = traits[trait].name.replace(traitType, "");
traitsAlreadyTaken.push("Blob:" + selectedTrait);
}
}
//Handling traits choosen at current LP
const choices = getRelevantChoices(data, singleSelectionTraits[traitIndex].group);
for (choice in choices) {
if (choices[choice] + "_choice" in data["lp-choices"].values) {
traitsAlreadyTaken.push(data["lp-choices"].values[choices[choice] + "_choice"]);
}
}
//Disable elements
for (choice in choices) {
//Enables all options
disableCharmancerOptions(choices[choice] + "_choice", "");
//Disable maneuvers arealdy selected at lv1 or during current LP
disableCharmancerOptions(choices[choice] + "_choice", traitsAlreadyTaken);
}
});
on("mancerchange:repeating_spellrow", function (eventinfo) {
if (eventinfo.sourceType == "player") {
changeCompendiumPage("spells-info", eventinfo.newValue, "card_only");
}
var mancerdata = getCharmancerData();
var known = knownSpells().known;
_.each(mancerdata["l1-spells"].repeating, function (id) {
disableCharmancerOptions(id + "_choice", known, { category: "Spells" });
});
});
//D&D 5e: Charactermancer Lvl+ Druid, Circle of Land feedback improvement (UC811)
//Land druids used to be able to change multiple lands once they levelup. This goes against the rules. This is a fix for that issue.
//By Miguel
on("mancerchange:repeating_row", function (eventinfo) {
const featureName = "Circle Spells";
const feedback = "You must choose your Circle Land at 3rd level";
const data = getCharmancerData();
//First we detect if this is circle spell, otherwise return.
if (eventinfo.currentStep !== "lp-choices") return;
if (eventinfo.sourceAttribute.indexOf("feature_choice") === -1 && eventinfo.sourceAttribute.indexOf("feature_info") === -1) return;
if (
!eventinfo.sourceSection + "_info" in data["lp-choices"].values ||
!data["lp-choices"].values[eventinfo.sourceSection + "_info"] === featureName
)
return;
//Safeguarding Compendium data:
const previousRepeating = Compendium.parseObject(data["lp-welcome"].values.previous_repeating, `E_9MZ1YYMS`);
const previousTraits = previousRepeating.traits;
const initialCircleTrait = previousTraits.findIndex((item) => item.name.indexOf(featureName + ": ") > -1);
//A land was had not been selected yet when Mancer was started (Ex: Levelling from LV 1st to 5th)
if (!/repeating_-.+_subclass-.+--[^3]+_feature/g.test(eventinfo.sourceSection)) {
const choices = data["lp-choices"].values;
for (choice in choices) {
if (choices[choice] === featureName) {
//Set all non 3rd level pickers to the choose value
if (/repeating_-.+_subclass-.+--[^3]+_feature/g.test(choice) && choice.indexOf("_info") > -1 && choices[choice] === featureName) {
const pickerClass = choice.replace("_info", "_choice");
const currentValue = eventinfo.newValue.replace("Blob:", "");
const initialLand = currentValue.replace(featureName, "").trim();
const choiceLevel = choice.match(/--./)[0].replace("--", "");
const choiceValue = initialLand + " " + featureName + "(" + choiceLevel + ")";
if ((eventinfo.newValue === "" || eventinfo.newValue === featureName) && eventinfo.previousValue !== feedback) {
setCharmancerOptions(pickerClass, [feedback], { selected: feedback }, function () {
disableCharmancerOptions(pickerClass, [""]);
});
} else {
setCharmancerOptions(pickerClass, [choiceValue], { category: "Blob", selected: "Blob:" + choiceValue }, function () {
disableCharmancerOptions(pickerClass, [""]);
});
}
}
}
}
return;
}
//A land was already selected when Mancer was started (Ex: Levelling from LV 3rd to 5th)
else if (initialCircleTrait > -1) {
const initialLand = previousTraits[initialCircleTrait].name.replace(featureName + ": ", "");
const choiceLevel = eventinfo.sourceSection.match(/--./)[0].replace("--", "");
const choiceValue = initialLand + " " + featureName + "(" + choiceLevel + ")";
const choiceClass = eventinfo.sourceSection + "_choice";
setCharmancerOptions(choiceClass, [choiceValue], { category: "Blob", selected: "Blob:" + choiceValue }, function () {
disableCharmancerOptions(choiceClass, [""]);
});
}
});
on("clicked:repeating_spellrow", function (eventinfo) {
var spell_name = "";
var data = getCharmancerData();
var card = "card_only";
if (_.last(eventinfo.sourceSection.split("_")) == "spellrow") {
spell_name = data["l1-spells"].values[eventinfo.sourceAttribute];
} else {
spell_name = data["l1-spells"].values[eventinfo.sourceSection + "_choice"]
? data["l1-spells"].values[eventinfo.sourceSection + "_choice"]
: "Rules:Spells";
card = data["l1-spells"].values[eventinfo.sourceSection + "_choice"] ? card : "";
}
changeCompendiumPage("spells-info", spell_name, card);
});
on("mancerchange:repeating_customrow", function (eventinfo) {
var mancerdata = getCharmancerData();
var profdata = getProficiencies(mancerdata, eventinfo.currentStep);
if (eventinfo.sourceAttribute.substr(-10) == "trait_name") {
var update = {};
update[eventinfo.sourceSection + " span"] = eventinfo.newValue;
setCharmancerText(update);
}
if (_.last(eventinfo.sourceSection.split("_")) == "proficiency") {
if (_.last(eventinfo.sourceAttribute.split("_")) == "type") {
var update = {};
var prof = eventinfo.newValue;
var choices = prof == "" ? [] : 'Category:Proficiencies "Type:' + prof + '"';
var settings = { category: "Proficiencies", disable: profdata.all[prof] };
update[eventinfo.sourceSection + " span + select"] = '<option value="" data-i18n="choose"></option>';
if (prof == "Language")
update[eventinfo.sourceSection + " span + select"] += '<option class="custom" value="custom" data-i18n="custom"></option>';
update[eventinfo.sourceSection + " span"] = eventinfo.newValue;
setCharmancerText(update);
if (prof == "") {
var toset = {};
toset[eventinfo.sourceSection + "_choice"] = "";
toset[eventinfo.sourceSection + "_custom"] = "";
setAttrs(toset);
hideChoices(["custom[name=comp_" + eventinfo.sourceSection + "_custom]"]);
} else {
setCharmancerOptions(eventinfo.sourceSection + "_choice", choices, settings);
}
} else if (_.last(eventinfo.sourceAttribute.split("_")) == "choice") {
if (eventinfo.newValue == "custom") {
showChoices(["custom[name=comp_" + eventinfo.sourceSection + "_custom]"]);
} else {
var toset = {};
hideChoices(["custom[name=comp_" + eventinfo.sourceSection + "_custom]"]);
toset[eventinfo.sourceSection + "_custom"] = "";
setAttrs(toset);
}
}
}
});
on("clicked:repeating_customrow", function (eventinfo) {
var mancerdata = getCharmancerData();
getRepeatingSections(function (repeating) {
var target = "";
var section = eventinfo.sourceSection.split("_")[3];
var type = _.last(eventinfo.sourceSection.split("_"));
_.each(repeating.tree, function (branch, parentid) {
_.each(branch, function (child, childid) {
if (childid == eventinfo.sourceSection) target = parentid;
});
});
addRepeatingSection(target, "custom-" + type, "custom_" + section + "_" + type);
});
});
on(customListeners, function (eventinfo) {
var mancerdata = getCharmancerData();
var num = parseInt(eventinfo.triggerName.substr(-1));
var base = eventinfo.triggerName.slice(0, -1);
var section = eventinfo.triggerName.split("_")[0].replace("sub", "");
if (
mancerdata["l1-" + section].values[base.split("_")[0]] &&
["Rules", "CategoryIndex"].indexOf(mancerdata["l1-" + section].values[base.split("_")[0]].split(":")[0]) != -1 &&
eventinfo.newValue
) {
showChoices([base + (num + 1)]);
}
});
on(changeListeners, function (eventinfo) {
if (eventinfo.currentStep && eventinfo.currentStep.split("-")[0] === "utility") {
recalcLpData(getAllUtilityBlobs(), "utility");
} else {
recalcData();
}
});
on("mancerchange:custom_race_spell_ability mancerchange:custom_class_spell_ability", function (eventinfo) {
var section = eventinfo.triggerName.split("_")[1];
if (eventinfo.newValue) {
showChoices(["custom_spellcasting"]);
} else {
var set = {};
set["custom_" + section + "_spell_number"] = "1";
set["custom_" + section + "_cantrip_number"] = "1";
set["custom_" + section + "_spell_list"] = "";
setAttrs(set);
hideChoices(["custom_spellcasting"]);
}
});
var addLV1Toolbar = function (mancerdata, eventinfo) {
mancerdata = mancerdata || getCharmancerData();
if (eventinfo.triggerName != "l1-welcome") {
addRepeatingSection("topbar-holder", "topbar");
recalcData();
getProficiencies(mancerdata, eventinfo.currentStep);
}
};
on(
"page:l1-welcome page:l1-class page:l1-race page:l1-abilities page:l1-background page:l1-equipment page:l1-spells page:l1-feat page:l1-bio page:l1-summary",
function (eventinfo) {
var mancerdata = getCharmancerData();
var buttons = "";
var buttoninfo = {
welcome: "charmancer-start",
race: "race-u",
class: "class-u",
abilities: "abilities-u",
background: "background-u",
equipment: "equipment-u",
spells: "spells-u",
feat: "feats-u",
bio: "bio-u",
summary: "review-u",
};
_.each(buttoninfo, function (translation, page) {
var here = "l1-" + page == eventinfo.triggerName;
buttons +=
'<li><button class="step' +
(here ? " here" : "") +
'" type="' +
(here ? "here" : "back") +
'" value="l1-' +
page +
'" data-i18n="' +
translation +
'"></button></li>';
});
buttons += '<li><button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button></li>';
setCharmancerText({ steps: buttons });
addLV1Toolbar(mancerdata, eventinfo);
}
);
on("mancerchange:class_feature_choice_1 mancerchange:class_feature_choice_2", function (eventinfo) {
var data = getCharmancerData();
var featureNum = _.last(eventinfo.triggerName.split("_"));
var update = {};
var set = {};
set["class_feature_choice_" + featureNum + "_desc"] = "";
if (eventinfo.newValue) {
getCompendiumPage(data["l1-class"].values.class, function (p) {
p = removeDuplicatedPageData(p);
var data = p["data"];
if (data["data-Feature Choices"]) {
//Safeguarding Compendium data:
var feature = Compendium.parseArray(
data["data-Feature Choices"],
`E_S4U4JH8N:Page:${data["l1-class"].values.class}:data-Feature Choices`
)[featureNum - 1];
var featureName = feature.Name;
var offset = 3;
if (eventinfo.newValue.split("~")[1] && eventinfo.newValue.split("~")[1] == "DBL") {
showChoices(["class_feature_row_" + featureNum + "_manual"]);
// var doubleChoiceNum = featureNum + offset;
// setCharmancerOptions("class_feature_choice_" + doubleChoiceNum, [eventinfo.newValue]);
} else {
hideChoices(["class_feature_row_" + featureNum + "_manual"]);
}
if (data["data-" + featureName + " Data"]) {
//Safeguarding Compendium data:
var json = Compendium.parseArray(
data["data-" + featureName + " Data"],
`E_5I20N8LB:Page:${data["l1-class"].values.class}:${"data-" + featureName + " Data"}`
);
var selected = _.findWhere(json, { Name: eventinfo.newValue });
if (selected) {
update["class_feature_description_" + featureNum] = selected.Desc;
set["class_feature_choice_" + featureNum + "_desc"] = selected.Desc;
setAttrs(set);
setCharmancerText(update);
}
}
}
});
} else {
update["class_feature_description_" + featureNum] = "";
setAttrs(set);
setCharmancerText(update);
}
});
on("mancerchange:custom_subclass_cantrip_number mancerchange:custom_subclass_spell_number", function (eventinfo) {
var section = eventinfo.triggerName.split("_")[2];
if (eventinfo.newValue && eventinfo.newValue > 0) {
showChoices(["custom_" + section + "_list"]);
} else {
var reset = {};
reset["custom_subclass_" + section + "_list"] = "";
hideChoices(["custom_" + section + "_list"]);
setAttrs(reset);
}
});
on("mancerchange:background_personality_choice1 mancerchange:background_personality_choice2", function (eventinfo) {
var mancerdata = getCharmancerData();
var choices = [];
if (mancerdata["l1-background"].values["background_personality_choice1"])
choices.push(mancerdata["l1-background"].values["background_personality_choice1"]);
if (mancerdata["l1-background"].values["background_personality_choice2"])
choices.push(mancerdata["l1-background"].values["background_personality_choice2"]);
disableCharmancerOptions("background_personality_choice1", choices);
disableCharmancerOptions("background_personality_choice2", choices);
});
on(
"mancerroll:background_detail_choice_roll mancerroll:background_personality_choice1_roll mancerroll:background_personality_choice2_roll mancerroll:background_ideal_choice_roll mancerroll:background_bond_choice_roll mancerroll:background_flaw_choice_roll",
function (eventinfo) {
var data = getCharmancerData();
var index = eventinfo.roll[0].dice[0] - 1;
var baseName = eventinfo.triggerName.split(":")[1].slice(0, -5);
//Safeguarding Compendium data:
var choices = Compendium.parseObject(data["l1-background"].values[baseName + "_array"], `E_5MD126CN`);
setCharmancerOptions(baseName, choices.array, { selected: choices.array[index] });
if (baseName.slice(0, -1) == "background_personality_choice") {
var num = baseName.substr(-1) == "1" ? "2" : "1";
setRollButton(baseName.slice(0, -1) + num, choices.array, choices.name, index + 1);
}
}
);
on("clicked:cancel", function () {
showChoices(["cancel-prompt"]);
var data = getCharmancerData();
});
on("clicked:continue", function () {
hideChoices(["cancel-prompt"]);
});
on("page:l1-spells", function (eventinfo) {
var stats = recalcData();
var mancerdata = getCharmancerData();
var spellData = knownSpells();
var set = {};
var titles = {};
var sorted = {};
var repids = [];
var totalchoices = 0;
var racename = getName("race", mancerdata, true);
var subracename = getName("subrace", mancerdata, true);
var classname = getName("class", mancerdata, true);
var subclassname = getName("subclass", mancerdata, true);
set["race_spells"] = racename + subracename;
set["class_spells"] = classname + subclassname;
set["race_number"] = spellData.race ? spellData.race.choices.length : 0;
set["class_number"] = spellData.class ? spellData.class.choices.length : 0;
subracename = getName("subrace", mancerdata) == "" ? "" : " - " + getName("subrace", mancerdata);
subclassname = getName("subclass", mancerdata) == "" ? "" : " - " + getName("subclass", mancerdata);
setAttrs(set, function () {
spellData = knownSpells();
titles["race"] = getName("race", mancerdata) + subracename;
titles["class"] = getName("class", mancerdata) + subclassname;
_.each(["race", "class"], function (section) {
if (spellData[section]) {
_.each(spellData[section].known, function (spell) {
sorted[spell.Level] = sorted[spell.Level] ? sorted[spell.Level] : {};
sorted[spell.Level][section] = sorted[spell.Level][section] ? sorted[spell.Level][section] : { known: [], choices: [] };
sorted[spell.Level][section].known.push(spell.Name);
});
_.each(spellData[section].choices, function (spell) {
sorted[spell.Level] = sorted[spell.Level] ? sorted[spell.Level] : {};
sorted[spell.Level][section] = sorted[spell.Level][section] ? sorted[spell.Level][section] : { known: [], choices: [] };
sorted[spell.Level][section].choices.push(spell);
});
}
});
//This is just to get the total number of repeating sections we need. when we've added enough, erase the rest
_.each(sorted, function (spellarray, level) {
_.each(spellarray, function (spells, section) {
totalchoices++;
_.each(spells.choices, function (spell) {
totalchoices++;
});
});
});
_.each(sorted, function (spellarray, level) {
_.each(spellarray, function (spells, section) {
addRepeatingSection("spell_holder", "row", "spellrow", function (rowid) {
level = isNaN(level) ? "" : level;
var repupdate = {};
var knownspells = [];
var toset = {};
repupdate[rowid + " label span"] = level == 0 ? titles[section] + " Cantrips" : titles[section] + " Level " + level;
_.each(spells.known, function (spell, index) {
var thisSpell = spell;
thisSpell += '<button type="action" class="choice action mancer_info" name="act_' + rowid + "_known" + index + '">i</button>';
thisSpell += '<input type="hidden" name="comp_' + rowid + "_known" + index + '">';
toset[rowid + "_known" + index] = "Spells:" + spell;
knownspells.push(thisSpell);
});
repupdate[rowid + " label p"] = knownspells.join(", ");
setAttrs(toset);
setCharmancerText(repupdate);
repids.push(rowid);
_.each(spells.choices, function (spell) {
addRepeatingSection(rowid, "choose", section + "_spell-l" + spell.Level, function (id) {
var toset = {};
var lists = [spell.List];
if (spell.AddList) lists = lists.concat(spell.AddList);
var choices = "Category:Spells Classes:*" + lists.join("|*") + " Level:" + spell.Level;
var settings = { category: "Spells", disable: spellData.known };
toset[id + "_info"] = spell.Ability;
if (section != "class") toset[id + "_custom"] = section;
repids.push(id);
getCompendiumQuery(choices, (data) => {
let options = new Set(
data
.map((entry) => {
return entry.name;
})
.concat(spell["Expanded List"])
);
options = Array.from(options)
.sort()
.filter((entry) => {
return typeof entry === "string";
});
settings.disable = settings.disable.filter((entry) => {
return options.indexOf(entry) > -1;
});
setCharmancerOptions(id + "_choice", options, settings);
setAttrs(toset);
showChoices(["action[name=act_" + id + "_action]"]);
if (repids.length >= totalchoices) {
clearRepeating(repids, "l1-spells");
}
});
});
});
});
});
});
if (!spellData.race && !spellData.class) {
showChoices(["no_spells"]);
clearRepeatingSections("spell_holder");
} else {
showChoices(["spell-info-container"]);
hideChoices(["no_spells"]);
}
setAttrs(set);
});
});
on("page:l1-feat", function (eventinfo) {
var stats = recalcData();
var mancerdata = getCharmancerData();
var showList = new Set(["no_feat"]);
var hideList = new Set(["yes_feat", "yes_feat_bg", "feat-info-container"]);
let hasFeat = false;
if (
SheetUtils.checkPath(mancerdata, "['l1-race']['data']['subrace']['data-Feats']") ||
SheetUtils.checkPath(mancerdata, "['l1-race']['data']['race']['data-Feats']")
) {
showList.add("yes_feat");
showList.add("feat-info-container");
showList.delete("no_feat");
hideList.delete("yes_feat");
hideList.delete("feat-info-container");
hasFeat = true;
}
if (SheetUtils.checkPath(mancerdata, "['l1-background']['data']['background']['data-Feats']")) {
setBackgroundFeat(mancerdata["l1-background"]["data"]["background"]["data-Feats"]);
showList.add("yes_feat_bg");
showList.add("feat-info-container");
showList.delete("no_feat");
hideList.delete("yes_feat_bg");
hideList.delete("feat-info-container");
hasFeat = true;
}
if (!hasFeat) {
deleteSlideData("l1-feat");
}
showChoices(Array.from(showList));
hideChoices(Array.from(hideList));
});
on("mancerchange:feat", function (eventinfo) {
changeCompendiumPage("feat-info", eventinfo.newValue);
var reset = {};
if (!(eventinfo.newValue === "" || eventinfo.newValue === undefined)) {
showChoices(["feat_options"]);
}
hideChoices(["feat_possible"]);
if (eventinfo.sourceType == "player") {
reset = {};
reset["feat_ability_choice"] = "";
reset["feat_language_choice1"] = "";
reset["feat_language_choice1_custom"] = "";
reset["feat_language_choice2"] = "";
reset["feat_language_choice2_custom"] = "";
reset["feat_language_choice3"] = "";
reset["feat_language_choice3_custom"] = "";
reset["feat_language_choice4"] = "";
reset["feat_language_choice4_custom"] = "";
reset["feat_skill_choice1"] = "";
reset["feat_skill_choice2"] = "";
reset["feat_skill_choice3"] = "";
reset["feat_skill_choice4"] = "";
reset["feat_weapon_choice1"] = "";
reset["feat_weapon_choice2"] = "";
reset["feat_weapon_choice3"] = "";
reset["feat_weapon_choice4"] = "";
reset["feat_armor_choice1"] = "";
reset["feat_armor_choice2"] = "";
reset["feat_armor_choice3"] = "";
reset["feat_armor_choice4"] = "";
reset["feat_tool_choice1"] = "";
reset["feat_tool_choice2"] = "";
reset["feat_tool_choice3"] = "";
reset["feat_tool_choice4"] = "";
reset["feat_spell_choice1"] = "";
reset["feat_spell_choice2"] = "";
reset["feat_spell_choice3"] = "";
reset["feat_spell_choice4"] = "";
reset["feat_expertise_choice_1"] = "";
reset["feat_expertise_choice_2"] = "";
reset["feat_feature_choice_1"] = "";
reset["feat_feature_choice_3"] = "";
reset["feat_feature_choice_2"] = "";
reset["other_options_and_features_choice1"] = "";
reset['other_options_and_features_choice2"'] = "";
setCharmancerText({ feat_text: "" });
}
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
setAttrs(reset, function () {
var mancerdata = getCharmancerData();
var data = p["data"];
var showList = [];
var update = {};
if (data["data-Ability Score Increase"]) {
var abilityText = [];
//Safeguarding Compendium data:
var json = Compendium.parseObject(
data["data-Ability Score Increase"],
`E_MZ9R1TZK:Page:${eventinfo.newValue}:data-Ability Score Increase`
);
_.each(json, function (increase, ability) {
abilityText.push(ability + " +" + increase);
});
update["feat_ability_score"] = abilityText.join(", ");
showList.push("feat_ability");
}
if (data["data-Ability Score Choice"]) {
//Safeguarding Compendium data:
var json = Compendium.parseArray(
data["data-Ability Score Choice"],
`E_GVUA0DZV:Page:${eventinfo.newValue}:data-Ability Score Choice`
);
setCharmancerOptions("feat_ability_choice", json);
showList.push("feat_ability", "feat_ability_choice");
}
if (data["Prerequisite"]) {
showList.push("feat_prereq");
update["feat_prerequisite"] = data["Prerequisite"];
}
if (data["Other Options and Features"]) {
//Safeguarding Compendium data:
const otherFeatures = Compendium.parseObject(
data["Other Options and Features"],
`E_21IVKQ07:Page:${eventinfo.newValue}:Other Options and Features`
);
if (otherFeatures["Choice"] && Array.isArray(otherFeatures["Choice"])) {
for (let i = 0; i < otherFeatures["Choice"].length; i++) {
const featureIndex = i + 1;
const selector = `other_options_and_features_choice${featureIndex}`;
const query = `Category:Other Options and Features Type:${otherFeatures["Choice"][i]}`;
const title = `Choose ${SheetUtils.aOrAn(otherFeatures["Choice"][i])}:`;
showList.push(`other_options_and_features${featureIndex}`);
setCharmancerText({ [`other_options_and_features_title${featureIndex}`]: title });
setCharmancerOptions(selector, query);
}
}
}
if (data["Skill Proficiency"]) {
//Safeguarding Compendium data:
const skills = Compendium.parseObject(data["Skill Proficiency"], `E_C4J7XSPB:Page:${eventinfo.newValue}:Skill Proficiency`);
if (skills["Choice"] && Array.isArray(skills["Choice"])) {
showList.push("feat_skill_row");
for (let i = 0; i < skills["Choice"].length; i++) {
const skillIndex = i + 1;
showList.push(`feat_skill_choice${skillIndex}`);
}
}
}
if (data["Expertise"]) {
//Safeguarding Compendium data:
const skills = Compendium.parseObject(data["Expertise"], `E_D4IAJNEA:Page:${eventinfo.newValue}:Expertise`);
if (skills["Choice"] && Array.isArray(skills["Choice"])) {
showList.push("feat_expertise_row");
for (let i = 0; i < skills["Choice"].length; i++) {
const skillIndex = i + 1;
showList.push(`feat_expertise_choice${skillIndex}`);
setValidExpertiseOptions(null, `comp_feat_expertise_choice_${skillIndex}`);
//const selectedSkills = getProficiencies()['all']['Skill'];
//setCharmancerOptions(`comp_feat_expertise_choice_${i}`, selectedSkills, {category:"Expertise"});
}
}
}
if (data["data-Pick Spells"]) {
//Safeguarding Compendium data:
const spells = Compendium.parseArray(data["data-Pick Spells"], `E_21IVKQ07:Page:${eventinfo.newValue}:data-Pick Spells`);
showList.push("feat_spell_row");
if (Array.isArray(spells)) {
for (let i = 0; i < spells.length; i++) {
const spellIndex = i + 1;
const selector = `comp_feat_spell_choice${spellIndex}`;
const query = `Category:Spells ${spells[i]["List"]}`;
showList.push(`feat_spell_choice${spellIndex}`);
setCharmancerOptions(selector, query);
}
}
}
setCharmancerText(update);
showChoices(showList);
recalcData();
});
});
});
const setBackgroundFeat = (name) => {
getCompendiumPage(`Feats:${name}`, (feat) => {
if (!SheetUtils.checkPath(feat, '["data"]["Category"]') || !feat.expansion) {
setAttrs({ feat_bg: "" });
setCharmancerText({ "feat-bg-name": "" });
return;
}
setCharmancerOptions("comp_feat_bg", "Category:Feats", {
selected: `Feats:${feat.name}?expansion=${feat.expansion}`,
});
setCharmancerText({ "feat-bg-name": feat.name });
});
};
on("clicked:info_feat_bg", () => {
var data = getCharmancerData();
if (SheetUtils.checkPath(data, "['l1-feat']['values']['feat_bg']")) {
changeCompendiumPage("feat-info", data["l1-feat"]["values"]["feat_bg"]);
}
});
on("mancerchange:feat_bg", function (eventinfo) {
changeCompendiumPage("feat-info", eventinfo.newValue);
var reset = {};
if (!(eventinfo.newValue === "" || eventinfo.newValue === undefined)) {
showChoices(["feat_bg_options"]);
}
hideChoices(["feat_bg_possible"]);
if (eventinfo.sourceType == "player") {
reset = {};
reset["feat_bg_ability_choice"] = "";
reset["feat_bg_other_options_and_features_choice1"] = "";
reset["feat_bg_other_options_and_features_choice2"] = "";
reset["feat_bg_skill_choice1"] = "";
reset["feat_bg_skill_choice2"] = "";
reset["feat_bg_skill_choice3"] = "";
reset["feat_bg_skill_choice4"] = "";
reset["feat_bg_expertise_choice1"] = "";
reset["feat_bg_expertise_choice2"] = "";
reset["feat_bg_spell_choice1"] = "";
reset["feat_bg_spell_choice2"] = "";
reset["feat_bg_spell_choice3"] = "";
reset["feat_bg_spell_choice4"] = "";
setCharmancerText({ feat_bg_text: "" });
}
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
setAttrs(reset, function () {
var mancerdata = getCharmancerData();
var data = p["data"];
var showList = [];
var update = {};
if (data["data-Ability Score Increase"]) {
var abilityText = [];
//Safeguarding Compendium data:
var json = Compendium.parseObject(
data["data-Ability Score Increase"],
`E_MZ9R1TZK:Page:${eventinfo.newValue}:data-Ability Score Increase`
);
_.each(json, function (increase, ability) {
abilityText.push(ability + " +" + increase);
});
update["feat_bg_ability_score"] = abilityText.join(", ");
showList.push("feat_bg_ability");
}
if (data["data-Ability Score Choice"]) {
//Safeguarding Compendium data:
var json = Compendium.parseArray(
data["data-Ability Score Choice"],
`E_GVUA0DZV:Page:${eventinfo.newValue}:data-Ability Score Choice`
);
setCharmancerOptions("feat_bg_ability_choice", json);
showList.push("feat_bg_ability", "feat_bg_ability_choice");
}
if (data["Prerequisite"]) {
showList.push("feat_bg_prereq");
update["feat_bg_prerequisite"] = data["Prerequisite"];
}
if (data["Other Options and Features"]) {
//Safeguarding Compendium data:
const otherFeatures = Compendium.parseObject(
data["Other Options and Features"],
`E_21IVKQ07:Page:${eventinfo.newValue}:Other Options and Features`
);
if (otherFeatures["Choice"] && Array.isArray(otherFeatures["Choice"])) {
for (let i = 0; i < otherFeatures["Choice"].length; i++) {
const featureIndex = i + 1;
const selector = `feat_bg_other_options_and_features_choice${featureIndex}`;
const query = `Category:Other Options and Features Type:${otherFeatures["Choice"][i]}`;
const title = `Choose ${SheetUtils.aOrAn(otherFeatures["Choice"][i])}:`;
showList.push(`feat_bg_other_options_and_features${featureIndex}`);
setCharmancerText({ [`feat_bg_other_options_and_features_title${featureIndex}`]: title });
setCharmancerOptions(selector, query);
}
}
}
if (data["Skill Proficiency"]) {
//Safeguarding Compendium data:
const skills = Compendium.parseObject(data["Skill Proficiency"], `E_C4J7XSPB:Page:${eventinfo.newValue}:Skill Proficiency`);
if (skills["Choice"] && Array.isArray(skills["Choice"])) {
showList.push("feat_bg_skill_row");
for (let i = 0; i < skills["Choice"].length; i++) {
const skillIndex = i + 1;
showList.push(`feat_bg_skill_choice${skillIndex}`);
}
}
}
if (data["Expertise"]) {
//Safeguarding Compendium data:
const skills = Compendium.parseObject(data["Expertise"], `E_D4IAJNEA:Page:${eventinfo.newValue}:Expertise`);
if (skills["Choice"] && Array.isArray(skills["Choice"])) {
showList.push("feat_bg_expertise_row");
for (let i = 0; i < skills["Choice"].length; i++) {
const skillIndex = i + 1;
showList.push(`feat_bg_expertise_choice${skillIndex}`);
setValidExpertiseOptions(null, `comp_feat_bg_expertise_choice_${skillIndex}`);
//const selectedSkills = getProficiencies()['all']['Skill'];
//setCharmancerOptions(`comp_feat_bg_expertise_choice_${i}`, selectedSkills, {category:"Expertise"});
}
}
}
if (data["data-Pick Spells"]) {
//Safeguarding Compendium data:
const spells = Compendium.parseArray(data["data-Pick Spells"], `E_21IVKQ07:Page:${eventinfo.newValue}:data-Pick Spells`);
showList.push("feat_bg_spell_row");
if (Array.isArray(spells)) {
for (let i = 0; i < spells.length; i++) {
const spellIndex = i + 1;
const selector = `comp_feat_bg_spell_choice${spellIndex}`;
const query = `Category:Spells ${spells[i]["List"]}`;
showList.push(`feat_bg_spell_choice${spellIndex}`);
setCharmancerOptions(selector, query);
}
}
}
setCharmancerText(update);
showChoices(showList);
recalcData();
});
});
});
/* BIO PAGE */
on("page:l1-bio", function (eventinfo) {
const attrs = ["age", "character_name", "eyes", "hair", "height", "weight", "skin"];
const mancerdata = getCharmancerData();
const biodata = mancerdata["l1-bio"] || undefined;
const race = getName("race");
//Check for any data that has already been filled in and populate the Bio slide
if (biodata === undefined) {
getAttrs(attrs, function (v) {
let update = {};
_.each(attrs, function (value) {
if (v[`${value}`] != "" || v[`${value}`] != undefined) {
update[`${value}`] = v[`${value}`];
}
});
setAttrs(update);
});
}
//Set the previous_race so the Summary page can offer a warning if needed
if (biodata === undefined || biodata.values["previous_race"] === undefined || biodata.values["previous_race"] != race) {
setAttrs({
previous_race: race,
});
}
if (mancerdata["l1-race"] != undefined) {
const raceInfo = mancerdata["l1-race"].values.race;
changeCompendiumPage("race-info", raceInfo);
}
});
/* SUMMARY PAGE */
on("page:l1-summary", function () {
var mancerdata = getCharmancerData();
var profdata = getProficiencies(mancerdata);
var set = {};
var racename = getName("race", mancerdata);
var subracename = getName("subrace", mancerdata);
var classname = getName("class", mancerdata);
var subclassname = getName("subclass", mancerdata);
var bgname = getName("background", mancerdata);
var spelldata = knownSpells();
var raceSpells = spelldata.race ? spelldata.race.choices.length : 0;
var classSpells = spelldata.class ? spelldata.class.choices.length : 0;
var ready = true;
var handleMissing = function (section, sectionName) {
var page = mancerdata["l1-" + section.replace("sub", "")] || false;
var missing = {};
var warnings = "";
sectionName = sectionName || section;
if (page && page.data && page.data[section]) {
if (page.data[section]["data-Ability Score Choice"]) {
var choices = 1;
if (typeof page.data[section]["data-Ability Score Choice"] == "string") {
choices = parseInt(page.data[section]["data-Ability Score Choice"].split("+")[0]);
}
var total = choices;
if (choices >= 3 && page.values[section + "_ability_choice3"]) choices--;
if (choices >= 2 && page.values[section + "_ability_choice2"]) choices--;
if (page.values[section + "_ability_choice"]) choices--;
if (page.values[section + "_ability_choice1"]) choices--;
if (choices > 0) missing.ability = [total, total - choices];
}
_.each(page.repeating, function (id) {
if (id.split("_")[2] == section && id.split("_")[3] == "feature") {
var featurename = page.values[id + "_info"];
if (page.values[id + "_choice"]) {
warnings += "<p>Your " + featurename + " is " + _.last(page.values[id + "_choice"].split(":")) + ".</p>";
} else {
warnings += '<p class="warning">Your have not chosen a ' + featurename + ".</p>";
}
}
});
_.each(proficiencyList, function (prof) {
var numChoices = 0;
var totalChoices = 0;
_.each(page.repeating, function (id) {
if (id.split("_")[2] == section && id.split("_")[3] == prof.toLowerCase()) {
totalChoices++;
numChoices++;
if (page.values[id + "_choice"]) {
numChoices--;
var thischoice = _.last(page.values[id + "_choice"].split(":"));
if (thischoice == "custom" && !page.values[id + "_choice"]) {
warnings += "<p class=\"warning\">You've chosen a custom language, but you haven't entered a custom language name.</p>";
}
_.each(profdata.auto, function (profs, source) {
if (profs.includes(thischoice)) {
warnings += '<p class="warning">You\'ve chosen the ' + prof.toLowerCase() + " " + thischoice;
warnings += ", but you already have that " + prof.toLowerCase() + " from your " + source + ".</p>";
}
});
}
}
});
if (numChoices > 0) missing[prof] = [totalChoices, numChoices];
});
var total = 0;
var choices = 0;
_.each(page.repeating, function (id) {
if (id.split("_")[2] == section && id.split("_")[3] == "expertise") {
total++;
choices++;
if (page.values[id + "_choice"]) choices--;
if (choices > 0) missing.expertise = [total, choices];
}
});
}
_.each(missing, function (number, type) {
var singular = type == "ability" ? "an" : "a";
if (type == "ability") {
type = "ability score increase";
}
warnings += '<p class="warning">Your ' + sectionName;
if (type == "expertise") {
warnings += " gives you expertise in ";
warnings += number[0] > 1 ? "up to " + number[0] + " skills" : " a skill";
} else {
warnings += " allows you to choose ";
warnings += number[0] > 1 ? "up to " + number[0] + " " + type.toLowerCase() + "s" : singular + " " + type.toLowerCase();
}
if (number[0] - number[1] > 0) {
warnings += ", but you've only chosen " + number[1] + "!</p>";
} else {
warnings += number[0] > 1 ? ", and you haven't chosen any!</p>" : ", and you haven't chosen one!</p>";
}
});
return warnings;
};
///Show the bio info we need to put in places
if (mancerdata["l1-bio"]) {
let length = Object.keys(mancerdata["l1-bio"].values).length;
set["bio_info"] = "";
//Check if race has changed since entering data and add a warning
if (length < 2) {
set["bio_info"] += "<p>You have not added new information to the Bio tab.</p>";
} else {
if (mancerdata["l1-bio"].values.character_name) {
set["bio_info"] += "<p>Your name is " + mancerdata["l1-bio"].values.character_name + ".</p>";
}
if (mancerdata["l1-bio"].values.age) {
set["bio_info"] += "<p>Your age is " + mancerdata["l1-bio"].values.age + ".</p>";
}
if (mancerdata["l1-bio"].values["height"]) {
set["bio_info"] += "<p>Your height is " + mancerdata["l1-bio"].values["height"] + ".</p>";
}
if (mancerdata["l1-bio"].values.weight) {
set["bio_info"] += "<p>Your weight is " + mancerdata["l1-bio"].values.weight + ".</p>";
}
if (mancerdata["l1-bio"].values.eyes) {
set["bio_info"] += "<p>Your eye color is " + mancerdata["l1-bio"].values.eyes + ".</p>";
}
if (mancerdata["l1-bio"].values.hair) {
set["bio_info"] += "<p>Your hair is " + mancerdata["l1-bio"].values.hair + ".</p>";
}
if (mancerdata["l1-bio"].values.skin) {
set["bio_info"] += "<p>Your skin is " + mancerdata["l1-bio"].values.skin + ".</p>";
}
if (mancerdata["l1-bio"].values.previous_race != racename) {
setCharmancerText({
race_warning:
'<p class="warning">Each race has unique look and different names. You might want to adjust your choices to reflect your race.</p>',
});
}
}
}
if (mancerdata["l1-race"] && racename) {
set["race_info"] = "<p>Your race is " + racename + "</p>";
if (!mancerdata["l1-race"].values.alignment) set["race_info"] += '<p class="warning">You haven\'t chosen your alignment.</p>';
set["race_info"] += handleMissing("race");
if (
SheetUtils.checkPath(mancerdata, '["l1-race"]["data"]["race"]["Size"]') &&
(mancerdata["l1-race"]["data"]["race"]["Size"].includes(",") ||
mancerdata["l1-race"]["data"]["race"]["Size"].includes(" or ") ||
mancerdata["l1-race"]["data"]["race"]["Size"].includes(" Or ")) &&
!SheetUtils.checkPath(mancerdata, '["l1-race"]["values"]["size_choice"]')
) {
set["race_info"] += `<p class="warning">Your race allows you to choose a size, and you haven't chosen one!</p>`;
}
if (!mancerdata["l1-race"].values.alignment) set["race_info"] += '<p class="warning">You haven\'t chosen your alignment.</p>';
if (mancerdata["l1-race"].values["has_subrace"] == "true") {
if (subracename) {
set["race_info"] += "<p>Your subrace is " + subracename + "</p>";
set["race_info"] += handleMissing("subrace");
} else {
if (mancerdata["l1-race"].values.subrace == "Rules:Races" && !mancerdata["l1-race"].values.subrace_name) {
set["race_info"] = '<p class="warning needed">You need to pick a name for your custom subrace!</p>';
} else {
set["race_info"] += '<p class="warning needed">You have not selected a subrace!</p>';
}
ready = false;
showChoices(["race_button"]);
}
}
_.each(spelldata.errors.race, function (error) {
switch (error) {
case "custom_race_spell_list":
set["race_info"] += '<p class="warning needed">You have not selected spell list for your innate spellcasting!</p>';
ready = false;
break;
default:
console.log("Unknown race spell error: " + error);
}
});
if (mancerdata["l1-race"].values.race == "Rules:Races") {
if (!mancerdata["l1-race"].values.size)
set["race_info"] += '<p class="warning">You have not selected a size for your custom race!</p>';
if (!mancerdata["l1-race"].values.speed)
set["race_info"] += '<p class="warning">You have not selected a walking speed for your custom race!</p>';
}
} else {
if (mancerdata["l1-race"] && mancerdata["l1-race"].values.race == "Rules:Races" && !mancerdata["l1-race"].values.race_name) {
set["race_info"] = '<p class="warning needed">You need to pick a name for your custom race!</p>';
} else {
set["race_info"] = '<p class="warning needed">You have not selected a race!</p>';
}
ready = false;
showChoices(["race_button"]);
}
if (mancerdata["l1-class"] && classname) {
set["class_info"] = "<p>Your class is " + classname + ".</p>";
set["class_info"] += handleMissing("class");
if (mancerdata["l1-class"].values.race == "Rules:Classes" && !mancerdata["l1-class"].values.custom_hit_die) {
set["class_info"] = '<p class="warning">You need to pick a hit die for your custom class!</p>';
}
if (mancerdata["l1-class"].data.class && mancerdata["l1-class"].data.class["data-Subclass Level"] == 1) {
if (subclassname && mancerdata["l1-class"].data.class && mancerdata["l1-class"].data.class["data-Subclass Level"] == 1) {
set["class_info"] += "<p>Your " + mancerdata["l1-class"].data.class["Subclass Name"] + " is " + subclassname + ".</p>";
set["class_info"] += handleMissing("subclass", mancerdata["l1-class"].data.class["Subclass Name"]);
} else {
if (mancerdata["l1-class"].values.subclass == "Rules:Classes" && !mancerdata["l1-class"].values.subrace_name) {
set["class_info"] =
'<p class="warning needed">You need to pick a name for your custom ' +
mancerdata["l1-class"].data.class["Subclass Name"] +
"!</p>";
} else {
set["class_info"] +=
'<p class="warning needed">You have not selected a ' + mancerdata["l1-class"].data.class["Subclass Name"] + "!</p>";
}
ready = false;
showChoices(["class_button"]);
}
}
_.each(spelldata.errors.class, function (error) {
switch (error) {
case "custom_class_spell_list":
set["race_info"] +=
'<p class="warning needed">You have selected a spellcasting ability for your custom class, but you have not selected a spell list.</p>';
ready = false;
showChoices(["class_button"]);
break;
case "custom_class_spell_number":
set["race_info"] +=
'<p class="warning needed">You have selected a spellcasting ability for your custom class, but you have not selected a number of spells.</p>';
ready = false;
showChoices(["class_button"]);
break;
default:
console.log("Unknown class spell error: " + error);
}
});
} else {
if (mancerdata["l1-class"] && mancerdata["l1-class"].values.race == "Rules:Classes" && !mancerdata["l1-class"].values.race_name) {
set["class_info"] = '<p class="warning needed">You need to pick a name for your custom class!</p>';
} else {
set["class_info"] = '<p class="warning needed">You have not selected a class!</p>';
}
ready = false;
showChoices(["class_button"]);
}
if (mancerdata["l1-abilities"] && mancerdata["l1-abilities"].values.abilities) {
switch (mancerdata["l1-abilities"].values.abilities) {
case "Standard Array":
set["ability_info"] = "<p>You generated your stats from the standard array.</p>";
break;
case "Roll for Stats":
set["ability_info"] = "<p>You generated your stats by rolling.</p>";
break;
case "Point Buy":
set["ability_info"] = "<p>You generated your stats with the point buy method.</p>";
break;
case "custom":
set["ability_info"] = "<p>You entered custom values for your stats.</p>";
break;
}
var x = 0;
_.each(abilityList, function (ability) {
if (!mancerdata["l1-abilities"].values[ability.toLowerCase()]) {
x++;
set["ability_info"] += '<p class="warning needed">You have not selected a score for your ' + ability.toLowerCase() + "!</p>";
ready = false;
showChoices(["abilities_button"]);
}
});
if (x == 6) set["ability_info"] = '<p class="warning">You have not selected your ability scores!</p>';
} else {
set["ability_info"] = '<p class="warning needed">You have not generated your ability scores!</p>';
ready = false;
showChoices(["abilities_button"]);
}
if (mancerdata["l1-background"] && bgname) {
set["background_info"] = "<p>You come from a " + bgname + " background.</p>";
if (mancerdata["l1-background"].data.background && mancerdata["l1-background"].data.background["data-Background Choice Name"]) {
if (!mancerdata["l1-background"].values["background_detail_choice"]) {
set["background_info"] += '<p class="warning">You haven\'t chosen your ';
set["background_info"] += mancerdata["l1-background"].data.background["data-Background Choice Name"] + ".</p>";
}
}
if (!mancerdata["l1-background"].values.traitless || mancerdata["l1-background"].values.traitless == 0) {
if (
!mancerdata["l1-background"].values["background_personality_choice1"] &&
!mancerdata["l1-background"].values["background_personality_choice2"]
) {
set["background_info"] += '<p class="warning">You haven\'t picked your personality traits.</p>';
} else if (
!mancerdata["l1-background"].values["background_personality_choice1"] ||
!mancerdata["l1-background"].values["background_personality_choice2"]
) {
set["background_info"] += '<p class="warning">You haven\'t picked one of your personality traits.</p>';
}
if (!mancerdata["l1-background"].values["background_ideal_choice"]) {
set["background_info"] += '<p class="warning">You haven\'t chosen your ideal.</p>';
}
if (!mancerdata["l1-background"].values["background_bond_choice"]) {
set["background_info"] += '<p class="warning">You haven\'t chosen your bond.</p>';
}
if (!mancerdata["l1-background"].values["background_flaw_choice"]) {
set["background_info"] += '<p class="warning">You haven\'t chosen your flaw.</p>';
}
}
set["background_info"] += handleMissing("background");
} else {
set["background_info"] = '<p class="warning needed">You have not selected a background!</p>';
ready = false;
showChoices(["background_button"]);
}
if (mancerdata["l1-equipment"] && mancerdata["l1-equipment"].values["equipment_type"]) {
switch (mancerdata["l1-equipment"].values["equipment_type"]) {
case "class":
set["equipment_info"] = "<p>You got starting equipment based on your class and background.</p>";
break;
case "gold":
set["equipment_info"] = "<p>You opted for gold instead of starting equipment.</p>";
break;
}
if (mancerdata["l1-equipment"].values["equipment_type"] == "class") {
var choices = { class: [0, 0], background: [0, 0] };
_.each(choices, function (numbers, section) {
var equipment =
mancerdata["l1-" + section].data[section] && mancerdata["l1-" + section].data[section]["data-Equipment"]
? mancerdata["l1-" + section].data[section]["data-Equipment"]
: {};
_.each(equipment, function (choice, choicekey) {
if (choicekey != "default") numbers[0]++;
});
_.each(mancerdata["l1-equipment"].values, function (val, key) {
if (key.includes(section + "_equipment_choice")) numbers[1]++;
});
});
_.each(choices, function (number, type) {
if (number[0] - number[1] > 0) {
set["equipment_info"] += '<p class="warning">Your ' + type + " gives you ";
set["equipment_info"] += number[0] > 1 ? number[0] + " equipment choices" : "an equipment choice";
if (number[1] > 0) {
set["equipment_info"] += ", but you've only chosen " + number[1] + "!</p>";
} else {
set["equipment_info"] += number[0] > 1 ? ", and you haven't chosen any!</p>" : ", and you haven't chosen it!</p>";
}
}
});
}
if (mancerdata["l1-equipment"].values["equipment_type"] == "gold" && mancerdata["l1-equipment"].values["starting_gold"] == "0") {
set["equipment_info"] = '<p class="warning">You selected starting wealth for your equipment, but you don\'t have any gold yet!</p>';
}
if (
mancerdata["l1-equipment"].values["equipment_class"] != getName("class", mancerdata, true) ||
(mancerdata["l1-equipment"].values["equipment_background"] != getName("background", mancerdata, true) &&
mancerdata["l1-equipment"].values["equipment_type"] != "gold")
) {
set["equipment_info"] = '<p class="warning needed">Your equipment options have changed, you\'ll need to choose again.</p>';
ready = false;
showChoices(["equipment_button"]);
}
} else {
set["equipment_info"] = '<p class="warning needed">You have not selected any equipment!</p>';
ready = false;
showChoices(["equipment_button"]);
}
var spellsready = true;
var toDelete = [];
if (mancerdata["l1-spells"]) {
prevRace = mancerdata["l1-spells"].values.race_spells || "";
prevClass = mancerdata["l1-spells"].values.class_spells || "";
currentRace = getName("race", mancerdata, true) + getName("subrace", mancerdata, true);
currentClass = getName("class", mancerdata, true) + getName("subclass", mancerdata, true);
set["spells_info"] = "";
if (raceSpells + classSpells == 0) {
set["spells_info"] += "<p>The arcane is a mystery to you.</p>";
deleteCharmancerData(["l1-spells"]);
spellsready = false;
} else if (
prevRace != currentRace ||
prevClass != currentClass ||
mancerdata["l1-spells"].values.race_number != raceSpells ||
mancerdata["l1-spells"].values.class_number != classSpells
) {
if (prevRace != currentRace || mancerdata["l1-spells"].values.race_number > raceSpells) {
for (var x = 1; x <= 9; x++) {
toDelete.push("race_level0_choice" + x);
toDelete.push("race_level1_choice" + x);
}
if (raceSpells > 0 && mancerdata["l1-spells"].values.race_number > 0) {
set["spells_info"] += '<p class="warning">Your racial spells were reset because your options have changed.</p>';
}
}
if (prevClass != currentClass || mancerdata["l1-spells"].values.class_number > classSpells) {
for (var x = 1; x <= 9; x++) {
toDelete.push("class_level0_choice" + x);
toDelete.push("class_level1_choice" + x);
}
if (classSpells > 0 && mancerdata["l1-spells"].values.class_number > 0) {
set["spells_info"] += '<p class="warning">Your class spells were reset because your options have changed.</p>';
}
}
}
} else if (raceSpells + classSpells > 0) {
set["spells_info"] = '<p class="warning">You haven\'t selected your spells!</p>';
spellsready = false;
showChoices(["spells_button"]);
} else {
set["spells_info"] = "<p>The arcane is a mystery to you.</p>";
spellsready = false;
}
let deleteFeatdata = true;
const backgroundFeat = SheetUtils.checkPath(mancerdata, '["l1-background"]["data"]["background"]["data-Feats"]')
? mancerdata["l1-background"]["data"]["background"]["data-Feats"]
: false;
if (
SheetUtils.checkPath(mancerdata, "['l1-race']['data']['subrace']['data-Feats']") ||
SheetUtils.checkPath(mancerdata, "['l1-race']['data']['race']['data-Feats']")
) {
deleteFeatdata = false;
if (mancerdata["l1-feat"] && mancerdata["l1-feat"].values.feat) {
set["feat_info"] = "<p>You've selected the " + removeExpansionInfo(mancerdata["l1-feat"].values.feat.split(":")[1]) + " feat.</p>";
set["feat_info"] += handleMissing("feat");
} else {
set["feat_info"] = '<p class="warning">You have access to a feat, but you have not yet selected one!</p>';
}
}
if (backgroundFeat) {
deleteFeatdata = false;
set["feat_info"] = set["feat_info"]
? `${set["feat_info"]}<p>Your Background gave you the ${backgroundFeat} feat.</p>`
: `<p>Your Background gave you the ${backgroundFeat} feat.</p>`;
}
if (deleteFeatdata) {
set["feat_info"] =
"<p>As you exchange your novice notions of the world for experience, a feat may become within reach. Not today, however.</p>";
deleteCharmancerData(["l1-feat"]);
}
if (ready) {
set.ready_message = "If you're ready to build your " + racename;
set.ready_message += subracename == "" ? " " : " (" + subracename + ") ";
set.ready_message += subclassname == "" ? classname : classname + " (" + subclassname + ")";
set.ready_message += " from a " + bgname + ' background, click "Apply Changes."';
showChoices(["apply_changes", "ready_text"]);
} else {
set.ready_message = "Hold on there! You've missed some required fields, which have been marked with a <span class='needed'></span>.";
}
//If you're ready to build your race class from a background background, click "Apply Changes."
deleteCharmancerData([{ "l1-spells": toDelete }], function () {
spelldata = knownSpells();
if (spellsready) {
if (spelldata.known && spelldata.known.length) {
set["spells_info"] += "<p>You know these spells: " + removeExpansionInfo(spelldata.known.join(", ")) + ".</p>";
}
var choices = { cantrip: [0, 0], spell: [0, 0] };
_.each(spelldata, function (section) {
_.each(section.choices, function (spell) {
if (spell.Level == 0) choices.cantrip[0]++;
if (spell.Level == 1) choices.spell[0]++;
});
});
choices.cantrip[1] = choices.cantrip[0];
choices.spell[1] = choices.spell[0];
_.each(mancerdata["l1-spells"].values, function (val, name) {
if (name.split("_")[3] == "spell-l0" && _.last(name.split("_")) == "choice") choices.cantrip[1]--;
if (name.split("_")[3] == "spell-l1" && _.last(name.split("_")) == "choice") choices.spell[1]--;
});
_.each(choices, function (number, type) {
if (number[1] > 0) {
set["spells_info"] += '<p class="warning">You can choose ';
set["spells_info"] += number[0] > 1 ? "up to " + number[0] + " " + type + "s" : "a " + type;
if (number[1] < number[0]) {
set["spells_info"] += ", but you've only chosen " + (number[0] - number[1]) + "!</p>";
} else {
set["spells_info"] += number[0] > 1 ? ", and you haven't chosen any!</p>" : ", and you haven't chosen one!</p>";
}
}
});
}
setCharmancerText(set);
});
});
/* Info Button Listeners */
on("clicked:info_race", function (eventinfo) {
var data = getCharmancerData();
var race = data["l1-race"] && data["l1-race"].values.race ? data["l1-race"].values.race : "Rules:Races";
changeCompendiumPage("race-info", race);
});
on("clicked:info_subrace", function (eventinfo) {
var data = getCharmancerData();
var subrace = data["l1-race"] && data["l1-race"].values.subrace ? data["l1-race"].values.subrace : data["l1-race"].values.race;
changeCompendiumPage("race-info", subrace);
});
on("clicked:info_class", function (eventinfo) {
var data = getCharmancerData();
var oclass = data["l1-class"] && data["l1-class"].values.class ? data["l1-class"].values.class : "Rules:Classes";
changeCompendiumPage("class-info", oclass);
});
on("clicked:info_subclass", function (eventinfo) {
var data = getCharmancerData();
var osubclass = data["l1-class"] && data["l1-class"].values.subclass ? data["l1-class"].values.subclass : data["l1-class"].values.class;
changeCompendiumPage("class-info", osubclass);
});
on("mancerfinish:l1-mancer", function (eventinfo) {
eventinfo = { data: getCharmancerData() };
var doAllDrops = function (dropArray, callback) {
getAttrs(["character_id"], function (v) {
_.each(stats.totals, function (scores, name) {
v[name + "_base"] = scores.total;
v[name + "_mod"] = scores.mod;
});
v.base_level = "1";
v.npc = "0";
v["class_resource_name"] = "";
v["other_resource_name"] = "";
var update = {};
var callbacks = [];
var totalDrops = dropArray.length;
var x = 0;
callbacks.push(update_class);
callbacks.push(update_race_display);
get_repeating_data(function (repeating) {
_.each(dropArray, function (page) {
page.data.Category = page.data.Category.replace("@@!!@@", "");
var dropUpdate = processDrop(page, v, repeating, true);
callbacks = callbacks.concat(dropUpdate.callbacks);
repeating.prof_names = _.uniq(repeating.prof_names.concat(dropUpdate.prof_names));
update = _.extend(update, dropUpdate.update);
x++;
setCharmancerText({ mancer_progress: '<div style="width: ' + (parseInt((x / totalDrops) * 70) + 20) + '%"></div>' });
});
callbacks.push(function () {
update_ac();
});
callbacks.push(function () {
update_weight();
});
callbacks.push(callback);
callbacks.push(function () {
Rest.checkPBResources();
});
setAttrs(update, { silent: true }, function () {
setCharmancerText({ mancer_progress: '<div style="width: 95%"></div>' });
_.each(callbacks, function (callback) {
callback();
});
}); /**/
});
});
};
var getOtherDrops = function (pagedata, pageName = "") {
var results = [];
if (pagedata["data-Equipment"]) {
var json = {};
try {
//Safeguarding Compendium data:
json = Compendium.parseObject(pagedata["data-Equipment"], `E_W0UKDZF6:Page:${pageName}:data-Equipment`);
} catch (e) {
json = pagedata["data-Equipment"];
}
var newItems = makeItemData(json.default);
results = results.concat(newItems);
}
if (pagedata["data-Bundle"]) {
var json = {};
try {
//Safeguarding Compendium data:
json = Compendium.parseArray(pagedata["data-Bundle"], `E_W0UKDZF7:Page:${pageName}:data-Bundle`);
} catch (e) {
json = pagedata["data-Bundle"];
}
var newItems = makeItemData(json);
results = results.concat(newItems);
}
return results;
};
var getAllPages = function (pageArray, callback) {
var getNames = [];
_.each(pageArray, function (page) {
if (page.name) {
getNames.push(page.name);
} else {
getNames.push(page);
}
});
getCompendiumPage(getNames, function (data) {
data = removeDuplicatedPageData(data);
var nextGet = [];
if (getNames.length === 1 && !Array.isArray(data)) {
data = { 0: data };
} //Fix single spell selections failing.
_.each(data, function (page, index) {
var pagedata = false;
_.each(pageArray, function (arrayData) {
if (arrayData.name.toLowerCase() == (page.data.Category + ":" + page.name).toLowerCase()) {
pagedata = arrayData;
}
});
if (pagedata && pagedata.data) {
page.data = _.extend(page.data, pagedata.data);
}
if (!page.id) page.data.Source = "Charactermancer";
page.name = page.name.replace(/@@!!@@/g, ""); // Hacky bugfix to prevent custom names from matching unavailable content
nextGet = nextGet.concat(getOtherDrops(page.data, page.name));
if (page.data["data-Starting Gold"] && !noEquipmentDrop) {
set["gp"] += parseInt(page.data["data-Starting Gold"]);
}
allPageData.push(page);
});
if (nextGet.length > 0) {
getAllPages(nextGet, callback);
} else {
callback();
}
});
};
var makeItemData = function (items) {
if (noEquipmentDrop) {
return [];
} else {
var itemArray = [];
var splitItems = [];
_.each(items, function (item) {
item = item.split(",");
_.each(item, function (splitItem) {
splitItems.push(splitItem.trim());
});
});
_.each(splitItems, function (item) {
var itemname = item.split("(")[0].trim().replace("Items:", "");
itemname = itemname.substring(0, 4) == "and " ? itemname.substr(4) : itemname;
var itemdata = { name: "Items:" + itemname, data: {} };
if (item.includes("(")) {
var par = item.split("(")[1].split(")")[0];
if (!isNaN(parseInt(par))) {
itemdata.data["itemcount"] = parseInt(par);
}
}
if (itemname != "") {
itemArray.push(itemdata);
}
});
return itemArray;
}
};
var eraseRepeating = function (sectionArray, callback) {
var thisSection = sectionArray.shift();
getSectionIDs(thisSection, function (itemids) {
_.each(itemids, function (item) {
removeRepeatingRow("repeating_" + thisSection + "_" + item);
});
if (sectionArray.length > 0) {
eraseRepeating(sectionArray, callback);
} else {
callback();
}
});
};
var noEquipmentDrop = false;
var startTime = Date.now();
var allPageData = [];
if (eventinfo.data["l1-equipment"]) {
noEquipmentDrop = eventinfo.data["l1-equipment"].values["equipment_type"] == "gold";
}
var data = eventinfo.data;
var stats = recalcData(data);
var profs = getProficiencies(data, "finish");
var spells = knownSpells(data);
var blobs = getRelevantBlobs(data, "1", "l1");
var customtraits = { race: [], subrace: [], class: [], subclass: [], background: [] };
var equipment = [];
var silentset = {};
var silentattrs = [
"class_resource_name",
"class_resource",
"class_resource_max",
"other_resource_name",
"other_resource",
"other_resource_max",
"other_resource_itemid",
"class",
"class_display",
"subclass",
"hitdietype",
"hitdie_final",
"race",
"subrace",
"race_display",
"custom_class",
"cust_classname",
];
var clearset = {};
const globalModifiers = [
"global_damage_mod_flag",
"global_ac_mod_flag",
"global_attack_mod_flag",
"global_save_mod_flag",
"global_skill_mod_flag",
];
var clearattrs = [
"hp",
"hp_max",
"creaturetype",
"size",
"speed",
"gp",
"alignment",
"spellcasting_ability",
"cust_hitdietype",
"cust_spellslots",
"cust_spellcasting_ability",
"ac",
"jack_bonus",
"jack_attr",
"death_save_bonus",
"weighttotal",
"initiative_bonus",
"hit_dice",
"hit_dice_max",
"pb",
"jack",
"caster_level",
"spell_attack_mod",
"spell_attack_bonus",
"spell_save_dc",
"passive_wisdom",
"custom_ac_base",
"custom_ac_part1",
"custom_ac_part2",
"custom_ac_shield",
"background",
].concat(globalModifiers);
var classname = "";
var set = { gp: 0 };
var allDrops = [];
var currentDrop = 0;
var totalDrops = 1;
var allSkills = [
"athletics",
"acrobatics",
"sleight_of_hand",
"stealth",
"arcana",
"history",
"investigation",
"nature",
"navigation",
"religion",
"animal_handling",
"insight",
"medicine",
"perception",
"survival",
"deception",
"intimidation",
"performance",
"persuasion",
];
var allAbilities = SheetUtils.toLowerCase(abilityList);
var eraseSections = [
"attack",
"inventory",
"traits",
"resource",
"proficiencies",
"tool",
"damagemod",
"spell-cantrip",
"hpmod",
"acmod",
"tohitmod",
"savemod",
"skillmod",
];
for (var x = 1; x <= 9; x++) {
eraseSections.push("spell-" + x);
}
//first set is silent to make sure certain things don't trigger workers
_.each(silentattrs, function (attr) {
silentset[attr] = "";
});
//Set up setAttrs to erase some fields
_.each(clearattrs, function (attr) {
clearset[attr] = "";
});
clearset["halflingluck_flag"] = "0";
clearset["tab"] = "core";
clearset["base_level"] = "1";
clearset["level"] = "1";
clearset["armorwarningflag"] = "hide";
clearset["customacwarningflag"] = "hide";
clearset["custom_ac_flag"] = "0";
clearset["custom_attack_flag"] = "0";
clearset["custom_ac_flag"] = "0";
clearset["multiclass1_flag"] = "0";
clearset["multiclass1_lvl"] = "1";
clearset["multiclass2_flag"] = "0";
clearset["multiclass2_lvl"] = "1";
clearset["multiclass3_flag"] = "0";
clearset["multiclass3_lvl"] = "1";
clearset["custom_class"] = "0";
_.each(allSkills, function (skill) {
clearset[skill + "_prof"] = ""; //0
clearset[skill + "_type"] = ""; //1
clearset[skill + "_bonus"] = "";
});
_.each(allAbilities, function (ability) {
clearset[ability + "_save_prof"] = "";
clearset[ability + "_save_bonus"] = "";
clearset[ability + "_bonus"] = "0";
clearset["cust_" + ability + "_save_prof"] = "";
});
clearset["personality_traits"] = "";
_.each(["ideal", "bond", "flaw"], function (type) {
clearset[type + "s"] = "";
});
//Set up second setAttrs with ability scores
_.each(stats.totals, function (scores, name) {
clearset[name + "_base"] = scores.total;
});
// Build new blobs for traits with inputs, collect custom traits and languages
_.each(data, function (slide, pagename) {
_.each(slide.values, function (value, name) {
if (name.substr(-11) == "trait_input") {
var sectionid = name.substring(0, name.length - 6);
var thisblob = {};
var thistrait = {};
thistrait.Name = slide.values[sectionid + "_name"].replace(/{{Input}}/g, value);
thistrait.Desc = slide.values[sectionid + "_desc"] ? slide.values[sectionid + "_desc"].replace(/{{Input}}/g, value) : "";
thisblob.Traits = JSON.stringify([thistrait]);
blobs.names[slide.values[sectionid + "_section"]].push(sectionid);
slide.data[slide.values[sectionid + "_section"]].blobs[sectionid] = thisblob;
}
if (name.split("_")[2] == "custom" && name.substr(-10) == "trait_name") {
var sectionid = name.substring(0, name.length - 5);
var thistrait = {};
thistrait.Name = slide.values[sectionid + "_name"];
thistrait.Desc = slide.values[sectionid + "_desc"] ? slide.values[sectionid + "_desc"] : "";
customtraits[name.split("_")[3]].push(thistrait);
}
if (value == "custom" && name.substr(-18) == "proficiency_choice") {
var sectionid = name.substring(0, name.length - 7);
if (slide.values[sectionid + "_custom"] && slide.values[sectionid + "_type"]) {
profs.all[slide.values[sectionid + "_type"]].push(slide.values[sectionid + "_custom"]);
}
}
//SD-1693
if (name.includes("randomize-trait_name")) {
const thistrait = {};
thistrait.Name = value;
thistrait.Desc = slide.values[name.replace("trait_name", "trait_choice")]
? slide.values[name.replace("trait_name", "trait_choice")]
: "";
if (!SheetUtils.checkPath(data, `['l1-background']['data']['background']['blobs']`))
data["l1-background"]["data"]["background"]["blobs"] = {};
data["l1-background"]["data"]["background"]["blobs"][thistrait.Name] = {
Level: "1",
Traits: JSON.stringify([thistrait]),
};
if (!blobs.names.background) blobs.names.background = [];
blobs.names.background.push(thistrait.Name);
}
});
});
//Set up drops. start with class, subclass, race, subrace, background
if (data["l1-class"].values["class_name"] && !data["l1-class"].data.class) {
classname = removeExpansionInfo(data["l1-class"].values["class_name"]);
var customClass = { name: classname, data: { Category: "Classes", blobs: {} } };
set["custom_class"] = "1";
set["cust_classname"] = data["l1-class"].values["class_name"];
set["cust_hitdietype"] = data["l1-class"].values["custom_hit_die"];
if (data["l1-class"].values["custom_class_spell_ability"]) {
set["cust_spellslots"] = "full";
set["cust_spellcasting_ability"] = "@{" + data["l1-class"].values["custom_class_spell_ability"].toLowerCase() + "_mod}+";
}
if (customtraits.class.length > 0) {
customClass.data.theseblobs = ["customblob"];
customClass.data.blobs.customblob = { Traits: JSON.stringify(customtraits.class) };
}
_.each(allAbilities, function (ability) {
if (data["l1-class"].values[ability.toLowerCase() + "_save"]) {
set["cust_" + ability + "_save_prof"] = "(@{pb})";
}
});
allPageData.push(customClass);
} else {
//allDrops.push(data["l1-class"].values.class);
classname = removeExpansionInfo(_.last(data["l1-class"].values.class.split(":")));
var thisdata = { name: classname, data: data["l1-class"].data.class };
thisdata.data.theseblobs = blobs.names.class;
allPageData.push(thisdata);
}
//set up subclass drop
if (data["l1-class"].values.subclass && data["l1-class"].values["subclass_name"]) {
var customSubclass = {
name: removeExpansionInfo(data["l1-class"].values["subclass_name"]),
data: { Category: "Subclasses", blobs: {} },
};
if (customtraits.subclass.length > 0) {
customSubclass.data.theseblobs = ["customblob"];
customSubclass.data.blobs.customblob = { Traits: JSON.stringify(customtraits.subclass) };
}
allPageData.push(customSubclass);
} else if (data["l1-class"].values.subclass) {
//allDrops.push(data["l1-class"].values.subclass);
var thisdata = { name: removeExpansionInfo(_.last(data["l1-class"].values.subclass.split(":"))), data: data["l1-class"].data.subclass };
thisdata.data.theseblobs = blobs.names.subclass;
allPageData.push(thisdata);
}
//set up race drop
if (data["l1-race"].values["race_name"] && !data["l1-race"].data.race) {
var customRace = { name: removeExpansionInfo(data["l1-race"].values["race_name"]), data: { Category: "Races", blobs: {} } };
if (data["l1-race"].values.size) {
customRace.data.Size = data["l1-race"].values.size;
}
if (data["l1-race"].values.speed) {
customRace.data.Speed = data["l1-race"].values.speed;
}
if (customtraits.race.length > 0) {
customRace.data.theseblobs = ["customblob"];
customRace.data.blobs.customblob = { Traits: JSON.stringify(customtraits.race) };
}
allPageData.push(customRace);
//Creature Type:
const creaturetype = SheetUtils.checkPath(data, "['l1-race']['values']['creaturetype']")
? data["l1-race"]["values"]["creaturetype"]
: "Humanoid";
set["creaturetype"] = creaturetype;
} else {
//allDrops.push(data["l1-race"].values.race);
var thisdata = { name: removeExpansionInfo(_.last(data["l1-race"].values.race.split(":"))), data: data["l1-race"].data.race };
thisdata.data.theseblobs = blobs.names.race;
//Flexible Size
if (
SheetUtils.checkPath(thisdata, '["data"]["Size"]') &&
(thisdata["data"]["Size"].includes(",") || thisdata["data"]["Size"].includes(" or ") || thisdata["data"]["Size"].includes(" Or "))
) {
let selectedSize = "";
if (SheetUtils.checkPath(data, '["l1-race"]["values"]["size_choice"]')) selectedSize = data["l1-race"]["values"]["size_choice"];
thisdata.data.Size = selectedSize;
}
allPageData.push(thisdata);
//Creature Type:
const creaturetype = SheetUtils.checkPath(data, "['l1-race']['data']['race']['Creature Type']")
? data["l1-race"]["data"]["race"]["Creature Type"]
: "Humanoid";
set["creaturetype"] = creaturetype;
}
//set up subrace drop
if (data["l1-race"].values.subrace && data["l1-race"].values["subrace_name"]) {
var customSubrace = { name: removeExpansionInfo(data["l1-race"].values["subrace_name"]), data: { Category: "Subraces", blobs: {} } };
customSubrace.data["data-Parent"] = data["l1-race"].values.race.split(":")[1];
if (data["l1-race"].values.speed) {
customSubrace.data.Speed = data["l1-race"].values.speed;
}
if (customtraits.subrace.length > 0) {
customSubrace.data.theseblobs = ["customblob"];
customSubrace.data.blobs.customblob = { Traits: JSON.stringify(customtraits.subrace) };
}
allPageData.push(customSubrace);
} else if (data["l1-race"].values.subrace) {
//allDrops.push(data["l1-race"].values.subrace);
var thisdata = { name: removeExpansionInfo(_.last(data["l1-race"].values.subrace.split(":"))), data: data["l1-race"].data.subrace };
thisdata.data.theseblobs = blobs.names.subrace;
allPageData.push(thisdata);
}
//set up feat drop
if (data["l1-feat"] && data["l1-feat"].values.feat) {
var feat = { name: data["l1-feat"].values.feat, data: { Properties: "1st Level" } };
allDrops.push(feat);
const otherOptionsAndFeatures = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("other_options_and_features");
});
otherOptionsAndFeatures.forEach((feature) => {
if (data["l1-feat"]["values"][feature]) {
const featureData = { name: data["l1-feat"]["values"][feature], data: { Properties: "1st Level" } };
allDrops.push(featureData);
}
});
const skills = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_skill_choice");
});
skills.forEach((skill) => {
if (data["l1-feat"]["values"][skill]) {
profs["all"]["Skill"].push(data["l1-feat"]["values"][skill].split(":")[1]);
}
});
const removeDuplicatedSkills = Array.from(new Set(profs["all"]["Skill"]));
profs["all"]["Skill"] = removeDuplicatedSkills;
const expertise = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_expertise_choice");
});
expertise.forEach((skill) => {
if (data["l1-feat"]["values"][skill]) {
profs["all"]["Expertise"].push(data["l1-feat"]["values"][skill].split(":")[1]);
}
});
const removeDuplicatedExpertise = Array.from(new Set(profs["all"]["Expertise"]));
profs["all"]["Expertise"] = removeDuplicatedExpertise;
const spells = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_spell_choice");
});
let i = 0;
spells.forEach((spell) => {
if (data["l1-feat"]["values"][spell]) {
const spellData = { name: removeExpansionInfo(data["l1-feat"]["values"][spell]), data: { Properties: "1st Level" } };
if (SheetUtils.checkPath(data, "['l1-feat']['data']['feat']['data-Pick Spells']")) {
const pickData = data["l1-feat"]["data"]["feat"]["data-Pick Spells"][i];
if (pickData["Spellcasting Ability"]) {
let spellcastingAbility = pickData["Spellcasting Ability"].toLowerCase();
if (spellcastingAbility === "asi") {
const asiKey = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_ability_choice");
});
if (asiKey) {
spellcastingAbility = asiKey.toLowerCase();
}
}
spellData.data["Spellcasting Ability"] = spellcastingAbility;
}
if (pickData["Innate"]) {
spellData.data["Innate"] = pickData["Innate"];
}
}
allDrops.push(spellData);
i++;
}
});
}
//set up background feat drop
if (data["l1-feat"] && data["l1-feat"].values.feat_bg) {
var feat_bg = { name: data["l1-feat"].values.feat_bg, data: { Properties: "1st Level" } };
allDrops.push(feat_bg);
const otherOptionsAndFeatures = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("other_options_and_features");
});
otherOptionsAndFeatures.forEach((feature) => {
if (data["l1-feat"]["values"][feature]) {
const featureData = { name: data["l1-feat"]["values"][feature], data: { Properties: "1st Level" } };
allDrops.push(featureData);
}
});
const skills = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_bg_skill_choice");
});
skills.forEach((skill) => {
if (data["l1-feat"]["values"][skill]) {
profs["all"]["Skill"].push(data["l1-feat"]["values"][skill].split(":")[1]);
}
});
const removeDuplicatedSkills = Array.from(new Set(profs["all"]["Skill"]));
profs["all"]["Skill"] = removeDuplicatedSkills;
const expertise = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_bg_expertise_choice");
});
expertise.forEach((skill) => {
if (data["l1-feat"]["values"][skill]) {
profs["all"]["Expertise"].push(data["l1-feat"]["values"][skill].split(":")[1]);
}
});
const removeDuplicatedExpertise = Array.from(new Set(profs["all"]["Expertise"]));
profs["all"]["Expertise"] = removeDuplicatedExpertise;
const spells = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_bg_spell_choice");
});
let i = 0;
spells.forEach((spell) => {
if (data["l1-feat"]["values"][spell]) {
const spellData = { name: removeExpansionInfo(data["l1-feat"]["values"][spell]), data: { Properties: "1st Level" } };
if (SheetUtils.checkPath(data, "['l1-feat']['data']['feat_bg']['data-Pick Spells']")) {
const pickData = data["l1-feat"]["data"]["feat_bg"]["data-Pick Spells"][i];
if (pickData["Spellcasting Ability"]) {
let spellcastingAbility = pickData["Spellcasting Ability"].toLowerCase();
if (spellcastingAbility === "asi") {
const asiKey = Object.keys(data["l1-feat"]["values"]).filter((entry) => {
return entry.includes("feat_bg_ability_choice");
});
if (asiKey) {
spellcastingAbility = asiKey.toLowerCase();
}
}
spellData.data["Spellcasting Ability"] = spellcastingAbility;
}
if (pickData["Innate"]) {
spellData.data["Innate"] = pickData["Innate"];
}
}
allDrops.push(spellData);
i++;
}
});
}
//set up background drop
if (data["l1-background"].values.background) {
if (data["l1-background"].values.background == "Rules:Backgrounds") {
var customBg = {
name: removeExpansionInfo(data["l1-background"].values["background_name"]),
data: { Category: "Backgrounds", blobs: {} },
};
if (customtraits.background.length > 0) {
customBg.data.theseblobs = ["customblob"];
customBg.data.blobs.customblob = { Traits: JSON.stringify(customtraits.background) };
}
allPageData.push(customBg);
} else {
var thisdata = {
name: removeExpansionInfo(_.last(data["l1-background"].values.background.split(":"))),
data: data["l1-background"].data.background,
};
thisdata.data.theseblobs = blobs.names.background;
allPageData.push(thisdata);
}
}
//Now add proficiency drops
//Before we move custom skills into proper place...
const t_allSkills = new Set(profs.all["Skill"]);
const t_allTools = new Set(profs.all["Tool"]);
for(let skill of t_allSkills) {
const skillAttribute = skill.toLowerCase().replace(/ /g, "_");
if(!globalAttributesByCategory.skills.all().includes(skillAttribute)) {
t_allTools.add(skill);
t_allSkills.delete(skill);
}
}
profs.all["Skill"] = Array.from(t_allSkills);
profs.all["Tool"] = Array.from(t_allTools);
_.each(["Armor", "Language", "Tool", "Weapon", "Other"], function (proftype) {
_.each(profs.all[proftype], function (prof) {
var profdata = { name: "Proficiencies:" + prof, data: { Type: proftype } };
if (profs.all.Expertise.indexOf(prof) != -1) {
profdata.data["toolbonus_base"] = "(@{pb}*2)";
allDrops.unshift(profdata);
} else {
allDrops.push(profdata);
}
});
});
//Next, add spell drops
_.each(spells.all, function (spell) {
var spelldata = { name: "Spells:" + spell.Name };
spelldata.data = { spellcasting_ability: spell.Ability };
if (spell.Source == "race") {
spelldata.data.spellclass = "Racial";
} else {
spelldata.data.spellclass = classname;
}
if (spell.Innate) {
spelldata.data.innate = spell.Innate;
}
allDrops.push(spelldata);
});
//Add equipment choice drops unless starting gold was chosen
if (data["l1-equipment"].values["equipment_type"] != "gold") {
_.each(data["l1-equipment"].values, function (val, name) {
if (name.includes("background_equipment_choice")) {
equipment = equipment.concat(val.split(" and "));
}
});
_.each(blobs.all, function (blob, blobName) {
if (blob.Equipment) {
var blobstuff = blob.Equipment;
try {
//Safeguarding Compendium data:
blobstuff = Compendium.parseObject(blob.Equipment, `E_QDAGSBKW:Blob:${blobName}:Equipment`);
} catch (e) {}
equipment = equipment.concat(blobstuff);
}
});
}
//Set up the final setAttrs()
set["alignment"] = data["l1-race"].values.alignment || "";
set["hp"] = stats.hp;
set["hp_max"] = stats.hp;
set["l1mancer_status"] = "completed";
set["options-class-selection"] = "0";
if (data["l1-equipment"].values["equipment_type"] == "class") {
_.each(data["l1-equipment"].values, function (val, name) {
if (name.includes("class_equipment_choice")) {
equipment = equipment.concat(val.split(" and "));
}
});
set["gp"] = parseInt(data["l1-equipment"].values["background_starting_gold"]) || 0;
} else if (data["l1-equipment"].values["equipment_type"] == "gold" && data["l1-equipment"].values["starting_gold"]) {
set["gp"] = parseInt(data["l1-equipment"].values["starting_gold"]);
}
//Add the bio info
if (data["l1-bio"]) {
_.each(["character_name", "age", "height", "weight", "eyes", "hair", "skin"], function (type) {
if (data["l1-bio"].values[type]) {
set[type] = data["l1-bio"].values[type] || "";
}
});
}
//Add skill proficiencies
_.each(_.uniq(profs.all.Skill.concat(profs.all.Expertise)), function (prof) {
var profName = prof.toLowerCase().replace(/ /g, "_");
set[profName + "_prof"] = "(@{pb}*@{" + profName + "_type})";
if (profs.all.Expertise.indexOf(prof) != -1) {
set[profName + "_type"] = 2;
}
});
//Add background traits
if (data["l1-background"].values["background_personality_choice1"] || data["l1-background"].values["background_personality_choice2"]) {
var choice1 = data["l1-background"].values["background_personality_choice1"] || false;
var choice2 = data["l1-background"].values["background_personality_choice2"] || false;
choice1 = choice1 || choice2;
if (choice1) {
set["personality_traits"] = choice1;
}
if (choice2) {
set["personality_traits"] += "\n\n" + choice2;
}
set["options-flag-personality"] = 0;
}
_.each(["ideal", "bond", "flaw"], function (type) {
if (data["l1-background"].values["background_" + type + "_choice"]) {
set[type + "s"] = data["l1-background"].values["background_" + type + "_choice"];
set["options-flag-" + type + "s"] = 0;
}
});
if (data["l1-background"].values["background_detail_choice"]) {
set["background"] =
removeExpansionInfo(_.last(data["l1-background"].values["background"].split(":"))) +
" (" +
data["l1-background"].values["background_detail_choice"] +
")";
}
allDrops = allDrops.concat(makeItemData(equipment));
totalDrops += allDrops.length;
_.each(allPageData, function (page) {
allDrops = allDrops.concat(getOtherDrops(page.data));
});
//erase all repeating sections
eraseRepeating(eraseSections, function () {
setCharmancerText({ mancer_progress: '<div style="width: 5%"></div>' });
//first set is silent to prevent unwanted sheet workers
setAttrs(silentset, { silent: true }, function () {
setCharmancerText({ mancer_progress: '<div style="width: 10%"></div>' });
//Remove modifiers set to be displayed by default on sheet.json from the clear set
getAttrs(globalModifiers, (modifiers) => {
const modifiersToggledOn = Object.keys(modifiers).filter((key) => {
return modifiers[key] === "1";
});
modifiersToggledOn.forEach((key) => {
delete clearset[key];
});
//reset remaining attributes, and set ability scores/custom info
setAttrs(clearset, function () {
setCharmancerText({ mancer_progress: '<div style="width: 15%"></div>' });
getAllPages(allDrops, function () {
setCharmancerText({ mancer_progress: '<div style="width: 20%"></div>' });
doAllDrops(allPageData, function () {
//Spellcasting Ability Choice Hook:
if (data["l1-class"] && data["l1-class"].repeating) {
Object.entries(data["l1-class"].values).forEach(([key, value]) => {
if (value === "Spellcasting Ability Choice") {
const valueAttribute = key.replace("_info", "_choice");
const ability = data["l1-class"].values[valueAttribute] || "Choice";
const uid = SheetUtils.getUID(key);
const hasRepeating = data["l1-class"].repeating.filter((repeating) => repeating.includes(uid)).length > 0;
const validAbility = globalAttributesByCategory.abilities.includes(ability.toLowerCase());
if (hasRepeating && validAbility) set["spellcasting_ability"] = `@{${ability.toLowerCase()}_mod}+`;
return;
}
});
}
console.log("DOING THE FINAL SET!!");
setAttrs(set, function () {
setCharmancerText({ mancer_progress: '<div style="width: 100%"></div>' });
update_class();
organize_section_proficiencies();
update_skills(allSkills);
update_attacks("all");
formatPrintedTools();
formatPrintedSkills();
var endTime = Date.now();
console.log(`Elapsed time: ${(endTime - startTime) / 1000}`);
finishCharactermancer();
});
});
});
});
});
});
});
/* */
});
on("page:l1-abilities", (eventinfo) => {
const optionalAttributes = SheetUtils.toLowerCase(abilityListEnabledOptionals);
const pointbuyPoints = (3 * optionalAttributes.length + 27).toString();
setAttrs({ pointbuy_points: pointbuyPoints });
const attribsRoll = ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]
.concat(optionalAttributes)
.map((key, index) => `{{r${index + 1}=[[4d6dl1]]}}`)
.join(" ");
const rollstatsButton = `<button type="roll" name="roll_rollstats" value="@{wtype}&{template:mancerroll} {{title=Roll for Stats}} ${attribsRoll}"><span data-i18n="standard-roll">(4d6, drop lowest)</span></button>`;
setCharmancerText({ "rollstats-button": rollstatsButton });
});
on(
"page:l1-welcome page:l1-race page:l1-class page:l1-abilities page:l1-background page:l1-equipment page:l1-spells page:l1-feat page:l1-bio page:l1-summary",
(eventInfo) => {
showOptionalAttributeFields();
}
);
on("mancerchange:customize_origin", (eventinfo) => {
if (eventinfo.sourceType && eventinfo.sourceType == "player") {
deleteSlideData("l1-race", (data) => {
changeCharmancerPage("l1-race");
});
}
});
on("page:l1-race", (eventInfo) => {
data = getCharmancerData();
let hasTasha = 0;
if (SheetUtils.checkPath(data, "['l1-welcome']['values']['has_tasha']") && data["l1-welcome"]["values"]["has_tasha"] == 1) hasTasha = 1;
setAttrs({ has_tasha: hasTasha });
});
on("page:l1-welcome", (eventInfo) => {
hasTasha((result) => {
const enabled = result ? 1 : 0;
setAttrs({ has_tasha: enabled });
});
});
on(
"mancerchange:feat_skill_choice1 mancerchange:feat_skill_choice2 mancerchange:feat_skill_choice3 mancerchange:feat_skill_choice4",
function (eventinfo) {
if (eventinfo.sourceType && eventinfo.sourceType == "player" && eventinfo.newValue) {
let choosenSkill = [eventinfo.newValue.split(":")[1]];
for (let i = 1; i <= 4; i++) {
setValidExpertiseOptions(null, `comp_feat_expertise_choice_${i}`, choosenSkill);
}
}
}
);
//SD-1693
on("mancerroll:repeating_randomize-trait", (eventinfo) => {
const charmancerData = getCharmancerData();
const arrayDataAttribute = `${eventinfo.sourceSection}_choice_array`;
const selectAttribute = `comp_${eventinfo.sourceSection}_choice`;
const index = eventinfo.roll[0].dice[0] - 1;
const arrayDataFound = SheetUtils.checkPath(charmancerData, `['l1-background']['values'][${arrayDataAttribute}]`);
if (!arrayDataFound) return;
const data = Compendium.parseObject(charmancerData["l1-background"]["values"][arrayDataAttribute], `E_5MD126XY`);
if (!data.array) return;
setCharmancerOptions(selectAttribute, data.array, { selected: data.array[index] });
});
/*********************************************************************/
/****** LEVEL UP MANCER WORKERS ******/
/*********************************************************************/
//Helper function to update the text in the top bar
var recalcLpData = function (blobs, mancer = "lp") {
var mancerdata = getCharmancerData();
var abilities = getAbilityTotals(mancerdata, blobs, mancer);
var update = {};
_.each(abilityList, function (ability) {
var selector = "attr-container ." + ability.toLowerCase() + "_total";
update[selector] = abilities[ability.toLowerCase()]; //+ " | " + abilities[ability.toLowerCase() + "_mod"];
});
update["hit_points"] = getHpTotal(mancerdata, blobs, abilities, mancer);
setCharmancerText(update);
};
//Helper function to get ability totals, including asi
var getAbilityTotals = function (mancerdata, blobs, mancer = "lp") {
mancerdata = mancerdata || getCharmancerData();
blobs = blobs || getAllLpBlobs(mancerdata, true);
var abilities = mancerdata[`${mancer}-welcome`].values["previous_abilities"]
? Compendium.parseObject(mancerdata[`${mancer}-welcome`].values["previous_abilities"], `E_MIJ6BYDF`)
: {};
var lcAbilities = SheetUtils.toLowerCase(abilityList);
const usingTasha = isTashaEnabled(mancerdata);
//Set previous scores first
_.each(lcAbilities, function (ability) {
abilities[ability + "_previous"] = abilities[ability];
abilities[ability + "_previous_mod"] = abilities[ability + "_mod"];
abilities[ability + "_maximum"] = abilities[ability + "_maximum"] ? parseInt(abilities[ability + "_maximum"]) : 20;
});
_.each(blobs.all, function (blob, blobName) {
if (blob["Ability Score Increase"]) {
//Safeguarding Compendium data:
var asi = Compendium.parseObject(blob["Ability Score Increase"], `E_LC1XILG4:Blob:${blobName}:Ability Score Increase`);
_.each(asi, function (increase, ability) {
abilities[ability.toLowerCase()] += parseInt(increase);
});
}
if (blob["Ability Score Max"]) {
//Safeguarding Compendium data:
var max = Compendium.parseObject(blob["Ability Score Max"], `E_NV7N4VEG:Blob:${blobName}:Ability Score Max`);
_.each(max, function (increase, ability) {
abilities[ability.toLowerCase() + "_maximum"] = parseInt(increase);
});
}
});
//Now add asi if it exists
if (mancerdata["lp-asi"]) {
_.each(mancerdata["lp-asi"].repeating, function (repid) {
if (repid.substr(31) == "asi-row" && mancerdata["lp-asi"].values[repid + "_switch"] != "feat") {
_.each(lcAbilities, function (ability) {
abilities[ability] += mancerdata["lp-asi"].values[repid + "_" + ability]
? parseInt(mancerdata["lp-asi"].values[repid + "_" + ability])
: 0;
abilities[ability + "_mod"] = Math.floor((abilities[ability] - 10) / 2);
});
} else if (repid.substr(31) == "asi-row" && mancerdata["lp-asi"].values[repid + "_switch"] == "feat") {
const rowID = SheetUtils.getUID(repid);
if (SheetUtils.checkPath(mancerdata, `['lp-asi']['values']['repeating_${rowID}_asi-row_feat']`)) {
const featAbilityIncrease = getFeatAbilityIncrease(mancerdata, rowID);
_.each(lcAbilities, function (ability) {
const choiceIncrease =
mancerdata["lp-asi"]["values"][`repeating_${rowID}_asi-row_feat_ability_choice`] &&
mancerdata["lp-asi"]["values"][`repeating_${rowID}_asi-row_feat_ability_choice`].toLowerCase() == ability
? 1
: 0;
const dataIncrease = featAbilityIncrease[ability] ? featAbilityIncrease[ability] : 0;
abilities[ability] += choiceIncrease;
abilities[ability] += dataIncrease;
abilities[ability + "_mod"] = Math.floor((abilities[ability] - 10) / 2);
});
}
}
});
}
if (mancer === "utility") {
Object.entries(mancerdata).forEach(([pageName, page]) => {
if (!pageName.includes("utility-") || pageName === "utility-welcome") return;
if (usingTasha) {
_.each(page.values, function (value, name) {
if (
name.search("ability") !== -1 &&
name.search("tasha") !== -1 &&
(name.length == 26 || name.length == 29) &&
page.values[`${name}_increase`]
) {
const choiceSection = name.split("_")[1];
const increase = parseInt(page.values[`${name}_increase`]);
allAbilities[choiceSection] = allAbilities[choiceSection] || {};
allAbilities[choiceSection][value.toLowerCase()] = increase;
disableAbilities[choiceSection] = disableAbilities[choiceSection] || [];
disableAbilities[choiceSection].push(value);
}
});
} else {
_.each(page.values, function (value, name) {
if (name.search("ability") !== -1) {
if (SheetUtils.checkPath(page, `["data"]["newrace"]["data-Ability Score Choice"]`)) {
var increase = 1;
if (typeof page.data.newrace["data-Ability Score Choice"] == "string") {
increase = parseInt(_.last(page.data.newrace["data-Ability Score Choice"].split("+")));
}
abilities[value.toLowerCase()] += parseInt(increase);
}
}
});
_.each(page.data, function (pagedata, increaseSection) {
if (!usingTasha) {
if (pagedata["data-Ability Score Increase"] && !isFlex(pagedata["data-Ability Score Increase"])) {
_.each(pagedata["data-Ability Score Increase"], function (amount, ability) {
abilities[ability.toLowerCase()] += parseInt(amount);
});
}
}
});
}
});
if (mancerdata["utility-newrace"] && mancerdata["utility-newrace"].values.newrace == "Rules:Races") {
_.each(abilityList, function (ability) {
var custom = mancerdata["utility-newrace"].values["race_custom_" + ability.toLowerCase()] || false;
if (custom) {
abilities[ability.toLowerCase()] += parseInt(custom);
}
});
}
}
if (mancerdata["utility-newrace"] && mancerdata["utility-newrace"].values) {
const newraceValues = mancerdata["utility-newrace"].values;
const decreases = Object.keys(mancerdata["utility-newrace"].values).filter((entry) => {
return /decrease[0-9]_selected$/g.test(entry);
});
decreases.forEach((decrease) => {
ability = newraceValues[decrease].split(" ")[0];
const mod = parseInt(newraceValues[decrease].split(" ")[1]);
abilities[ability] += mod;
});
}
return abilities;
};
var getFeatAbilityIncrease = function (data, rowID) {
const result = {};
if (!SheetUtils.checkPath(data, `['lp-asi']['data']['repeating_${rowID}_asi-row_feat]['data-Ability Score Increase']`)) return result;
//Safeguarding Compendium data:
var asi = Compendium.parseObject(data["lp-asi"]["data"][`repeating_${rowID}_asi-row_feat`]["data-Ability Score Increase"], `E_TL21BMKI`);
_.each(asi, function (increase, ability) {
result[ability.toLowerCase()] = typeof increase === "number" ? increase : parseInt(increase);
});
return result;
};
//Helper function to get hp total
var getHpTotal = function (mancerdata, blobs, abilities, mancer = "lp") {
mancerdata = mancerdata || getCharmancerData();
var leveldata = getLevelingData(mancerdata);
var abilities = abilities || getAbilityTotals(mancerdata, blobs);
//Safeguarding Compendium data:
var previous = mancerdata[`${mancer}-welcome`].values["previous_attributes"]
? Compendium.parseObject(mancerdata[`${mancer}-welcome`].values["previous_attributes"], `E_UE4IYN0K`)
: {};
var prev_repeat = Compendium.parseObject(mancerdata[`${mancer}-welcome`].values["previous_repeating"], `E_JZ6XCM6Z`);
hpmod = prev_repeat["hpmod"];
var newhp = 0;
var additionalhp = 0;
var totalpreviouslevel = parseInt(previous["base_level"]);
for (var x = 1; x <= 3; x++) {
if (previous["multiclass" + x + "_flag"] != "0") totalpreviouslevel += parseInt(previous["multiclass" + x + "_lvl"]);
}
//Calculate HP Mods things like Hill Dwarf
let levelbonus = 0;
_.each(hpmod, (mod) => {
const bonus = mod["mod"];
if (mod["levels"] === "total") {
levelbonus += parseInt(bonus);
}
});
//Add up the hp per level
_.each(leveldata, function (level) {
newhp += level.addhp > 0 ? level.addhp + level.addlevel * abilities["constitution_mod"] + level.addlevel * levelbonus : 0;
});
additionalhp = totalpreviouslevel * (abilities["constitution_mod"] - abilities["constitution_previous_mod"]);
return parseInt(previous["hp_max"]) + newhp + additionalhp;
};
var recalcButtons = function (mancerdata, thispage) {
mancerdata = mancerdata || getCharmancerData();
var buttons = "";
var buttoninfo = {
welcome: "charmancer-start",
levels: "levels-u",
choices: "features-u",
asi: "asi-u",
spells: "spells-u",
summary: "review-u",
};
_.each(buttoninfo, function (translation, page) {
let here = "lp-" + page == thispage;
let disabled = false;
if (thispage === "lp-spellchoice") disabled = true;
if (!here && page !== "levels" && thispage === "lp-welcome") disabled = true;
if (mancerdata["lp-levels"]) {
if (!here && page === "asi" && mancerdata["lp-levels"].values.asi !== "true") disabled = true;
if (!here && page === "spells" && mancerdata["lp-levels"].values.spells !== "true") disabled = true;
} else {
if (!here && (page === "asi" || page === "spells")) disabled = true;
}
buttons += '<li><button class="step' + (here ? " here" : "");
if (disabled) buttons += " disabled";
buttons += '" type="' + (here ? "here" : "back") + '" value="lp-' + page + '" data-i18n="' + translation + '"></button></li>';
});
buttons += '<li><button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button></li>';
setCharmancerText({ steps: buttons });
};
//Worker for every page to add topbar/buttons
on("page:lp-welcome page:lp-levels page:lp-choices page:lp-asi page:lp-spells page:lp-summary page:lp-spellchoice", function (eventinfo) {
var mancerdata = getCharmancerData();
if (mancerdata["lp-welcome"]) {
recalcButtons(mancerdata, eventinfo.triggerName);
if (eventinfo.triggerName != "lp-welcome" && eventinfo.triggerName != "lp-summary") {
addRepeatingSection("topbar-holder", "leveler-topbar");
if (eventinfo.triggerName != "lp-choices" && eventinfo.triggerName != "lp-spells") recalcLpData();
getProficiencies(mancerdata, eventinfo.currentStep);
}
} else {
addLV1Toolbar(mancerdata, eventinfo);
}
});
on("page:lp-welcome", function (eventinfo) {
var getInfo = (sections, callback, results) => {
results = results || {};
if (sections.length > 0) {
var section = sections.pop();
getSectionIDs(section, function (ids) {
results[section] = ids;
getInfo(sections, callback, results);
});
} else {
callback(results);
}
};
var capitalize = function (string) {
return string
.split(" ")
.map((x) => {
x = x.toLowerCase();
return x[0].toUpperCase() + x.substr(1, x.length);
})
.join(" ");
};
var spellSections = ["spell-cantrip"];
var allSkills = [
"athletics",
"acrobatics",
"sleight_of_hand",
"stealth",
"arcana",
"history",
"investigation",
"nature",
"navigation",
"religion",
"animal_handling",
"insight",
"medicine",
"perception",
"survival",
"deception",
"intimidation",
"performance",
"persuasion",
];
var skillget = [];
for (var x = 1; x <= 9; x++) {
spellSections.push("spell-" + x);
}
getInfo(spellSections, function (results) {
var getList = [];
_.each(results, function (sectionids, sectionname) {
_.each(sectionids, function (spellid) {
getList.push("repeating_" + sectionname + "_" + spellid + "_spellname");
getList.push("repeating_" + sectionname + "_" + spellid + "_spellclass");
getList.push("repeating_" + sectionname + "_" + spellid + "_spelllevel");
getList.push("repeating_" + sectionname + "_" + spellid + "_spellsource");
});
});
getAttrs(getList, function (attrs) {
var spellInfo = {};
_.each(attrs, function (data, name) {
var namearray = name.split("_");
var thisattr = namearray.pop();
var thisname = namearray.join("_");
spellInfo[thisname] = spellInfo[thisname] || {};
spellInfo[thisname][thisattr] = data;
});
setAttrs({ spellinfo: JSON.stringify(spellInfo) });
});
});
const attribsWithModsAndMaximum = SheetUtils.toLowerCase(
abilityList
.concat(
abilityList.map((attribute) => {
return `${attribute}_mod`;
})
)
.concat(
abilityList.map((attribute) => {
return `${attribute}_maximum`;
})
)
);
const baseAttributes = SheetUtils.toLowerCase(abilityList)
.map((entry) => {
return `${entry}_base`;
})
.concat(
SheetUtils.toLowerCase(abilityList).map((entry) => {
return `${entry}_flag`;
})
);
getAttrs(attribsWithModsAndMaximum, (final) => {
getAttrs(baseAttributes, (base) => {
const modfiedAbilityList = Object.keys(base).filter((entry) => {
return entry.indexOf("_base") > -1 && base[entry.replace("_base", "_flag")] == 1;
});
_.each(final, function (x, y) {
final[y] = parseInt(x);
});
modfiedAbilityList.forEach((attribute) => {
final[attribute.replace("_base", "")] = parseInt(base[attribute]);
final[attribute.replace("_base", "_mod")] = Math.floor((parseInt(base[attribute]) - 10) / 2);
});
setAttrs({ previous_abilities: JSON.stringify(final) });
});
});
getAttrs(
[
"class",
"subclass",
"base_level",
"multiclass1_flag",
"multiclass2_flag",
"multiclass3_flag",
"multiclass1",
"multiclass1_lvl",
"multiclass1_subclass",
"multiclass2",
"multiclass2_lvl",
"multiclass2_subclass",
"multiclass3",
"multiclass3_lvl",
"multiclass3_subclass",
"hp_max",
"class_resource_name",
"other_resource_name",
"speed",
"race",
"subrace",
"background",
"custom_class",
"cust_classname",
"cust_hitdietype",
],
function (v) {
let set = {};
set["previous_attributes"] = JSON.stringify(v);
set["lp-race"] = "Races:" + v.race;
set["lp-subrace"] = "Subraces:" + v.subrace;
set["lp-background"] = "Backgrounds:" + v.background.split("(")[0].trim();
setAttrs(set);
}
);
_.each(allSkills, function (skill) {
skillget.push(skill + "_prof");
skillget.push(skill + "_type");
});
getAttrs(skillget, function (v) {
var proficiencies = { Weapon: [], Armor: [], Skill: [], Tool: [], Language: [], Other: [], Expertise: [] };
_.each(allSkills, function (skill) {
if (v[skill + "_prof"] !== "0") {
proficiencies.Skill.push(capitalize(skill.replace(/_/g, " ")));
}
if (v[skill + "_type"] !== "1" && v[skill + "_prof"] !== "0") {
proficiencies.Expertise.push(capitalize(skill.replace(/_/g, " ")));
}
});
getSectionIDs("tool", function (ids) {
var getList = [];
_.each(ids, function (id) {
getList.push("repeating_tool_" + id + "_toolname");
getList.push("repeating_tool_" + id + "_toolbonus_base");
});
getAttrs(getList, function (v) {
_.each(ids, function (id) {
proficiencies.Tool.push(v["repeating_tool_" + id + "_toolname"]);
if (v["repeating_tool_" + id + "_toolbonus_base"] == "(@{pb}*2)") {
proficiencies.Expertise.push(v["repeating_tool_" + id + "_toolname"]);
}
});
getSectionIDs("proficiencies", function (ids) {
var getList = [];
_.each(ids, function (id) {
getList.push("repeating_proficiencies_" + id + "_name");
getList.push("repeating_proficiencies_" + id + "_prof_type");
});
getAttrs(getList, function (v) {
_.each(ids, function (id) {
proficiencies[capitalize(v["repeating_proficiencies_" + id + "_prof_type"])].push(
v["repeating_proficiencies_" + id + "_name"]
);
});
setAttrs({ previous_proficiencies: JSON.stringify(proficiencies) });
});
});
});
});
});
get_repeating_data(function (repeating) {
setAttrs({ previous_repeating: JSON.stringify(repeating) });
});
});
on("mancerchange:lp-race mancerchange:lp-subrace mancerchange:lp-background", function (eventinfo) {
getCompendiumPage(eventinfo.newValue, function (results) {});
});
on("page:lp-levels", function (eventinfo) {
const mancerdata = getCharmancerData();
//Safeguarding Compendium data:
const previous = mancerdata["lp-welcome"].values["previous_attributes"]
? Compendium.parseObject(mancerdata["lp-welcome"].values["previous_attributes"], `E_D3JQGQUX`)
: {};
let levelclass = ["class1"];
let set = {};
let update = {};
let baseclassExpansion = "";
if (
SheetUtils.checkPath(mancerdata, "['l1-class']['values']['class']") &&
/\?expansion=[0-9]/g.test(mancerdata["l1-class"]["values"]["class"])
) {
baseclassExpansion = `?expansion=${mancerdata["l1-class"]["values"]["class"].match(/([0-9]+)$/g)}`;
}
let baseclass = previous["class"] + baseclassExpansion;
let classcompend = ["Classes:"] + previous["class"];
let custom_lock = 0;
if (previous["custom_class"] && previous["custom_class"] == 1) {
baseclass = previous["cust_classname"];
classcompend = "Rules:Classes";
custom_lock = 1;
set["class1_addlevel"] = 0;
}
set["lockcustomclass"] = custom_lock;
set["class1"] = "Classes:" + baseclass;
set["class1_currentlevel"] = previous["base_level"];
update["class1_selector"] = '<span class="compendium-class-name">' + removeExpansionInfo(baseclass) + "</span>";
if (previous["subclass"] && previous["custom_class"] === "0") {
update[`subclass1_selector`] = `<div>` + previous["subclass"] + `</div>`;
if (
(mancerdata["lp-levels"] && mancerdata["lp-levels"].values["class1_subclass"] !== "Subclasses" + previous["subclass"]) ||
!mancerdata["lp-levels"]
)
set["class1_subclass"] = "Subclasses:" + previous["subclass"];
}
const multiclass =
parseInt(previous["multiclass1_flag"]) + parseInt(previous["multiclass2_flag"]) + parseInt(previous["multiclass3_flag"]);
set["multiclass"] =
mancerdata["lp-levels"] && mancerdata["lp-levels"].values["multiclass"] ? mancerdata["lp-levels"].values["multiclass"] : multiclass;
set["multiclass_initial"] = multiclass;
for (x = 1; x <= 3; x++) {
const x1 = x + 1;
if (previous[`multiclass${x}_flag`] === "1") {
set[`class${x1}`] = "Classes:" + previous[`multiclass${x}`];
set[`class${x1}_currentlevel`] = previous[`multiclass${x}_lvl`];
update[`class${x1}_selector`] = `<span class="compendium-class-name">${previous[`multiclass${x}`]}</span>`;
if (previous[`multiclass${x}_subclass`]) {
set[`class${x1}_subclass`] = "Subclasses:" + previous[`multiclass${x}_subclass`];
update[`subclass${x1}_selector`] = `<div>` + previous[`multiclass${x}_subclass`] + `</div>`;
}
levelclass.push(`class${x1}`);
} else {
set[`class${x1}_currentlevel`] = 0;
}
//This is handling classes that have been multiclassed into while in the leveler
if (
mancerdata["lp-levels"] &&
mancerdata["lp-levels"].values[`class${x1}`] &&
parseInt(mancerdata["lp-levels"].values[`class${x1}_addlevel`]) > 0 &&
!levelclass.includes(`class${x1}`)
) {
levelclass.push(`class${x1}`);
}
}
setAttrs(set, () => {
update_hp(true, levelclass);
});
setCharmancerText(update);
changeCompendiumPage("levels-info", classcompend);
});
on("mancerchange:class1 mancerchange:class2 mancerchange:class3 mancerchange:class4", function (eventinfo) {
if (eventinfo.sourceType !== "worker") deleteCharmancerData(["lp-choices", "lp-asi", "lp-spells", "lp-summary"]);
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"];
const levelclass = eventinfo.sourceAttribute;
//Safeguarding Compendium data:
const previous = mancerdata["lp-welcome"].values["previous_attributes"]
? Compendium.parseObject(mancerdata["lp-welcome"].values["previous_attributes"], `E_IF8GW5ZA`)
: {};
getCompendiumPage(eventinfo.newValue, function (p) {
p = removeDuplicatedPageData(p);
const data = p.data;
const class_name =
eventinfo.newValue && eventinfo.newValue.split(":").length > 1 && eventinfo.newValue.split(":")[0] === "Classes"
? eventinfo.newValue.split(":")[1]
: false;
const subclass_name = data["Subclass Name"];
let set = {};
let update = {};
if (levelclass === "class1" && (!previous["custom_class"] || previous["custom_class"] == 0)) {
set[`${levelclass}_addlevel`] = leveldata.values[`${levelclass}_addlevel`] ? leveldata.values[`${levelclass}_addlevel`] : 1;
} else {
set[`${levelclass}_addlevel`] = leveldata.values[`${levelclass}_addlevel`] ? leveldata.values[`${levelclass}_addlevel`] : 0;
}
set[`${levelclass}_hitdie`] = data["Hit Die"];
update[`sub${levelclass}_name`] = `<h4>${subclass_name}</h4>`;
//Need to add a check here if a previous subclass value exists, skip this part if so.
if (class_name) {
var subOptions = { show_source: true };
let reset = {};
reset[levelclass + "_subclass"] = '<option value="" data-i18n="choose">Choose</option>';
//setCharmancerText(reset)
eventinfo.sourceType != "player" ? (subOptions.silent = true) : (subOptions.selected = "");
setCharmancerOptions(levelclass + "_subclass", "Category:Subclasses data-Parent:" + class_name, subOptions, function (values) {});
}
const presets = [leveldata.values.class1, leveldata.values.class2, leveldata.values.class3, leveldata.values.class4];
for (x = 2; x <= 4; x++) {
disableCharmancerOptions(`class${x}`, presets);
}
setCharmancerText(update);
setAttrs(set, function () {
let set = {};
set[eventinfo.sourceAttribute] = eventinfo.newValue;
setAttrs(set, { silent: true });
});
});
});
//Trigger a change event when subclass changes
["class1", "class2", "class3", "class4"].forEach((levelclass) => {
on(`mancerchange:${levelclass}_subclass`, (eventinfo) => {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"].values;
const repeating = mancerdata["lp-levels"].repeating || [];
let set = {};
if (eventinfo.sourceType !== "worker") deleteCharmancerData(["lp-choices", "lp-asi", "lp-spells", "lp-summary"]);
getCompendiumPage(eventinfo.newValue, function (data) {
data = removeDuplicatedPageData(data);
updateClassLevel(undefined, eventinfo.sourceAttribute.slice(0, 6));
});
//This will update the results inputs with new hp values if the subclass is Draconic
if (eventinfo.sourceType !== "worker") {
const newValue = eventinfo.newValue ? eventinfo.newValue : " ";
const previousValue = eventinfo.previousValue ? eventinfo.previousValue : " ";
let repids = [],
updateArray = [];
_.each(repeating, (repid) => {
if (repid.includes(`_${levelclass}-`)) {
updateArray.push(repid);
}
});
_.each(updateArray, (repid) => {
if (newValue.includes("Draconic Bloodline") || previousValue.includes("Draconic Bloodline")) {
repids.push(repid);
}
});
if (repids.length > 0) {
updateButtons(levelclass, repids);
updateResults(levelclass, repids, eventinfo);
}
}
setAttrs(set);
});
});
on(
"mancerchange:class1_addlevel mancerchange:class2_addlevel mancerchange:class3_addlevel mancerchange:class4_addlevel",
function (eventinfo) {
if (eventinfo.sourceType !== "worker") deleteCharmancerData(["lp-choices", "lp-asi", "lp-spells", "lp-summary"]);
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"];
const levelclass = eventinfo.sourceAttribute.slice(0, 6);
const classlevel = (parseInt(eventinfo.newValue) || 0) + (parseInt(leveldata.values[`${levelclass}_currentlevel`]) || 0);
const subclasslevel =
leveldata.data[`${levelclass}`] && leveldata.data[`${levelclass}`]["data-Subclass Level"]
? parseInt(leveldata.data[`${levelclass}`]["data-Subclass Level"])
: 3;
let set = {};
let update = {};
if (eventinfo.currentStep === "lp-levels") updateClassLevel(mancerdata, levelclass);
set[`${levelclass}_classlevel`] = classlevel;
if (classlevel >= subclasslevel) {
update[`sub${levelclass}_warning`] = ``;
showChoices([`sub${levelclass}`]);
} else {
update[`sub${levelclass}_warning`] = `<p>Subclass chosen at level ${subclasslevel}. Your total level is ${classlevel}.</p>`;
hideChoices([`sub${levelclass}`]);
}
const level = leveldata.values[`${levelclass}_currentlevel`] === undefined ? 0 : leveldata.values[`${levelclass}_currentlevel`];
update[`${levelclass}_update`] = `level ${level} + `;
update[`${levelclass}_tot`] = `= level ${classlevel}`;
setCharmancerText(update);
setAttrs(set);
}
);
const updateClassLevel = function (mancerdata, levelclass) {
mancerdata = mancerdata || getCharmancerData();
const leveling = getLevelingData(mancerdata);
const blobs = getAllLpBlobs(mancerdata, true);
const spells = getNewSpells(mancerdata, leveling, blobs);
let set = {};
let update = {};
let asi = false;
for (let x = 0; x <= 4; x++) {
update["class" + x + "_features"] = "";
update["subclass" + x + "_features"] = "";
}
_.each(leveling, function (thislevel) {
_.each(["", "sub"], function (prefix) {
let thiskey = "class" + thislevel.classnumber;
let final = [];
if (prefix === "sub") thiskey += "_subclass";
_.each(blobs.names[thiskey], function (blobname) {
if (blobname.substr(0, 30) == "Ability Score Increase - Level") {
asi = true;
final.push("Ability Score Increase");
} else if (
(thislevel[prefix + "class"].blobs[blobname].Title ||
thislevel[prefix + "class"].blobs[blobname].Description ||
thislevel[prefix + "class"].blobs[blobname].Traits) &&
!thislevel[prefix + "class"].blobs[blobname].Group
) {
final.push(thislevel[prefix + "class"].blobs[blobname].Title || blobname);
}
});
update[prefix + "class" + thislevel.classnumber + "_features"] = _.uniq(final).join(", ");
});
});
set.asi = asi.toString();
set.spells = spells.toString();
setCharmancerText(update);
setAttrs(set, () => {
update_hp(false, [`${levelclass}`]);
recalcButtons(undefined, "lp-levels");
});
};
//Build a function to update the level & class each time they change
on(
"mancerchange:class1_classlevel mancerchange:class2_classlevel mancerchange:class3_classlevel mancerchange:class4_classlevel",
function (eventinfo) {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"];
const class1levels = parseInt(leveldata.values["class1_classlevel"]) || 0,
class2levels = parseInt(leveldata.values["class2_classlevel"]) || 0,
class3levels = parseInt(leveldata.values["class3_classlevel"]) || 0,
class4levels = parseInt(leveldata.values["class4_classlevel"]) || 0;
const characterlevel = class1levels + class2levels + class3levels + class4levels;
let update = {};
let set = {};
let end = [];
for (x = 1; x <= 4; x++) {
let name = leveldata.values[`class${x}`] ? leveldata.values[`class${x}`].slice(8) : "";
let lvl = parseInt(leveldata.values[`class${x}_classlevel`]) || 0;
lvl > 0 && x === 1 ? end.push(`${name} ${lvl}`) : lvl > 0 && x > 1 ? end.push(` ${name} ${lvl}`) : false;
}
update["levels_message"] = `<p>Character will be level ${characterlevel} (${end.map((entry) => {
return removeExpansionInfo(entry);
})})</p>`;
set[`characterlevel`] = characterlevel;
setAttrs(set);
setCharmancerText(update);
}
);
const recalcHpByLevel = (section) => {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"].values;
const repeating = mancerdata["lp-levels"].repeating || [];
let set = {};
const sections = section ? [section] : ["class1", "class2", "class3", "class4"];
_.each(sections, (levelclass) => {
let thishp = 0;
let first = true;
let rollflag = "";
_.each(repeating, function (repid) {
if (repid.split("_")[2].split("-")[0] == levelclass && leveldata[`${repid}_result`]) {
let thisflag = leveldata[`${repid}_rollflag`] || "average";
thishp += parseInt(leveldata[`${repid}_result`]);
if (first) {
rollflag = thisflag;
first = false;
}
if (rollflag !== thisflag) rollflag = "none";
}
});
set[`${levelclass}_hp_flag`] = rollflag;
set[`${levelclass}_addhp`] = thishp;
});
setAttrs(set, () => {
recalcLpData();
});
};
const update_hp = (firstTime, levelclass) => {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"].values;
const repeating = mancerdata["lp-levels"].repeating || [];
const updateByLevel = (firstTime, levelclass) => {
_.each(levelclass, (levelclass) => {
const current = leveldata[`${levelclass}_currentlevel`] ? parseInt(leveldata[`${levelclass}_currentlevel`]) : 0,
addlevel = leveldata[`${levelclass}_addlevel`] ? parseInt(leveldata[`${levelclass}_addlevel`]) : 0;
let levelarray = [],
addarray = [],
removearray = [],
existing = [];
for (let x = current + 1; x <= current + addlevel; x++) {
levelarray.push(`${levelclass}-${x}`);
}
addarray = levelarray;
if (!firstTime) {
_.each(repeating, function (id) {
addarray = _.without(addarray, id.split("_")[2]);
});
}
_.each(repeating, function (id) {
if (
id.split("_")[2].split("-")[0] == levelclass &&
!levelarray.includes(id.split("_")[2]) &&
!existing.includes(id.split("_")[2])
) {
removearray.push(id);
existing.push(id.split("_")[2]);
}
});
if (addarray.length > 0) {
const last = addarray[addarray.length - 2];
let set = {},
update = {},
rowid = "",
repids = [];
_.each(repeating, (repid) => {
if (repid.split("_")[2] == `${levelclass}hprow`) rowid = repid;
});
_.each(addarray, (repname) => {
addRepeatingSection(rowid, "hpbylevel", repname, function (repid) {
repids.push(repid);
if (repid.includes(last) || addarray.length == 1) {
updateResults(levelclass, repids);
updateButtons(levelclass, repids);
}
update[`${repid} label`] = `Level ${_.last(repname.split("-"))}`;
if (repname == _.last(addarray)) {
setCharmancerText(update);
setAttrs(set, () => {
clearRepeatingSectionById(removearray, () => {
recalcHpByLevel(levelclass);
});
});
}
});
});
} else {
clearRepeatingSectionById(removearray, () => {
recalcHpByLevel(levelclass);
});
}
});
};
if (firstTime) {
addRepeatingSection("class1_by-levels", "row", "class1hprow", function () {
addRepeatingSection("class2_by-levels", "row", "class2hprow", function () {
addRepeatingSection("class3_by-levels", "row", "class3hprow", function () {
addRepeatingSection("class4_by-levels", "row", "class4hprow", function () {
updateByLevel(true, levelclass);
});
});
});
});
} else {
updateByLevel(false, levelclass);
}
};
const updateButtons = (levelclass, repids) => {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"].values;
const addlevel = leveldata[`${levelclass}_addlevel`] ? parseInt(leveldata[`${levelclass}_addlevel`]) : 0;
const hitdie = leveldata[`${levelclass}_hitdie`];
let average = getDieAvg(hitdie),
set = {},
bonus = hpBonus(levelclass),
hpbonus = bonus[0],
bonusSource = bonus[1];
const templateBonus = hpbonus > 0 ? `+${hpbonus}[${bonusSource}]` : "";
_.each(repids, (repid) => {
set[`roll_${repid}_rollhp`] = `@{wtype}&{template:mancerhproll} {{title=Roll for HP Level ${_.last(
repid.split(`${levelclass}-`)
)}}} {{r1=[[1${hitdie}${templateBonus}]]}}`;
set[`roll_${repid}_averagehp`] = `@{wtype}&{template:mancerhproll} {{title=Average for HP Level ${_.last(
repid.split(`${levelclass}-`)
)}}} {{a1=[[${average}${templateBonus}]]}}`;
if (addlevel && hitdie) {
let dice = [];
//Set the roll buttons
for (x = 1; x <= addlevel; x++) {
dice.push(`{{r${x}=[[1${hitdie}${templateBonus}]]}}`);
}
set[`roll_${levelclass}_rollhp`] = `@{wtype}&{template:mancerhproll} {{title=Roll for HP}} ` + dice.join();
//Set the average hp button
set[
`roll_${levelclass}_averagehp`
] = `@{wtype}&{template:mancerhproll} {{title=Average for HP}}{{a1=[[${average}${templateBonus}]]}}`;
}
});
setAttrs(set);
};
const hpBonus = (levelclass) => {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"].values;
let hpmod = {};
//HP Bonus from subclass hp mods like Draconic Bloodline for Sorcerers
if (SheetUtils.checkPath(mancerdata, '["lp-welcome"]["values"]["previous_repeating"]')) {
//Safeguarding Compendium data:
const prev_repeat = Compendium.parseObject(mancerdata["lp-welcome"].values["previous_repeating"], `E_P6L2KSF6`);
hpmod = prev_repeat["hpmod"];
}
const subclass = leveldata[`${levelclass}_subclass`];
let hpbonus = 0;
let bonusSource = [];
_.each(hpmod, (mod) => {
if (mod["levels"] === "base" && subclass && subclass.includes(mod["source"])) {
bonusSource.push(mod["source"]);
hpbonus += parseInt(mod["mod"]);
}
});
//Multiclassing into Sorcerer Draconic needs to update buttons
if (subclass && subclass.includes("Draconic Bloodline") && !bonusSource.includes("Draconic Bloodline")) {
bonusSource.push("Draconic Bloodline");
hpbonus += parseInt(1);
}
return [hpbonus, bonusSource];
};
const updateResults = (levelclass, repids, eventinfo) => {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"].values;
const hitdie = leveldata[`${levelclass}_hitdie`];
let average = getDieAvg(hitdie),
bonus = hpBonus(levelclass),
hpbonus = bonus[0],
set = {};
_.each(repids, (repid) => {
if (!leveldata[`${repid}_result`]) {
set[`${repid}_result`] = parseInt(average) + parseInt(hpbonus);
} else if (eventinfo && eventinfo.newValue.includes("Draconic Bloodline")) {
set[`${repid}_result`] = parseInt(leveldata[`${repid}_result`]) + parseInt(hpbonus);
} else if (eventinfo && eventinfo.previousValue.includes("Draconic Bloodline")) {
set[`${repid}_result`] = parseInt(leveldata[`${repid}_result`]) - 1;
} else {
false;
}
});
setAttrs(set);
};
const getDieAvg = (hitdie) => {
if (!hitdie) return 0;
const size = hitdie.slice(1);
let num = [],
sum = 0;
//Push an array of numbers
for (x = 1; x <= size; x++) {
num.push(x);
}
//Add up the nummbers above
for (i = 0; i < num.length; i++) {
sum += num[i];
}
return Math.ceil(sum / num.length);
};
//Roll & Average buttons inside the repeating hp section for each level
["class1", "class2", "class3", "class4"].forEach((levelclass) => {
on(`mancerroll:repeating_${levelclass}hprow`, (eventinfo) => {
const source = eventinfo.sourceAttribute,
flag = source.includes("rollhp") ? "roll" : "average";
let set = {};
set[`${eventinfo.sourceSection}_result`] = eventinfo.roll[0].result;
set[`${eventinfo.sourceSection}_rollflag`] = flag;
setAttrs(set, () => {
recalcHpByLevel();
});
});
});
//Roll & Average top level buttons for each class
["class1", "class2", "class3", "class4"].forEach((levelclass) => {
on(`mancerroll:${levelclass}_rollhp mancerroll:${levelclass}_averagehp`, (eventinfo) => {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"].values;
const repeating = mancerdata["lp-levels"].repeating || [];
const source = eventinfo.sourceAttribute,
flag = source.includes("rollhp") ? "roll" : "average";
let set = {},
sum = 0,
rows = "",
num = 0;
repeating.forEach((row) => {
if (row.includes(`${levelclass}-`) && source.includes("rollhp")) {
set[row + "_result"] = eventinfo.roll[`${num}`].result;
set[row + "_rollflag"] = flag;
num += 1;
} else if (row.includes(`${levelclass}-`) && source.includes("average")) {
set[row + "_result"] = eventinfo.roll[0].result;
set[row + "_rollflag"] = flag;
} else {
false;
}
});
//Add up the results for rollhp or multiply the average option by added levels
if (source.includes("rollhp")) {
_.each(eventinfo.roll, function (roll) {
sum += parseInt(roll.result);
});
} else {
sum = parseInt(eventinfo.roll[0].result) * leveldata[`${levelclass}_addlevel`];
}
// Set addhp to the total
set[`${levelclass}_addhp`] = sum;
//Set hp_flag to be roll or average so the CSS will highlight the appropriate button
set[`${levelclass}_hp_flag`] = flag;
setAttrs(set, function () {
recalcLpData();
});
});
});
on("clicked:multiclass_add clicked:multiclass_remove", function (eventinfo) {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"];
const levelclass = "class" + (parseInt(leveldata.values["multiclass"]) + 1);
const repeating = mancerdata["lp-levels"].repeating || [];
let set = {};
let update = {};
if (eventinfo.sourceAttribute === "multiclass_add") {
leveldata.values["multiclass"] < 3 ? (set["multiclass"] = parseInt(leveldata.values["multiclass"]) + 1) : false;
update["add_multiclass"] = `<button type="action" name="act_multiclass_add" disabled>+</button>`;
//set[`${levelclass}_addlevel`] = 1;
} else {
if (leveldata.values["multiclass"] > leveldata.values["multiclass_initial"]) {
const multiclass = parseInt(leveldata.values["multiclass"]);
let rows = "";
set["multiclass"] = multiclass - 1;
update["add_multiclass"] = `<button type="action" name="act_multiclass_add">+</button>`;
repeating.forEach((row) => {
if (row.includes(`${levelclass}-`)) {
set[row + "_result"] = "";
}
});
set[`${levelclass}_addhp`] = 0;
set[`${levelclass}_hp_flag`] = "";
} else {
false;
}
}
setAttrs(set, function () {
recalcLpData();
});
setCharmancerText(update);
});
on("mancerchange:class1_addhp mancerchange:class2_addhp mancerchange:class3_addhp mancerchange:class4_addhp", function (eventinfo) {
if (eventinfo.sourceType === "player") {
const mancerdata = getCharmancerData(),
levelclass = eventinfo.sourceAttribute.slice(0, 6);
const repeating = mancerdata["lp-levels"].repeating || [];
let set = {};
repeating.forEach((row) => {
if (row.includes(`${levelclass}-`)) {
set[row + "_result"] = "";
}
});
set[`${levelclass}_hp_flag`] = "";
setAttrs(set);
}
});
on("mancerchange:multiclass", function () {
const mancerdata = getCharmancerData();
const leveldata = mancerdata["lp-levels"];
const tot = isNaN(parseInt(leveldata.values["multiclass"])) ? 1 : parseInt(leveldata.values["multiclass"]) + 1;
let update = {};
let presets = [];
for (var x = 1; x <= tot - 1; x++) {
if (leveldata.values["class" + x]) presets.push(_.last(leveldata.values["class" + x].split(":")));
}
setCharmancerOptions("class" + tot, "Category:Classes", { disable: presets, category: "Classes" });
update["multiclass_message"] = `<h3>Multiclass (${tot}/4)</h3>`;
setCharmancerText(update);
});
on(
"clicked:class1 clicked:class2 clicked:class3 clicked:class4 clicked:class1_subclass clicked:class2_subclass clicked:class3_subclass clicked:class4_subclass",
function (eventinfo) {
const mancerdata = getCharmancerData();
const source = eventinfo.sourceAttribute;
const page = mancerdata["lp-levels"].values[`${source}`];
changeCompendiumPage("levels-info", page);
getCompendiumPage(page, function (p) {});
}
);
var getLevelingData = function (mancerdata) {
mancerdata = mancerdata || getCharmancerData();
var leveling = [];
if (!mancerdata["lp-levels"]) return leveling;
var multiclass = mancerdata["lp-levels"].values.multiclass ? parseInt(mancerdata["lp-levels"].values.multiclass) : 0;
for (var x = 1; x <= multiclass + 1; x++) {
if (
mancerdata["lp-levels"].data["class" + x] &&
mancerdata["lp-levels"].values["class" + x + "_addlevel"] &&
mancerdata["lp-levels"].values["class" + x + "_addlevel"] != "0"
) {
var thisclass = {};
var maxspell = 0;
thisclass.class = mancerdata["lp-levels"].data["class" + x];
thisclass.subclass = mancerdata["lp-levels"].data["class" + x + "_subclass"] || {};
thisclass.classname = _.last(mancerdata["lp-levels"].values["class" + x].split(":"));
thisclass.subclassname = mancerdata["lp-levels"].values["class" + x + "_subclass"]
? _.last(mancerdata["lp-levels"].values["class" + x + "_subclass"].split(":"))
: "";
thisclass.currentlevel = mancerdata["lp-levels"].values["class" + x + "_currentlevel"]
? parseInt(mancerdata["lp-levels"].values["class" + x + "_currentlevel"])
: 0;
thisclass.addlevel = parseInt(mancerdata["lp-levels"].values["class" + x + "_addlevel"]);
thisclass.addhp = mancerdata["lp-levels"].values["class" + x + "_addhp"]
? parseInt(mancerdata["lp-levels"].values["class" + x + "_addhp"])
: 0;
thisclass.classnumber = x;
if (thisclass.subclass["Spellcasting Ability"]) {
thisclass.spellcasting = thisclass.subclass["Spellcasting Ability"];
} else if (thisclass.class["Spellcasting Ability"]) {
thisclass.spellcasting = thisclass.class["Spellcasting Ability"];
}
_.each(thisclass.class.blobs, function (blob, blobName) {
if (blob["Spell Slots"] && parseInt(blob.Level) <= thisclass.currentlevel + thisclass.addlevel) {
//Safeguarding Compendium data:
_.each(Compendium.parseObject(blob["Spell Slots"], `E_KMNNY7KK:Blob:${blobName}:Spell Slots`), function (slots, name) {
if (parseInt(_.last(name.split(" "))) > maxspell) maxspell = parseInt(_.last(name.split(" ")));
});
}
});
thisclass.maxspell = maxspell;
leveling.push(thisclass);
}
}
return leveling;
};
var getLpRaceData = function (mancerdata, leveldata) {
mancerdata = mancerdata || getCharmancerData();
leveldata = leveldata || getLevelingData(mancerdata);
let results = {};
let currentlevel = 0;
let addlevel = 0;
_.each(leveldata, function (level) {
addlevel += level.addlevel;
currentlevel += level.currentlevel;
});
const characterlevel = SheetUtils.checkPath(mancerdata, '["lp-levels"]["values"]["characterlevel"]')
? Math.max(parseInt(mancerdata["lp-levels"]["values"]["characterlevel"]) - addlevel, 0)
: 0;
currentlevel = currentlevel === 0 ? 1 : currentlevel;
currentlevel = characterlevel > currentlevel ? characterlevel : currentlevel;
_.each(["race", "background"], function (section) {
results[section] = { addlevel: addlevel, currentlevel: currentlevel };
results[section][section] = mancerdata["lp-welcome"].data["lp-" + section] || {};
results[section]["sub" + section] = mancerdata["lp-welcome"].data["lp-sub" + section] || {};
results[section].type = section;
if (SheetUtils.checkPath(mancerdata, `["lp-welcome"]["values"]["lp-${section}"]`)) {
results[section].name = _.last(mancerdata["lp-welcome"].values["lp-" + section].split(":")) || "";
}
if (SheetUtils.checkPath(mancerdata, `["lp-welcome"]["values"]["lp-sub${section}"]`)) {
results[section].name += mancerdata["lp-welcome"].values["lp-sub" + section]
? " - " + _.last(mancerdata["lp-welcome"].values["lp-sub" + section].split(":"))
: "";
}
});
return results;
};
var getLpBlobs = function (data, include_asi, verbose) {
const type = data.type || "class";
var asi = type == "class" ? data[type]["data-Ability Score Levels"] : [];
var allblobs = {};
allblobs[type] = {};
allblobs["sub" + type] = {};
_.times(data.addlevel, function (x) {
var thislevel = data.currentlevel + x + 1;
allblobs[type] = _.extend(allblobs[type], filterBlobs(data[type].blobs, { Level: "" + thislevel, multiclass: true }));
allblobs["sub" + type] = _.extend(
allblobs["sub" + type],
filterBlobs(data["sub" + type].blobs, { Level: "" + thislevel, multiclass: true })
);
if (asi.includes("" + thislevel) && include_asi) {
allblobs[type]["Ability Score Increase - Level " + thislevel] = { Level: thislevel, parentSection: type };
data[type].blobs["Ability Score Increase - Level " + thislevel] = {
Level: thislevel,
Title: "Ability Score Increase - Level " + thislevel,
};
}
});
allblobs[type] = _.extend(allblobs[type], filterBlobs(data[type].blobs, { Level: "every", multiclass: true }));
allblobs["sub" + type] = _.extend(allblobs["sub" + type], filterBlobs(data["sub" + type].blobs, { Level: "every", multiclass: true }));
var remove = {};
remove[type] = [];
remove["sub" + type] = [];
//Figure out which blobs are just the same feature at a different level, add them to a list to remove (unless we're useing the verbose option)
if (!verbose) {
_.each(allblobs, function (blobs, section) {
_.each(blobs, function (blob, name) {
var basename = name.split("(")[0];
blob.Level = parseInt(blob.Level);
if (basename.trim().toLowerCase() == "spell slots") {
remove[section].push(name);
} else if (basename.trim().toLowerCase() != "proficiencies") {
_.each(allblobs, function (otherblobs, othersection) {
_.each(otherblobs, function (otherblob, othername) {
if (name != othername && basename == othername.split("(")[0]) {
if (blob.Level > parseInt(otherblob.Level)) {
remove[section].push(othername);
} else {
remove[section].push(name);
}
}
});
});
}
});
});
}
//remove any blobs that are the same feature
_.each(remove, function (removeArray, section) {
_.each(_.uniq(removeArray), function (thisblob) {
delete allblobs[section][thisblob];
});
});
//here we build the final list of blobs that will be added
var filteredblobs = {};
filteredblobs[type] = [];
filteredblobs["sub" + type] = [];
_.each(allblobs, function (blobs, section) {
_.each(blobs, function (blob, name) {
var thisblob = {
Level: blob.Level,
name: name,
};
filteredblobs[section].push(thisblob);
});
});
_.each(filteredblobs, function (bloblist) {
bloblist = _.sortBy(bloblist, "Level");
});
filteredblobs.allblobs = _.extend(data[type].blobs, data["sub" + type].blobs);
return filteredblobs;
};
var getAllLpBlobs = function (mancerdata, include_asi) {
const pickBlobs = function (theseblobs, allblobs, key, section) {
blobs.names[key] = _.pluck(theseblobs, "name") || [];
if (mancerdata["lp-choices"]) {
_.each(mancerdata["lp-choices"].values, function (value, id) {
if ((value + "").split(":")[0] == "Blob" && id.split("_")[2].split("--")[0] == section) {
blobs.names[key].push(value.split(":")[1]);
}
});
}
_.each(blobs.names[key], function (blobname) {
_.each(filterBlobs(allblobs, { name: blobname, multiclass: true }), function (blob, blobname) {
blobs.all.push(blob);
blobs.sorted[key] = blobs.sorted[key] ? blobs.sorted[key] : [];
blobs.sorted[key].push(blob);
});
});
};
mancerdata = mancerdata || getCharmancerData();
const leveldata = getLevelingData(mancerdata);
const racedata = getLpRaceData(mancerdata, leveldata);
var blobs = { all: [], sorted: {}, names: {} };
_.each(leveldata, function (level) {
var name = "class" + level.classnumber;
var theseblobs = getLpBlobs(level, include_asi, true);
pickBlobs(theseblobs.class, level.class.blobs, name, "class-" + level.classnumber);
if (level.subclass) {
pickBlobs(theseblobs.subclass, level.subclass.blobs, name + "_subclass", "subclass-" + level.classnumber);
}
});
_.each(racedata, function (level) {
var name = level.type;
var theseblobs = getLpBlobs(level, include_asi, true);
pickBlobs(theseblobs[name], level[name].blobs, name, name);
if (level["sub" + name]) {
pickBlobs(theseblobs["sub" + name], level["sub" + name].blobs, "sub" + name, "sub" + name);
}
});
return blobs;
};
on("page:lp-choices", function (eventinfo) {
const resortBlobs = function (blobs, section) {
_.each(blobs[section], function (x) {
x.parentSection = section;
});
_.each(blobs["sub" + section], function (x) {
x.parentSection = "sub" + section;
});
let filteredblobs = _.sortBy(blobs[section].concat(blobs["sub" + section]), "Level");
let resorted = [];
_.each(filteredblobs, function (blob) {
if (blob.name.split(" ")[0] == "Proficiencies") {
resorted.unshift(blob);
} else {
resorted.push(blob);
}
});
return resorted;
};
const mancerdata = getCharmancerData();
const leveling = getLevelingData(mancerdata);
const racedata = getLpRaceData(mancerdata, leveling);
_.each(racedata, function (data, section) {
const blobs = getLpBlobs(data, true);
const relevant = resortBlobs(blobs, section);
if (relevant.length > 0) {
addRepeatingSection("choices", "row", function (thisrow) {
let update = {};
update[thisrow] = "<h2>" + removeExpansionInfo(data.name) + "<span>";
update[thisrow] +=
data.addlevel > 1
? " Levels " + (data.currentlevel + 1) + " - " + (data.currentlevel + data.addlevel)
: " Level " + (data.currentlevel + 1);
update[thisrow] += "</span></h2>";
setCharmancerText(update);
_.each(relevant, function (blob, y) {
handleBlobs(blobs.allblobs, {
filters: { multiclass: true },
slide: "lp-choices",
section: blob.parentSection + "--" + blob.Level,
element: thisrow,
thisblob: blob.name,
maxlevel: data.currentlevel + data.addlevel,
parent: section,
});
if (y === relevant.length - 1) {
recalcLpData();
}
});
});
}
});
_.each(leveling, function (data) {
var x = data.classnumber;
var blobs = getLpBlobs(data, true);
addRepeatingSection("choices", "row", function (classrow) {
var classname = data.subclassname
? removeExpansionInfo(data.subclassname) + " " + removeExpansionInfo(data.classname)
: removeExpansionInfo(data.classname);
var update = {};
update[classrow] = "<h2>" + classname + "<span>";
update[classrow] +=
data.addlevel > 1
? " Levels " + (data.currentlevel + 1) + " - " + (data.currentlevel + data.addlevel)
: " Level " + (data.currentlevel + 1);
update[classrow] += "</span></h2>";
setCharmancerText(update);
resorted = resortBlobs(blobs, "class");
_.each(resorted, function (blob, y) {
handleBlobs(blobs.allblobs, {
filters: { multiclass: true },
slide: "lp-choices",
section: blob.parentSection + "-" + x + "--" + blob.Level,
element: classrow,
thisblob: blob.name,
maxlevel: data.currentlevel + data.addlevel,
parent: "class" + data.classnumber + (blob.parentSection === "class" ? "" : "_subclass"),
});
if (y === resorted.length - 1) {
recalcLpData();
}
});
});
});
if (leveling[0] && leveling[0].classname) {
changeCompendiumPage("class-info", "Classes:" + leveling[0].classname);
}
});
const restorePreviousASI = function (prevData, callback) {
console.log("Restoring previous state...");
if (!SheetUtils.checkPath(prevData, "['lp-asi']['values']")) {
callback();
return;
}
setAttrs(prevData["lp-asi"]["values"], callback);
};
const readPreviousFeats = function (data) {
if (!SheetUtils.checkPath(data, "['lp-welcome']['values']['previous_repeating']")) return;
//Safeguarding Compendium data:
const previousData = Compendium.parseObject(data["lp-welcome"]["values"]["previous_repeating"], `E_MDS8J6GQ`);
if (!SheetUtils.checkPath(previousData, "['traits']")) return;
let feats = previousData["traits"]
.filter((trait) => {
return trait.source === "Feat";
})
.map((trait) => {
return `${trait.name}`;
});
return feats;
};
on("page:lp-asi", function (eventinfo) {
setCharmancerText({ "spinner-toggle": "Loading..." });
const previousState = getCharmancerData();
deleteCharmancerData(["lp-asi"], () => {
var mancerdata = getCharmancerData();
var leveling = getLevelingData(mancerdata);
var asis = [];
var update = {};
var multiclass = mancerdata["lp-levels"].values.multiclass ? parseInt(mancerdata["lp-levels"].values.multiclass) : 0;
for (var x = 1; x <= multiclass + 1; x++) {
if (mancerdata["lp-levels"].data["class" + x]) {
var thisclass = {};
thisclass.class = mancerdata["lp-levels"].data["class" + x];
thisclass.classname = _.last(mancerdata["lp-levels"].values["class" + x].split(":"));
thisclass.currentlevel = parseInt(mancerdata["lp-levels"].values["class" + x + "_currentlevel"]);
thisclass.addlevel = parseInt(mancerdata["lp-levels"].values["class" + x + "_addlevel"]);
leveling["class" + x] = thisclass;
}
}
_.each(leveling, function (data) {
var asi = data.class["data-Ability Score Levels"];
_.times(data.addlevel, function (x) {
var thislevel = data.currentlevel + x + 1;
if (asi.includes("" + thislevel)) {
asis.push(data.classname + " Level " + thislevel);
}
});
});
let counter = 0;
_.each(asis, function (asi) {
addRepeatingSection("choices", "asi-row", function (rowid) {
update[rowid + " .title"] = "Ability Score Increase: " + removeExpansionInfo(asi);
setAttrs({ [`repeating_${SheetUtils.getUID(rowid)}_asi-row_source`]: asi });
getCompendiumQuery("Category:Feats", (data) => {
const names = data
.map((entry) => {
return entry.name;
})
.sort();
setCharmancerOptions(rowid + "_feat", names, { disable: readPreviousFeats(mancerdata), category: "Feats" });
updateAbilityLock(rowid);
if (asi == _.last(asis)) {
setCharmancerText(update);
}
counter++;
if (counter === asis.length)
restorePreviousASI(previousState, () => {
setCharmancerText({ "spinner-toggle": "" });
});
});
});
});
});
});
var getKnownLpSpells = function (mancerdata) {
mancerdata = mancerdata || getCharmancerData();
var leveldata = getLevelingData(mancerdata);
//Safeguarding Compendium data:
var previousspells = mancerdata["lp-welcome"].values["spellinfo"]
? Compendium.parseObject(mancerdata["lp-welcome"].values["spellinfo"], `E_HP6AMXOF`)
: {};
var spelldata = {};
var totallevel = 0;
var prevtotallevel = 0;
_.each(previousspells, function (spell) {
spelldata[spell.spellname] = { level: spell.spelllevel };
if (spell.spellclass) spelldata[spell.spellname].spellclass = spell.spellclass;
if (spell.spellclass && spell.spellclass.toLowerCase() == "racial") spelldata[spell.spellname].known = "Racial";
if (spell.spellsource) spelldata[spell.spellname].known = spell.spellsource;
});
_.each(leveldata, function (level) {
totallevel += level.currentlevel + level.addlevel;
prevtotallevel += level.currentlevel;
});
//Gather known spells
_.each(mancerdata, function (page, pagename) {
if (pagename.split("-")[0] == "lp") {
let choices = [];
let choicepage = ["lp-welcome", "lp-levels"].includes(pagename)
? mancerdata["lp-choices"]
? mancerdata["lp-choices"].values
: []
: page.values;
_.each(choicepage, function (val) {
if ((val + "").split(":")[0] === "Blob") choices.push(val.split(":")[1]);
});
_.each(page.data, function (data, dataname) {
var thislevel = 0;
var prevlevel = 0;
var thisclass = {};
if (dataname.substring(0, 5) == "class") {
thisclass = _.findWhere(leveldata, { classnumber: parseInt(dataname[5]) });
thislevel = thisclass ? thisclass.currentlevel + thisclass.addlevel : 0;
prevlevel = 0;
} else {
thislevel = totallevel;
prevlevel = prevtotallevel;
}
_.each(data.blobs, function (blob, blobname) {
if (
(blob.Multiclass != "no" && (parseInt(blob.Level) <= thislevel || blob.Level == "all") && !blob.Group) ||
choices.includes(blobname)
) {
if (blob.Spells) {
//Safeguarding Compendium data:
var blobspells = Compendium.parseObject(blob.Spells, `E_239E3WTI:Blob:${blobname}:Spells`);
_.each(blobspells, function (blobspell) {
if (blobspell.Known) {
_.each(blobspell.Known, function (spell) {
spelldata[spell] = spelldata[spell] || { level: blobspell.Level };
spelldata[spell].known = blobspell.Source || data.Category.substring(0, data.Category.length - 2);
spelldata[spell].spellclass = thisclass.classname;
if (dataname.substring(0, 5) != "class") {
spelldata[spell].known = data.Category.substring(0, data.Category.length - 1);
spelldata[spell].spellclass =
data.Category == "Races" ? "Racial" : data.Category.substring(0, data.Category.length - 1);
}
if (blobspell.Ability) {
spelldata[spell].ability = blobspell.Ability;
} else if (thisclass.class && thisclass.class["Spellcasting Ability"]) {
spelldata[spell].ability = thisclass.class["Spellcasting Ability"];
} else if (thisclass.subclass && thisclass.subclass["Spellcasting Ability"]) {
spelldata[spell].ability = thisclass.subclass["Spellcasting Ability"];
}
});
}
});
}
}
});
});
//Gater spells from class features
_.each(page.values, function (value, name) {
if (name.substr(-18) === "_utilityrow_spells") {
//Safeguarding Compendium data:
spelldata = _.extend(spelldata, Compendium.parseObject(value, `E_LNIP09P9:Page:${name}`));
}
});
}
});
/**/
return spelldata;
};
const getKnownLPSpellList = () => {
const list = [];
const known = getKnownLpSpells();
Object.keys(known).forEach((spell) => {
const newSpell = { name: spell, ...known[spell] };
if (newSpell.level && String(newSpell.level).toLowerCase() === "cantrip") newSpell.level = 0;
list.push(newSpell);
});
return list;
};
const isExistingSpell = (name, level) => {
const known = getKnownLPSpellList().filter(
(spell) => spell.hasOwnProperty("name") && spell.name == name && spell.hasOwnProperty("level") && spell.level == level
);
return known.length > 0;
};
var getNewSpells = function (mancerdata, leveldata, blobs) {
mancerdata = mancerdata || getCharmancerData();
leveldata = leveldata || getCharmancerData();
blobs = blobs || getAllLpBlobs(mancerdata, false);
var result = false;
_.each(leveldata, function (level) {
var prevlevel = level.currentlevel;
var thislevel = level.addlevel + prevlevel;
var thisclass = { class: level.classname, number: level.classnumber, newlevel: thislevel, additional: [] };
var prevknown = 0;
var currentknown = 0;
var prevcantrips = 0;
var currentcantrips = 0;
var spelladd = 0;
thisclass.newspells = 0;
_.each(["class", "subclass"], function (section) {
if (level[section]) {
_.each(level[section].blobs, function (blob) {
if (blob.Level == thislevel) {
if (blob["Spells Known"]) {
currentknown = parseInt(blob["Spells Known"]);
if (level.class["data-Spell Replace"]) result = true;
}
if (blob.Cantrips) {
thisclass.cantrips = true;
currentcantrips = parseInt(blob.Cantrips);
}
if (blob["Spells Prepared"]) {
result = true;
}
}
if (blob.Level == prevlevel || (prevlevel === 0 && blob.Level == 1)) {
if (blob["Spells Known"]) {
prevknown = prevlevel === 0 ? 0 : parseInt(blob["Spells Known"]);
if (prevlevel === 0) thisclass.newspells = parseInt(blob["Spells Known"]);
}
if (blob.Cantrips) {
thisclass.cantrips = true;
prevcantrips = prevlevel === 0 ? 0 : parseInt(blob.Cantrips);
}
}
if (level[section]["data-Spell Add"]) {
spelladd = parseInt(level[section]["data-Spell Add"]);
}
});
}
});
if (spelladd) {
thisclass.newspells += prevlevel === 0 ? spelladd * (thislevel - prevlevel - 1) : spelladd * (thislevel - prevlevel);
} else {
thisclass.newspells = currentknown - prevknown;
}
thisclass.newcantrips = currentcantrips - prevcantrips;
_.each(["", "_subclass"], function (section) {
if (blobs.sorted[`class${level.classnumber}${section}`]) {
_.each(blobs.sorted[`class${level.classnumber}${section}`], function (blob, blobName) {
if (blob.Spells) {
//Safeguarding Compendium data:
let spells = Compendium.parseArray(blob.Spells, `E_T37RNN4K:Blob:${blobName}:Spells`);
_.each(spells, function (spell) {
if (spell.Choose) {
result = true;
}
});
}
});
}
});
if (thisclass.newcantrips || thisclass.newspells) result = true;
});
return result;
};
const roughSizeOfObject = (object) => {
var objectList = [];
var stack = [object];
var bytes = 0;
while (stack.length) {
var value = stack.pop();
if (typeof value === "boolean") {
bytes += 4;
} else if (typeof value === "string") {
bytes += value.length * 2;
} else if (typeof value === "number") {
bytes += 8;
} else if (typeof value === "object" && objectList.indexOf(value) === -1) {
objectList.push(value);
for (var i in value) {
stack.push(value[i]);
}
}
}
return bytes;
};
var getKnownLPSpellListWithRemoved = () => {
const list = [];
const selectedSpells = getSelectedSpells();
const knownSpells = getKnownLPSpellList();
knownSpells.forEach((known) => {
const filtered = selectedSpells.filter((selected) => selected.name == known.name && selected.level == known.level);
if (filtered.length === 0) list.push({ removed: true, ...known });
else list.push({ removed: false, ...known });
});
return list;
};
const getSelectedSpells = (filters) => {
let spells = [];
const slideValues =
getCharmancerData()["lp-spells"] && getCharmancerData()["lp-spells"].values ? getCharmancerData()["lp-spells"].values : {};
Object.keys(slideValues)
.filter((value) => /spell_[0-9]/g.test(value))
.forEach((spell) => {
try {
const decoded = JSON.parse(decodeURI(slideValues[spell]));
if (typeof decoded === "object") spells.push(decoded);
} catch (e) {}
});
if (typeof filters === "object") {
Object.keys(filters).forEach((filterBy) => {
spells = spells.filter((spell) => spell.hasOwnProperty(filterBy) && spell[filterBy] == filters[filterBy]);
});
}
return spells;
};
const getSelectedSpellsByLevel = (filter) => {
const byLevel = {};
const selectedSpells = getSelectedSpells(filter);
selectedSpells.forEach((spell) => {
if (!byLevel[spell.level]) byLevel[spell.level] = [];
byLevel[spell.level].push(spell);
});
return byLevel;
};
const getSanitizedSpellName = (name, level) => {
return `spell_${level}_${name.toLowerCase().replace(/[^a-z]/g, "")}`;
};
/*
$20(".spell-item .spellcheck:checked").on("click", (event) => {
const id = `#${event.htmlAttributes.id}`;
const value = event.htmlAttributes.value;
console.log("checking spell");
setAttrs({ id: value }, () => {
updateSpellSummary();
});
});
$20(".spell-item .spellcheck:not(:checked)").on("click", (event) => {
const id = `#${event.htmlAttributes.id}`;
const value = "";
console.log("unchecking spell");
setAttrs({ id: value }, () => {
updateSpellSummary();
});
});
$20(".spell-item .mancer_info").on("click", (event) => {
const page = event.htmlAttributes.value;
console.log("loading spell page");
changeCompendiumPage("spells-info", page, "card_only");
});*/
var hasSentEvent = [];
var getSpellRow = (spellData, slideData, settings) => {
const locked = settings.locked || [];
const prepared = settings.prepared || false;
const newcantrips = settings.newcantrips || 0;
const newspells = settings.newspells || 0;
const replace = settings.replace || 0;
const dataWithDefaults = { existing: false, name: false, level: false, ability: false, classes: false, source: false, ...spellData };
const spellName = getSanitizedSpellName(dataWithDefaults.name, dataWithDefaults.level);
const isExisting =
dataWithDefaults.existing || (Array.isArray(settings.existinglist) && settings.existinglist.includes(dataWithDefaults.name));
const spellValue = encodeURI(
JSON.stringify({
name: dataWithDefaults.name,
level: dataWithDefaults.level,
existing: dataWithDefaults.existing,
ability: dataWithDefaults.ability,
classes: dataWithDefaults.classes,
source: dataWithDefaults.source,
})
);
let isHardlocked = false;
let isSoftlocked = false;
let isDisplayed = true;
if (dataWithDefaults.level == 0) {
if (newcantrips == 0 && replace == 0) isDisplayed = false;
} else {
if (newspells == 0 && replace == 0 && !prepared) isDisplayed = false;
}
if (!isDisplayed) return "";
if (locked.includes(dataWithDefaults.name)) isHardlocked = true;
if (dataWithDefaults.level == 0) {
if (isExisting) {
if (replace == 0) isHardlocked = true;
} else {
if (newcantrips == 0) isHardlocked = true;
}
} else {
if (isExisting) {
if (replace == 0 && !prepared) isHardlocked = true;
} else {
if (newspells == 0) isHardlocked = true;
}
}
if (isHardlocked) isSoftlocked = true;
if (!hasSentEvent.includes(spellName)) {
hasSentEvent.push(spellName);
on(`mancerchange:${spellName}`, (eventinfo) => {
if (!eventinfo.sourceType || eventinfo.sourceType !== "player") return;
updateSpellSummary();
});
on(`clicked:${spellName}_item_info`, function (eventinfo) {
changeCompendiumPage("spells-info", `Spells:${dataWithDefaults.name}`, "card_only");
});
}
if (isExisting) {
setAttrs({ [spellName]: spellValue }, { silent: true });
}
const checked = isExisting ? " checked" : "";
const cssClasses = ["spell-item", `spell-${spellName}`];
if (isExisting) cssClasses.push("spell-item--existing");
if (isExisting && prepared && dataWithDefaults.level > 0) cssClasses.push("spell-item--prepared");
return parseCharmancerHTML(
`
<div class="${cssClasses.join(" ")}" id="${spellName}">
<span class="_hardlock" style="display:none">${isHardlocked ? "locked" : ""}</span>
<span class="_lock" style="display:none">${isSoftlocked ? "locked" : ""}</span>
<label class="nostyle">
<input type="checkbox" class="spellcheck" name="comp_${spellName}" value="${spellValue}"${checked}>
<input type="input" name="comp_${spellName}" style="display:none">
<span class="name">${dataWithDefaults.name ? dataWithDefaults.name : ""}</span>
<span class="classes">${dataWithDefaults.classes ? dataWithDefaults.classes : ""}</span>
</label>
<button type="action" class="mancer_info" name="act_${spellName}_item_info">i</button>
</div>
`,
slideData
);
};
on("mancerchange:dynamicfield", (eventinfo) => {
console.log(eventinfo);
});
on("page:lp-spells", function (eventinfo) {
var ellapsed = {
init: Date.now(),
};
var mancerdata = getCharmancerData();
var queries = [];
var blobs = getAllLpBlobs(mancerdata, false);
var leveldata = getLevelingData(mancerdata);
var classes = [];
var knowncantrips = 0;
var knownspells = 0;
var newcantrips = 0;
var newspells = 0;
var replace = 0;
var totallevel = 0;
var spellmaxlevel = 0;
var prepared = true;
//Safeguarding Compendium data:
var prevspells = mancerdata["lp-welcome"].values["spellinfo"]
? Compendium.parseObject(mancerdata["lp-welcome"].values["spellinfo"], `E_T787PBZW`)
: {};
var spelldata = getKnownLpSpells(mancerdata);
var abilities = getAbilityTotals(mancerdata, blobs);
_.each(spelldata, function (spell, spellname) {
if (spell.level == "cantrip") {
knowncantrips++;
} else {
knownspells++;
}
});
recalcLpData(blobs);
//Gather spell data about each class
_.each(leveldata, function (level) {
var prevlevel = level.currentlevel;
var thislevel = level.addlevel + prevlevel;
var thisclass = { class: level.classname, number: level.classnumber, newlevel: thislevel, additional: [] };
var toSwap = level.class["data-Spell Replace"] ? parseInt(level.class["data-Spell Replace"]) : 0;
var prevknown = 0;
var currentknown = 0;
var prevcantrips = 0;
var currentcantrips = 0;
var spelladd = 0;
thisclass.list = [thisclass.class];
totallevel += thislevel;
thisclass.newspells = 0;
_.each(["class", "subclass"], function (section) {
if (level[section]) {
if (level[section]["data-Spell List"]) thisclass.list = [level[section]["data-Spell List"]];
if (level[section]["Spellcasting Ability"]) thisclass.ability = level[section]["Spellcasting Ability"];
_.each(level[section].blobs, function (blob, blobName) {
if (blob.Level == thislevel) {
if (blob["Spell Slots"]) {
//Safeguarding Compendium data:
var slots = Compendium.parseObject(blob["Spell Slots"], `E_953M5H9U:Blob:${blobName}:Spell Slots`);
thisclass.maxlevel = Compendium.parseInt(_.last(_.last(_.keys(slots).sort()).split(" ")));
spellmaxlevel = Math.max(thisclass.maxlevel, spellmaxlevel);
}
if (blob["Spells Known"]) {
currentknown = parseInt(blob["Spells Known"]);
}
if (blob.Cantrips) {
thisclass.cantrips = true;
currentcantrips = parseInt(blob.Cantrips);
}
if (blob["Spells Prepared"]) {
var splitted = blob["Spells Prepared"].split("+").map((x) => {
return x.trim();
});
thisclass.prepared = 0;
_.each(splitted, function (term, x) {
if (isNaN(parseInt(term))) {
thisclass.prepared += abilities[term.toLowerCase() + "_mod"] ? abilities[term.toLowerCase() + "_mod"] : 0;
} else {
thisclass.prepared += parseInt(term);
}
});
thisclass.prepared = Math.max(thisclass.prepared, 1);
}
}
if (blob.Level == prevlevel || (prevlevel === 0 && blob.Level == 1)) {
if (blob["Spells Known"]) {
prevknown = prevlevel === 0 ? 0 : parseInt(blob["Spells Known"]);
if (prevlevel === 0) thisclass.newspells = parseInt(blob["Spells Known"]);
}
if (blob.Cantrips) {
thisclass.cantrips = true;
prevcantrips = prevlevel === 0 ? 0 : parseInt(blob.Cantrips);
if (knowncantrips < prevcantrips) prevcantrips = knowncantrips;
}
}
if (blob.Level == "every" || blob.Level == thislevel) {
if (blob["Additional Spell List"]) thisclass.list.push(blob["Additional Spell List"]);
}
if (level[section]["data-Spell Add"]) {
spelladd = parseInt(level[section]["data-Spell Add"]);
}
});
}
});
if (spelladd) {
thisclass.newspells += prevlevel === 0 ? spelladd * (thislevel - prevlevel - 1) : spelladd * (thislevel - prevlevel);
} else {
thisclass.newspells = currentknown - prevknown;
}
if (thisclass.prepared) thisclass.newspells = thisclass.prepared;
thisclass.newcantrips = currentcantrips - prevcantrips;
thisclass.replace = toSwap * (thislevel - prevlevel);
_.each(["", "_subclass"], function (section) {
if (blobs.sorted[`class${level.classnumber}${section}`]) {
_.each(blobs.sorted[`class${level.classnumber}${section}`], function (blob, blobName) {
if (blob.Spells) {
//Safeguarding Compendium data:
let spells = Compendium.parseObject(blob.Spells, `E_YRF27209:Blob:${blobName}:Spells`);
_.each(spells, function (spell) {
if (spell.Choose && spell.Level === "0" && thislevel > 1) {
thisclass.newcantrips += parseInt(spell.Choose);
}
});
}
});
}
});
if (thisclass.newcantrips || thisclass.newspells || toSwap > 0) classes.push(thisclass);
});
if (classes.length > 0) {
var existinglist = _.pluck(prevspells, "spellname");
var knownlist = _.keys(spelldata);
//Look through all the data to see if there's any expanded lists
_.each(mancerdata, function (page, pagename) {
if (pagename.split("-")[0] == "lp") {
_.each(page.data, function (data, dataname) {
var thislevel = 0;
if (dataname.substring(0, 5) == "class") {
var thisclass = _.findWhere(leveldata, { classnumber: parseInt(dataname[5]) });
thislevel = thisclass ? thisclass.currentlevel + thisclass.addlevel : 0;
} else {
thislevel = totallevel;
}
_.each(data.blobs, function (blob, blobname) {
if ((parseInt(blob.Level) <= thislevel || blob.Level == "every") && blob.Multiclass != "no") {
if (blob.Spells) {
//Safeguarding Compendium data:
const isArray = blob.Spells[0] === "[";
var blobspells = isArray
? Compendium.parseArray(blob.Spells, `E_HNZJJUEH:Blob:${blobname}:Spells`)
: Compendium.parseObject(blob.Spells, `E_HNZJJUEH:Blob:${blobname}:Spells`);
_.each(blobspells, function (thisspell) {
if (parseInt(thisspell.Level) <= spellmaxlevel) {
if (thisspell["Expanded List"]) {
knownlist = knownlist.concat(thisspell["Expanded List"]);
}
}
});
}
}
});
});
}
});
if (knownlist) {
queries.push("Category:Spells Name:" + knownlist.join("|"));
}
_.each(classes, function (thisclass) {
if (thisclass.maxlevel || thisclass.newcantrips) {
newcantrips += thisclass.newcantrips || 0;
newspells += thisclass.newspells;
replace += thisclass.replace;
if (!thisclass.prepared) prepared = false;
thisquery = "Category:Spells Classes:*" + _.uniq(thisclass.list).join("|*") + " Level:";
if (thisclass.cantrips) thisquery += "0|";
for (var x = 1; x <= thisclass.maxlevel; x++) {
thisquery += x;
if (x != thisclass.maxlevel) thisquery += "|";
}
_.each(thisclass.additional, function (lvl) {
thisquery += "|" + lvl;
});
queries.push(thisquery);
}
});
var toSet = {};
toSet.knowncantrips = knowncantrips;
toSet.knownspells = knownspells;
toSet.newcantrips = newcantrips;
toSet.newspells = newspells;
toSet.replace = replace;
setAttrs(toSet);
var update = {};
const levelToStringLookup = [
"Cantrips",
"1st-level",
"2st-level",
"3rd-level",
"4th-level",
"5th-level",
"6th-level",
"7th-level",
"8th-level",
"9th-level",
];
update["spells-summary"] = "";
if (newcantrips != 0 || knowncantrips != 0)
update["spells-summary"] = '<div class="level_0"><div class="title">Cantrips: </div><div class="list"></div></div>';
_.times(spellmaxlevel, function (x) {
update["spells-summary"] +=
'<div class="level_' + (x + 1) + '"><div class="title">' + levelToStringLookup[x + 1] + ': </div><div class="list"></div></div>';
});
if (classes.length > 1) {
update["cantripinfo"] = "";
_.each(classes, function (thisclass) {
if (thisclass.newspells) {
update["cantripinfo"] += "<p>You can add " + thisclass.newcantrips + " " + thisclass.class + " cantrips.</p>";
}
});
}
setCharmancerText(update);
queries = queries.map((query) => {
return removeExpansionInfo(query);
});
let spellSettings = {};
getCompendiumQuery(queries, (data) => {
ellapsed.size = roughSizeOfObject(data);
ellapsed.compendium = Date.now();
data = removeDuplicatedPageData(data);
let byLevel = {};
_.each(data, (spell) => {
let classes = [];
if (spell.data.Classes) {
classes = spell.data.Classes;
//Ravinca introduced Spells without Classes
} else {
["class1", "class2", "class3", "class4"].forEach((levelclass) => {
//Get the class name out of values & confirm its a spellcaster using data Spellcasting Ability
const name = mancerdata["lp-levels"].values[`${levelclass}`]
? mancerdata["lp-levels"].values[`${levelclass}`].split("Classes:")[1]
: false;
const ability =
//Check if the class has a Spellcasting Ability attribute
mancerdata["lp-levels"].data[`${levelclass}`] && mancerdata["lp-levels"].data[`${levelclass}`]["Spellcasting Ability"]
? true
: //Check if the subclass has a Spellcasting Ability attribute
mancerdata["lp-levels"].data[`${levelclass}_subclass`] &&
mancerdata["lp-levels"].data[`${levelclass}_subclass`]["Spellcasting Ability"]
? true
: //Set to false if the class is not a spellcaster
false;
if (name && ability) {
//Spell casters have thier class name pushed to the array above
classes.push(name);
}
});
if (classes.length > 0) {
classes = classes.join(", ");
}
}
/* This will set the information for populating the spell list */
if (classes.length > 0) {
byLevel[spell.data.Level] = byLevel[spell.data.Level] || [];
byLevel[spell.data.Level].push({
name: spell.name,
classes: classes.split(",").map((x) => x.trim()),
level: parseInt(spell.data.Level),
});
}
});
spellSettings = {
newspells: newspells,
newcantrips: newcantrips,
replace: replace,
prepared: prepared,
classes: classes,
spelldata: spelldata,
existinglist: existinglist,
};
addSpellSections(byLevel, spellSettings);
ellapsed.repeating = Date.now();
console.log(`Data init: ${ellapsed.init}`);
console.log(`Compendium load: ${ellapsed.compendium - ellapsed.init}`);
console.log(`Populating repeat: ${ellapsed.repeating - ellapsed.init}`);
console.log(`Data size: ${ellapsed.size}`);
});
}
});
const addSpellSections = function (byLevel, settings) {
settings = settings || {};
let newspells = settings.newspells || 0;
let newcantrips = settings.newcantrips || 0;
let replace = settings.replace || 0;
let prepared = settings.prepared || false;
let classes = settings.classes || [];
let spelldata = settings.spelldata || [];
let existinglist = settings.existinglist || [];
let spellnumber = 0;
const slideData = getCharmancerData()["lp-spells"].values ? getCharmancerData()["lp-spells"].values : {};
//D&D 5e Lvl+ Mancer: Spells tab "Level undefined" (UC605)
//When invoking Lvl+ from an incomplete manually levelled character, an undefined lv is generated due to missing information.
//This code prevents an incomplete list from being displayed.
//By Miguel
if ("undefined" in byLevel) {
delete byLevel.undefined;
}
_.each(byLevel, function (level) {
spellnumber += level.length;
});
byLevel["0"] = byLevel["0"] || [];
let loadedData = { levels: [], spells: [] };
let parsedHTML = ``; //<div class="row">
_.each(byLevel, function (spells, x) {
let levelHTML = "";
_.each(spells, function (spell) {
const spellclasses = [];
_.each(classes, function (thisclass, y) {
if (spell.classes.includes(thisclass.list) && spell.level <= thisclass.maxlevel) {
spellclasses.push({ class: thisclass.class, ability: thisclass.ability });
}
});
if (!loadedData.levels.includes(x)) loadedData.levels.push(x);
loadedData.spells.push({ attribute: getSanitizedSpellName(spell.name, spell.level), name: spell.name, level: spell.level });
levelHTML += getSpellRow(
{
name: spell.name,
level: x,
class: Array.isArray(spellclasses) && spellclasses[0] ? spellclasses[0].class : classes[0].class,
ability: Array.isArray(spellclasses) && spellclasses[0] ? spellclasses[0].ability : classes[0].ability,
existing: isExistingSpell(spell.name, x),
},
slideData,
settings
);
});
let spelltextsummary = "";
let replacetextsummary = "";
if (x == "0") {
if (newspells > 0) {
if (classes.length > 1) {
spelltextsummary = "";
_.each(classes, function (thisclass) {
if (thisclass.newspells) {
spelltextsummary += "<p>You can add " + thisclass.newspells + " " + thisclass.class + " spells.</p>";
}
});
} else {
spelltextsummary = "<p>You can add " + newspells + " new spells.</p>";
}
}
if (replace > 0) {
if (classes.length > 1) {
replacetextsummary = "";
_.each(classes, function (thisclass) {
if (thisclass.replace) {
replacetextsummary += "<p>You can replace " + thisclass.replace + " " + thisclass.class + " spells.</p>";
}
});
} else {
replacetextsummary = "<p>You can replace " + replace + " spells.</p>";
}
}
} else {
//Do something
}
const sectionHeader = x == 0 ? "Cantrips" : `Level ${x}`;
parsedHTML += `
<div class="spell-holder spell-holder-${x}">
<label class="spellheader">
<span class="title">${sectionHeader}<span class="choice">r</span></span>
<span class="spellholder__counter"></span>
</label>
<input type="checkbox" value="1" class="spellholder__toggle__checkbox" style="opacity: 0;">
<span class="spellholder__toggle__icon"></span>
<span class="controller" style="display: none"></span>
<div class="container">${levelHTML}</div>
<div class="spellinfo">
<div class="spellstext">
<div class="summary"><p>${spelltextsummary}</p></div>
<div class="levels"></div>
<div class="total"></div>
</div>
<div class="replacetext">
<div class="summary">${replacetextsummary}</div>
<div class="replace-info choice" data-i18n="charmancer-removed-spells-marked-pt1">Removed spells will be marked with a <span>r</span> symbol so you can find it easily if you change your mind.</div>
<div class="total"></div>
</div>
</div>
</div>
`;
});
setAttrs({ spell_loaded_data: JSON.stringify(loadedData) }, () => {
setCharmancerText({ pagelock: "unlock", spelllist: parsedHTML });
updateSpellSummary("all", undefined, settings.page);
});
};
const updateSpellSummary = function () {
const mancerdata = getCharmancerData();
const spellpage = mancerdata["lp-spells"];
const selectecSpells = getSelectedSpells();
const knownSpellsWithRemoved = getKnownLPSpellListWithRemoved();
const allowedCantrips = spellpage.values.newcantrips || 0;
const allowedSpells = spellpage.values.newspells || 0;
const allowedreplace = spellpage.values.replace || 0;
const newSpells = selectecSpells.filter((spell) => !spell.existing && spell.level && parseInt(spell.level) > 0);
const newCantrips = selectecSpells.filter((spell) => !spell.existing && spell.level && parseInt(spell.level) === 0);
const replacedSpells = knownSpellsWithRemoved.filter((spell) => spell.removed && parseInt(spell.level) > 0);
const spellsPerLevel = getSelectedSpellsByLevel();
const cantripContainerSelector = "spell-holder-0";
const levelToStringLookup = [
"Cantrips",
"1st-level",
"2st-level",
"3rd-level",
"4th-level",
"5th-level",
"6th-level",
"7th-level",
"8th-level",
"9th-level",
];
const update = {};
//first, list spells you already know
knownSpellsWithRemoved.forEach((spell) => {
const selector = "spells-summary .level_" + spell.level + " .list";
update[selector] = update[selector] || "";
update[selector] += spell.removed ? '<span class="removed">' : "<span>";
update[selector] += spell.name + "</span>";
});
//then, list new spells you've checked
selectecSpells.forEach((spell) => {
if (!spell.existing) {
const selector = "spells-summary .level_" + spell.level + " .list";
update[selector] = update[selector] || "";
update[selector] += '<span class="new">' + spell.name + "</span>";
}
});
lockedCantrips = newCantrips.length >= allowedCantrips ? "locked" : "";
lockedSpells = newSpells.length >= allowedSpells + replacedSpells.length ? "locked" : "";
update["locked--cantrips"] = lockedCantrips;
update["locked--spells"] = lockedSpells;
update[`${cantripContainerSelector} .spellstext .levels`] = "";
for (let level = 0; level <= 9; level++) {
const currentLevelContainerSelector = `spell-holder-${level}`;
const counterSelector = `${currentLevelContainerSelector} label .spellholder__counter`;
const existingSpellsAtLevel = knownSpellsWithRemoved.filter((spell) => spell.level === level).length;
update[counterSelector] = "";
if (spellsPerLevel[level] && spellsPerLevel[level].length > 0) {
if (level === 0) {
update[counterSelector] = `${spellsPerLevel[level].length}/${allowedCantrips + existingSpellsAtLevel}`;
} else {
const newSpells = spellsPerLevel[level].filter((spell) => !spell.existing);
if (newSpells.length > 0) {
update[counterSelector] = `+${newSpells.length}`;
update[
`${cantripContainerSelector} .spellstext .levels`
] += `<p><b>${levelToStringLookup[level]}:</b> ${newSpells.length} spells added.</p>`;
}
}
}
}
setCharmancerText(update);
};
const __updateSpellSummary = function (sourcelevel, data, thispage) {
var mancerdata = data || getCharmancerData();
var spellpage = thispage ? mancerdata[thispage] : mancerdata["lp-spells"];
const cantripsection = "spell-holder-0";
const selectecSpells = getSelectedSpells();
const removedSpells = getKnownLPSpellListWithRemoved();
const loadedSpellData = spellpage.values.spell_loaded_data ? JSON.parse(spellpage.values.spell_loaded_data) : { levels: [], spells: [] };
const loadedSpellList = loadedSpellData.spells;
const levels = loadedSpellData.levels;
const update = {};
const sections = [];
const perlevel = {};
let allowed = 0;
let allowedreplace = 0;
//first, list spells you already know
removedSpells.forEach((spell) => {
const selector = "spells-summary .level_" + spell.level + " .list";
update[selector] = update[selector] || "";
update[selector] += spell.removed ? '<span class="removed">' : "<span>";
update[selector] += spell.name + "</span>";
});
//then, list new spells you've checked
selectecSpells.forEach((spell) => {
if (!spell.existing) {
const selector = "spells-summary .level_" + spell.level + " .list";
update[selector] = update[selector] || "";
update[selector] += '<span class="new">' + spell.name + "</span>";
}
});
if (sourcelevel == "0") {
sections.push(cantripsection);
allowed = spellpage.values.newcantrips || 0;
} else {
update[cantripsection + " .spellstext .levels"] = "";
levels.forEach((level) => {
const thissection = `spell-holder-${level}`;
allowed = spellpage.values.newspells;
allowedreplace = spellpage.values.replace;
perlevel[level + ""] = { number: 0, section: thissection, replaced: false };
sections.push(thissection);
});
}
const newSpells = selectecSpells.filter((spell) => !spell.existing && spell.level && parseInt(spell.level) !== 0);
const newCantrips = selectecSpells.filter((spell) => !spell.existing && spell.level && parseInt(spell.level) === 0);
const replaced = Math.max(newSpells.length - allowed, 0);
const exitingSpells = getKnownLPSpellList();
selectecSpells.forEach((spell) => {
if (!spell.existing) {
if (!perlevel.hasOwnProperty(spell.level)) perlevel[spell.level] = {};
if (!perlevel[spell.level].number) perlevel[spell.level].number = 0;
perlevel[spell.level].number++;
}
});
removedSpells.forEach((spell) => {
if (spell.removed) {
if (!perlevel.hasOwnProperty(spell.level)) perlevel[spell.level] = {};
if (!perlevel[spell.level].number) perlevel[spell.level].replaced = false;
perlevel[spell.level].replaced = true;
}
});
//now lock/unlock spells depending on if we've selected as many as we're allowed
loadedSpellList.forEach((spell) => {
const name = spell.name;
const attribute = spell.attribute;
const existing = exitingSpells.filter((spell) => spell.name == name).length > 0;
const isNewSpell = selectecSpells.filter((spell) => spell.name === name && !spell.existing).length > 0;
if (isNewSpell) {
update["spell-" + attribute + " .lock"] = "";
} else if (existing) {
update["spell-" + attribute + " .lock"] = allowedreplace <= replaced ? "locked" : "";
} else {
const added = sourcelevel == 0 ? newCantrips.length : newSpells.length;
if (sourcelevel == 0 && spell.level == 0) update["spell-" + attribute + " .lock"] = added >= allowed + replaced ? "locked" : "";
else if (sourcelevel > 0 && spell.level > 0) update["spell-" + attribute + " .lock"] = added >= allowed + replaced ? "locked" : "";
}
});
if (sourcelevel == "0") {
const known = thispage != "lp-spellchoice" && spellpage.values.knowncantrips ? spellpage.values.knowncantrips : 0;
update[cantripsection + " label .spellholder__counter"] = known + newCantrips.length + " / " + (known + allowed);
} else {
if (thispage != "lp-spellchoice") {
update[cantripsection + " .spellstext .total"] = newSpells.length + " / " + (allowed + replaced) + " spells chosen.";
if (replaced > 0) {
update[cantripsection + " .spellstext .total"] += "<br>(" + allowed + " new, " + replaced + " replaced)";
}
if (allowedreplace > 0) {
update[cantripsection + " .replacetext .total"] = replaced + " / " + allowedreplace + " spells replaced.";
}
}
_.each(perlevel, function (info, level) {
if (level == 0) return;
const selector = `spell-holder-${level}`;
if (thispage != "lp-spellchoice")
update[cantripsection + " .spellstext .levels"] += "<p>" + info.number + " Level " + level + " spells added.</p>";
update[selector + " label .spellholder__counter"] = "";
if (info.number > 0) update[selector + " label .spellholder__counter"] = "+" + info.number;
if (info.replaced) {
showChoices([selector + " .spellheader .title span"]);
} else {
hideChoices([selector + " .spellheader .title span"]);
}
});
}
setCharmancerText(update);
if (sourcelevel == "all") updateSpellSummary("0", mancerdata, thispage);
};
const _addSpellSections = function (byLevel, settings) {
settings = settings || {};
let newspells = settings.newspells || 0;
let newcantrips = settings.newcantrips || 0;
let replace = settings.replace || 0;
let prepared = settings.prepared || false;
let classes = settings.classes || [];
let spelldata = settings.spelldata || [];
let existinglist = settings.existinglist || [];
let spellnumber = 0;
//D&D 5e Lvl+ Mancer: Spells tab "Level undefined" (UC605)
//When invoking Lvl+ from an incomplete manually levelled character, an undefined lv is generated due to missing information.
//This code prevents an incomplete list from being displayed.
//By Miguel
if ("undefined" in byLevel) {
delete byLevel.undefined;
}
_.each(byLevel, function (level) {
spellnumber += level.length;
});
byLevel["0"] = byLevel["0"] || [];
addRepeatingSection("choices", "row", "spellsrow", function (spellsrow) {
let toSet = {};
let update = {};
_.each(byLevel, function (spells, x) {
addRepeatingSection(spellsrow, "spell-holder", "spell-holder-" + x, function (levelrow) {
if (x == "0") {
if (newspells > 0) {
if (classes.length > 1) {
update[levelrow + " .spellstext .summary"] = "";
_.each(classes, function (thisclass) {
if (thisclass.newspells) {
update[levelrow + " .spellstext .summary"] +=
"<p>You can add " + thisclass.newspells + " " + thisclass.class + " spells.</p>";
}
});
} else {
update[levelrow + " .spellstext .summary"] = "<p>You can add " + newspells + " new spells.</p>";
}
}
if (replace > 0) {
if (classes.length > 1) {
update[levelrow + " .replacetext .summary"] = "";
_.each(classes, function (thisclass) {
if (thisclass.replace) {
update[levelrow + " .replacetext .summary"] +=
"<p>You can replace " + thisclass.replace + " " + thisclass.class + " spells.</p>";
}
});
} else {
update[levelrow + " .replacetext .summary"] = "<p>You can replace " + replace + " spells.</p>";
}
showChoices([levelrow + " .replace-info"]);
}
} else {
update[levelrow + " .spellinfo"] = "";
}
update[levelrow + " label .title"] = x == "0" ? "Cantrips" : "Level " + x;
update[levelrow + " label .title"] += '<span class="choice">r</span>';
if (x == "0" && newcantrips == 0) {
toSet[levelrow + "_show"] = "1";
update[levelrow + " .controller"] = "1";
}
if (spells.length === 0) hideChoices([levelrow + " label"]);
spells = _.sortBy(spells, "name");
setCharmancerText(update);
_.each(spells, function (spell) {
addRepeatingSection(levelrow + " .container", "spell-item", function (spellid) {
var update = {};
toSet[spellid + "_name"] = spell.name;
toSet[spellid + "_level"] = x;
update[spellid + " .name"] = spell.name;
update[spellid + " .classes"] = "";
if (x == "0" && newcantrips == 0) update[spellid + " .hardlock"] = "locked";
var spellclasses = [];
_.each(classes, function (thisclass, y) {
if (spell.classes.includes(thisclass.list) && spell.level <= thisclass.maxlevel) {
spellclasses.push({ class: thisclass.class, ability: thisclass.ability });
if (classes.length > 1) {
update[spellid + " .classes"] += '<span class="class' + y + '">' + thisclass.class + "</span>";
}
}
});
if (spellclasses.length == 1 || classes.length == 1) {
toSet[spellid + "_class"] = spellclasses[0] ? spellclasses[0].class : classes[0].class;
toSet[spellid + "_ability"] = spellclasses[0] ? spellclasses[0].ability : classes[0].ability;
}
_.each(spelldata, function (thisspell, spellname) {
if (spellname == spell.name) {
toSet[spellid + "_checked"] = "1";
if (!prepared || x == "0") {
toSet[spellid + "_existing"] = "1";
if (x == "0" || replace == 0) update[spellid + " .hardlock"] = "locked";
}
if (thisspell.known) {
toSet[spellid + "_checked"] = "1";
toSet[spellid + "_existing"] = "1";
toSet[spellid + "_source"] = thisspell.known;
update[spellid + " .classes"] = '<span class="known">' + thisspell.known + " Spell</span>";
update[spellid + " .hardlock"] = "locked";
}
if (thisspell.spellclass) toSet[spellid + "_class"] = thisspell.spellclass;
if (thisspell.ability) toSet[spellid + "_ability"] = thisspell.ability;
}
});
if (settings.selected && settings.selected.includes(spell.name)) {
toSet[spellid + "_checked"] = "1";
}
if (settings.locked && settings.locked.includes(spell.name)) {
toSet[spellid + "_checked"] = "1";
toSet[spellid + "_existing"] = "1";
update[spellid + " .hardlock"] = "locked";
}
spellnumber--;
setCharmancerText(update);
if (spellnumber <= 0) {
setAttrs(toSet, { silent: true }, function () {
_updateSpellSummary("all", undefined, settings.page);
});
}
});
});
});
});
});
};
const _updateSpellSummary = function (sourcelevel, data, thispage) {
var mancerdata = data || getCharmancerData();
var spellpage = thispage ? mancerdata[thispage] : mancerdata["lp-spells"];
var replist = spellpage.repeating || [];
var cantripsection = replist.find((x) => {
return x.includes("_spell-holder-0");
});
var update = {};
var sections = [];
var perlevel = {};
var allowed = 0;
var allowedreplace = 0;
//first, list spells you already know
_.each(replist, function (repid) {
if (spellpage.values[repid + "_existing"]) {
var selector = "spells-summary .level_" + spellpage.values[repid + "_level"] + " .list";
update[selector] = update[selector] || "";
update[selector] += spellpage.values[repid + "_checked"] ? "<span>" : '<span class="removed">';
update[selector] += spellpage.values[repid + "_name"] + "</span>";
}
});
//then, list new spells you've checked
_.each(replist, function (repid) {
if (spellpage.values[repid + "_checked"] && !spellpage.values[repid + "_existing"]) {
var selector = "spells-summary .level_" + spellpage.values[repid + "_level"] + " .list";
update[selector] = update[selector] || "";
update[selector] += '<span class="new">' + spellpage.values[repid + "_name"] + "</span>";
}
});
if (sourcelevel == "0") {
sections.push(cantripsection);
allowed = spellpage.values.newcantrips || 0;
} else {
update[cantripsection + " .spellstext .levels"] = "";
for (var x = 1; x <= 9; x++) {
var thissection = replist.find((y) => {
return y.includes("_spell-holder-" + x);
});
allowed = spellpage.values.newspells;
allowedreplace = spellpage.values.replace;
if (thissection) {
perlevel[x + ""] = { number: 0, section: thissection, replaced: false };
sections.push(thissection);
}
}
}
getRepeatingSections(
replist.find((x) => {
return x.includes("spellsrow");
}),
function (repeating) {
var toSet = {};
var spellids = [];
var newspells = 0;
var replaced = 0;
_.each(repeating.tree, function (spells, levelrow) {
if (sections.includes(levelrow)) {
spellids = spellids.concat(_.keys(spells));
}
});
_.each(spellids, function (id) {
if (spellpage.values[id + "_checked"] && !spellpage.values[id + "_existing"]) {
newspells++;
if (perlevel[spellpage.values[id + "_level"]]) perlevel[spellpage.values[id + "_level"]].number++;
}
if (!spellpage.values[id + "_checked"] && spellpage.values[id + "_existing"]) {
replaced++;
if (perlevel[spellpage.values[id + "_level"]]) perlevel[spellpage.values[id + "_level"]].replaced = true;
}
});
//now lock/unlock spells depending on if we've selected as many as we're allowed
_.each(spellids, function (id) {
if (!spellpage.values[id + "_checked"]) {
update[id + " .lock"] = newspells >= allowed + replaced ? "locked" : "";
} else if (spellpage.values[id + "_checked"] && spellpage.values[id + "_existing"]) {
update[id + " .lock"] = allowedreplace <= replaced ? "locked" : "";
}
});
if (sourcelevel == "0") {
var known = thispage != "lp-spellchoice" && spellpage.values.knowncantrips ? spellpage.values.knowncantrips : 0;
update[cantripsection + " label .number"] = known + newspells + " / " + (known + allowed);
} else {
if (thispage != "lp-spellchoice") {
update[cantripsection + " .spellstext .total"] = newspells + " / " + (allowed + replaced) + " spells chosen.";
if (replaced > 0) {
update[cantripsection + " .spellstext .total"] += "<br>(" + allowed + " new, " + replaced + " replaced)";
}
if (allowedreplace > 0) {
update[cantripsection + " .replacetext .total"] = replaced + " / " + allowedreplace + " spells replaced.";
}
}
_.each(perlevel, function (info, level) {
if (thispage != "lp-spellchoice")
update[cantripsection + " .spellstext .levels"] += "<p>" + info.number + " Level " + level + " spells added.</p>";
update[info.section + " label .number"] = "";
if (info.number > 0) update[info.section + " label .number"] = "+" + info.number;
if (info.replaced) {
showChoices([info.section + " .spellheader .title span"]);
} else {
hideChoices([info.section + " .spellheader .title span"]);
}
});
}
setCharmancerText(update);
if (sourcelevel == "all") _updateSpellSummary("0", mancerdata, thispage);
setCharmancerText({ pagelock: "unlock" });
}
);
};
on("clicked:repeating_spell-item", function (eventinfo) {
if (eventinfo.sourceAttribute.indexOf("spell-item_info") === -1) return;
var mancerdata = getCharmancerData();
changeCompendiumPage("spells-info", "Spells:" + mancerdata[eventinfo.currentStep].values[eventinfo.sourceSection + "_name"], "card_only");
});
on("mancerchange:repeating_spellsrow", function (eventinfo) {
var mancerdata = getCharmancerData();
if (eventinfo.sourceAttribute && _.last(eventinfo.sourceAttribute.split("_")) == "show") {
var update = {};
update[eventinfo.sourceSection + " .controller"] = eventinfo.newValue || "";
setCharmancerText(update);
} else {
_updateSpellSummary(mancerdata[eventinfo.currentStep].values[eventinfo.sourceSection + "_level"], mancerdata, eventinfo.currentStep);
}
});
const resetASI = function (id) {
let reset = {};
["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma", "sanity"].forEach((ability) => {
reset[`repeating_${id}_asi-row_${ability}`] = 0;
});
setAttrs(reset);
};
const resetFeats = function (id, callback) {
let reset = {};
reset[`repeating_${id}_asi-row_feat`] = "";
setAttrs(reset, () => {
resetFeatDetails(id, callback);
});
};
const resetFeatDetails = function (id, callback) {
const details = [
{ name: `repeating_${id}_asi-row_feat_ability_choice` },
{ name: `repeating_${id}_asi-row_feat_language_choice#`, amount: 4 },
{ name: `repeating_${id}_asi-row_feat_language_choice#_custom`, amount: 4 },
{ name: `repeating_${id}_asi-row_feat_skill_choice#`, amount: 4 },
{ name: `repeating_${id}_asi-row_feat_weapon_choice#`, amount: 4 },
{ name: `repeating_${id}_asi-row_feat_armor_choice#`, amount: 4 },
{ name: `repeating_${id}_asi-row_feat_tool_choice#`, amount: 4 },
{ name: `repeating_${id}_asi-row_feat_expertise_choice_#`, amount: 2 },
{ name: `repeating_${id}_asi-row_feat_feature_choice_#`, amount: 3 },
{ name: `repeating_${id}_asi-row_other_options_and_features_choice#`, amount: 2 },
{ name: `repeating_${id}_asi-row_feat_spell_choice#`, amount: 4 },
];
let reset = {};
details.forEach((entry) => {
if (entry["amount"]) {
for (let i = 0; i < entry["amount"]; i++) {
let name = entry["name"].replace("#", i + 1);
reset[name] = "";
}
} else {
reset[entry["name"]] = "";
}
});
setAttrs(reset, callback);
};
on("mancerchange:repeating_asi-row", function (eventinfo) {
var trigger = eventinfo.sourceAttribute.substr(39);
if (trigger === "switch") {
const showFeats = eventinfo.newValue === "feat";
const id = SheetUtils.getUID(eventinfo.sourceSection);
if (showFeats) {
if (eventinfo.sourceType && eventinfo.sourceType === "player")
resetASI(id, () => {
recalcLpData();
});
changeCompendiumPage("class-info", "Rules:Feats");
setAttrs({ [`repeating_${id}_asi-row_switch`]: "feat" }, { silent: true });
} else {
if (eventinfo.sourceType && eventinfo.sourceType === "player")
resetFeats(id, () => {
recalcLpData();
});
changeCompendiumPage("class-info", "Rules:Ability Scores");
setAttrs({ [`repeating_${id}_asi-row_switch`]: "asi" }, { silent: true });
}
} else if (trigger === "feat" && eventinfo.sourceType === "woker") {
} else if (globalAttributesByCategory.abilitiesWithAllOptionals.indexOf(trigger) > -1) {
updateAbilityLock(eventinfo.sourceSection);
//This will lock other ASI when leveling up multiple levels at once
//By Miguel
var repeatingData = getCharmancerData()["lp-asi"].repeating || [];
for (data in repeatingData) {
if (repeatingData[data].indexOf("_asi-row") > -1) {
updateAbilityLock(repeatingData[data]);
}
}
recalcLpData();
}
});
on("mancerchange:repeating_asi-row_feat", (eventinfo) => {
const rowID = SheetUtils.getUID(eventinfo.sourceAttribute);
const rowClass = `repeating_${rowID}_asi-row`;
const rowName = `repeating_${rowID}_asi-row`;
if (!(eventinfo.newValue === "" || eventinfo.newValue === undefined)) {
showChoices([`${rowClass} .feat_options`]);
}
getCompendiumPage(eventinfo.newValue, rowName, function (p) {
p = removeDuplicatedPageData(p);
let resetAndUpdate = {};
/*
if(eventinfo.sourceType && eventinfo.sourceType === "player") {
resetAndUpdate[`${rowName}_feat_ability_choice`] = "";
resetAndUpdate[`${rowName}_feat_language_choice1`] = "";
resetAndUpdate[`${rowName}_feat_language_choice1_custom`] = "";
resetAndUpdate[`${rowName}_feat_language_choice2`] = "";
resetAndUpdate[`${rowName}_feat_language_choice2_custom`] = "";
resetAndUpdate[`${rowName}_feat_language_choice3`] = "";
resetAndUpdate[`${rowName}_feat_language_choice3_custom`] = "";
resetAndUpdate[`${rowName}_feat_language_choice4`] = "";
resetAndUpdate[`${rowName}_feat_language_choice4_custom`] = "";
resetAndUpdate[`${rowName}_feat_skill_choice1`] = "";
resetAndUpdate[`${rowName}_feat_skill_choice2`] = "";
resetAndUpdate[`${rowName}_feat_skill_choice3`] = "";
resetAndUpdate[`${rowName}_feat_skill_choice4`] = "";
resetAndUpdate[`${rowName}_feat_weapon_choice1`] = "";
resetAndUpdate[`${rowName}_feat_weapon_choice2`] = "";
resetAndUpdate[`${rowName}_feat_weapon_choice3`] = "";
resetAndUpdate[`${rowName}_feat_weapon_choice4`] = "";
resetAndUpdate[`${rowName}_feat_armor_choice1`] = "";
resetAndUpdate[`${rowName}_feat_armor_choice2`] = "";
resetAndUpdate[`${rowName}_feat_armor_choice3`] = "";
resetAndUpdate[`${rowName}_feat_armor_choice4`] = "";
resetAndUpdate[`${rowName}_feat_tool_choice1`] = "";
resetAndUpdate[`${rowName}_feat_tool_choice2`] = "";
resetAndUpdate[`${rowName}_feat_tool_choice3`] = "";
resetAndUpdate[`${rowName}_feat_tool_choice4`] = "";
resetAndUpdate[`${rowName}_feat_spell_choice1`] = "";
resetAndUpdate[`${rowName}_feat_spell_choice2`] = "";
resetAndUpdate[`${rowName}_feat_spell_choice3`] = "";
resetAndUpdate[`${rowName}_feat_spell_choice4`] = "";
resetAndUpdate[`${rowName}_feat_expertise_choice_1`] = "";
resetAndUpdate[`${rowName}_feat_expertise_choice_2`] = "";
resetAndUpdate[`${rowName}_feat_feature_choice_1`] = "";
resetAndUpdate[`${rowName}_feat_feature_choice_3`] = "";
resetAndUpdate[`${rowName}_feat_feature_choice_2`] = "";
resetAndUpdate[`${rowName}_other_options_and_features_choice1`] = "";
resetAndUpdate[`${rowName}_other_options_and_features_choice2"`] = "";
}
*/
resetAndUpdate[`${rowName}_feat_data`] = JSON.stringify(p.data);
const hideList = [
`${rowClass} .feat_ability`,
`${rowClass} .feat_ability_choice`,
`${rowClass} .other_options_and_features1`,
`${rowClass} .other_options_and_features2`,
`${rowClass} .feat_skill_row`,
`${rowClass} .feat_skill_choice1`,
`${rowClass} .feat_skill_choice2`,
`${rowClass} .feat_skill_choice3`,
`${rowClass} .feat_skill_choice4`,
`${rowClass} .feat_expertise_row`,
`${rowClass} .feat_expertise_choice_1`,
`${rowClass} .feat_expertise_choice_2`,
`${rowClass} .feat_prereq`,
`${rowClass} .feat_spell_row`,
`${rowClass} .feat_spell_choice1`,
`${rowClass} .feat_spell_choice2`,
`${rowClass} .feat_spell_choice3`,
`${rowClass} .feat_spell_choice4`,
];
hideChoices(hideList);
setCharmancerText({ [`${rowClass} .feat_text`]: "" });
setAttrs(resetAndUpdate, function () {
var data = p["data"];
var showList = [];
var update = {};
if (data["data-Ability Score Increase"]) {
var abilityText = [];
//Safeguarding Compendium data:
const json = Compendium.parseObject(
data["data-Ability Score Increase"],
`E_Q675P32Q:Page:${eventinfo.newValue}:data-Ability Score Increase`
);
_.each(json, function (increase, ability) {
abilityText.push(ability + " +" + increase);
});
update[`${rowClass} .feat_ability_score`] = abilityText.join(", ");
showList.push(`${rowClass} .feat_ability`);
}
if (data["data-Ability Score Choice"]) {
//Safeguarding Compendium data:
const json = Compendium.parseArray(
data["data-Ability Score Choice"],
`E_NLN9A997:Page:${eventinfo.newValue}:data-Ability Score Choice`
);
const name = `repeating_${rowID}_asi-row_feat_ability_choice`;
setCharmancerOptions(name, json);
showList.push(`${rowClass} .feat_ability`, `${rowClass} .feat_ability_choice`);
}
if (data["Prerequisite"]) {
showList.push(`${rowClass} .feat_prereq`);
update[`${rowClass} .feat_prerequisite`] = data["Prerequisite"];
}
if (data["Other Options and Features"]) {
//Safeguarding Compendium data:
const otherFeatures = Compendium.parseObject(
data["Other Options and Features"],
`E_U0E3FNEC:Page:${eventinfo.newValue}:Other Options and Features`
);
if (otherFeatures["Choice"] && Array.isArray(otherFeatures["Choice"])) {
for (let i = 0; i < otherFeatures["Choice"].length; i++) {
const featureIndex = i + 1;
const selector = `comp_repeating_${rowID}_asi-row_other_options_and_features_choice${featureIndex}`;
const query = `Category:Other Options and Features Type:${otherFeatures["Choice"][i]}`;
const title = `Choose ${SheetUtils.aOrAn(otherFeatures["Choice"][i])}:`;
showList.push(`${rowClass} .other_options_and_features${featureIndex}`);
setCharmancerText({ [`${rowClass} .other_options_and_features_title${featureIndex}`]: title });
setCharmancerOptions(selector, query);
}
}
}
if (data["Skill Proficiency"]) {
//Safeguarding Compendium data:
const skills = Compendium.parseObject(data["Skill Proficiency"], `E_MDONO4X9:Page:${eventinfo.newValue}:Skill Proficiency`);
if (skills["Choice"] && Array.isArray(skills["Choice"])) {
showList.push(`${rowClass} .feat_skill_row`);
for (let i = 0; i < skills["Choice"].length; i++) {
const skillIndex = i + 1;
const selector = `comp_repeating_${rowID}_asi-row_feat_skill_choice${skillIndex}`;
const query = "Category:Proficiencies Type:Skill";
showList.push(`${rowClass} .feat_skill_choice${skillIndex}`);
setCharmancerOptions(selector, query);
}
}
}
if (data["Expertise"]) {
//Safeguarding Compendium data:
const skills = Compendium.parseObject(data["Expertise"], `E_N5C3ZNLF:Page:${eventinfo.newValue}:Expertise`);
if (skills["Choice"] && Array.isArray(skills["Choice"])) {
showList.push(`${rowClass} .feat_expertise_row`);
for (let i = 0; i < skills["Choice"].length; i++) {
const skillIndex = i + 1;
const selector = `comp_repeating_${rowID}_asi-row_feat_expertise_choice_${skillIndex}`;
showList.push(`${rowClass} .feat_expertise_choice_${skillIndex}`);
setValidExpertiseOptions("lp-asi", selector);
}
}
}
if (data["data-Pick Spells"]) {
//Safeguarding Compendium data:
const spells = Compendium.parseArray(data["data-Pick Spells"], `E_7OBKIIRN:Page:${eventinfo.newValue}:data-Pick Spells`);
showList.push("feat_spell_row");
if (Array.isArray(spells)) {
for (let i = 0; i < spells.length; i++) {
const spellIndex = i + 1;
const selector = `comp_repeating_${rowID}_asi-row_feat_spell_choice${spellIndex}`;
const query = `Category:Spells ${spells[i]["List"]}`;
showList.push(`feat_spell_choice${spellIndex}`);
setCharmancerOptions(selector, query);
}
}
}
if (Object.keys(update).length > 0) setCharmancerText(update);
if (eventinfo.sourceType && eventinfo.sourceType === "player") {
resetFeatDetails(rowID, () => {
if (showList.length > 0) showChoices(showList);
recalcLpData();
});
} else {
if (showList.length > 0) showChoices(showList);
recalcLpData();
}
});
});
changeCompendiumPage("class-info", eventinfo.newValue);
});
on("mancerchange:repeating_asi-row_feat_ability_choice", (eventinfo) => {
recalcLpData();
});
on("clicked:repeating_asi-row", function (eventinfo) {
var mancerdata = getCharmancerData();
var thischange = eventinfo.sourceAttribute.substr(0, eventinfo.sourceAttribute.length - 2);
var currentvalue = mancerdata["lp-asi"].values[thischange] ? parseInt(mancerdata["lp-asi"].values[thischange]) : 0;
var totalincrease = 0;
var update = {};
var lockinfo = {};
_.each(abilityList, function (ability) {
var thisincrease = mancerdata["lp-asi"].values[eventinfo.sourceSection + "_" + ability.toLowerCase()] || 0;
totalincrease += parseInt(thisincrease);
});
if (_.last(eventinfo.sourceAttribute) == "u" && totalincrease < 2 && currentvalue < 2) {
update[thischange] = currentvalue + 1;
mancerdata["lp-asi"].values[thischange] = currentvalue + 1;
} else if (_.last(eventinfo.sourceAttribute) == "d" && currentvalue > 0) {
update[thischange] = currentvalue - 1;
mancerdata["lp-asi"].values[thischange] = currentvalue - 1;
}
setAttrs(update, function () {
recalcLpData();
});
});
var updateAbilityLock = function (sourceSection, mancerdata) {
mancerdata = mancerdata || getCharmancerData();
var abilities = getAbilityTotals(mancerdata);
var totalincrease = 0;
var update = {};
//First, get the total increase for this section
_.each(abilityList, function (ability) {
var thisincrease = mancerdata["lp-asi"].values[sourceSection + "_" + ability.toLowerCase()] || 0;
totalincrease += parseInt(thisincrease);
});
//Then, figure out what to lock (based on increase)
_.each(abilityList, function (ability) {
var thisincrease = mancerdata["lp-asi"].values[sourceSection + "_" + ability.toLowerCase()] || 0;
update[sourceSection + " ." + ability.toLowerCase() + "_lock-down"] = thisincrease > 0 ? "" : "lock";
update[sourceSection + " ." + ability.toLowerCase() + "_lock-up"] = thisincrease < 2 && totalincrease < 2 ? "" : "lock";
});
//Now, figure out if any stats are maxed out
_.each(abilityList, function (ability) {
if (abilities[ability.toLowerCase()] >= abilities[ability.toLowerCase() + "_maximum"]) {
update[sourceSection + " ." + ability.toLowerCase() + "_lock-up"] = "lock";
}
});
setCharmancerText(update);
};
on("page:lp-summary", function () {
var mancerdata = getCharmancerData();
var abilities = getAbilityTotals(mancerdata);
//Safeguarding Compendium data:
var previous = mancerdata["lp-welcome"].values["previous_attributes"]
? Compendium.parseObject(mancerdata["lp-welcome"].values["previous_attributes"], `E_PSY9BZX7`)
: {};
var spelldata = mancerdata["lp-welcome"].values["spellinfo"]
? Compendium.parseObject(mancerdata["lp-welcome"].values["spellinfo"], `E_PSY9BZX7`)
: {};
var lcAbilities = SheetUtils.toLowerCase(abilityList);
var update = {};
update["before div"] = '<div class="row"><p>Class: <span>';
if (previous.subclass) update["before div"] += previous.subclass + " ";
update["before div"] += previous.class + " " + previous.base_level;
for (var x = 1; x <= 3; x++) {
if (previous["multiclass" + x + "_flag"] != "0") {
update["before div"] += ", " + previous["multiclass" + x] + " " + previous["multiclass" + x + "_lvl"];
}
}
update["before div"] += "</span></p>";
update["before div"] += "<p>Hit Points: <span>" + previous.hp_max + "</span></p></div>";
update["before div"] += '<div class="row"><div class="ability-row">';
_.each(lcAbilities, function (ability) {
update["before div"] += '<div><h5 data-i18n="' + ability.substr(0, 3) + '-u"></h5>';
update["before div"] += '<span class="score">' + abilities[ability + "_previous"] + "</span> ";
update["before div"] += '(<span class="mod">';
update["before div"] +=
abilities[ability + "_previous_mod"] >= 0 ? "+" + abilities[ability + "_previous_mod"] : abilities[ability + "_previous_mod"];
update["before div"] += "</span>)</div>";
});
update["before div"] += '</div></div><div class="row"></div>';
update["after div"] = '<div class="row"><p class="highlight">Class: <span>';
for (var x = 1; x <= 4; x++) {
if (mancerdata["lp-levels"].values["class" + x]) {
if (x !== 1) update["after div"] += ", ";
if (mancerdata["lp-levels"].values["class" + x + "_subclass"]) {
if (
(x == 1 && !previous.subclass) ||
(x != 1 && previous["multiclass" + x + "_flag"] != "0" && !previous["multiclass" + x + "_subclass"])
) {
update["after div"] += '<span class="highlight">';
} else {
update["after div"] += "<span>";
}
update["after div"] += removeExpansionInfo(mancerdata["lp-levels"].values["class" + x + "_subclass"].split(":")[1]) + " </span>";
}
if (x != 1 && previous["multiclass" + x + "_flag"] != "0" && !previous["multiclass" + x + "_subclass"]) {
update["after div"] += '<span class="highlight">';
} else {
update["after div"] += "<span>";
}
update["after div"] += removeExpansionInfo(mancerdata["lp-levels"].values["class" + x].split(":")[1]) + " </span>";
if (mancerdata["lp-levels"].values["class" + x + "_addlevel"]) {
update["after div"] += '<span class="highlight">';
} else {
update["after div"] += "<span>";
}
var prevlevel = 0;
if (x == 1) {
prevlevel = parseInt(previous["base_level"]);
} else {
prevlevel = previous["multiclass" + x + "_flag"] == "0" ? 0 : parseInt(previous["multiclass" + x + "_lvl"]);
}
update["after div"] += prevlevel + parseInt(mancerdata["lp-levels"].values["class" + x + "_addlevel"]) + " </span>";
}
}
update["after div"] += "</span></p>";
update["after div"] += '<p class="highlight">Hit Points: <span class="highlight">' + getHpTotal(mancerdata) + "</span></p></div>";
update["after div"] += '<div class="row"><div class="ability-row">';
_.each(lcAbilities, function (ability) {
update["after div"] += abilities[ability] == abilities[ability + "_previous"] ? "<div>" : '<div class="highlight">';
update["after div"] += '<h5 data-i18n="' + ability.substr(0, 3) + '-u"></h5><span>';
update["after div"] +=
abilities[ability] == abilities[ability + "_previous"] ? '<span class="score">' : '<span class="score highlight">';
update["after div"] += abilities[ability] + "</span> (";
update["after div"] +=
abilities[ability + "_mod"] == abilities[ability + "_previous_mod"] ? '<span class="mod">' : '<span class="mod highlight">';
update["after div"] += abilities[ability + "_mod"] >= 0 ? "+" + abilities[ability + "_mod"] : abilities[ability + "_mod"];
update["after div"] += "</span>)</span></div>";
});
update["after div"] += '</div></div><div class="row"></div>';
//Feats
const getLPFeats = (data) => {
const feats = [];
if (!SheetUtils.checkPath(data, "['lp-asi']['values']")) return feats;
else {
Object.keys(data["lp-asi"]["values"]).forEach((value) => {
if (value.indexOf("asi-row_switch") && data["lp-asi"]["values"][value] === "feat") {
const rowID = SheetUtils.getUID(value);
feats.push(data["lp-asi"]["values"][`repeating_${rowID}_asi-row_feat`].replace(/Feats:/g, ""));
}
});
return feats;
}
};
const noFeatsMsg =
"<p>As you exchange your novice notions of the world for experience, a feat may become within reach. Not today, however.</p>";
const feats = getLPFeats(mancerdata);
update["feat_info"] = feats.length > 0 ? `<p>You've selected ${feats.join(", ")}` : noFeatsMsg;
//Spells
let selectedSpells = getSelectedSpells()
.filter((spell) => !spell.existing)
.map((spell) => spell.name);
update["spell_info"] = "<p>You haven't gained any spells</p>";
if (selectedSpells.length > 0) update["spell_info"] = "<p>You have learnt these spells: " + selectedSpells.sort().join(", ") + "</p>";
let gainedFeatures = getGainedFeatures();
update["feature_info"] =
gainedFeatures.length > 0
? "<p>You have gained these class features: " + gainedFeatures.sort().join(", ") + "</p>"
: "You haven't gained any class features.";
setCharmancerText(update);
showChoices(["apply_changes"]);
});
on("clicked:lp-choices_next", function (eventinfo) {
const mancerdata = getCharmancerData();
if (mancerdata["lp-levels"].values.asi === "true") {
changeCharmancerPage("lp-asi");
} else if (mancerdata["lp-levels"].values.spells === "true") {
changeCharmancerPage("lp-spells");
} else {
changeCharmancerPage("lp-summary");
}
});
on("clicked:lp-asi_next", function (eventinfo) {
const mancerdata = getCharmancerData();
if (mancerdata["lp-levels"].values.spells === "true") {
changeCharmancerPage("lp-spells");
} else {
changeCharmancerPage("lp-summary");
}
});
on("clicked:lp-spells_back", function (eventinfo) {
const mancerdata = getCharmancerData();
if (mancerdata["lp-levels"].values.asi === "true") {
changeCharmancerPage("lp-asi");
} else {
changeCharmancerPage("lp-choices");
}
});
on("clicked:lp-summary_back", function (eventinfo) {
const mancerdata = getCharmancerData();
if (mancerdata["lp-levels"].values.spells === "true") {
changeCharmancerPage("lp-spells");
} else if (mancerdata["lp-levels"].values.asi === "true") {
changeCharmancerPage("lp-asi");
} else {
changeCharmancerPage("lp-choices");
}
});
//Fixing Bard's Magical Secrets (UC-10)
//By Miguel
//The ways this was being handled before, binded to clicked:repeating_utilityrow_launch, was not being triggered due to the way the DOM is built.
var pickerLP = function (eventinfo) {
if (eventinfo.triggerName.indexOf("utilityrow_launch") === -1) return;
const mancerdata = getCharmancerData();
var querysettings = {};
var spelldata = getKnownLpSpells(mancerdata);
var leveldata = _.findWhere(getLevelingData(mancerdata), {
classnumber: parseInt(mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_parent`][5]),
});
var query = "";
let newspells = 0;
let newcantrips = 0;
var spellnames = [];
var current = [];
var locked = [];
var update = {};
update[`${eventinfo.sourceSection} .warning`] = "";
//Safeguarding Compendium data:
querysettings = Compendium.parseObject(mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_info`], `E_MT1ZERF2`);
if (mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_result`])
current = mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_result`].split(", ");
if (mancerdata["lp-spells"] && mancerdata["lp-spells"].repeating) {
_.each(mancerdata["lp-spells"].repeating, function (repid) {
if (mancerdata["lp-spells"].values[`${repid}_checked`]) {
spelldata[mancerdata["lp-spells"].values[`${repid}_name`]] = {
level: mancerdata["lp-spells"].values[`${repid}_level`],
spellclass: mancerdata["lp-spells"].values[`${repid}_class`],
};
}
});
}
_.each(current, function (currentspell) {
delete spelldata[currentspell];
});
if (querysettings.Known) {
if (querysettings.Level + "" === "0") {
newcantrips = 1;
} else {
newspells = 1;
}
_.each(spelldata, function (spell, spellname) {
if (!querysettings.Level || (querysettings.Level && querysettings.Level == spell.level)) spellnames.push(spellname);
});
if (spellnames.length > 0) {
query = "Category:Spells Name:" + spellnames.join("|");
} else {
update[`${eventinfo.sourceSection} .warning`] = `You do not currently know any level ${querysettings.Level} spells.`;
setCharmancerText(update);
}
} else {
query = "Category:Spells";
if (querysettings.Level) {
let levels = [querysettings.Level];
if (querysettings.Level == "max") {
levels = [];
for (let x = 1; x <= leveldata.maxspell; x++) {
levels.push(x);
}
}
query += " Level:" + levels.join("|");
}
if (querysettings.List && querysettings.List.toLowerCase() !== "any") {
query += " Classes:*" + querysettings.List;
}
if (querysettings.Level + "" === "0") {
newcantrips = querysettings.Number ? parseInt(querysettings.Number) : 1;
} else {
newspells = querysettings.Number ? parseInt(querysettings.Number) : 1;
}
locked = _.keys(spelldata);
}
if (query) {
setCharmancerText(update);
changeCharmancerPage("lp-spellchoice", function () {
getCompendiumQuery([query], function (data) {
data = removeDuplicatedPageData(data);
let byLevel = {};
let filters = {};
let filtered = [];
let toSet = {};
let update = {};
update["instructions"] = querysettings.HelpText;
_.each(querysettings, function (setting, key) {
if (!["Level", "List", "Number", "Type", "ButtonText", "HelpText", "Known", "Class"].includes(key)) {
filters[key] = setting;
}
});
if (querysettings.Known) {
delete querysettings.Level;
}
delete querysettings.Known;
_.each(data, function (spell) {
let match = true;
_.each(filters, function (value, name) {
if (spell.data[name] !== value) match = false;
});
if (match) filtered.push(spell);
});
_.each(filtered, function (spell) {
if (spell.data.Classes) {
byLevel[spell.data.Level] = byLevel[spell.data.Level] || [];
byLevel[spell.data.Level].push({
name: spell.name,
classes: spell.data.Classes.split(",").map((x) => x.trim()),
level: parseInt(spell.data.Level),
});
}
});
toSet.newspells = newspells;
toSet.newcantrips = newcantrips;
toSet.valid = "now";
toSet.source = eventinfo.sourceSection;
setCharmancerText(update);
setAttrs(toSet, function () {
_addSpellSections(byLevel, {
newcantrips: newcantrips,
newspells: newspells,
page: "lp-spellchoice",
selected: current,
locked: locked,
});
});
});
}); /**/
}
};
var pickerL1 = function (eventinfo) {
if (eventinfo.triggerName.indexOf("utilityrow_launch") === -1) return;
const mancerdata = getCharmancerData();
var querysettings = {};
var spelldata = {}; //getKnownLpSpells(mancerdata);
var leveldata = {}; //_.findWhere(getLevelingData(mancerdata), {classnumber: parseInt(mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_parent`][5])});
var query = "";
let newspells = 0;
let newcantrips = 0;
var spellnames = [];
var current = [];
var locked = [];
var update = {};
update[`${eventinfo.sourceSection} .sheet-warning`] = "";
//Safeguarding Compendium data:
querysettings = Compendium.parseObject(mancerdata["l1-class"].values[`${eventinfo.sourceSection}_info`], `E_Z3OQP6WU`);
if (mancerdata["l1-class"].values[`${eventinfo.sourceSection}_result`])
current = mancerdata["l1-class"].values[`${eventinfo.sourceSection}_result`].split(", ");
/*
if(mancerdata["lp-spells"] && mancerdata["lp-spells"].repeating) {
_.each(mancerdata["lp-spells"].repeating, function(repid) {
if(mancerdata["lp-spells"].values[`${repid}_checked`]) {
spelldata[mancerdata["lp-spells"].values[`${repid}_name`]] = {
level: mancerdata["lp-spells"].values[`${repid}_level`],
spellclass: mancerdata["lp-spells"].values[`${repid}_class`]
}
}
});
}
*/
_.each(current, function (currentspell) {
delete spelldata[currentspell];
});
if (querysettings.Known) {
if (querysettings.Level + "" === "0") {
newcantrips = 1;
} else {
newspells = 1;
}
_.each(spelldata, function (spell, spellname) {
if (!querysettings.Level || (querysettings.Level && querysettings.Level == spell.level)) spellnames.push(spellname);
});
if (spellnames.length > 0) {
query = "Category:Spells Name:" + spellnames.join("|");
} else {
update[`${eventinfo.sourceSection} .sheet-warning`] = `You do not currently know any level ${querysettings.Level} spells.`;
setCharmancerText(update);
}
} else {
query = "Category:Spells";
if (querysettings.Level) {
let levels = [querysettings.Level];
if (querysettings.Level == "max") {
levels = [];
for (let x = 1; x <= leveldata.maxspell; x++) {
levels.push(x);
}
}
query += " Level:" + levels.join("|");
}
if (querysettings.List && querysettings.List.toLowerCase() !== "any") {
query += " Classes:*" + querysettings.List;
}
if (querysettings.Level + "" === "0") {
newcantrips = querysettings.Number ? parseInt(querysettings.Number) : 1;
} else {
newspells = querysettings.Number ? parseInt(querysettings.Number) : 1;
}
locked = _.keys(spelldata);
}
if (query) {
setCharmancerText(update);
changeCharmancerPage("lp-spellchoice", function () {
getCompendiumQuery([query], function (data) {
data = removeDuplicatedPageData(data);
let byLevel = {};
let filters = {};
let filtered = [];
let toSet = {};
let update = {};
update["instructions"] = querysettings.HelpText;
_.each(querysettings, function (setting, key) {
if (!["Level", "List", "Number", "Type", "ButtonText", "HelpText", "Known", "Class"].includes(key)) {
filters[key] = setting;
}
});
if (querysettings.Known) {
delete querysettings.Level;
}
delete querysettings.Known;
_.each(data, function (spell) {
let match = true;
_.each(filters, function (value, name) {
if (spell.data[name] !== value) match = false;
});
if (match) filtered.push(spell);
});
_.each(filtered, function (spell) {
if (spell.data.Classes) {
byLevel[spell.data.Level] = byLevel[spell.data.Level] || [];
byLevel[spell.data.Level].push({
name: spell.name,
classes: spell.data.Classes.split(",").map((x) => x.trim()),
level: parseInt(spell.data.Level),
});
}
});
toSet.newspells = newspells;
toSet.newcantrips = newcantrips;
toSet.valid = "now";
toSet.source = eventinfo.sourceSection;
setCharmancerText(update);
setAttrs(toSet, function () {
_addSpellSections(byLevel, {
newcantrips: newcantrips,
newspells: newspells,
page: "lp-spellchoice",
selected: current,
locked: locked,
});
});
});
});
}
};
on("clicked:repeating_utilityrow", function (eventinfo) {
const mancerdata = getCharmancerData();
if (mancerdata["lp-welcome"]) {
pickerLP(eventinfo);
} else {
pickerL1(eventinfo);
}
});
//TODO: Investigate if the duplicated event below listener can be removed
//By Miguel
on("clicked:repeating_utilityrow_launch", function (eventinfo) {
const mancerdata = getCharmancerData();
var querysettings = {};
var spelldata = getKnownLpSpells(mancerdata);
var leveldata = _.findWhere(getLevelingData(mancerdata), {
classnumber: parseInt(mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_parent`][5]),
});
var query = "";
let newspells = 0;
let newcantrips = 0;
var spellnames = [];
var current = [];
var locked = [];
var update = {};
update[`${eventinfo.sourceSection} .warning`] = "";
//Safeguarding Compendium data:
querysettings = Compendium.parseObject(mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_info`], `E_OXUC4FF2`);
if (mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_result`])
current = mancerdata["lp-choices"].values[`${eventinfo.sourceSection}_result`].split(", ");
if (mancerdata["lp-spells"] && mancerdata["lp-spells"].repeating) {
_.each(mancerdata["lp-spells"].repeating, function (repid) {
if (mancerdata["lp-spells"].values[`${repid}_checked`]) {
spelldata[mancerdata["lp-spells"].values[`${repid}_name`]] = {
level: mancerdata["lp-spells"].values[`${repid}_level`],
spellclass: mancerdata["lp-spells"].values[`${repid}_class`],
};
}
});
}
_.each(current, function (currentspell) {
delete spelldata[currentspell];
});
if (querysettings.Known) {
if (querysettings.Level + "" === "0") {
newcantrips = 1;
} else {
newspells = 1;
}
_.each(spelldata, function (spell, spellname) {
if (!querysettings.Level || (querysettings.Level && querysettings.Level == spell.level)) spellnames.push(spellname);
});
if (spellnames.length > 0) {
query = "Category:Spells Name:" + spellnames.join("|");
} else {
update[`${eventinfo.sourceSection} .warning`] = `You do not currently know any level ${querysettings.Level} spells.`;
setCharmancerText(update);
}
} else {
query = "Category:Spells";
if (querysettings.Level) {
let levels = [querysettings.Level];
if (querysettings.Level == "max") {
levels = [];
for (let x = 1; x <= leveldata.maxspell; x++) {
levels.push(x);
}
}
query += " Level:" + levels.join("|");
}
if (querysettings.List && querysettings.List.toLowerCase() !== "any") {
query += " Classes:*" + querysettings.List;
}
if (querysettings.Level + "" === "0") {
newcantrips = querysettings.Number ? parseInt(querysettings.Number) : 1;
} else {
newspells = querysettings.Number ? parseInt(querysettings.Number) : 1;
}
locked = _.keys(spelldata);
}
if (query) {
setCharmancerText(update);
changeCharmancerPage("lp-spellchoice", function () {
getCompendiumQuery([query], function (data) {
data = removeDuplicatedPageData(data);
let byLevel = {};
let filters = {};
let filtered = [];
let toSet = {};
let update = {};
update["instructions"] = querysettings.HelpText;
_.each(querysettings, function (setting, key) {
if (!["Level", "List", "Number", "Type", "ButtonText", "HelpText", "Known", "Class"].includes(key)) {
filters[key] = setting;
}
});
if (querysettings.Known) {
delete querysettings.Level;
}
delete querysettings.Known;
_.each(data, function (spell) {
let match = true;
_.each(filters, function (value, name) {
if (spell.data[name] !== value) match = false;
});
if (match) filtered.push(spell);
});
_.each(filtered, function (spell) {
if (spell.data.Classes) {
byLevel[spell.data.Level] = byLevel[spell.data.Level] || [];
byLevel[spell.data.Level].push({
name: spell.name,
classes: spell.data.Classes.split(",").map((x) => x.trim()),
level: parseInt(spell.data.Level),
});
}
});
toSet.newspells = newspells;
toSet.newcantrips = newcantrips;
toSet.valid = "now";
toSet.source = eventinfo.sourceSection;
setCharmancerText(update);
setAttrs(toSet, function () {
_addSpellSections(byLevel, {
newcantrips: newcantrips,
newspells: newspells,
page: "lp-spellchoice",
selected: current,
locked: locked,
});
});
});
}); /**/
}
});
on("page:lp-spellchoice", function () {
const mancerdata = getCharmancerData();
if (mancerdata["lp-spellchoice"] && mancerdata["lp-spellchoice"].values.valid === "now") {
changeCharmancerPage("lp-choices", function () {
deleteCharmancerData(["lp-spellchoice"]);
});
}
});
on("mancerchange:repeating_utilityrow_result", function (eventinfo) {
if (eventinfo.sourceSection) {
const mancerdata = getCharmancerData();
let update = {};
update[`${eventinfo.sourceSection} label span .result`] = eventinfo.newValue;
setCharmancerText(update);
}
});
var closePickerL1 = function (eventinfo) {
const mancerdata = getCharmancerData();
const leveldata = getLevelingData(mancerdata);
let result = [];
let source = mancerdata["lp-spellchoice"].values.source;
let newspells = false;
let spellsettings = {};
if (mancerdata["l1-class"].values[`${source}_info`]) {
//Safeguarding Compendium data:
spellsettings = Compendium.parseObject(mancerdata["l1-class"].values[`${source}_info`], `E_1IVCSB33`);
}
_.each(mancerdata["lp-spellchoice"].repeating, function (repid) {
if (mancerdata["lp-spellchoice"].values[`${repid}_checked`] && !mancerdata["lp-spellchoice"].values[`${repid}_existing`])
result.push(mancerdata["lp-spellchoice"].values[`${repid}_name`]);
});
if (mancerdata["l1-class"].values[`${source}_type`] !== "trait") {
const thisclass = mancerdata["l1-class"]["data"]["class"];
newspells = {};
_.each(mancerdata["lp-spellchoice"].repeating, function (repid) {
if (mancerdata["lp-spellchoice"].values[`${repid}_checked`] && !mancerdata["lp-spellchoice"].values[`${repid}_existing`]) {
let thisspell = {
ability: thisclass["Spellcasting Ability"],
spellclass: removeExpansionInfo(mancerdata["l1-class"]["values"]["class"]).replace("Classes:", ""),
level:
mancerdata["lp-spellchoice"].values[`${repid}_level`] === "0"
? "cantrip"
: mancerdata["lp-spellchoice"].values[`${repid}_level`],
};
if (spellsettings.Type) thisspell.known = spellsettings.Type;
newspells[mancerdata["lp-spellchoice"].values[`${repid}_name`]] = thisspell;
}
});
}
changeCharmancerPage("l1-class", function () {
deleteCharmancerData(["lp-spellchoice"]);
let set = {};
set[`${source}_result`] = result.join(", ");
if (newspells) set[`${source}_spells`] = JSON.stringify(newspells);
setAttrs(set);
});
};
var closePickerLP = function (eventinfo) {
const mancerdata = getCharmancerData();
const leveldata = getLevelingData(mancerdata);
let result = [];
let source = mancerdata["lp-spellchoice"].values.source;
let newspells = false;
let spellsettings = {};
if (mancerdata["lp-choices"].values[`${source}_info`]) {
//Safeguarding Compendium data:
spellsettings = Compendium.parseObject(mancerdata["lp-choices"].values[`${source}_info`], `E_5QPC41IW`);
}
_.each(mancerdata["lp-spellchoice"].repeating, function (repid) {
if (mancerdata["lp-spellchoice"].values[`${repid}_checked`] && !mancerdata["lp-spellchoice"].values[`${repid}_existing`])
result.push(mancerdata["lp-spellchoice"].values[`${repid}_name`]);
});
if (mancerdata["lp-choices"].values[`${source}_type`] !== "trait") {
const thisclass = _.findWhere(leveldata, { classnumber: parseInt(mancerdata["lp-choices"].values[`${source}_parent`][5]) });
newspells = {};
_.each(mancerdata["lp-spellchoice"].repeating, function (repid) {
if (mancerdata["lp-spellchoice"].values[`${repid}_checked`] && !mancerdata["lp-spellchoice"].values[`${repid}_existing`]) {
let thisspell = {
ability: thisclass.spellcasting,
spellclass: thisclass.classname,
level:
mancerdata["lp-spellchoice"].values[`${repid}_level`] === "0"
? "cantrip"
: mancerdata["lp-spellchoice"].values[`${repid}_level`],
};
if (spellsettings.Type) thisspell.known = spellsettings.Type;
newspells[mancerdata["lp-spellchoice"].values[`${repid}_name`]] = thisspell;
}
});
}
changeCharmancerPage("lp-choices", function () {
deleteCharmancerData(["lp-spellchoice"]);
let set = {};
set[`${source}_result`] = result.join(", ");
if (newspells) set[`${source}_spells`] = JSON.stringify(newspells);
setAttrs(set);
});
};
on("clicked:lp-spellchoice_back", function (eventinfo) {
const mancerdata = getCharmancerData();
if (mancerdata["lp-welcome"]) {
closePickerLP(eventinfo);
} else {
closePickerL1(eventinfo);
}
});
on("mancerfinish:lp-mancer", function (eventinfo) {
console.log("****************************************");
console.log("****** STARTING FINISH FUNCTION ******");
console.log("****************************************");
var doAllDrops = function (dropArray, callback) {
getAttrs(["character_id"], function (v) {
_.each(allAbilities, function (ability) {
v[ability + "_base"] = abilities[ability];
v[ability + "_mod"] = abilities[ability];
});
v.base_level = set["base_level"];
v["multiclass1_lvl"] = initial["multiclass1_lvl"];
v["multiclass2_lvl"] = initial["multiclass2_lvl"];
v["multiclass3_lvl"] = initial["multiclass3_lvl"];
v.npc = "0";
v["class_resource_name"] = previous["class_resource_name"];
v["other_resource_name"] = previous["other_resource_name"];
v.speed = previous.speed;
var update = {};
var callbacks = [];
var totalDrops = dropArray.length;
var x = 0;
callbacks.push(set_level);
callbacks.push(update_race_display);
_.each(dropArray, function (page) {
page.data.Category = page.data.Category ? page.data.Category.replace("@@!!@@", "") : "";
var dropUpdate = processDrop(page, v, repeating, true);
callbacks = callbacks.concat(dropUpdate.callbacks);
repeating.prof_names = repeating.prof_names || [];
dropUpdate.prof_names = dropUpdate.prof_names || [];
repeating.prof_names = _.uniq(repeating.prof_names.concat(dropUpdate.prof_names));
update = _.extend(update, dropUpdate.update);
x++;
setCharmancerText({ mancer_progress: '<div style="width: ' + (parseInt((x / totalDrops) * 70) + 20) + '%"></div>' });
});
callbacks.push(function () {
update_ac();
});
callbacks.push(function () {
update_weight();
});
callbacks.push(callback);
setAttrs(update, { silent: true }, function () {
setCharmancerText({ mancer_progress: '<div style="width: 95%"></div>' });
_.each(callbacks, function (callback) {
callback();
});
});
});
};
var getOtherDrops = function (pagedata, pageName = "") {
var results = [];
if (pagedata["data-Equipment"]) {
var json = {};
//Safeguarding Compendium data:
json = Compendium.parseArray(pagedata["data-Equipment"], `E_ZX21JB2W:Page:${pageName}:data-Equipment`);
var newItems = makeItemData(json.default);
results = results.concat(newItems);
}
if (pagedata["data-Bundle"]) {
var json = {};
//Safeguarding Compendium data:
json = Compendium.parseArray(pagedata["data-Bundle"], `E_D9F1YQRC:Page:${pageName}:data-Bundle`);
var newItems = makeItemData(json);
results = results.concat(newItems);
}
return results;
};
var getAllPages = function (pageArray, callback) {
if (pageArray.length < 1) {
callback();
return;
}
var getNames = [];
_.each(pageArray, function (page) {
if (page.name) {
getNames.push(page.name);
} else {
getNames.push(page);
}
});
getCompendiumPage(getNames, function (data) {
data = removeDuplicatedPageData(data);
var nextGet = [];
if (getNames.length === 1 && !Array.isArray(data)) {
data = { 0: data };
} //Fix single spell selections failing.
_.each(data, function (page, index) {
var pagedata = false;
_.each(pageArray, function (arrayData) {
if (arrayData.name.toLowerCase() == (page.data.Category + ":" + page.name).toLowerCase()) {
pagedata = arrayData;
}
});
if (pagedata && pagedata.data) {
page.data = _.extend(page.data, pagedata.data);
}
if (!page.id) page.data.Source = "Charactermancer";
page.name = page.name.replace(/@@!!@@/g, ""); // Hacky bugfix to prevent custom names from matching unavailable content
nextGet = nextGet.concat(getOtherDrops(page.data, page.name));
if (page.data["data-Starting Gold"] && !noEquipmentDrop) {
set["gp"] += parseInt(page.data["data-Starting Gold"]);
}
allPageData.push(page);
});
if (nextGet.length > 0) {
getAllPages(nextGet, callback);
} else {
callback();
}
});
};
var getRemovableSwaps = function () {
if (!SheetUtils.checkPath(data, "['lp-welcome']['values']['previous_repeating']")) return [];
if (!SheetUtils.checkPath(data, "['lp-choices']['values']")) return [];
//Safeguarding Compendium data:
const previousRepeating = Compendium.parseObject(data["lp-welcome"]["values"]["previous_repeating"], `E_0XPM9TX7`);
if (!previousRepeating["traits"]) return [];
const swaps = Object.keys(data["lp-choices"]["values"]).filter((entry) => {
return entry.indexOf("_feature_swap") > -1;
});
let removableSwaps = [];
for (let i = 0; i < swaps.length; i++) {
const swap = swaps[i];
const swapFor = swap.replace("_swap", "_for");
if (data["lp-choices"]["values"][swap] && data["lp-choices"]["values"][swapFor]) {
const swapTitle = data["lp-choices"]["values"][swap].replace(":", ": ");
const sectionsToRemove = previousRepeating["traits"]
.filter((entry) => {
return entry.name === swapTitle;
})
.map((entry) => {
return `repeating_traits_${entry.id}`;
});
removableSwaps = removableSwaps.concat(sectionsToRemove);
}
}
return removableSwaps;
};
var startTime = Date.now();
var allPageData = [];
var data = getCharmancerData(); //eventinfo.data;
var blobs = getAllLpBlobs(data, true);
var leveldata = getLevelingData(data);
var racedata = getLpRaceData(data, leveldata);
var abilities = getAbilityTotals(data, blobs);
var previous = data["lp-welcome"].values["previous_attributes"]
? Compendium.parseObject(data["lp-welcome"].values["previous_attributes"], `E_VDII7PKM`)
: {};
var repeating = data["lp-welcome"].values["previous_repeating"]
? Compendium.parseObject(data["lp-welcome"].values["previous_repeating"], `E_3G1ONHO4`)
: {};
var spelldata = data["lp-welcome"].values["spellinfo"]
? Compendium.parseObject(data["lp-welcome"].values["spellinfo"], `E_VJT809P5`)
: {};
var customtraits = { race: [], subrace: [], class: [], subclass: [], background: [] };
var profs = getProficiencies(data, "lp-finish", blobs);
var removesections = [];
var loudset = {};
var set = {};
var initial = {};
var allDrops = [];
var currentDrop = 0;
var totalDrops = 1;
var allSkills = [
"athletics",
"acrobatics",
"sleight_of_hand",
"stealth",
"arcana",
"history",
"investigation",
"nature",
"navigation",
"religion",
"animal_handling",
"insight",
"medicine",
"perception",
"survival",
"deception",
"intimidation",
"performance",
"persuasion",
];
var allAbilities = SheetUtils.toLowerCase(abilityList);
set["lp-mancer_status"] = "";
// Build new blobs for traits with inputs, collect custom traits and languages
_.each(["lp-choices", "lp-asi"], function (pagename) {
var slide = data[pagename];
if (slide) {
_.each(slide.values, function (value, name) {
if (name.substr(-11) == "trait_input") {
let sectionid = name.substring(0, name.length - 6);
let thisblob = {};
let thistrait = {};
let classnumber = _.last(slide.values[sectionid + "_section"].split("--")[0].split("-"));
let sectionname = "class" + classnumber;
if (slide.values[sectionid + "_section"].includes("subclass")) sectionname += "_subclass";
let thissection = _.findWhere(leveldata, { classnumber: parseInt(classnumber) });
thistrait.Name = slide.values[sectionid + "_name"].replace(/{{Input}}/g, value);
thistrait.Desc = slide.values[sectionid + "_desc"] ? slide.values[sectionid + "_desc"].replace(/{{Input}}/g, value) : "";
thisblob.Traits = JSON.stringify([thistrait]);
blobs.names[sectionname].push(sectionid);
thissection[slide.values[sectionid + "_section"].includes("subclass") ? "subclass" : "class"].blobs[sectionid] = thisblob;
}
if (name.split("_")[2] == "custom" && name.substr(-10) == "trait_name") {
var sectionid = name.substring(0, name.length - 5);
var thistrait = {};
thistrait.Name = slide.values[sectionid + "_name"];
thistrait.Desc = slide.values[sectionid + "_desc"] ? slide.values[sectionid + "_desc"] : "";
customtraits[name.split("_")[3]].push(thistrait);
}
if (value == "custom" && name.substr(-18) == "proficiency_choice") {
var sectionid = name.substring(0, name.length - 7);
if (slide.values[sectionid + "_custom"] && slide.values[sectionid + "_type"]) {
profs.all[slide.values[sectionid + "_type"]].push(slide.values[sectionid + "_custom"]);
}
}
if (name.substr(-17) === "utilityrow_result") {
let sectionid = name.substring(0, name.length - 7);
let thissection = _.findWhere(leveldata, { classnumber: parseInt(slide.values[sectionid + "_parent"][5]) });
let section = slide.values[sectionid + "_parent"].includes("subclass") ? "subclass" : "class";
if (thissection[section].blobs[slide.values[sectionid + "_blob"]].Traits)
thissection[section].blobs[slide.values[sectionid + "_blob"]].Traits = thissection[section].blobs[
slide.values[sectionid + "_blob"]
].Traits.replace(/{{Input}}/g, value);
}
});
}
});
_.each(leveldata, function (level) {
var name = "class" + level.classnumber;
var mc = "multiclass" + (level.classnumber - 1);
var thisclass = { name: removeExpansionInfo(level.classname), data: level.class };
//if this is an mc class, add mc data
if (name != "class1") thisclass.data.multiclass = mc;
thisclass.data.theseblobs = blobs.names[name];
allPageData.push(thisclass);
if (level.subclass) {
var thissubclass = { name: removeExpansionInfo(level.subclassname), data: level.subclass };
//if this is an mc subclass, add mc data
if (name != "class1") thissubclass.data.multiclass = mc;
thissubclass.data.theseblobs = blobs.names[name + "_subclass"];
allPageData.push(thissubclass);
}
//set up the setAttrs for this class
if (name === "class1") {
set["base_level"] = parseInt(previous["base_level"]) + level.addlevel;
} else {
initial[mc + "_lvl"] = previous[mc + "_flag"] === "0" ? level.addlevel : parseInt(previous[mc + "_lvl"]) + level.addlevel;
}
});
_.each(racedata, function (level, label) {
_.each(["", "sub"], function (prefix) {
let section = prefix + label;
if (previous[section] && blobs.names[section] && blobs.names[section].length > 0) {
let thisDrop = {
name: previous[section],
data: { Category: section[0].toUpperCase() + section.substring(1, section.length) + "s" },
};
thisDrop.data.blobs = data["lp-welcome"].data["lp-" + section].blobs;
thisDrop.data.theseblobs = blobs.names[section];
allPageData.push(thisDrop);
}
});
});
//Set up proficiency drops
_.each(["Armor", "Language", "Tool", "Weapon", "Other"], function (proftype) {
_.each(profs.all[proftype], function (prof) {
if (prof) {
var profdata = { name: "Proficiencies:" + prof, data: { Type: proftype } };
if (profs.all.Expertise.includes(prof)) {
profdata.data["toolbonus_base"] = "(@{pb}*2)";
allDrops.unshift(profdata);
} else {
allDrops.push(profdata);
}
}
});
});
//Set up expertise drops
_.each(_.uniq(profs.all.Skill.concat(profs.all.Expertise)), function (prof) {
var profName = prof.toLowerCase().replace(/ /g, "_");
initial[profName + "_prof"] = "(@{pb}*@{" + profName + "_type})";
if (profs.all.Expertise.indexOf(prof) != -1) {
set[profName + "_type"] = 2;
}
});
//Setup feat drops
const getLPFeats = () => {
if (!SheetUtils.checkPath(data, "['lp-asi']['values']")) return 0;
else {
var feats = {};
let featSelected = false;
Object.keys(data["lp-asi"]["values"]).forEach((value) => {
if (value.indexOf("asi-row_switch") > -1 && data["lp-asi"]["values"][value] === "feat") {
const rowID = SheetUtils.getUID(value);
const feat = {
name: data["lp-asi"]["values"][`repeating_${rowID}_asi-row_feat`],
data: { Properties: data["lp-asi"]["values"][`repeating_${rowID}_asi-row_source`] },
};
feats[rowID] = feat;
featSelected = true;
}
});
if (featSelected) {
const otherOptionsAndFeatures = Object.keys(data["lp-asi"]["values"]).filter((entry) => {
return entry.includes("other_options_and_features");
});
otherOptionsAndFeatures.forEach((feature) => {
if (data["lp-asi"]["values"][feature]) {
const featureData = { name: data["lp-asi"]["values"][feature], data: { Properties: "1st Level" } };
allDrops.push(featureData);
}
});
const skills = Object.keys(data["lp-asi"]["values"]).filter((entry) => {
return entry.includes("feat_skill_choice");
});
skills.forEach((skill) => {
if (data["lp-asi"]["values"][skill]) {
const skillData = { name: data["lp-asi"]["values"][skill], data: { Properties: "1st Level" } };
allDrops.push(skillData);
}
});
const expertise = Object.keys(data["lp-asi"]["values"]).filter((entry) => {
return entry.includes("feat_expertise_choice");
});
expertise.forEach((skill) => {
if (data["lp-asi"]["values"][skill]) {
const expertiseData = { name: data["lp-asi"]["values"][skill], data: { Properties: "1st Level" } };
allDrops.push(expertiseData);
}
});
const spells = Object.keys(data["lp-asi"]["values"]).filter((entry) => {
return entry.includes("feat_spell_choice");
});
let i = 0;
spells.forEach((spell) => {
if (data["lp-asi"]["values"][spell]) {
const spellData = { name: removeExpansionInfo(data["lp-asi"]["values"][spell]), data: { Properties: "1st Level" } };
const rowID = SheetUtils.getUID(spell);
if (
SheetUtils.checkPath(data, "['lp-asi']['data']") &&
data["lp-asi"]["data"][`repeating_${rowID}_asi-row_feat`] &&
data["lp-asi"]["data"][`repeating_${rowID}_asi-row_feat`]["data-Pick Spells"]
) {
//Safeguarding Compendium data:
const pickData = Compendium.parseArray(
data["lp-asi"]["data"][`repeating_${rowID}_asi-row_feat`]["data-Pick Spells"],
`E_1ILI6VSS`
)[i];
if (pickData["Spellcasting Ability"]) {
let spellcastingAbility = pickData["Spellcasting Ability"].toLowerCase();
if (spellcastingAbility === "asi") {
let asiKey =
SheetUtils.checkPath(data, "['lp-asi']['values']") &&
data["lp-asi"]["values"][`repeating_${rowID}_asi-row_feat_ability_choice`]
? data["lp-asi"]["values"][`repeating_${rowID}_asi-row_feat_ability_choice`]
: false;
if (asiKey) {
spellcastingAbility = asiKey.toLowerCase();
}
}
spellData.data["Spellcasting Ability"] = spellcastingAbility;
}
if (pickData["Innate"]) {
spellData.data["Innate"] = pickData["Innate"];
}
}
allDrops.push(spellData);
i++;
}
});
}
return feats;
}
};
const feats = getLPFeats();
Object.keys(feats).forEach((feat) => {
allDrops.push(feats[feat]);
});
//Set up Ability scores
initial["hp"] = getHpTotal(data, blobs);
initial["hp_max"] = initial["hp"];
_.each(allAbilities, function (ability) {
loudset[ability + "_base"] = abilities[ability];
loudset[ability + "_maximum"] = abilities[ability + "_maximum"];
});
//Set up spell drops
const selectedSpells = getSelectedSpells();
if (selectedSpells.length > 0) {
selectedSpells.forEach((spell) => {
const spelldata = {
name: `Spells:${spell.name}`,
ability: spell.ability,
data: {},
};
allDrops.push(spelldata);
});
/*
_.each(data["lp-spells"].repeating, function (repid) {
if (data["lp-spells"].values[repid + "_checked"]) {
var spelldata = { name: "Spells:" + data["lp-spells"].values[repid + "_name"], data: {} };
if (data["lp-spells"].values[repid + "_ability"])
spelldata.data["spellcasting_ability"] = data["lp-spells"].values[repid + "_ability"];
if (data["lp-spells"].values[repid + "_class"]) spelldata.data["spellclass"] = data["lp-spells"].values[repid + "_class"];
if (data["lp-spells"].values[repid + "_source"]) spelldata.data["spellsource"] = data["lp-spells"].values[repid + "_source"];
allDrops.push(spelldata);
}
if (data["lp-spells"].values[repid + "_existing"] && !data["lp-spells"].values[repid + "_checked"]) {
var lvl = data["lp-spells"].values[repid + "_level"];
var id = "";
_.each(repeating["spell-" + lvl], function (spell, spellid) {
if (spell.spellname.toLowerCase() == data["lp-spells"].values[repid + "_name"].toLowerCase()) {
id = spellid;
if (spell.spellattackid) removesections.push("repeating_attack_" + spell.spellattackid);
}
});
removesections.push("repeating_spell-" + lvl + "_" + id);
}
});
*/
} else {
//If there was no spell page, need to drop any new "known" spells
_.each(blobs.sorted, function (section, name) {
_.each(section, function (blob, blobName) {
if (blob.Spells) {
//Safeguarding Compendium data:
spells = Compendium.parseObject(blob.Spells, `E_Q911IA8P:Blob:${blobName}:Spells`);
_.each(spells, function (spell) {
if (spell.Known) {
_.each(spell.Known, function (spellname) {
var thisspell = { name: "Spells:" + spellname, data: {} };
var thisclass =
name.substring(0, 5) === "class" ? _.last(data["lp-levels"].values[name.split("_")[0]].split(":")) : "Racial";
if (spell.Ability) thisspell.data["spellcasting_ability"] = spell.Ability;
if (spell.Source) thisspell.data["spellsource"] = spell.Source;
if (spell.Innate) thisspell.data["innate"] = spell.Innate;
thisspell.data["spellclass"] = thisclass;
allDrops.push(thisspell);
});
}
});
}
});
});
}
//Gater spells from class features
_.each(data, function (page, pagename) {
if (pagename.split("-")[0] == "lp") {
_.each(page.values, function (value, name) {
if (name.substr(-18) === "_utilityrow_spells") {
//Safeguarding Compendium data:
let featurespells = Compendium.parseObject(value, `E_0WMFEWSE:Page:${pagename}:${name}`);
_.each(featurespells, function (spell, spellname) {
var thisspell = { name: "Spells:" + spellname, data: {} };
if (spell.ability) thisspell.data["spellcasting_ability"] = spell.ability;
if (spell.known) thisspell.data["spellsource"] = spell.known;
if (spell.spellclass) thisspell.data["spellclass"] = spell.spellclass;
allDrops.push(thisspell);
});
}
});
}
});
//unset charmancer step
set["lpmancer_status"] = "";
set["charactermancer_step"] = "lp-welcome";
_.each(removesections, function (rowid) {
removeRepeatingRow(rowid);
});
setCharmancerText({ mancer_progress: '<div style="width: 5%"></div>' });
//first set is silent to prevent unwanted sheet workers
setAttrs(initial, { silent: true }, function () {
setCharmancerText({ mancer_progress: '<div style="width: 10%"></div>' });
//reset remaining attributes, and set ability scores/custom info
setAttrs(loudset, function () {
setCharmancerText({ mancer_progress: '<div style="width: 15%"></div>' });
getAllPages(allDrops, function () {
setCharmancerText({ mancer_progress: '<div style="width: 20%"></div>' });
doAllDrops(allPageData, function () {
setAttrs(set, function () {
setCharmancerText({ mancer_progress: '<div style="width: 100%"></div>' });
organize_section_proficiencies();
update_skills(allSkills);
update_attacks("all");
formatPrintedTools();
formatPrintedSkills();
deleteCharmancerData(["lp-welcome", "lp-levels", "lp-choices", "lp-asi", "lp-spells", "lp-summary"]);
var endTime = Date.now();
//console.log("Elapsed time: ");
//console.log((endTime-startTime)/1000);
finishCharactermancer();
});
});
});
});
});
getRemovableSwaps().forEach((rowid) => {
removeRepeatingRow(rowid);
});
});
on("page:lp-welcome page:lp-levels page:lp-choices page:lp-asi page:lp-spells page:lp-summary page:lp-spellchoice", (eventInfo) => {
showOptionalAttributeFields();
});
on("mancerchange:repeating_row", function (eventinfo) {
if (eventinfo.sourceAttribute && eventinfo.sourceAttribute.indexOf("feature_swap") > -1) {
const UID = SheetUtils.getUID(eventinfo.sourceAttribute);
const flag = `${eventinfo.sourceSection} .swap-selected`;
if (!eventinfo.newValue) {
const swapFor = eventinfo.sourceAttribute.replace("_swap", "_for");
setAttrs({ [swapFor]: "" });
setCharmancerText({ [flag]: "" });
} else {
setCharmancerText({ [flag]: "yes" });
}
}
});
on(
"mancerchange:repeating_asi-row_feat_skill_choice1 mancerchange:repeating_asi-row_feat_skill_choice2 mancerchange:repeating_asi-row_feat_skill_choice3 mancerchange:repeating_asi-row_feat_skill_choice4",
(eventinfo) => {
if (eventinfo.sourceType && eventinfo.sourceType === "player") {
getRepeatingSections("choices", (repeating) => {
const asi = repeating.list.filter((entry) => {
return entry.includes("asi-row");
});
const choosenSkill = [eventinfo.newValue.split(":")[1]];
asi.forEach((row) => {
for (let i = 1; i <= 4; i++) {
const selector = `${row}_feat_expertise_choice_${i}`;
setValidExpertiseOptions("lp-asi", selector, choosenSkill);
}
});
});
}
}
);
//Worker for every page to add topbar/buttons
on("page:utility-newrace", function(eventinfo) {
var mancerdata = getCharmancerData();
recalcButtons(mancerdata, eventinfo.triggerName);
if(eventinfo.triggerName != "utility-welcome" && eventinfo.triggerName != "utility-summary") {
addRepeatingSection("topbar-holder", "leveler-topbar");
if(eventinfo.triggerName != "utility-choices" && eventinfo.triggerName != "utility-spells") recalcLpData(getAllUtilityBlobs(), "utility");
getProficiencies(mancerdata, eventinfo.currentStep);
}
});
on("page:utility-welcome", function(eventinfo) {
deleteCharmancerData(["utility-welcome", "utility-newrace"], () => {
loadSheetData(sheet => {
setAttrs({utility_data: JSON.stringify(sheet)}, () => {
loadPreviousData(eventinfo);
});
});
});
});
on("page:utility-newrace", function(eventinfo) {
const utility_data = JSON.parse(getCharmancerData()["utility-welcome"]["values"]["utility_data"]);
handlePreviousRaceAbilities();
handlePreviousRaceTraits(utility_data);
if(!SheetUtils.checkPath(getCharmancerData(), `["utility-newrace"]["values"]["newrace"]`)) {
setAttrs({newrace: utility_data.core.options});
}
setCharmancerText({"lineage-name": utility_data.core.options.replace(/Races:|\?.+/g, "")});
});
on("mancerchange:newrace", function(eventinfo) {
handleMancerchangeNewrace(eventinfo);
});
on("mancerchange:newsubrace", function(eventinfo) {
handleMancerchangeNewsubrace(eventinfo);
});
for(let i=1; i<=6; i++) {
on(`mancerchange:repeating_decrease${i}_selected`, eventinfo => {
recalcLpData(getAllUtilityBlobs(), "utility");
});
}
const isFieldSet = field => {
const mancerdata = getCharmancerData();
return SheetUtils.checkPath(mancerdata, `["utility-newrace"]["values"]["${field}"]`);
}
const getCurrentFieldValue = (field, initialValue) => {
const mancerdata = getCharmancerData();
if(isFieldSet(field)) {
return mancerdata ["utility-newrace"]["values"][field];
}
return initialValue || "";
};
const handlePreviousRaceAbilities = data => {
data = data || getCharmancerData();
const abilitiyBonuses = [];
["race", "subrace"].forEach(section => {
if(SheetUtils.checkPath(data, `["utility-welcome"]["data"]["lp-${section}"]`)) {
if(data["utility-welcome"]["data"][`lp-${section}`]["data-Ability Score Increase"]) {
const increase = data["utility-welcome"]["data"][`lp-${section}`]["data-Ability Score Increase"];
Object.entries(increase).forEach(([key, value]) => {
abilitiyBonuses.push({
ability: key.toLowerCase(),
bonus: parseInt(value)
});
});
}
if(data["utility-welcome"]["data"][`lp-${section}`]["data-Ability Score Choice"]) {
const increase = data["utility-welcome"]["data"][`lp-${section}`]["data-Ability Score Choice"];
const abilities = parseInt(increase.split("+")[0]);
const bonus = parseInt(increase.split("+")[1]);
for(let i=0; i<abilities; i++) {
abilitiyBonuses.push({
bonus: bonus
});
}
}
}
});
for(let i=0; i<abilitiyBonuses.length; i++) {
const id = `ability-decrease-${i+1}`;
addRepeatingSection("previous_race_abilities", "utility-select", `decrease${1}`, sectionid => {
const defaultValue = (abilitiyBonuses[i].ability) ? `${abilitiyBonuses[i].ability} -${abilitiyBonuses[i].bonus}` : null;
const currentMancerValue = getCurrentFieldValue(`${sectionid}_selected`);
const currentValue = (!isFieldSet(`${sectionid}_default`)) ?
getCurrentFieldValue(`${sectionid}_selected`, defaultValue) :
currentMancerValue;
if(!isFieldSet(`${sectionid}_default`)) {
const update = {
[`${sectionid}_default`]: defaultValue || "*"
};
if(defaultValue)
setAttrs(update, {silent:true});
}
const options = (currentValue) ? { selected: currentValue } : {};
const abilities = globalAttributesByCategory.abilities.map(ability => { return `${ability} -${abilitiyBonuses[i].bonus}`; });
setCharmancerOptions(`${sectionid}_selected`, abilities, options, () => {});
});
};
};
const handlePreviousRaceTraits = data => {
const mancerdata = getCharmancerData();
let index = 1;
//Removing traits:
let showTraits = false;
Object.entries(data.repeating_traits).forEach(([rowid, trait]) => {
const race = data.core.race;
const subrace = data.core.subrace;
const id = `remove-repeating-traits-${rowid}`;
if(isRacialTrait(race, subrace, trait)) {
addRepeatingSection("previous_race_traits", "utility-check", `remove${index}`, sectionid => {
if(!isFieldSet(`${sectionid}_target`)) {
const update = {
[`${sectionid}_checked`]: "1",
[`${sectionid}_target`]: `repeating_traits_${rowid}`
};
setAttrs(update, {silent:true});
}
const labelClass = `${sectionid} .check-label`;
setCharmancerText({[labelClass]: trait.name});
});
showTraits = true;
index++;
if(SheetUtils.checkPath(mancerdata, `["utility-welcome"]["data"]["lp-race"]["blobs"]["${trait.name}"]`)) {
const blob = mancerdata["utility-welcome"]["data"]["lp-race"]["blobs"][trait.name];
if(blob["Actions"]) {
JSON.parse(blob["Actions"]).forEach(action => {
const atkname = action["Name"];
Object.entries(data.repeating_attack).forEach(([id, attack]) => {
if(attack.atkname === atkname) {
addRepeatingSection("previous_race_traits", "utility-check", `remove${index}`, sectionid => {
if(!isFieldSet(`${sectionid}_target`)) {
const update = {
[`${sectionid}_checked`]: "1",
[`${sectionid}_target`]: `repeating_attack_${id}`
};
setAttrs(update, {silent:true});
}
const labelClass = `${sectionid} .check-label`;
setCharmancerText({[labelClass]: `${atkname} (Attack)`});
});
showTraits = true;
index++;
}
});
});
}
}
}
});
if(!showTraits) setCharmancerText({"previous_race_traits": ""});
//Removing spells:
let showSpells = false;
Object.entries(data.spells).forEach(([repeating, spells]) => {
Object.entries(spells).forEach(([rowid, spell]) => {
const race = data.core.race;
const subrace = data.core.subrace;
const id = `remove-${repeating}-${rowid}`;
if(isRacialSpell(race, subrace, spell)) {
addRepeatingSection("previous_race_spells", "utility-check", `remove${index}`, sectionid => {
if(!isFieldSet(`${sectionid}_target`)) {
const update = {
[`${sectionid}_checked`]: "1",
[`${sectionid}_target`]: `${repeating}_${rowid}`
};
setAttrs(update, {silent:true});
}
const labelClass = `${sectionid} .check-label`;
setCharmancerText({[labelClass]: spell.spellname});
});
showSpells = true;
index++;
}
});
});
if(!showSpells) setCharmancerText({"previous_race_spells": ""});
let otherProfIndex = 1;
let skillIndex = 1;
["race", "subrace"].forEach(section => {
if(SheetUtils.checkPath(mancerdata, `["utility-welcome"]["data"]["lp-${section}"]["blobs"]`)) {
const blobs = mancerdata["utility-welcome"]["data"][`lp-${section}`]["blobs"];
const getList = value => {
let result = [];
if(value["Proficiencies"]) {
result = [...result, ...value["Proficiencies"]];
}
if(value["Choice"]) {
value["Choice"].forEach(choice => { result.push("*"); });
}
return result;
};
const generateCustomOptions = (id, options, selection, def="Choose") => {
const data = getCharmancerData();
if(SheetUtils.checkPath(data, `["utility-newrace"]["values"]["${id}_selected"]`)) {
const index = options.filter(opt => { return opt[1] === data["utility-newrace"]["values"][`${id}_selected`]; });
if(index.length > 0) selection = index[0][0];
else selection = "";
}
let result = `<option>${def}</option>`;
options.forEach(option => {
const selected = (selection === option[0]) ? " selected" : "";
result += `<option value="${option[1]}"${selected}>${option[0]}</option>`;
});
return result;
};
const processOtherProficiencies = (type, results) => {
results.forEach(proficiency => {
const id = `remove-proficiency-${otherProfIndex}`;
addRepeatingSection("previous_race_other_proficiencies", "utility-select", `remove${index}`, sectionid => {
customOptions = [];
Object.entries(data.repeating_proficiencies).filter(([key, value]) => {
if(value.prof_type !== type.toUpperCase()) return;
customOptions.push([
value.name,
`repeating_proficiencies_${key}`
]);
});
const selected = (proficiency !== "*") ? proficiency : false;
const opts = generateCustomOptions(sectionid, customOptions, selected, `Choose a ${type.toLowerCase()} proficiency`);
setCharmancerText({[`${sectionid} select`]: opts});
});
otherProfIndex++;
index++
});
};
Object.entries(blobs).forEach(([name, blob]) => {
if(blob["Group"] && blob["Group"] === "Ancestral Legacy") return;
Object.entries(blob).forEach(([property, value]) => {
if(property === "Skill Proficiency") {
const results = getList(JSON.parse(value));
results.forEach(skill => {
const id = `unset-skill-${skillIndex}`;
addRepeatingSection("previous_race_skills", "utility-select", `unset${skillIndex}`, sectionid => {
const defaultValue = (skill !== "*") ? skill : null;
const currentMancerValue = getCurrentFieldValue(`${sectionid}_selected`);
const currentValue = (!isFieldSet(`${sectionid}_default`)) ?
getCurrentFieldValue(`${sectionid}_selected`, defaultValue) :
currentMancerValue;
if(!isFieldSet(`${sectionid}_default`)) {
const update = {
[`${sectionid}_default`]: defaultValue || "*"
};
if(defaultValue)
setAttrs(update, {silent:true});
}
const options = (currentValue) ? { selected: currentValue } : {};
setCharmancerOptions(`${sectionid}_selected`, data.skills, options, () => {});
});
});
skillIndex++;
}
const proficiencySections = (isAncestralLineage()) ? ["Weapon", "Armor"] : ["Language", "Weapon", "Armor", "Other"];
proficiencySections.forEach(section => {
if(property === `${section} Proficiency`) {
processOtherProficiencies(section, getList(JSON.parse(value)));
}
});
});
});
}
});
if(skillIndex === 1) setCharmancerText({"previous_race_skills": ""});
if(otherProfIndex === 1) setCharmancerText({"previous_race_other_proficiencies": ""});
};
const handleMancerchangeNewrace = eventinfo => {
clearTasha(eventinfo, false, () => {
var mancerdata = getCharmancerData();
getProficiencies(mancerdata, eventinfo.currentStep);
changeCompendiumPage("race-info", eventinfo.newValue);
hideChoices();
var reset = {};
if(!(eventinfo.newValue === "" || eventinfo.newValue === undefined)) {
showChoices(["race_options"]);
}
recalcLpData(getAllUtilityBlobs(), "utility");
if(eventinfo.sourceType == "player") {
reset = {race_ability_choice1: "", race_ability_choice2: "", subrace_ability_choice1: "", subrace_ability_choice2: "", newsubrace: "", race_name: "", subrace_name: ""};
_.each(abilityList, function(ability){
reset["race_custom_" + ability.toLowerCase()] = 0;
reset["subrace_custom_" + ability.toLowerCase()] = 0;
reset[ability.toLowerCase() + "_save"] = "";
});
reset["custom_race_spell_ability"] = "";
reset["custom_race_spell_number"] = "1";
reset["custom_race_spell_list"] = "";
reset["race_name"] = "";
reset["subrace_name"] = "";
reset["has_subrace"] = "";
clearRepeatingSections("race_holder");
clearRepeatingSections("subrace_holder");
}
if(eventinfo.newValue === "Rules:Races") {
//Clears saved data for this field
getCompendiumPage("");
setAttrs(reset, function() {
var update = {"race_text": ""};
var options = eventinfo.sourceType == "player" ? {selected: ""} : {};
showChoices(["custom_race"]);
setCharmancerText(update);
addCustomSections("Race");
deleteSlideData('l1-feat');
});
} else {
getCompendiumPage(eventinfo.newValue, function(p) {
p = removeDuplicatedPageData(p);
setAttrs(reset, function() {
const usingTasha = isTashaEnabled();
mancerdata = getCharmancerData();
var update = {};
var showList = ["race_always", "race_traits"];
var possibles = ["race_size", "race_speed", "race_ability_score", "race_traits"];
var data = p["data"];
_.each(proficiencyList, function(type){
possibles.push("race_" + type.toLowerCase() + "s");
});
_.each(possibles, function(key) {
update[key] = "";
});
if(data["Size"]) {update["race_size"] = data["Size"];};
if(data["Speed"]) {update["race_speed"] = data["Speed"];};
showList = showList.concat(handleAbilities(data, "race", usingTasha));
if(data["data-Ability Score Increase"]) {
var abilityText = [];
var json = JSON.parse(data["data-Ability Score Increase"]);
_.each(json, function(increase, ability) {
abilityText.push(ability + " +" + increase);
});
update["race_ability_score"] = abilityText.join(", ");
}
let previousLevel = "1";
if(eventinfo.currentStep && eventinfo.currentStep.split("-")[0] === "utility") {
previousLevel = String(parseInt(mancerdata["utility-welcome"].values["lp-previous_level"])+1);
}
if(isAncestralLineage() && data.blobs) {
handleBlobs(data.blobs, {context: "Utilitymancer", filters: {"Level": `<${previousLevel}`}, section: "race", slide: eventinfo.currentStep});
} else {
handleBlobs(data.blobs, {filters: {"Level": `<${previousLevel}`}, section: "race", slide: eventinfo.currentStep});
}
setCharmancerText(update);
var race_name = eventinfo.newValue && eventinfo.newValue.split(":").length > 1 && eventinfo.newValue.split(":")[0] === "Races" ? eventinfo.newValue.split(":")[1] : false;
race_name = removeExpansionInfo(race_name);
if(race_name) {
var subOptions = {show_source: true};
if(eventinfo.sourceType != "player") {
subOptions.silent = true;
} else {
subOptions.selected = "";
}
setCharmancerOptions("newsubrace","Category:Subraces data-Parent:" + race_name, subOptions, function(values) {
if (values.length) {
setAttrs({"has_subrace": "true"});
showChoices(["subrace"]);
}
});
}
else {
hideChoices(["subrace"]);
}
showChoices(showList);
recalcLpData(getAllUtilityBlobs(), "utility");
});
});
}
});
};
const handleMancerchangeNewsubrace = eventinfo => {
clearTasha(eventinfo, true, () => {
var mancerdata = getCharmancerData();
getProficiencies(mancerdata, eventinfo.currentStep);
changeCompendiumPage("race-info", eventinfo.newValue);
var initHide = ["subrace_possible", "custom_trait_2", "custom_trait_3", "custom_trait_4"];
var reset = {};
if(eventinfo.newValue === "" || eventinfo.newValue === undefined) {
initHide.push("subrace_options");
}
else {
showChoices(["subrace_options"]);
}
hideChoices(initHide);
recalcLpData(getAllUtilityBlobs(), "utility");
if(eventinfo.sourceType == "player") {
reset = {subrace_ability_choice1: "", subrace_ability_choice2: "", subrace_ability_choice3: "", subrace_name: ""};
_.each(abilityList, function(ability){
reset["race_custom_" + ability.toLowerCase()] = 0;
reset["subrace_custom_" + ability.toLowerCase()] = 0;
});
reset["custom_race_spell_ability"] = "";
reset["custom_race_spell_number"] = "1";
reset["custom_race_spell_list"] = "";
reset["subrace_name"] = "";
clearRepeatingSections("subrace_holder");
}
if(eventinfo.newValue === "Rules:Races") {
//Clears saved data for this field
getCompendiumPage("");
setAttrs(reset, function() {
var update = {"subrace_text": ""};
var options = eventinfo.sourceType == "player" ? {selected: ""} : {};
showChoices(["custom_subrace"]);
setCharmancerText(update);
addCustomSections("Subrace");
deleteSlideData('l1-feat');
});
} else {
const usingTasha = isTashaEnabled();
hideChoices(["custom_subrace"]);
getCompendiumPage(eventinfo.newValue, function(p) {
p = removeDuplicatedPageData(p);
var update = {};
var showList = ["subrace_choices"];
var possibles = ["subrace_speed", "subrace_ability_score"];
var data = p["data"];
_.each(possibles, function(key) {
update[key] = "";
});
if(!data["data-Feats"]) deleteSlideData('l1-feat', function() { recalcLpData(getAllUtilityBlobs(), "utility"); });
if(data["Speed"]) {
update["subrace_speed"] = data["Speed"];
showList.push("subrace_speed_row");
};
showList = showList.concat(handleAbilities(data, "subrace", usingTasha));
if(data["data-Ability Score Increase"]) {
var abilityText = [];
var json = JSON.parse(data["data-Ability Score Increase"]);
_.each(json, function(increase, ability) {
abilityText.push(ability + " +" + increase);
});
update["subrace_ability_score"] = abilityText.join(", ");
}
let previousLevel = "1";
if(eventinfo.currentStep && eventinfo.currentStep.split("-")[0] === "utility") {
previousLevel = String(parseInt(mancerdata["utility-welcome"].values["lp-previous_level"])+1);
}
if(isAncestralLineage() && data.blobs) {
handleBlobs(data.blobs, {context: "Utilitymancer", filters: {"Level": `<${previousLevel}`}, section: "subrace", slide: eventinfo.currentStep});
} else {
handleBlobs(data.blobs, {filters: {"Level": `<${previousLevel}`}, section: "subrace", slide: eventinfo.currentStep});
}
setCharmancerText(update);
showChoices(showList);
setAttrs(reset);
recalcLpData(getAllUtilityBlobs(), "utility");
});
}
});
}
const loadPreviousData = eventinfo => {
var getInfo = (sections, callback, results) => {
results = results || {};
if(sections.length > 0) {
var section = sections.pop();
getSectionIDs(section, function(ids) {
results[section] = ids;
getInfo(sections, callback, results);
});
} else {
callback(results);
}
};
var capitalize = function(string) {
return string.split(" ").map((x) => {
x = x.toLowerCase();
return x[0].toUpperCase() + x.substr(1, x.length);
}).join(" ");
};
var spellSections = ["spell-cantrip"];
var allSkills = ["athletics", "acrobatics", "sleight_of_hand", "stealth", "arcana", "history", "investigation", "nature", "navigation", "religion", "animal_handling", "insight", "medicine", "perception", "survival","deception", "intimidation", "performance", "persuasion"];
var skillget = [];
for(var x=1; x<=9; x++) {
spellSections.push("spell-" + x);
}
getInfo(spellSections, function(results) {
var getList = [];
_.each(results, function(sectionids, sectionname) {
_.each(sectionids, function(spellid) {
getList.push("repeating_" + sectionname + "_" + spellid + "_spellname");
getList.push("repeating_" + sectionname + "_" + spellid + "_spellclass");
getList.push("repeating_" + sectionname + "_" + spellid + "_spelllevel");
getList.push("repeating_" + sectionname + "_" + spellid + "_spellsource");
});
});
getAttrs(getList, function(attrs) {
var spellInfo = {};
_.each(attrs, function(data, name) {
var namearray = name.split("_");
var thisattr = namearray.pop();
var thisname = namearray.join("_");
spellInfo[thisname] = spellInfo[thisname] || {};
spellInfo[thisname][thisattr] = data;
});
setAttrs({spellinfo: JSON.stringify(spellInfo)});
});
});
const attribsWithModsAndMaximum = SheetUtils.toLowerCase(abilityList.concat(abilityList.map(attribute => { return `${attribute}_mod`})).concat(abilityList.map(attribute => { return `${attribute}_maximum`})));
const baseAttributes = SheetUtils.toLowerCase(abilityList).map(entry => { return `${entry}_base`}).concat(SheetUtils.toLowerCase(abilityList).map(entry => { return `${entry}_flag`}));
getAttrs(attribsWithModsAndMaximum, final => {
getAttrs(baseAttributes, base => {
const modfiedAbilityList = Object.keys(base).filter(entry => { return entry.indexOf('_base') > -1 && base[entry.replace('_base', '_flag')] == 1});
_.each(final, function(x, y) {
final[y] = parseInt(x);
});
modfiedAbilityList.forEach(attribute => {
final[attribute.replace('_base', '')] = parseInt(base[attribute]);
final[attribute.replace('_base', '_mod')] = Math.floor((parseInt(base[attribute]) - 10) / 2);
});
setAttrs({previous_abilities: JSON.stringify(final)});
});
});
getAttrs(["level", "class", "subclass", "base_level", "multiclass1_flag", "multiclass2_flag", "multiclass3_flag", "multiclass1", "multiclass1_lvl", "multiclass1_subclass", "multiclass2", "multiclass2_lvl", "multiclass2_subclass", "multiclass3", "multiclass3_lvl", "multiclass3_subclass", "hp_max", "class_resource_name", "other_resource_name", "speed", "race", "subrace", "background", "custom_class", "cust_classname", "cust_hitdietype"], function(v) {
let set = {};
set["previous_attributes"] = JSON.stringify(v);
set["lp-race"] = "Races:" + v.race;
set["lp-subrace"] = "Subraces:" + v.subrace;
set["lp-background"] = "Backgrounds:" + v.background.split("(")[0].trim();
set["lp-previous_level"] = v["level"];
setAttrs(set);
});
_.each(allSkills, function(skill) {
skillget.push(skill + "_prof");
skillget.push(skill + "_type");
});
getAttrs(skillget, function(v) {
var proficiencies = {"Weapon": [], "Armor": [], "Skill": [], "Tool": [], "Language": [], "Other": [], "Expertise": []};
_.each(allSkills, function(skill) {
if(v[skill + "_prof"] !== "0") {
proficiencies.Skill.push(capitalize(skill.replace(/_/g, " ")));
}
if(v[skill + "_type"] !== "1" && v[skill + "_prof"] !== "0") {
proficiencies.Expertise.push(capitalize(skill.replace(/_/g, " ")));
}
});
getSectionIDs("tool", function(ids) {
var getList = [];
_.each(ids, function(id) {
getList.push("repeating_tool_" + id + "_toolname");
getList.push("repeating_tool_" + id + "_toolbonus_base");
});
getAttrs(getList, function(v) {
_.each(ids, function(id) {
proficiencies.Tool.push(v["repeating_tool_" + id + "_toolname"]);
if(v["repeating_tool_" + id + "_toolbonus_base"] == "(@{pb}*2)") {
proficiencies.Expertise.push(v["repeating_tool_" + id + "_toolname"]);
};
});
getSectionIDs("proficiencies", function(ids) {
var getList = [];
_.each(ids, function(id) {
getList.push("repeating_proficiencies_" + id + "_name");
getList.push("repeating_proficiencies_" + id + "_prof_type");
});
getAttrs(getList, function(v) {
_.each(ids, function(id) {
proficiencies[capitalize(v["repeating_proficiencies_" + id + "_prof_type"])].push(v["repeating_proficiencies_" + id + "_name"]);
});
setAttrs({previous_proficiencies: JSON.stringify(proficiencies)});
});
});
});
});
});
get_repeating_data(function(repeating) {
setAttrs({previous_repeating: JSON.stringify(repeating)});
});
};
const loadSheetData = callback => {
//Populating skill list:
var allSkills = globalAttributesByCategory.skills.all();
var skillget = [];
allSkills.forEach(skill => {
skillget.push(skill + "_prof");
skillget.push(skill + "_type");
});
//Populating attribute lists:
const attribsWithModsAndMaximum = [];
const baseAttributes = [];
abilityList.forEach(ability => {
ability = ability.toLowerCase();
attribsWithModsAndMaximum.push(ability);
attribsWithModsAndMaximum.push(`${ability}_mod`);
attribsWithModsAndMaximum.push(`${ability}_maximum`);
baseAttributes.push(`${ability}_base`);
baseAttributes.push(`${ability}_flag`);
});
const coreInformation = ["utilitymancer_options", "class", "subclass", "base_level", "multiclass1_flag", "multiclass2_flag", "multiclass3_flag", "multiclass1", "multiclass1_lvl", "multiclass1_subclass", "multiclass2", "multiclass2_lvl", "multiclass2_subclass", "multiclass3", "multiclass3_lvl", "multiclass3_subclass", "hp_max", "class_resource_name", "other_resource_name", "speed", "race", "subrace", "background", "custom_class", "cust_classname", "cust_hitdietype"];
const getReatingAttributes = (section, attributes, callback) => {
const result = [];
getSectionIDs(section, ids => {
ids.forEach(id => {
attributes.forEach(attribute => {
result.push(`repeating_${section}_${id}_${attribute}`);
});
});
if(typeof callback === "function") callback(result);
});
};
const capitalize = function(string) {
return string.split(" ").map((x) => {
x = x.toLowerCase();
return x[0].toUpperCase() + x.substr(1, x.length);
}).join(" ");
};
const filterAttributesInList = (values, list) => {
const result = {};
list.forEach(key => {
if(values.hasOwnProperty(key)) result[key] = values[key];
});
return result;
};
const setPreviousAbilities = data => {
const final = filterAttributesInList(data, attribsWithModsAndMaximum);
const base = filterAttributesInList(data, baseAttributes);
const modfiedAbilityList = Object.keys(base).filter(entry => { return entry.indexOf('_base') > -1 && base[entry.replace('_base', '_flag')] == 1});
Object.entries(final).forEach(([ability, value]) => {
final[ability] = parseInt(value);
});
modfiedAbilityList.forEach(attribute => {
final[attribute.replace('_base', '')] = parseInt(base[attribute]);
final[attribute.replace('_base', '_mod')] = Math.floor((parseInt(base[attribute]) - 10) / 2);
});
const update = {previous_abilities: final};
return update;
};
const setPreviousCoreInformation = data => {
const core = filterAttributesInList(data, coreInformation);
const update = {};
update["previous_attributes"] = core;
update["options"] = core.utilitymancer_options;
update["race"] = core.race;
update["subrace"] = core.subrace;
update["background"] = core.background.split("(")[0].trim();
return { core: update };
};
const setPreviousSkills = (data, tools, profs) => {
const proficiencies = {"skills": [], "expertise": [],"repeating_tool": {}, "repeating_proficiencies": {}};
const skillAttributes = filterAttributesInList(data, skillget);
allSkills.forEach(skill => {
if(skillAttributes[skill + "_prof"] !== "0") {
proficiencies.skills.push(capitalize(skill.replace(/_/g, " ")));
}
if(skillAttributes[skill + "_type"] !== "1" && skillAttributes[skill + "_prof"] !== "0") {
proficiencies.expertise.push(capitalize(skill.replace(/_/g, " ")));
}
});
const toolAttributes = filterAttributesInList(data, tools);
Object.entries(toolAttributes).forEach(([key, value]) => {
if(key.includes("_toolname")) {
const id = SheetUtils.getUID(key);
proficiencies.repeating_tool[id] = {
toolname: value,
toolbonus_base: toolAttributes[`repeating_tool_${id}_toolbonus_base`]
};
if(toolAttributes[`repeating_tool_${id}_toolbonus_base`] == "(@{pb}*2)") {
proficiencies.expertise.push(key);
};
}
});
const profAttributes = filterAttributesInList(data, profs);
Object.entries(profAttributes).forEach(([key, value]) => {
if(key.includes("_name")) {
const id = SheetUtils.getUID(key);
proficiencies.repeating_proficiencies[id] = {
name: value,
prof_type: profAttributes[`repeating_proficiencies_${id}_prof_type`]
};
}
});
return proficiencies;
};
const setPreviousTraits = (data, list) => {
const traits = filterAttributesInList(data, list);
const update = {};
Object.entries(traits).forEach(([key, value]) => {
if(key.includes("_name")) {
const id = SheetUtils.getUID(key);
update[id] = {
name: traits[key] || "",
source: traits[`repeating_traits_${id}_source`] || "",
source_type: traits[`repeating_traits_${id}_source_type`] || "",
description: traits[`repeating_traits_${id}_description`] || ""
};
}
});
return { repeating_traits: update };
};
const setPreviousAttacks = (data, list) => {
const attacks = filterAttributesInList(data, list);
const update = {};
Object.entries(attacks).forEach(([key, value]) => {
if(key.includes("_atkname")) {
const id = SheetUtils.getUID(key);
update[id] = {
atkname: value || ""
};
}
});
return { repeating_attack: update };
};
const setPreviousSpells = (data, ...args) => {
const update = {};
for(let i=0; i<args.length; i++) {
const spells = filterAttributesInList(data, args[i]);
const level = (i === 0) ? "cantrip" : String(i);
Object.entries(spells).forEach(([key, value]) => {
if(key.includes("_spellname")) {
const id = SheetUtils.getUID(key);
if(!update[`repeating_spell-${level}`]) update[`repeating_spell-${level}`] = {};
update[`repeating_spell-${level}`][id] = {
spellname: value || "",
spellclass: spells[`repeating_spell-${level}_${id}_spellclass`] || "",
spellsource: spells[`repeating_spell-${level}_${id}_spellsource`] || ""
};
}
});
}
return { spells: update };
};
const getCurrentData = callback => {
getReatingAttributes("spell-cantrip", ["spellname", "spellclass", "spellsource"], spell_cantrip => {
getReatingAttributes("spell-1", ["spellname", "spellclass", "spellsource"], spell_1 => {
getReatingAttributes("spell-2", ["spellname", "spellclass", "spellsource"], spell_2 => {
getReatingAttributes("spell-3", ["spellname", "spellclass", "spellsource"], spell_3 => {
getReatingAttributes("spell-4", ["spellname", "spellclass", "spellsource"], spell_4 => {
getReatingAttributes("spell-5", ["spellname", "spellclass", "spellsource"], spell_5 => {
getReatingAttributes("spell-6", ["spellname", "spellclass", "spellsource"], spell_6 => {
getReatingAttributes("spell-7", ["spellname", "spellclass", "spellsource"], spell_7 => {
getReatingAttributes("spell-8", ["spellname", "spellclass", "spellsource"], spell_8 => {
getReatingAttributes("spell-9", ["spellname", "spellclass", "spellsource"], spell_9 => {
getReatingAttributes("traits", ["name", "source", "source_type", "description"], traits => {
getReatingAttributes("proficiencies", ["name", "prof_type"], profs => {
getReatingAttributes("tool", ["toolname", "toolbonus_base"], tools => {
getReatingAttributes("attack", ["atkname"], attacks => {
getAttrs([...attribsWithModsAndMaximum, ...baseAttributes, ...coreInformation, ...skillget, ...traits, ...profs, ...tools, ...attacks, ...spell_cantrip, ...spell_1, ...spell_2, ...spell_3, ...spell_4, ...spell_5, ...spell_6, ...spell_7, ...spell_8, ...spell_9], data => {
const update = {
...setPreviousAbilities(data),
...setPreviousCoreInformation(data),
...setPreviousSkills(data, tools, profs),
...setPreviousTraits(data, traits),
...setPreviousAttacks(data, attacks),
...setPreviousSpells(data, spell_cantrip, spell_1, spell_2, spell_3, spell_4, spell_5, spell_6, spell_7, spell_8, spell_9)
};
callback(update);
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
}
getCurrentData(callback)
};
const getAllUtilityBlobs = function(mancerdata) {
mancerdata = mancerdata || getCharmancerData();
const previousLevel = String(parseInt(mancerdata["utility-welcome"].values["lp-previous_level"])+1);
return getRelevantBlobs(mancerdata, `<${previousLevel}`, "utility");
};
on("mancerfinish:utility-mancer", eventinfo => {
removePreviousRaceTraits(() => {
finishCustomNewrace(eventinfo);
});
});
const isAncestralLineage = data => {
return true;
};
const isMovementRelatedTrait = trait => {
const hasMovementKeyword = /swimming|flying|climbing/gi.test(trait.description);
const hasSpeed = /speed of [0-9][0-9] feet/gi.test(trait.description);
return (hasMovementKeyword && hasSpeed);
};
const isProficiencyRelatedTrait = trait => {
return false;
};
const isRacialTrait = (race, subrace, trait) => {
if(trait.source.toLowerCase() !== "racial") return false;
if(trait.source_type.toLowerCase() !== race.toLowerCase() && trait.source_type.toLowerCase() !== subrace.toLowerCase()) return false;
return true;
};
const isRacialSpell = (race, subrace, spell) => {
const racialKeywords = [race.toLowerCase(), subrace.toLowerCase(), "racial"];
if(racialKeywords.indexOf(spell.spellclass.toLowerCase()) === -1 && racialKeywords.indexOf(spell.spellsource.toLowerCase()) === -1) return false;
return true;
};
const removePreviousRaceTraits = callback => {
const mancerdata = getCharmancerData();
const utilitydata = JSON.parse(getCharmancerData()["utility-welcome"]["values"]["utility_data"]);
const values = mancerdata["utility-newrace"]["values"];
const toUnset = {};
const toRemove = [];
Object.keys(values).forEach(key => {
if(/repeating_.+_unset[0-9]+_selected/g.test(key) && values[key]) {
const skillAttr = values[key].toLowerCase().replace(/ /g, "_") + "_prof";
toUnset[skillAttr] = "";
}
if((/repeating_.+_remove[0-9]+_selected/g.test(key) || /repeating_.+_remove[0-9]+_checked/g.test(key))
&& values[key]) {
if(values[key] === "1") {
const target = key.replace("_checked", "_target");
if(values[target]) toRemove.push(values[target]);
} else {
toRemove.push(values[key]);
}
}
});
toRemove.forEach(repeating => {
removeRepeatingRow(repeating);
});
setAttrs({subrace: "", ...toUnset}, () => {
callback();
});
};
const finishCustomNewrace = eventinfo => {
console.log("****************************************");
console.log("****** FINISHING CUSTOM newrace ******");
console.log("****************************************");
var doAllDrops = function(dropArray, callback) {
getAttrs(["character_id"], function(v) {
_.each(allAbilities, function(ability) {
v[ability + "_base"] = abilities[ability];
v[ability + "_mod"] = abilities[ability];
});
v.base_level = set["base_level"];
v["multiclass1_lvl"] = initial["multiclass1_lvl"];
v["multiclass2_lvl"] = initial["multiclass2_lvl"];
v["multiclass3_lvl"] = initial["multiclass3_lvl"];
v.npc = "0";
v["class_resource_name"] = previous["class_resource_name"];
v["other_resource_name"] = previous["other_resource_name"];
v.speed = previous.speed;
var update = {};
var callbacks = [];
var totalDrops = dropArray.length;
var x = 0;
callbacks.push(set_level);
callbacks.push(update_race_display);
_.each(dropArray, function(page) {
page.data.Category = page.data.Category ? page.data.Category.replace("@@!!@@", "") : "";
var dropUpdate = processDrop(page, v, repeating, true);
callbacks = callbacks.concat(dropUpdate.callbacks);
repeating.prof_names = _.uniq(repeating.prof_names.concat(dropUpdate.prof_names));
update = _.extend(update, dropUpdate.update);
x++;
setCharmancerText({"mancer_progress" :'<div style="width: ' + (parseInt(x/totalDrops*70) + 20) + '%"></div>'});
});
callbacks.push( function() {update_ac();} );
callbacks.push( function() {update_weight();} );
callbacks.push(callback);
setAttrs(update, {silent: true}, function() {
setCharmancerText({"mancer_progress" :'<div style="width: 95%"></div>'});
_.each(callbacks, function(callback) {
callback();
});
});
});
};
var getOtherDrops = function(pagedata) {
var results = [];
if(pagedata["data-Equipment"]) {
var json = {};
try {
json = JSON.parse(pagedata["data-Equipment"]);
} catch(e) {
json = pagedata["data-Equipment"];
}
var newItems = makeItemData(json.default);
results = results.concat(newItems);
}
if(pagedata["data-Bundle"]) {
var json = {};
try {
json = JSON.parse(pagedata["data-Bundle"]);
} catch(e) {
json = pagedata["data-Bundle"];
}
var newItems = makeItemData(json);
results = results.concat(newItems);
}
return results;
};
var getAllPages = function(pageArray, callback) {
if(pageArray.length < 1) {
callback();
return;
}
var getNames = [];
_.each(pageArray, function(page){
if(page.name) {
getNames.push(page.name);
} else {
getNames.push(page);
}
});
getCompendiumPage(getNames, function(data) {
data = removeDuplicatedPageData(data);
var nextGet = [];
if (getNames.length === 1 && !Array.isArray(data)) { data = {0:data} }; //Fix single spell selections failing.
_.each(data, function(page, index) {
var pagedata = false;
_.each(pageArray, function(arrayData) {
if(arrayData.name.toLowerCase() == (page.data.Category + ":" + page.name).toLowerCase()) {
pagedata = arrayData;
}
});
if(pagedata && pagedata.data) {
page.data = _.extend(page.data, pagedata.data);
}
if(!page.id) page.data.Source = "Charactermancer";
page.name = page.name.replace(/@@!!@@/g,""); // Hacky bugfix to prevent custom names from matching unavailable content
nextGet = nextGet.concat(getOtherDrops(page.data));
if(page.data["data-Starting Gold"] && !noEquipmentDrop) {
set["gp"] += parseInt(page.data["data-Starting Gold"]);
}
allPageData.push(page);
});
if(nextGet.length > 0) {
getAllPages(nextGet, callback);
} else {
callback();
}
});
};
var getRemovableSwaps = function() {
if(!SheetUtils.checkPath(data, "['utility-welcome']['values']['previous_repeating']")) return [];
if(!SheetUtils.checkPath(data, "['utility-choices']['values']")) return [];
const previousRepeating = JSON.parse(data['utility-welcome']['values']['previous_repeating']);
if(!previousRepeating['traits']) return [];
const swaps = Object.keys(data['utility-choices']['values']).filter(entry => { return entry.indexOf('_feature_swap') > -1; });
let removableSwaps = []
for(let i=0; i<swaps.length; i++) {
const swap = swaps[i];
const swapFor = swap.replace('_swap', '_for');
if(data['utility-choices']['values'][swap] && data['utility-choices']['values'][swapFor]) {
const swapTitle = data['utility-choices']['values'][swap].replace(':', ': ');
const sectionsToRemove = previousRepeating['traits'].filter(entry => { return entry.name === swapTitle;}).map(entry => { return `repeating_traits_${entry.id}`});
removableSwaps = removableSwaps.concat(sectionsToRemove);
}
}
return removableSwaps;
};
var allPageData = [];
var data = getCharmancerData();
var blobs = getAllUtilityBlobs();
var abilities = getAbilityTotals(data, blobs, "utility");
var previous = data["utility-welcome"].values["previous_attributes"] ? JSON.parse(data["utility-welcome"].values["previous_attributes"]) : {};
var repeating = data["utility-welcome"].values["previous_repeating"] ? JSON.parse(data["utility-welcome"].values["previous_repeating"]) : {};
var loudset = {};
var set = {};
var initial = {};
var allDrops = [];
var allSkills = ["athletics", "acrobatics", "sleight_of_hand", "stealth", "arcana", "history", "investigation", "nature", "religion", "animal_handling", "insight", "medicine", "perception", "survival","deception", "intimidation", "performance", "persuasion"];
var allAbilities = SheetUtils.toLowerCase(abilityList);
set["utility-mancer_status"] = "";
//set up race drop
if(SheetUtils.checkPath(data, `["utility-newrace"]["values"]["newrace"]`)) {
const raceData = {name: removeExpansionInfo(_.last(data["utility-newrace"].values.newrace.split(":"))), data: data["utility-newrace"].data.newrace};
raceData.data.theseblobs = blobs.names.newrace;
allPageData.push(raceData);
}
//set up subrace drop
if(SheetUtils.checkPath(data, `["utility-newrace"]["values"]["newsubrace"]`)) {
var subraceData = {name: removeExpansionInfo(_.last(data["utility-newrace"].values.newsubrace.split(":"))), data: data["utility-newrace"].data.newsubrace};
subraceData.data.theseblobs = blobs.names.newsubrace;
allPageData.push(subraceData);
}
//Skill drops
Object.entries(data["utility-newrace"]["values"]).forEach(([key, value]) => {
if(/^Proficiencies:/g.test(value)) {
const skill = value.replace(/^Proficiencies:/g, "").replace(/ /g, "_").toLowerCase().trim();
loudset[`${skill}_prof`] = `(@{pb}*@{${skill}_type})`;
}
});
//Set up Ability scores
initial["hp"] = getHpTotal(data, blobs, getAbilityTotals(data, blobs, "utility"), "utility");
initial["hp_max"] = initial["hp"];
_.each(allAbilities, function(ability) {
loudset[ability + "_base"] = abilities[ability];
});
//Set up spell drops
_.each(blobs.sorted, function(section, name) {
_.each(section, function(blob) {
if(blob.Spells) {
spells = JSON.parse(blob.Spells);
_.each(spells, function(spell) {
if(spell.Known) {
_.each(spell.Known, function(spellname) {
var thisspell = {name: "Spells:" + spellname, data: {}};
var thisclass = name.substring(0,5) === "class" ? _.last(data["utility-levels"].values[name.split("_")[0]].split(":")) : "Racial";
if(spell.Ability) thisspell.data["spellcasting_ability"] = spell.Ability;
if(spell.Source) thisspell.data["spellsource"] = spell.Source;
thisspell.data["spellclass"] = thisclass;
allDrops.push(thisspell);
});
};
});
};
});
});
setCharmancerText({"mancer_progress" :'<div style="width: 5%"></div>'});
//first set is silent to prevent unwanted sheet workers
setAttrs(initial, {silent:true}, function() {
setCharmancerText({"mancer_progress" :'<div style="width: 10%"></div>'});
//reset remaining attributes, and set ability scores/custom info
setAttrs(loudset, function() {
setCharmancerText({"mancer_progress" :'<div style="width: 15%"></div>'});
getAllPages(allDrops, function() {
setCharmancerText({"mancer_progress" :'<div style="width: 20%"></div>'});
doAllDrops(allPageData, function() {
setAttrs(set, function() {
setCharmancerText({"mancer_progress" :'<div style="width: 100%"></div>'});
organize_section_proficiencies();
update_skills(allSkills);
update_attacks("all");
deleteCharmancerData(["utility-welcome", "utility-newrace"]);
finishCharactermancer();
globalAttributesByCategory.abilitiesWithAllOptionals.forEach(attr => {
update_attr(`${attr}`);
});
});
});
});
});
});
getRemovableSwaps().forEach(rowid => {
removeRepeatingRow(rowid);
});
};
const getUtilityRaceData = function(mancerdata, leveldata) {
mancerdata = mancerdata || getCharmancerData();
let results = {};
let currentlevel = parseInt(mancerdata["utility-welcome"].values["lp-previous_level"]);
let addlevel = 0;
const characterlevel = SheetUtils.checkPath(mancerdata, '["utility-levels"]["values"]["characterlevel"]') ? Math.max(parseInt(mancerdata["utility-levels"]["values"]["characterlevel"]) - addlevel, 0) : 0;
currentlevel = currentlevel === 0 ? 1 : currentlevel;
currentlevel = (characterlevel > currentlevel) ? characterlevel : currentlevel;
_.each(["race", "background"], function(section) {
results[section] = {addlevel: addlevel, currentlevel: currentlevel};
results[section][section] = mancerdata["utility-welcome"].data["utility-" + section] || {};
results[section]["sub" + section] = mancerdata["utility-welcome"].data["utility-sub" + section] || {};
results[section].type = section;
if(SheetUtils.checkPath(mancerdata, `["utility-welcome"]["values"]["utility-${section}"]`)) {
results[section].name = _.last(mancerdata["utility-welcome"].values["utility-" + section].split(":")) || "";
}
if(SheetUtils.checkPath(mancerdata, `["utility-welcome"]["values"]["utility-sub${section}"]`)) {
results[section].name += mancerdata["utility-welcome"].values["utility-sub" + section] ? " - " + _.last(mancerdata["utility-welcome"].values["utility-sub" + section].split(":")) : "";
}
});
return results;
};
on("mancerchange:repeating_row", eventinfo => {
if(eventinfo.currentStep !== "utility-newrace" || (!eventinfo.newValue && !eventinfo.previousValue)) return;
const mancerdata = getCharmancerData();
const id = SheetUtils.getUID(eventinfo.sourceAttribute);
const label = `repeating_${id}_race_feature_info`;
if(SheetUtils.checkPath(mancerdata, `["utility-newrace"]["values"]["${label}"]`) &&
mancerdata["utility-newrace"]["values"][label] === "Ancestral Legacy") {
switch(eventinfo.newValue) {
case "Blob:Keep Skills and Movement":
setAncenstralLegacyOptions(true);
break;
default:
setAncenstralLegacyOptions(false);
}
}
});
const doubleCheck = eveninfo => {
value = eveninfo.newValue || "";
if(eveninfo.sourceType === "player") {
setAttrs({[eveninfo.sourceAttribute]: value}, {silent: true}, () => {
console.log("done!");
});
}
};
["decrease", "unset", "remove"].forEach(repeating => {
for(let i=1; i<=20; i++) {
on(`mancerchange:repeating_${repeating}${i}`, eventinfo => {
doubleCheck(eventinfo);
});
}
});
const setAncenstralLegacyOptions = keepOriginalRaceFeatures => {
const mancerdata = getCharmancerData();
const utilityData = JSON.parse(getCharmancerData()["utility-welcome"]["values"]["utility_data"]);
if(keepOriginalRaceFeatures) {
const update = {};
Object.keys(mancerdata["utility-newrace"]["values"]).forEach(key => {
if(/repeating_.+_unset[0-9]+_selected/g.test(key)) {
update[key] = "";
}
if(/repeating_.+_remove[0-9]+_checked/g.test(key)) {
const target = key.replace("_checked", "_target");
if(mancerdata["utility-newrace"]["values"][target]) {
const row = mancerdata["utility-newrace"]["values"][target];
const id = SheetUtils.getUID(row);
if(utilityData.repeating_traits[id] && isMovementRelatedTrait(utilityData.repeating_traits[id])) {
update[key] = "";
}
}
}
});
setAttrs(update, () => {
console.log("Keeping Original Race Features");
});
} else {
const update = {};
Object.keys(mancerdata["utility-newrace"]["values"]).forEach(key => {
if(/repeating_.+_unset[0-9]+_default/g.test(key) &&
mancerdata["utility-newrace"]["values"][key] !== "*") {
const selected = key.replace("_default", "_selected");
update[selected] = mancerdata["utility-newrace"]["values"][key];
}
if(/repeating_.+_remove[0-9]+_target/g.test(key)) {
const id = SheetUtils.getUID(mancerdata["utility-newrace"]["values"][key]);
if(utilityData.repeating_traits[id] && isMovementRelatedTrait(utilityData.repeating_traits[id])) {
const target = key.replace("_target", "_checked");
update[target] = "1";
}
}
});
setAttrs(update, () => {
console.log("Keeping Original Race Features");
});
}
};
for(let i=1; i<=10; i++) {
on(`mancerchange:repeating_unset${i}_selected`, function(eventinfo) {
if(eventinfo.sourceType === "player") getProficiencies(getCharmancerData(), "utility-newrace");
});
}
//# sourceURL=dnd5e.js
</script><charmancer class="sheet-repeating-topbar">
<div class="topbar-container">
<div class="sheet-attr-container">
<h4 data-i18n="strength">Strength</h4>
<span class="strength_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="dexterity">Dexterity</h4>
<span class="dexterity_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="constitution">Constitution</h4>
<span class="constitution_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="intelligence">Intelligence</h4>
<span class="intelligence_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="wisdom">Wisdom</h4>
<span class="wisdom_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="charisma">Charisma</h4>
<span class="charisma_total">-</span>
</div>
<div class="sheet-attr-container choice honor_toggle">
<h4 data-i18n="honor">Honor</h4>
<span class="honor_total">-</span>
</div>
<div class="sheet-attr-container choice sanity_toggle">
<h4 data-i18n="sanity">Saninty</h4>
<span class="sanity_total">-</span>
</div>
</div>
<div class="meta-info-container">
<span class="label"><span data-i18n="hp">Hit Points</span>: <span class="hit_points"></span></span>
<span class="choice label class_suggested_abilities_container"><span data-i18n="suggested-abilities">Suggested Abilities: </span><span class="label class_suggested_abilities"></span></span>
<span class="choice label class_spellcasting_ability_container"><span data-i18n="class-spellcasting">Class Spellcasting Ability: </span><span class="label class_spellcasting_ability"></span></span>
</div>
</charmancer>
<charmancer class="sheet-repeating-leveler-topbar">
<div class="topbar-container">
<div class="sheet-attr-container">
<h4 data-i18n="strength">Strength</h4>
<span class="strength_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="dexterity">Dexterity</h4>
<span class="dexterity_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="constitution">Constitution</h4>
<span class="constitution_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="intelligence">Intelligence</h4>
<span class="intelligence_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="wisdom">Wisdom</h4>
<span class="wisdom_total">-</span>
</div>
<div class="sheet-attr-container">
<h4 data-i18n="charisma">Charisma</h4>
<span class="charisma_total">-</span>
</div>
<div class="sheet-attr-container choice honor_toggle">
<h4 data-i18n="honor">Honor</h4>
<span class="honor_total">-</span>
</div>
<div class="sheet-attr-container choice sanity_toggle">
<h4 data-i18n="sanity">Saninty</h4>
<span class="sanity_total">-</span>
</div>
</div>
<div class="meta-info-container">
<span class="label"><span data-i18n="hp">Hit Points</span>: <span class="hit_points"></span></span>
<span class="choice label spelldc_container"><span data-i18n="spelldc">Spellcasting DC: </span><span class="label class_spelldc"></span></span>
<span class="label class_list"></span>
</div>
</charmancer>
<charmancer class="sheet-repeating-row">
<label><span></span>
<p></p>
</label>
</charmancer>
<charmancer class="sheet-repeating-utilityrow">
<label><span><span class="title"></span><span class="result"></span></span>
<p></p>
</label>
<div class="warning"></div>
<button type="action" name="act_launch"></button>
<input type="hidden" name="comp_parent">
<input type="hidden" name="comp_blob">
<input type="hidden" name="comp_type">
<input type="hidden" name="comp_result">
<input type="hidden" name="comp_info">
<input type="hidden" name="comp_title">
<input type="hidden" name="comp_desc">
<input type="hidden" name="comp_spells">
</charmancer>
<charmancer class="sheet-repeating-swap">
<span class="sheet-swap-selected"></span>
<select name="comp_swap">
<option value="" data-i18n="Swap"></option>
</select>
<select name="comp_for">
<option value="" data-i18n="For"></option>
</select>
<span></span>
<input type="hidden" name="comp_info">
<input type="text" class="choice custom" name="comp_custom">
<button type="roll" class="choice roll" name="roll_roll" value="" data-i18n="roll">Roll</button>
<button type="action" class="choice action" name="act_action">i</button>
</charmancer>
<charmancer class="sheet-repeating-choose">
<select name="comp_choice">
<option value="" data-i18n="choose"></option>
</select>
<span></span>
<input type="hidden" name="comp_info">
<input type="text" class="choice custom" name="comp_custom">
<button type="roll" class="choice roll" name="roll_roll" value="" data-i18n="roll">Roll</button>
<button type="action" class="choice action" name="act_action">i</button>
</charmancer>
<charmancer class="sheet-repeating-custom-proficiency">
<label class="custom_type">
Type:
<select name="comp_type">
<option value="" data-i18n="choose"></option>
<option value="Weapon" data-i18n="weapon">Weapon</option>
<option value="Armor" data-i18n="armor">Armor</option>
<option value="Skill" data-i18n="skill">Skill</option>
<option value="Tool" data-i18n="tool">Tool</option>
<option value="Language" data-i18n="language">Language</option>
</select>
</label>
<span style="display:none"></span>
<select name="comp_choice">
<option value="" data-i18n="choose"></option>
</select>
<input type="text" class="choice custom" name="comp_custom">
<button type="action" class="custom_add_action" name="act_action" data-i18n="add-another-proficiency">Add another proficiency</button>
</charmancer>
<charmancer class="sheet-repeating-custom-trait">
<label class="custom_trait">
<div class="custom_trait">
<input type="text" name="comp_name">
<textarea name="comp_desc"></textarea>
</div>
</label>
<span style="display:none"></span>
<button type="action" class="custom_add_action" name="act_action" data-i18n="add-another-feature">Add another feature</button>
</charmancer>
<charmancer class="sheet-repeating-spell-holder">
<label class="spellheader choice-showing"><span class="title"></span><input type="checkbox" name="comp_show" value="1"><span class="number"></span></label>
<span class="controller" style="display: none"></span>
<div class="container"></div>
<div class="spellinfo">
<div class="spellstext">
<div class="summary"></div>
<div class="levels"></div>
<div class="total"></div>
</div>
<div class="replacetext">
<div class="summary"></div>
<div class="replace-info choice" data-i18n="charmancer-removed-spells-marked-pt1">Removed spells will be marked with a <span>r</span data-i18n="charmancer-removed-spells-marked-pt2"> symbol so you can find it easily if you change your mind.</div>
<div class="total"></div>
</div>
</div>
</charmancer>
<charmancer class="sheet-repeating-spell-item">
<input class="existing" type="hidden" name="comp_existing">
<span class="hardlock" style="display:none"></span>
<span class="lock" style="display:none"></span>
<label class="nostyle">
<input type="checkbox" class="spellcheck" name="comp_checked" value="1">
<span class="name"></span>
<span class="classes"></span>
</label>
<button type="action" class="mancer_info" name="act_info">i</button>
<input type="hidden" name="comp_name">
<input type="hidden" name="comp_level">
<input type="hidden" name="comp_ability">
<input type="hidden" name="comp_class">
<input type="hidden" name="comp_source">
</charmancer>
<charmancer class="sheet-repeating-asi-row">
<h4 class="title"></h4>
<input type="hidden" name="comp_source">
<input type="hidden" class='switchval' name="comp_switch" value='asi'>
<div class="switch-container">
<label class="switch-tab asi-tab">
<input type="radio" name="comp_switch" value="asi">
<span data-i18n="ability-score">Ability Score</span>
</label>
<label class="switch-tab feat-tab">
<input type="radio" name="comp_switch" value="feat">
<span data-i18n="feat">Feat</span>
</label>
</div>
<div class="asi">
<p data-i18n="you-can-increase-two-abilites">You can increase two of your abilities by 1, or one of your abilities by 2</p>
<div class="abilities-holder">
<div>
<span class="strength_lock-up lock-up" style="display:none"></span>
<span class="strength_lock-down lock-down" style="display:none" data-i18n="lock">lock</span>
<span>+</span><input type="text" name="comp_strength" value="0" disabled>
<div>
<button type="action" class="spinner up-spinner" name="act_strength_u">+</button>
<button type="action" class="spinner down-spinner" name="act_strength_d">-</button>
</div>
<h5 data-i18n="strength">Strength</h5>
</div>
<div>
<span class="dexterity_lock-up lock-up" style="display:none"></span>
<span class="dexterity_lock-down lock-down" style="display:none" data-i18n="lock">lock</span>
<span>+</span><input type="text" name="comp_dexterity" value="0" disabled>
<div>
<button type="action" class="spinner up-spinner" name="act_dexterity_u">+</button>
<button type="action" class="spinner down-spinner" name="act_dexterity_d">-</button>
</div>
<h5 data-i18n="dexterity">Dexterity</h5>
</div>
<div>
<span class="constitution_lock-up lock-up" style="display:none"></span>
<span class="constitution_lock-down lock-down" style="display:none" data-i18n="lock">lock</span>
<span>+</span><input type="text" name="comp_constitution" value="0" disabled>
<div>
<button type="action" class="spinner up-spinner" name="act_constitution_u">+</button>
<button type="action" class="spinner down-spinner" name="act_constitution_d">-</button>
</div>
<h5 data-i18n="constitution">Constitution</h5>
</div>
<div>
<span class="intelligence_lock-up lock-up" style="display:none"></span>
<span class="intelligence_lock-down lock-down" style="display:none" data-i18n="lock">lock</span>
<span>+</span><input type="text" name="comp_intelligence" value="0" disabled>
<div>
<button type="action" class="spinner up-spinner" name="act_intelligence_u">+</button>
<button type="action" class="spinner down-spinner" name="act_intelligence_d">-</button>
</div>
<h5 data-i18n="intelligence">Intelligence</h5>
</div>
<div>
<span class="wisdom_lock-up lock-up" style="display:none"></span>
<span class="wisdom_lock-down lock-down" style="display:none" data-i18n="lock">lock</span>
<span>+</span><input type="text" name="comp_wisdom" value="0" disabled>
<div>
<button type="action" class="spinner up-spinner" name="act_wisdom_u">+</button>
<button type="action" class="spinner down-spinner" name="act_wisdom_d">-</button>
</div>
<h5 data-i18n="wisdom">Wisdom</h5>
</div>
<div>
<span class="charisma_lock-up lock-up" style="display:none"></span>
<span class="charisma_lock-down lock-down" style="display:none">lock</span>
<span>+</span><input type="text" name="comp_charisma" value="0" disabled>
<div>
<button type="action" class="spinner up-spinner" name="act_charisma_u">+</button>
<button type="action" class="spinner down-spinner" name="act_charisma_d">-</button>
</div>
<h5 data-i18n="charisma">Charisma</h5>
</div>
<div class="choice sanity_toggle">
<span class="sanity_lock-up lock-up" style="display:none"></span>
<span class="sanity_lock-down lock-down" style="display:none">lock</span>
<span>+</span><input type="text" name="comp_sanity" value="0" disabled>
<div>
<button type="action" class="spinner up-spinner" name="act_sanity_u">+</button>
<button type="action" class="spinner down-spinner" name="act_sanity_d">-</button>
</div>
<h5 data-i18n="sanity">Sanity</h5>
</div>
</div>
</div>
<div class="feat">
<select name="comp_feat">
<option value="" data-i18n="choose">Choose</option>
</select>
<input type="hidden" name="comp_feat_data">
<div class="choice feat_options">
<div class="choice row feat_prereq feat_possible">
<label class="warning"><span data-i18n="prerequisite:">Prerequisite:</span>
<p class="feat_prerequisite feat_text"></p>
<p class="feat_text" data-i18n="feat-pre-check"><span >Look carefully! Prerequisites are not checked automatically.</span></p>
</label>
</div>
<div class="choice row feat_ability feat_possible">
<label><span data-i18n="ability-score-increase:">Ability Score Increase:</span>
<p class="feat_ability_score feat_text"></p>
<select class="choice feat_possible feat_ability_choice" name="comp_feat_ability_choice">
<option value="" data-i18n="choose">Choose</option>
</select>
</label>
</div>
<div class="choice row feat_possible feat_language_row">
<label><span data-i18n="language-proficiencies">Language Proficiencies:</span>
<p class="feat_languages feat_text"></p>
<select class="choice feat_possible feat_language_choice1" name="comp_feat_language_choice1">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" value="custom" data-i18n="custom">Custom</option>
</select>
<input type="text" class="choice feat_possible feat_language_choice1_custom" name="comp_feat_language_choice1_custom">
<select class="choice feat_possible feat_language_choice2" name="comp_feat_language_choice2">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" value="custom" data-i18n="custom">Custom</option>
</select>
<input type="text" class="choice feat_possible feat_language_choice2_custom" name="comp_feat_language_choice2_custom">
<select class="choice feat_possible feat_language_choice3" name="comp_feat_language_choice3">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" value="custom" data-i18n="custom">Custom</option>
</select>
<input type="text" class="choice feat_possible feat_language_choice3_custom" name="comp_feat_language_choice3_custom">
<select class="choice feat_possible feat_language_choice4" name="comp_feat_language_choice4">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" value="custom" data-i18n="custom">Custom</option>
</select>
<input type="text" class="choice feat_possible feat_language_choice4_custom" name="comp_feat_language_choice4_custom">
</label>
</div>
<div class="choice row feat_possible feat_skill_row">
<label><span data-i18n="skill-proficiencies">Skill Proficiencies</span>:
<p class="feat_skills feat_text"></p>
<select class="choice feat_possible feat_skill_choice1" name="comp_feat_skill_choice1">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_skill_choice2" name="comp_feat_skill_choice2">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_skill_choice3" name="comp_feat_skill_choice3">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_skill_choice4" name="comp_feat_skill_choice4">
<option value="" data-i18n="choose">Choose</option>
</select>
</label>
</div>
<div class="choice row feat_possible feat_weapon_row">
<label><span data-i18n="weapon-proficiencies">Weapon Proficiencies</span>:
<p class="feat_weapons feat_text"></p>
<select class="choice feat_possible feat_weapon_choice1" name="comp_feat_weapon_choice1">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_weapon_choice2" name="comp_feat_weapon_choice2">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_weapon_choice3" name="comp_feat_weapon_choice3">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_weapon_choice4" name="comp_feat_weapon_choice4">
<option value="" data-i18n="choose">Choose</option>
</select>
</label>
</div>
<div class="choice row feat_possible feat_armor_row">
<label><span data-i18n="armor-proficiencies">Armor Proficiencies</span>:
<p class="feat_armors feat_text"></p>
<select class="choice feat_possible feat_armor_choice1" name="comp_feat_armor_choice1">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_armor_choice2" name="comp_feat_armor_choice2">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_armor_choice3" name="comp_feat_armor_choice3">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_armor_choice4" name="comp_feat_armor_choice4">
<option value="" data-i18n="choose">Choose</option>
</select>
</label>
</div>
<div class="choice row feat_possible feat_tool_row">
<label><span data-i18n="tool-proficiencies">Tool Proficiencies</span>:
<p class="feat_tools feat_text"></p>
<select class="choice feat_possible feat_tool_choice1" name="comp_feat_tool_choice1">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_tool_choice2" name="comp_feat_tool_choice2">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_tool_choice3" name="comp_feat_tool_choice3">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_tool_choice4" name="comp_feat_tool_choice4">
<option value="" data-i18n="choose">Choose</option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_spell_row">
<label>
<span data-i18n="spell-list">Spell List:<span style="color: red;"></span></span>
<p class="feat_text feat_spells"></p>
<select class="choice feat_possible feat_spell_choice1" name="comp_feat_spell_choice1">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="choice feat_possible feat_spell_choice2" name="comp_feat_spell_choice2">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="choice feat_possible feat_spell_choice3" name="comp_feat_spell_choice3">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="choice feat_possible feat_spell_choice4" name="comp_feat_spell_choice4">
<option data-i18n="choose" value="">Choose</option>
</select>
</label>
</div>
<div class="choice row feat_possible feat_expertise_row">
<h4 data-i18n="expertise">Expertise</h4>
<span class="feat_expertise feat_text"></span>
<select class="choice feat_possible feat_expertise_choice_1" name="comp_feat_expertise_choice_1">
<option value="" data-i18n="choose">Choose</option>
</select>
<select class="choice feat_possible feat_expertise_choice_2" name="comp_feat_expertise_choice_2">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
<div class="choice row feat_possible feat_feature_row_1">
<h4 class="feat_feature_name_1"></h4>
<span class="feat_feature_1"></span>
<select class="feat_possible feat_feature_choice_1" name="comp_feat_feature_choice_1">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" data-i18n="custom">Custom</option>
</select>
<p class="description feat_feature_description_1"></p>
</div>
<div class="choice row feat_possible feat_feature_row_1_double">
<h4 class="feat_feature_name_1_double"></h4>
<span class="feat_feature_1_double"></span>
<select class="feat_possible feat_feature_choice_1_double" name="comp_feat_feature_choice_3">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" data-i18n="custom">Custom</option>
</select>
<p class="description feat_feature_description_1_double"></p>
</div>
<div class="choice row feat_possible feat_feature_row_2">
<h4 class="feat_feature_name_2"></h4>
<span class="feat_feature_2"></span>
<select class="feat_possible feat_feature_choice_2" name="comp_feat_feature_choice_2">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" data-i18n="custom">Custom</option>
</select>
<p class="description feat_feature_description_2"></p>
</div>
<div class="choice row feat_possible other_options_and_features1">
<label><span class="other_options_and_features_title1"></span>
<select class="feat_possible other_options_and_features_choice1" name="comp_other_options_and_features_choice1">
<option value="" data-i18n="choose">Choose</option>
</select>
</label>
</div>
<div class="choice row feat_possible other_options_and_features2">
<label><span class="other_options_and_features_title2"></span>
<select class="feat_possible other_options_and_features_choice1" name="comp_other_options_and_features_choice2">
<option value="" data-i18n="choose">Choose</option>
</select>
</label>
</div>
</div>
</div>
</charmancer>
<charmancer class="sheet-repeating-hpbylevel">
<input class="hp-flag" type="hidden" name="comp_rollflag" value="average">
<label></label>
<input type="number" class="" name="comp_result" min="0">
<button type="roll" name="roll_rollhp" value="" title="" data-i18n="roll">Roll</button>
<button type="roll" name="roll_averagehp" value="" title="" data-i18n="average">Average</button>
</charmancer>
<charmancer class="sheet-repeating-randomize-trait">
<div class="row">
<span class="label trait-label"></span>
<span class="trait-description"></span>
<input type="hidden" name="comp_name">
<select class="randomized-trait-choice" name="comp_choice">
<option data-i18n="choose" value=""></option>
</select>
<button class="standardbg bg_roll" data-i18n="roll" name="roll_choice_roll" type="roll" value=""></button>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-welcome">
<div class="charmancer container">
<input type="hidden" name="comp_has_tasha" value="0"/>
<ol class="steps"></ol>
<div class="logos"><span class="charmancer-logo"></span><span class="compendium-logo"></span></div>
<p class="label" data-i18n="charmancer-welcome"></p>
<p data-i18n="charmancer-what-is"></p>
<p class="label" data-i18n="charmancer-compendium"></p>
<p data-i18n="charmancer-compendium-detail"></p>
<p class="label" data-i18n="get-started"></p>
<div class="bottombar">
<button class="prev" data-i18n="opt-out-u" type="cancel" value="l1-cancel"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-race"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-welcome"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-race">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choices">
<div class="row">
<input type="hidden" name="comp_has_tasha"/>
<div class="customize-origin">
<label><span data-i18n="customize-your-origin"></span><span data-i18n="customize-your-origin-info"></span></label>
<div class="toggle">
<input type="checkbox" name="comp_customize_origin" value="1"/>
<div class="knob"></div>
</div>
</div>
</div>
<div class="row">
<label class="hilite"><span data-i18n="charmancer-choose-race"></span>
<select accept="Category:Races" name="comp_race" required="required">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom" value="Rules:Races"></option>
</select>
<button class="mancer_info" name="act_info_race" type="action" required="required">i</button>
</label>
<div class="choice custom_race">
<label class="hilite" data-i18n="charmancer-custom-race-name"></label>
<input name="comp_race_name" placeholder="Custom Race" type="text" required="required"/>
</div>
</div>
<div class="row choice race_possible custom_race race_abilities">
<label>
<div class="choice tasha_race_enabled">
<input type="hidden" name="comp_tasha_race_ability_choice1_increase"/><span class="choice tasha_race_ability_choice1 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice1" name="comp_tasha_race_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice2_increase"/><span class="choice tasha_race_ability_choice2 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice2" name="comp_tasha_race_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice3_increase"/><span class="choice tasha_race_ability_choice3 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice3" name="comp_tasha_race_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice4_increase"/><span class="choice tasha_race_ability_choice4 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice4" name="comp_tasha_race_ability_choice4">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice5_increase"/><span class="choice tasha_race_ability_choice5 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice5" name="comp_tasha_race_ability_choice5">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice6_increase"/><span class="choice tasha_race_ability_choice6 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice6" name="comp_tasha_race_ability_choice6">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<div class="choice tasha_race_disabled"><span data-i18n="ability-score-increase"></span>
<p class="race_ability_score race_text"></p>
<p class="race_ability_choice_rule race_text"></p>
<select class="choice race_possible race_ability_choice1" name="comp_race_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice race_possible race_ability_choice2" name="comp_race_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice race_possible race_ability_choice3" name="comp_race_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<label class="choice custom_race"><span data-i18n="strength"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_strength" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="dexterity"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_dexterity" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="constitution"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_constitution" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="intelligence"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_intelligence" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="wisdom"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_wisdom" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="charisma"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_charisma" type="number" value="0"/>
</label>
</label>
</div>
<div class="row choice race_always custom_race">
<label><span data-i18n="alignment"></span>
<select name="comp_alignment">
<option data-i18n="choose" value=""></option>
<option data-i18n="lawful-good" value="Lawful Good"></option>
<option data-i18n="neutral-good" value="Neutral Good"></option>
<option data-i18n="chaotic-good" value="Chaotic Good"></option>
<option data-i18n="lawful-neutral" value="Lawful Neutral"></option>
<option data-i18n="neutral" value="Neutral"></option>
<option data-i18n="chaotic-neutral" value="Chaotic Neutral"></option>
<option data-i18n="lawful-evil" value="Lawful Evil"></option>
<option data-i18n="neutral-evil" value="Neutral Evil"></option>
<option data-i18n="chaotic-evil" value="Chaotic Evil"></option>
</select>
</label>
</div>
<div class="row choice race_always custom_race">
<label><span data-i18n="creaturetype"></span>
<p class="race_creature_type race_text"></p>
<input class="choice custom_race" name="comp_creaturetype" type="text"/>
</label>
</div>
<div class="row choice race_always custom_race">
<label><span data-i18n="size"></span>
<p class="race_size race_text"></p>
<select class="choice custom_size" name="comp_size_choice">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice custom_race" name="comp_size">
<option data-i18n="choose" value="">
<option data-i18n="tiny" value="Tiny"></option>
<option data-i18n="small" value="Small"></option>
<option data-i18n="medium" value="Medium"></option>
<option data-i18n="large" value="Large"></option>
<option data-i18n="huge" value="Huge"></option>
<option data-i18n="gargantuan" value="Gargantuan"></option>
</option>
</select>
</label>
</div>
<div class="row choice race_always custom_race">
<label><span data-i18n="speed"></span>
<p class="race_speed race_text"></p>
<input class="choice custom_race" min="0" name="comp_speed" type="number"/>
</label>
</div>
<div class="race_holder holder"></div>
<div class="choice subrace">
<div class="row">
<label class="hilite"><span data-i18n="subrace"></span>
<select class="choose_subrace" name="comp_subrace" required="required">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom" value="Rules:Races"></option>
</select>
<button class="mancer_info" name="act_info_subrace" type="action" required="required">i</button>
</label>
<div class="choice custom_subrace">
<label class="hilite"><span data-i18n="charmancer-custom-subrace-name"></span>
<input name="comp_subrace_name" placeholder="Custom Subrace" type="text" required="required"/>
</label>
</div>
</div>
<div class="choice subrace_options">
<div class="row choice subrace_possible subrace_abilities custom_subrace">
<label>
<div class="choice tasha_subrace_enabled">
<input type="hidden" name="comp_tasha_subrace_ability_choice1_increase"/><span class="choice tasha_subrace_ability_choice1 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice1" name="comp_tasha_subrace_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice2_increase"/><span class="choice tasha_subrace_ability_choice2 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice2" name="comp_tasha_subrace_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice3_increase"/><span class="choice tasha_subrace_ability_choice3 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice3" name="comp_tasha_subrace_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice4_increase"/><span class="choice tasha_subrace_ability_choice4 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice4" name="comp_tasha_subrace_ability_choice4">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice5_increase"/><span class="choice tasha_subrace_ability_choice5 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice5" name="comp_tasha_subrace_ability_choice5">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice6_increase"/><span class="choice tasha_subrace_ability_choice6 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice6" name="comp_tasha_subrace_ability_choice6">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<div class="choice tasha_subrace_disabled"><span data-i18n="subrace-ability-increase"></span>
<p class="subrace_ability_score subrace_text"></p>
<select class="choice subrace_possible subrace_ability_choice1" name="comp_subrace_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice subrace_possible subrace_ability_choice2" name="comp_subrace_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice subrace_possible subrace_ability_choice3" name="comp_subrace_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<label class="choice custom_subrace"><span data-i18n="strength"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_strength" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="dexterity"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_dexterity" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="constitution"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_constitution" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="intelligence"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_intelligence" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="wisdom"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_wisdom" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="charisma"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_charisma" type="number" value="0"/>
</label>
</label>
</div>
<div class="row choice subrace_possible subrace_speed_row custom_subrace">
<label><span data-i18n="speed"></span>
<p class="subrace_speed subrace_text"></p>
<input class="choice custom_subrace" name="comp_speed" min="0" type="number"/>
</label>
</div>
<div class="subrace_holder holder"></div>
</div>
</div>
<div class="row choice race_possible custom_race custom_subrace">
<label><span data-i18n="innate-spellcasting:"></span>
<label><span data-i18n="spellcasting-ability"></span>
<select name="comp_custom_race_spell_ability">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</label>
<label class="hilite choice custom_spellcasting"><span data-i18n="number-of-cantrips"></span>
<input min="1" name="comp_custom_race_spell_number" type="number" value="1" required="required"/>
</label>
<label class="hilite choice custom_spellcasting"><span data-i18n="spell-list"></span>
<select name="comp_custom_race_spell_list">
<option data-i18n="choose" value=""></option>
<option data-i18n="artificer" value="artificer"></option>
<option data-i18n="bard" value="bard"></option>
<option data-i18n="cleric" value="cleric"></option>
<option data-i18n="druid" value="druid"></option>
<option data-i18n="paladin" value="paladin"></option>
<option data-i18n="ranger" value="ranger"></option>
<option data-i18n="sorcerer" value="sorcerer"></option>
<option data-i18n="warlock" value="warlock"></option>
<option data-i18n="wizard" value="wizard"></option>
</select>
</label>
</label>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-race-info" accept="Rules:Races"></div>
</div>
<div class="bottombar">
<button class="prev" data-i18n="back" type="back" value="l1-welcome"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-class"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-race"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
<input name="comp_has_subrace" type="hidden"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-class">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choices">
<div>
<div class="row">
<label class="hilite"><span data-i18n="choose-a-class:">Choose a class:</span>
<select name="comp_class" accept="Category:Classes" required="">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" value="Rules:Classes" data-i18n="custom">Custom</option>
</select>
<button class="mancer_info" type="action" name="act_info_class">i</button>
</label>
<div class="choice custom_class">
<label class="hilite"><span data-i18n="custom-class-name:">Custom Class Name:</span>
<input type="text" name="comp_class_name" placeholder="Custom Class" required=""/>
</label>
</div>
</div>
<div class="choice classes_options">
<div class="choice row custom_class">
<label class="hilite"><span data-i18n="hit-die:">Hit Die:</span>
<select name="comp_custom_hit_die" required="">
<option value="" data-i18n="choose">Choose</option>
<option value="4">d4</option>
<option value="6">d6</option>
<option value="8">d8</option>
<option value="10">d10</option>
<option value="12">d12</option>
</select>
</label>
</div>
<div class="choice row custom_class"><span><span data-i18n="saving-throw-proficiency">Saving Throw Proficiency:</span>
<label>
<input type="checkbox" name="comp_strength_save" value="true" data-i18n="strength"/>Strength
</label>
<label>
<input type="checkbox" name="comp_dexterity_save" value="true" data-i18n="dexterity"/>Dexterity
</label>
<label>
<input type="checkbox" name="comp_constitution_save" value="true" data-i18n="constitution"/>Constitution
</label>
<label>
<input type="checkbox" name="comp_intelligence_save" value="true" data-i18n="intelligence"/>Intelligence
</label>
<label>
<input type="checkbox" name="comp_wisdom_save" value="true" data-i18n="wisdom"/>Wisdom
</label>
<label>
<input type="checkbox" name="comp_charisma_save" value="true" data-i18n="charisma"/>Charisma
</label></span></div>
<div class="class_holder holder"></div>
<div class="choice subclass">
<div class="row">
<label class="hilite"><span class="subclass_prompt subclass_text"></span>
<select name="comp_subclass" accept="Category:Classes" required="">
<option value="" data-i18n="choose">Choose</option>
<option class="custom" value="Rules:Classes" data-i18n="custom">Custom</option>
</select>
<button class="mancer_info" type="action" name="act_info_subclass">i</button>
</label>
<div class="choice custom_subclass">
<label class="hilite"><span class="custom_subclass_name" data-i18n="charmancer-custom-subclass-name:">Custom sublass Name:</span>
<input type="text" name="comp_subclass_name" placeholder="Custom Sublass" required=""/>
</label>
</div>
</div>
<div class="subclass_holder holder"></div>
</div>
<div class="choice row custom_class custom_class_spells">
<label><span data-i18n="spellcasting-ability">Spellcasting Ability:</span>
<select name="comp_custom_class_spell_ability">
<option value="" data-i18n="choose">Choose</option>
<option value="Strength" data-i18n="strength">Strength</option>
<option value="Dexterity" data-i18n="dexterity">Dexterity</option>
<option value="Constitution" data-i18n="constitution">Constitution</option>
<option value="Intelligence" data-i18n="intelligence">Intelligence</option>
<option value="Wisdom" data-i18n="wisdom">Wisdom</option>
<option value="Charisma" data-i18n="charisma">Charisma</option>
</select>
</label>
<label class="hilite choice custom_spellcasting"><span data-i18n="number-of-cantrips">Number of Cantrips:</span>
<input type="number" name="comp_custom_class_cantrip_number" min="0" value="1" required=""/>
</label>
<label class="hilite choice custom_spellcasting"><span data-i18n="number-of-lvl-1-spells">Number of Level 1 Spells:</span>
<input type="number" name="comp_custom_class_spell_number" min="0" value="1" required=""/>
</label>
<label class="hilite choice custom_spellcasting"><span data-i18n="spell-list">Spell List:</span>
<select name="comp_custom_class_spell_list" required="">
<option value="" data-i18n="choose">Choose</option>
<option value="artificer" data-i18n="artificer"></option>
<option value="bard" data-i18n="bard"></option>
<option value="cleric" data-i18n="cleric"></option>
<option value="druid" data-i18n="druid"></option>
<option value="paladin" data-i18n="paladin"></option>
<option value="ranger" data-i18n="ranger"></option>
<option value="sorcerer" data-i18n="sorcerer"></option>
<option value="warlock" data-i18n="warlock"></option>
<option value="wizard" data-i18n="wizard"></option>
</select>
</label>
</div>
<div class="choice row custom_additional_spells custom_subclass">
<label><span data-i18n="charmancer-additional-cantrips">Additional Cantrips</span>:
<input type="number" name="comp_custom_subclass_cantrip_number" min="0" value="0" required=""/>
<label class="choice custom_cantrip_list"><span data-i18n="cantrip-list-if-diff">Cantrip List (if different from default class list)</span>:
<select name="comp_custom_subclass_cantrip_list">
<option value="" data-i18n="choose">Choose</option>
<option value="artificer" data-i18n="artificer"></option>
<option value="bard" data-i18n="bard"></option>
<option value="cleric" data-i18n="cleric"></option>
<option value="druid" data-i18n="druid"></option>
<option value="paladin" data-i18n="paladin"></option>
<option value="ranger" data-i18n="ranger"></option>
<option value="sorcerer" data-i18n="sorcerer"></option>
<option value="warlock" data-i18n="warlock"></option>
<option value="wizard" data-i18n="wizard"></option>
</select>
</label>
</label>
<label><span data-i18n="charmancer-additional-lvl-1-spells">Additional Level 1 Spells</span>:
<input type="number" name="comp_custom_subclass_spell_number" min="0" value="0" required=""/>
<label class="choice custom_spell_list"><span data-i18n="spell-list-if-diff">Spell List (if different from default class list)</span>:
<select name="comp_custom_subclass_spell_list">
<option value="" data-i18n="choose">Choose</option>
<option value="artificer" data-i18n="artificer"></option>
<option value="bard" data-i18n="bard"></option>
<option value="cleric" data-i18n="cleric"></option>
<option value="druid" data-i18n="druid"></option>
<option value="paladin" data-i18n="paladin"></option>
<option value="ranger" data-i18n="ranger"></option>
<option value="sorcerer" data-i18n="sorcerer"></option>
<option value="warlock" data-i18n="warlock"></option>
<option value="wizard" data-i18n="wizard"></option>
</select>
</label>
</label>
</div>
<div class="choice row class_possible class_equipment_row">
<h4 data-i18n="equipment">Equipment</h4><span class="class_standard_equipment"></span>
</div>
</div>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Classes"></div>
</div>
<div class="bottombar">
<button class="prev" type="back" value="l1-race" data-i18n="back">Back</button>
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next" type="back" value="l1-abilities" data-i18n="next">Next</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="l1-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="cancel" value="l1-class" data-i18n="save_progress_and_exit">Save and Exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-abilities">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<input name="comp_pointbuy_points" type="hidden" value="27"/>
<input name="comp_roll_results" type="hidden" value="0"/>
<div class="choices">
<div class="row">
<label class="hilite"><span data-i18n="ability-score-method"></span>
<select name="comp_abilities" required="required">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom" value="custom"></option>
</select>
</label>
</div>
<div class="row choice abilities_possible abilities_stdarray">
<p data-i18n="select-your-choices"></p>
<button data-i18n="clear-abilities" name="act_clearstats" type="action"></button>
</div>
<div class="row choice abilities_possible abilities_rollstats">
<p data-i18n="select-your-choices"></p>
<p>
<div class="rollstats-button">
<button data-i18n="standard-roll" type="roll" name="roll_rollstats" value="@{wtype}&amp;{template:mancerroll} {{title=Roll for Stats}} {{r1=[[4d6dl1]]}} {{r2=[[4d6dl1]]}} {{r3=[[4d6dl1]]}} {{r4=[[4d6dl1]]}} {{r5=[[4d6dl1]]}} {{r6=[[4d6dl1]]}}"></button>
</div>
<button data-i18n="clear-abilities" name="act_clearstats" type="action"></button>
</p>
</div>
<div class="row choice abilities_possible abilities_rollstats_rolled">
<p><span data-i18n="stats-rolled-hint"></span></p>
</div>
<div class="row choice abilities_possible abilities_pointbuy">
<p data-i18n="select-your-choices"></p>
<p><span data-i18n="available-points:"></span><strong><span class="points_available_display"></span></strong>
<button data-i18n="clear-abilities" name="act_clearstats" type="action"></button>
</p>
</div>
<div class="choice abilities_possible abilities_selects">
<div class="row">
<div class="hilite">
<div><span class="label" data-i18n="strength"></span><span class="abilities_radio_strength" name="comp_strength" required="required"></span></div>
<div><span class="label" data-i18n="dexterity"></span><span class="abilities_radio_dexterity" name="comp_dexterity" required="required"></span></div>
<div><span class="label" data-i18n="constitution"></span><span class="abilities_radio_constitution" name="comp_constitution" required="required"></span></div>
<div><span class="label" data-i18n="intelligence"></span><span class="abilities_radio_intelligence" name="comp_intelligence" required="required"></span></div>
<div><span class="label" data-i18n="wisdom"></span><span class="abilities_radio_wisdom" name="comp_wisdom" required="required"></span></div>
<div><span class="label" data-i18n="charisma"></span><span class="abilities_radio_charisma" name="comp_charisma" required="required"></span></div>
<div class="sheet-choice honor_toggle"><span class="label" data-i18n="honor"></span><span class="abilities_radio_honor" name="comp_honor" required="required"></span></div>
<div class="sheet-choice sanity_toggle"><span class="label" data-i18n="sanity"></span><span class="abilities_radio_sanity" name="comp_sanity" required="required"></span></div>
</div>
</div>
</div>
<div class="row choice abilities_possible abilities_custom">
<p data-i18n="input-your-scores"></p>
<div class="abilities_possible abilities_selects">
<div class="hilite">
<div><span class="label" data-i18n="strength"></span>
<input max="20" min="0" name="comp_strength" type="number" required="required"/>
</div>
<div><span class="label" data-i18n="dexterity"></span>
<input max="20" min="0" name="comp_dexterity" type="number" required="required"/>
</div>
<div><span class="label" data-i18n="constitution"></span>
<input max="20" min="0" name="comp_constitution" type="number" required="required"/>
</div>
<div><span class="label" data-i18n="intelligence"></span>
<input max="20" min="0" name="comp_intelligence" type="number" required="required"/>
</div>
<div><span class="label" data-i18n="wisdom"></span>
<input max="20" min="0" name="comp_wisdom" type="number" required="required"/>
</div>
<div><span class="label" data-i18n="charisma"></span>
<input max="20" min="0" name="comp_charisma" type="number" required="required"/>
</div>
<div class="sheet-choice honor_toggle"><span class="label" data-i18n="honor"></span>
<input min="0" max="20" name="comp_honor" type="number" required="required"/>
</div>
<div class="sheet-choice sanity_toggle"><span class="label" data-i18n="sanity"></span>
<input min="0" max="20" name="comp_sanity" type="number" required="required"/>
</div>
</div>
</div>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-abilscores-info" accept="Rules:Ability%20Scores"></div>
</div>
<div class="bottombar">
<button class="prev" data-i18n="back" type="back" value="l1-class"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-background"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-abilities"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-background">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choices">
<div class="row">
<label class="hilite"><span data-i18n="choose-background"></span>
<select accept="Category:Backgrounds" name="comp_background" required="required">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom" value="Rules:Backgrounds"></option>
</select>
</label>
<div class="choice custom_background">
<label class="hilite"><span data-i18n="name-background"></span>
<input name="comp_background_name" placeholder="Artisan" type="text" required="required"/>
</label>
</div>
</div>
<div class="choice backgrounds_options">
<div class="holder background_holder"></div>
<input name="comp_traitless" type="hidden"/>
<div class="choice randomize-traits"></div>
<div class="row choice background_possible background_detail_choice_row">
<label><span class="label"><span class="background_detail_choice_name" data-i18n="option" name="comp_background_detail_choice_name"></span><br/>
<select class="choice background_possible background_detail_choice" name="comp_background_detail_choice">
<option data-i18n="choose" value=""></option>
</select>
<button class="choice standardbg bg_roll" data-i18n="roll" name="roll_background_detail_choice_roll" type="roll" value=""></button></span></label>
</div>
<div class="row choice background_possible background_personality_row custom_background">
<label><span data-i18n="personality-traits"></span><br/>
<select class="choice background_possible background_personality_choice1" name="comp_background_personality_choice1">
<option data-i18n="choose" value=""></option>
</select>
<button class="choice standardbg bg_roll" data-i18n="roll" name="roll_background_personality_choice1_roll" type="roll" value=""></button>
<input class="choice custom_background" name="comp_background_personality_choice1" type="text"/>
<select class="choice background_possible background_personality_choice1" name="comp_background_personality_choice2">
<option data-i18n="choose" value=""></option>
</select>
<button class="choice standardbg bg_roll" data-i18n="roll" name="roll_background_personality_choice2_roll" type="roll" value=""></button>
<input class="choice custom_background" name="comp_background_personality_choice2" type="text"/>
</label>
<label><span data-i18n="ideal"></span><br/>
<select class="choice background_possible background_ideal_choice" name="comp_background_ideal_choice">
<option data-i18n="choose" value=""></option>
</select>
<button class="choice standardbg bg_roll" data-i18n="roll" name="roll_background_ideal_choice_roll" type="roll" value=""></button>
<input class="choice custom_background" name="comp_background_ideal_choice" type="text"/>
</label>
<label><span data-i18n="bond"></span><br/>
<select class="choice background_possible background_ideal_choice" name="comp_background_bond_choice">
<option data-i18n="choose" value=""></option>
</select>
<button class="choice standardbg bg_roll" data-i18n="roll" name="roll_background_bond_choice_roll" type="roll" value=""></button>
<input class="choice custom_background" name="comp_background_bond_choice" type="text"/>
</label>
<label><span data-i18n="flaw"></span><br/>
<select class="choice background_possible background_ideal_choice" name="comp_background_flaw_choice">
<option data-i18n="choose" value=""></option>
</select>
<button class="choice standardbg bg_roll" data-i18n="roll" name="roll_background_flaw_choice_roll" type="roll" value=""></button>
<input class="choice custom_background" name="comp_background_flaw_choice" type="text"/>
</label>
</div>
<div class="row choice background_possible background_equipment_row">
<p><span class="label" data-i18n="equipment"></span><span class="background_standard_equipment"></span></p>
</div>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-background-info" accept="Rules:Backgrounds"></div>
</div>
<div class="bottombar">
<button class="prev" data-i18n="back" type="back" value="l1-abilities"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-equipment"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-background"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
<input name="comp_background_detail_choice_array" type="hidden"/>
<input name="comp_background_personality_choice1_array" type="hidden"/>
<input name="comp_background_personality_choice2_array" type="hidden"/>
<input name="comp_background_ideal_choice_array" type="hidden"/>
<input name="comp_background_bond_choice_array" type="hidden"/>
<input name="comp_background_flaw_choice_array" type="hidden"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-equipment">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choices">
<input name="comp_equipment_class" type="hidden"/>
<input name="comp_equipment_background" type="hidden"/>
<input name="comp_starting_gold_roll" type="hidden"/>
<input name="comp_background_starting_gold" type="hidden"/>
<div class="row">
<p class="choice no_class"><span class="label" data-i18n="you-havent-selected-class"></span></p>
<p class="choice eqp_custom_class"><span class="label" data-i18n="you-have-created-custom-class"></span></p>
<p class="choice no_gold_option"><span class="label" data-i18n="pick-your-equipment"></span><span>: </span><span class="class_label"></span></p>
<div class="choice has_class equipment_type hilite">
<p><span class="label" data-i18n="equipment-choice-text"><span data-i18n="class"></span><span>: </span><span class="class_label"></span></span></p>
<select class="equipment_type" name="comp_equipment_type" required="required">
<option data-i18n="choose" value=""></option>
<option data-i18n="class-equipment" value="class"></option>
<option data-i18n="starting-wealth" value="gold"></option>
</select>
</div>
</div>
<div class="row choice gold_option">
<p class="choice custom_class_gold"><span class="label" data-i18n="equipment-from-class"></span><span>(</span><span class="class_label"></span><span>)</span></p>
<p><span class="label" data-i18n="starting-gold:"></span></p>
<p>
<div class="choice random_gold_option">
<button name="roll_starting_gold" type="roll" value=""><span class="starting_gold_btn_label"></span></button>
<input name="comp_starting_gold" type="number" value="0"/>
</div>
</p>
</div>
<div class="row choice class_option">
<p><span class="class_equipment"></span></p>
<p class="equipment_choice_title"><span class="choice class_possible class_equipment_choice1 class_equipment_choice1_title"></span></p>
<select class="choice class_possible class_equipment_choice1" name="comp_class_equipment_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice class_possible class_equipment_choice1_double" name="comp_class_equipment_choice5">
<option data-i18n="choose" value=""></option>
</select>
<p class="equipment_choice_title"><span class="choice class_possible class_equipment_choice2 class_equipment_choice2_title"></span></p>
<select class="choice class_possible class_equipment_choice2" name="comp_class_equipment_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice class_possible class_equipment_choice2_double" name="comp_class_equipment_choice6">
<option data-i18n="choose" value=""></option>
</select>
<p class="equipment_choice_title"><span class="choice class_possible class_equipment_choice3 class_equipment_choice3_title"></span></p>
<select class="choice class_possible class_equipment_choice3" name="comp_class_equipment_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice class_possible class_equipment_choice3_double" name="comp_class_equipment_choice7">
<option data-i18n="choose" value=""></option>
</select>
<p class="equipment_choice_title"><span class="choice class_possible class_equipment_choice4 class_equipment_choice4_title"></span></p>
<select class="choice class_possible class_equipment_choice4" name="comp_class_equipment_choice4">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice class_possible class_equipment_choice4_double" name="comp_class_equipment_choice8">
<option data-i18n="choose" value=""></option>
</select>
</div>
<div class="row choice no_class_warning"><span class="warning" data-i18n="no_class_warning-u"></span></div>
<div class="row">
<p class="choice no_background"><span class="label" data-i18n="you-havent-chosen-background"></span></p>
<p class="choice has_background class_option"><span class="label" data-i18n="equipment-from-background"></span><span>(</span><span class="background_label"></span><span>)</span></p>
<p class="choice background_chose_starting_gold background_gold_option"><span class="label" data-i18n="you-have-chosen-starting-gold"></span></p>
<p class="choice has_background class_option"><span class="background_equipment"></span></p>
<div class="choice class_option">
<p class="equipment_choice_title"><span class="choice background_possible background_equipment_choice1 background_equipment_choice1_title"></span>
<select class="choice background_possible background_equipment_choice1" name="comp_background_equipment_choice1">
<option data-i18n="choose" value=""></option>
</select>
</p>
<p class="equipment_choice_title"><span class="choice background_possible background_equipment_choice2 background_equipment_choice2_title"></span>
<select class="choice background_possible background_equipment_choice2" name="comp_background_equipment_choice2">
<option data-i18n="choose" value=""></option>
</select>
</p>
<p class="equipment_choice_title"><span class="choice background_possible background_equipment_choice3 background_equipment_choice3_title"></span>
<select class="choice background_possible background_equipment_choice3" name="comp_background_equipment_choice3">
<option data-i18n="choose" value=""></option>
</select>
</p>
<p class="equipment_choice_title"><span class="choice background_possible background_equipment_choice4 background_equipment_choice4_title"></span>
<select class="choice background_possible background_equipment_choice4" name="comp_background_equipment_choice4">
<option data-i18n="choose" value=""></option>
</select>
</p>
</div>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-background-info" accept="Rules:Equipment"></div>
</div>
<div class="bottombar">
<button class="prev" data-i18n="back" type="back" value="l1-background"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-spells"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-equipment"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-spells">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choice no_spells">
<p class="label" data-i18n="arcane-is-mystery"></p>
<p data-i18n="your-character-dont-have-spells-this-lvl"></p>
</div>
<div class="choices spell_holder"></div>
<div class="info choice spell-info-container">
<div class="sheet-compendium-page sheet-spell-info sheet-spells-info" accept="Rules:Spells"></div>
</div>
<div class="bottombar">
<button class="prev" data-i18n="back" type="back" value="l1-equipment"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-feat"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-spells"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
<input name="comp_race_spells" type="hidden"/>
<input name="comp_class_spells" type="hidden"/>
<input name="comp_race_number" type="hidden"/>
<input name="comp_class_number" type="hidden"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-feat">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choice no_feat">
<p class="label" data-i18n="charmancer-a-feat-represents"></p>
<p data-i18n="you-really-havent-done"></p>
</div>
<div class="feat-selection">
<div class="choices choice yes_feat">
<div class="row">
<label><span data-i18n="choose-a-feat:"></span>
<select accept="Category:Feats" name="comp_feat">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="choice feat_options">
<div class="row choice feat_prereq feat_possible">
<label class="warning"><span data-i18n="prerequisite:"></span>
<p class="feat_prerequisite feat_text"></p>
<p class="feat_text" data-i18n="feat-pre-check"></p>
</label>
</div>
<div class="row choice feat_ability feat_possible">
<label><span data-i18n="ability-score-increase:"></span>
<p class="feat_ability_score feat_text"></p>
<select class="choice feat_possible feat_ability_choice" name="comp_feat_ability_choice">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_language_row">
<label><span data-i18n="language-proficiencies"></span>
<p class="feat_text feat_languages"></p>
<select class="choice feat_possible feat_language_choice1" name="comp_feat_language_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_language_choice2" name="comp_feat_language_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_language_choice3" name="comp_feat_language_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_language_choice4" name="comp_feat_language_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_skill_row">
<label><span data-i18n="skill-proficiencies"></span>
<p class="feat_text feat_skills"></p>
<select class="choice feat_possible feat_skill_choice1" name="comp_feat_skill_choice1" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_skill_choice2" name="comp_feat_skill_choice2" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_skill_choice3" name="comp_feat_skill_choice3" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_skill_choice4" name="comp_feat_skill_choice4" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_weapon_row">
<label><span data-i18n="weapon-proficiencies"></span>
<p class="feat_text feat_weapons"></p>
<select class="choice feat_possible feat_weapon_choice1" name="comp_feat_weapon_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_weapon_choice2" name="comp_feat_weapon_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_weapon_choice3" name="comp_feat_weapon_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_weapon_choice4" name="comp_feat_weapon_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_armor_row">
<label><span data-i18n="armor-proficiencies"></span>
<p class="feat_text feat_armors"></p>
<select class="choice feat_possible feat_armor_choice1" name="comp_feat_armor_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_armor_choice2" name="comp_feat_armor_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_armor_choice3" name="comp_feat_armor_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_armor_choice4" name="comp_feat_armor_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_tool_row">
<label><span data-i18n="tool-proficiencies"></span>
<p class="feat_text feat_tools"></p>
<select class="choice feat_possible feat_tool_choice1" name="comp_feat_tool_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_tool_choice2" name="comp_feat_tool_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_tool_choice3" name="comp_feat_tool_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_tool_choice4" name="comp_feat_tool_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="sheet-row sheet-choice sheet-feat_possible sheet-feat_spell_row">
<label><span data-i18n="spell-list">Spell List:<span style="color: red;"></span></span>
<p class="sheet-feat_text sheet-feat_spells"></p>
<select class="sheet-choice sheet-feat_possible sheet-feat_spell_choice1" name="comp_feat_spell_choice1">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="sheet-choice sheet-feat_possible sheet-feat_spell_choice2" name="comp_feat_spell_choice2">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="sheet-choice sheet-feat_possible sheet-feat_spell_choice3" name="comp_feat_spell_choice3">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="sheet-choice sheet-feat_possible sheet-feat_spell_choice4" name="comp_feat_spell_choice4">
<option data-i18n="choose" value="">Choose</option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_expertise_row">
<label><span data-i18n="expertise"></span>
<select class="choice feat_possible feat_expertise_choice1" name="comp_feat_expertise_choice_1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_expertise_choice2" name="comp_feat_expertise_choice_2">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_feature_row_1">
<h4 class="feat_feature_name_1"></h4><span class="feat_feature_1"></span>
<select class="feat_possible feat_feature_choice_1" name="comp_feat_feature_choice_1">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom"></option>
</select>
<p class="description feat_feature_description_1"></p>
</div>
<div class="row choice feat_possible feat_feature_row_1_double">
<h4 class="feat_feature_name_1_double"></h4><span class="feat_feature_1_double"></span>
<select class="feat_possible feat_feature_choice_1_double" name="comp_feat_feature_choice_3">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom"></option>
</select>
<p class="description feat_feature_description_1_double"></p>
</div>
<div class="row choice feat_possible feat_feature_row_2">
<h4 class="feat_feature_name_2"></h4><span class="feat_feature_2"></span>
<select class="feat_possible feat_feature_choice_2" name="comp_feat_feature_choice_2">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom"></option>
</select>
<p class="description feat_feature_description_2"></p>
</div>
<div class="choice row feat_possible other_options_and_features1">
<label><span class="other_options_and_features_title1"></span>
<select class="feat_possible other_options_and_features_choice1" name="comp_other_options_and_features_choice1">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="choice row feat_possible other_options_and_features2">
<label><span class="other_options_and_features_title2"></span>
<select class="feat_possible other_options_and_features_choice1" name="comp_other_options_and_features_choice2">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
</div>
</div>
<div class="choices choice yes_feat_bg">
<div class="row">
<label><span data-i18n="choose-a-feat:"></span>
<div class="feat-bg-container"><span class="feat-bg-name">Loading...</span><button class="mancer_info" name="act_info_feat_bg" type="action" required="required">i</button></div>
<select name="comp_feat_bg">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="choice feat_bg_options">
<div class="row choice feat_bg_prereq feat_bg_possible">
<label class="warning"><span data-i18n="prerequisite:"></span>
<p class="feat_bg_prerequisite feat_bg_text"></p>
<p class="feat_bg_text" data-i18n="feat-pre-check"></p>
</label>
</div>
<div class="row choice feat_bg_ability feat_bg_possible">
<label><span data-i18n="ability-score-increase:"></span>
<p class="feat_bg_ability_score feat_bg_text"></p>
<select class="choice feat_bg_possible feat_bg_ability_choice" name="comp_feat_bg_ability_choice">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_language_row">
<label><span data-i18n="language-proficiencies"></span>
<p class="feat_text feat_languages"></p>
<select class="choice feat_possible feat_language_choice1" name="comp_feat_language_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_language_choice2" name="comp_feat_language_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_language_choice3" name="comp_feat_language_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_language_choice4" name="comp_feat_language_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_skill_row">
<label><span data-i18n="skill-proficiencies"></span>
<p class="feat_text feat_skills"></p>
<select class="choice feat_possible feat_skill_choice1" name="comp_feat_skill_choice1" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_skill_choice2" name="comp_feat_skill_choice2" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_skill_choice3" name="comp_feat_skill_choice3" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_skill_choice4" name="comp_feat_skill_choice4" accept="Category:Proficiencies Type:Skill">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_weapon_row">
<label><span data-i18n="weapon-proficiencies"></span>
<p class="feat_text feat_weapons"></p>
<select class="choice feat_possible feat_weapon_choice1" name="comp_feat_weapon_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_weapon_choice2" name="comp_feat_weapon_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_weapon_choice3" name="comp_feat_weapon_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_weapon_choice4" name="comp_feat_weapon_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_armor_row">
<label><span data-i18n="armor-proficiencies"></span>
<p class="feat_text feat_armors"></p>
<select class="choice feat_possible feat_armor_choice1" name="comp_feat_armor_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_armor_choice2" name="comp_feat_armor_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_armor_choice3" name="comp_feat_armor_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_armor_choice4" name="comp_feat_armor_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_possible feat_tool_row">
<label><span data-i18n="tool-proficiencies"></span>
<p class="feat_text feat_tools"></p>
<select class="choice feat_possible feat_tool_choice1" name="comp_feat_tool_choice1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_tool_choice2" name="comp_feat_tool_choice2">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_tool_choice3" name="comp_feat_tool_choice3">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_possible feat_tool_choice4" name="comp_feat_tool_choice4">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="sheet-row sheet-choice sheet-feat_bg_possible sheet-feat_bg_spell_row">
<label><span data-i18n="spell-list">Spell List:<span style="color: red;"></span></span>
<p class="sheet-feat_bg_text sheet-feat_bg_spells"></p>
<select class="sheet-choice sheet-feat_bg_possible sheet-feat_bg_spell_choice1" name="comp_feat_bg_spell_choice1">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="sheet-choice sheet-feat_bg_possible sheet-feat_bg_spell_choice2" name="comp_feat_bg_spell_choice2">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="sheet-choice sheet-feat_bg_possible sheet-feat_bg_spell_choice3" name="comp_feat_bg_spell_choice3">
<option data-i18n="choose" value="">Choose</option>
</select>
<select class="sheet-choice sheet-feat_bg_possible sheet-feat_bg_spell_choice4" name="comp_feat_bg_spell_choice4">
<option data-i18n="choose" value="">Choose</option>
</select>
</label>
</div>
<div class="row choice feat_bg_possible feat_bg_expertise_row">
<label><span data-i18n="expertise"></span>
<select class="choice feat_bg_possible feat_bg_expertise_choice1" name="comp_feat_bg_expertise_choice_1">
<option data-i18n="choose" value=""></option>
</select>
<select class="choice feat_bg_possible feat_bg_expertise_choice2" name="comp_feat_bg_expertise_choice_2">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="row choice feat_bg_possible feat_bg_feature_row_1">
<h4 class="feat_bg_feature_name_1"></h4><span class="feat_bg_feature_1"></span>
<select class="feat_bg_possible feat_bg_feature_choice_1" name="comp_feat_bg_feature_choice_1">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom"></option>
</select>
<p class="description feat_bg_feature_description_1"></p>
</div>
<div class="row choice feat_bg_possible feat_bg_feature_row_1_double">
<h4 class="feat_bg_feature_name_1_double"></h4><span class="feat_bg_feature_1_double"></span>
<select class="feat_bg_possible feat_bg_feature_choice_1_double" name="comp_feat_bg_feature_choice_3">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom"></option>
</select>
<p class="description feat_bg_feature_description_1_double"></p>
</div>
<div class="row choice feat_bg_possible feat_bg_feature_row_2">
<h4 class="feat_bg_feature_name_2"></h4><span class="feat_bg_feature_2"></span>
<select class="feat_bg_possible feat_bg_feature_choice_2" name="comp_feat_bg_feature_choice_2">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom"></option>
</select>
<p class="description feat_bg_feature_description_2"></p>
</div>
<div class="choice row feat_bg_possible feat_bg_other_options_and_features1">
<label><span class="feat_bg_other_options_and_features_title1"></span>
<select class="feat_bg_possible feat_bg_other_options_and_features_choice1" name="comp_feat_bg_other_options_and_features_choice1">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
<div class="choice row feat_bg_possible feat_bg_other_options_and_features2">
<label><span class="feat_bg_other_options_and_features_title2"></span>
<select class="feat_bg_possible feat_bg_other_options_and_features_choice1" name="comp_feat_bg_other_options_and_features_choice2">
<option data-i18n="choose" value=""></option>
</select>
</label>
</div>
</div>
</div>
</div>
<div class="info feat-info-container choice">
<div class="sheet-compendium-page sheet-feat-info" accept="Rules:Feats"></div>
</div>
<div class="bottombar">
<button class="prev" data-i18n="back" type="back" value="l1-spells"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-bio"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-feat"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-bio">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choices">
<input name="comp_previous_race" type="hidden" value=""/>
<div class="row">
<label>
<div data-i18n="char-name-u"></div>
<input name="comp_character_name" type="text"/>
</label>
</div>
<div class="row">
<label>
<div data-i18n="age-u"></div>
<input name="comp_age" type="text"/>
</label>
</div>
<div class="row">
<label>
<div data-i18n="height-u"></div>
<input name="comp_height" type="text"/>
</label>
</div>
<div class="row">
<label>
<div data-i18n="weight-u"></div>
<input name="comp_weight" type="text"/>
</label>
</div>
<div class="row">
<label>
<div data-i18n="eye-u"></div>
<input name="comp_eyes" type="text"/>
</label>
</div>
<div class="row">
<label>
<div data-i18n="hair-u"></div>
<input name="comp_hair" type="text"/>
</label>
</div>
<div class="row">
<label>
<div data-i18n="skin-u"></div>
<input name="comp_skin" type="text"/>
</label>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-race-info" accept="Rules:Races"></div>
</div>
<div class="bottombar">
<button class="prev" data-i18n="back" type="back" value="l1-feat"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next" data-i18n="next" type="back" value="l1-summary"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-bio"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-l1-summary">
<div class="charmancer">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<p class="label" data-i18n="review-your-choices"></p>
<div class="summary-container">
<div class="summary">
<p class="label" data-i18n="race"></p>
<div class="race_info"></div>
<div class="choice returnblock race_button"><span data-i18n="return-to:"></span>
<button class="step" data-i18n="race-u" type="back" value="l1-race"></button>
</div>
</div>
<div class="summary">
<p class="label" data-i18n="class"></p>
<div class="class_info"></div>
<div class="choice returnblock class_button"><span data-i18n="return-to:"></span>
<button class="step" data-i18n="class-u" type="back" value="l1-class"></button>
</div>
</div>
<!--+summaryBlocks('abilities')-->
<div class="summary">
<p class="label" data-i18n="background"></p>
<div class="background_info"></div>
<div class="choice returnblock background_button"><span data-i18n="return-to:"></span>
<button class="step" data-i18n="background-u" type="back" value="l1-background"></button>
</div>
</div>
<div class="summary">
<p class="label" data-i18n="equipment"></p>
<div class="equipment_info"></div>
<div class="choice returnblock equipment_button"><span data-i18n="return-to:"></span>
<button class="step" data-i18n="equipment-u" type="back" value="l1-equipment"></button>
</div>
</div>
<div class="summary">
<p class="label" data-i18n="spells"></p>
<div class="spells_info"></div>
<div class="choice returnblock spells_button"><span data-i18n="return-to:"></span>
<button class="step" data-i18n="spells-u" type="back" value="l1-spells"></button>
</div>
</div>
<div class="summary">
<p class="label" data-i18n="feats"></p>
<div class="feat_info"></div>
</div>
<div class="summary">
<p class="label" data-i18n="bio"></p>
<div class="race_warning"></div>
<div class="bio_info"></div>
</div>
<div class="bottombar">
<p class="choice ready_text" data-i18n="charmancer-only-choice-left-is-on-or"></p>
<p class="label ready_message"></p>
<button class="prev" data-i18n="back" type="back" value="l1-bio"></button>
<button class="jump" data-i18n="back-to-top" data-scroll="#top" type="button"></button>
<button class="next apply_changes" data-i18n="apply-changes" type="finish" value="l1-mancer"></button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading"></p>
<button class="warning" data-i18n="cancel_charactermancer" type="cancel" value="l1-cancel"></button>
<button data-i18n="save_progress_and_exit" type="cancel" value="l1-summary"></button>
<button data-i18n="continue-charmancer" name="act_continue" type="action"></button>
<p class="warning"><span data-i18n="charmancer-discard-warning"></span></p>
</div>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-final">
<div class="charmancer">
<div>
<h1 data-i18n="building-your-character"></h1>
</div>
<div class="mancer_progress">
<div style="width: 0%"></div>
</div>
<input name="attr_mancer_category" type="hidden"/>
<input name="attr_mancer_progress" type="hidden"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-welcome">
<div class="charmancer container">
<ol class="steps lp-steps">
</ol>
<div class="logos"><span class="charmancer-logo"></span><span class="compendium-logo"></span></div>
<p class="label" data-i18n="charmancer-welcome-lp">Welcome to the Charactermancer Level Up!</p>
<p data-i18n="charmancer-leveler-what-is-lp">Look at you, with all that experience. Now it&apos;s time to claim your reward. You&apos;ll walk through the step-by-step process to make a few decisions, and your character will be at the next level in no time. Changes are saved as you move through the process, but if you change your mind, you&apos;ll need to revisit the steps and choices that come after.</p>
<p class="label" data-i18n="charmancer-compendium">Your Compendium Awaits.</p>
<p data-i18n="charmancer-compendium-detail-lp">The Charactermancer currently supports Dungeons &amp; Dragons 5th Edition SRD, Player&apos;s Handbook, and Guildmasters&apos; Guide to Ravnica. Xanathar&apos;s Guide to everything will be supported in the coming weeks. You can add the Player&apos;s Handbook and Guildmasters&apos; Guide to Ravnica to your compendium in the Marketplace, or the game owner can share their compendium with everyone in the game.</p>
<p class="label" data-i18n="get-started">Get started by clicking &quot;Next&quot;.</p>
<div class="bottombar">
<button class="prev" type="cancel" value="lp-cancel" data-i18n="opt-out-u">OPT OUT</button>
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next" type="back" value="lp-levels" data-i18n="next">Next</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="lp-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
<input type="hidden" name="comp_spellinfo"/>
<input type="hidden" name="comp_previous_abilities"/>
<input type="hidden" name="comp_previous_attributes"/>
<input type="hidden" name="comp_previous_proficiencies"/>
<input type="hidden" name="comp_previous_repeating"/>
<input type="hidden" name="comp_lp-race"/>
<input type="hidden" name="comp_lp-subrace"/>
<input type="hidden" name="comp_lp-background"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-levels">
<div class="charmancer container">
<ol class="steps lp-steps">
<li>
<button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button>
</li>
</ol>
<div class="topbar-holder"></div>
<div style="width: 100%;">
<p data-i18n="charmancer-leveler-level-info1">Your existing class levels have been pre-populated for you. By default your primary class has been set to be increased by one level. You can change how many levels to increase below, as well as adding a new multiclass.</p>
<p data-i18n="charmancer-leveler-level-info2">In each class section you&apos;ll have the option to take the average number of hit points or to roll randomly, choose class features for the levels you&apos;re advancing, and choose your subclass if appropriate.</p>
</div>
<div style="width: 100%; font-weight: bold;">
<div class="levels_message"></div>
</div>
<div class="choices levels-multiclass">
<input class="lock-custom-class" type="hidden" name="comp_lockcustomclass"/>
<input class="hide-multiclass" type="hidden" name="comp_multiclass" min="0" max="3"/>
<input type="hidden" name="comp_multiclass_initial" min="0" max="3"/>
<div class="row class1_container">
<div class="custom_warning"><span data-i18n="charmancer-leveler-custom-class-warning">The Level Up tool does not support custom classes at this time.</span></div>
<div>
<div class="class1_selector">
<select name="comp_class1" value=""></select>
</div>
<div class="levels-message"><span class="class1_update"></span>
<input class="addinglevel" type="number" name="comp_class1_addlevel" min="0" max="19" value="1"/><span class="class1_tot"></span>
</div>
</div>
<div class="levels-hp-row">
<h2 data-i18n="hp-u">HP</h2>
<input type="number" name="comp_class1_addhp"/>
<input class="hp-flag" type="hidden" name="comp_class1_hp_flag" value="average"/>
<div class="class1_rollhp" style="display: inline;">
<button type="roll" name="roll_class1_rollhp" value="" title="" data-i18n="roll">Roll</button>
</div>
<div class="class1_averagehp" style="display: inline;">
<button type="roll" name="roll_class1_averagehp" value="" title="" data-i18n="average">Average</button>
</div>
<input class="hpbylevel-toggle" type="checkbox" name="comp_hpbyLevel"/><span class="collapse">9</span>
<div class="class1_by-levels"></div>
</div>
<div class="class_features_container">
<h3 class="class1_features_title choice" style="display: inline;" data-i18n="charmancer-class-features:">Class Features: </h3>
<button class="mancer_info" type="action" name="act_class1">i</button>
<div class="class1_features"></div>
<div class="subclass1_name"></div>
<div class="subclass1_warning"></div>
<div class="subclass1_selector">
<select class="choice subclass1" name="comp_class1_subclass">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
</div>
<div class="subclass_features_container">
<h3 class="subclass1_features_title choice" style="display: inline;" data-i18n="charmancer-subclass-features:">Subclass Features: </h3>
<button class="mancer_info" type="action" name="act_class1_subclass">i</button>
<div class="subclass1_features"></div>
</div>
</div>
<div class="row">
<div style="display: inline;">
<div class="class2_selector">
<select class="multiclass_select" name="comp_class2" style="width: 85%;" value="">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
<div class="levels-message"><span class="class2_update"></span>
<input class="addinglevel" type="number" name="comp_class2_addlevel" min="0" max="19"/><span class="class2_tot"></span>
</div>
</div>
<div class="levels-hp-row">
<h2 data-i18n="hp-u">HP</h2>
<input type="number" name="comp_class2_addhp"/>
<input class="hp-flag" type="hidden" name="comp_class2_hp_flag" value="average"/>
<div class="class2_rollhp" style="display: inline;">
<button type="roll" name="roll_class2_rollhp" value="" title="You must input a level increase to this class" data-i18n="roll">Roll</button>
</div>
<div class="class2_averagehp" style="display: inline;">
<button type="roll" name="roll_class2_averagehp" value="" title="" data-i18n="average">Average</button>
</div>
<input class="hpbylevel-toggle" type="checkbox" name="comp_hpbyLevel"/><span class="collapse">9</span>
<div class="class2_by-levels"></div>
</div>
<div>
<h3 class="class2_features_title choice" style="display: inline;" data-i18n="charmancer-class-features:">Class Features: </h3>
<button class="mancer_info" type="action" name="act_class2">i</button>
<div class="class2_features"></div>
<div class="subclass2_name"></div>
<div class="subclass2_warning"></div>
<div class="subclass2_selector">
<select class="choice subclass2" name="comp_class2_subclass">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
</div>
<div>
<h3 class="subclass2_features_title choice" style="display: inline;" data-i18n="charmancer-subclass-features:">Subclass Features: </h3>
<button class="mancer_info" type="action" name="act_class2_subclass">i</button>
<div class="subclass2_features"></div>
</div>
</div>
<div class="row">
<div style="display: inline;">
<div class="class3_selector">
<select class="multiclass_select" name="comp_class3" style="width: 85%;" value="">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
<div class="levels-message"><span class="class3_update"></span>
<input class="addinglevel" type="number" name="comp_class3_addlevel" min="0" max="19"/><span class="class3_tot"></span>
</div>
</div>
<div class="levels-hp-row">
<h2 data-i18n="hp-u">HP</h2>
<input type="number" name="comp_class3_addhp"/>
<input class="hp-flag" type="hidden" name="comp_class3_hp_flag" value="average"/>
<div class="class3_rollhp" style="display: inline;">
<button type="roll" name="roll_class3_rollhp" value="" title="You must input a level increase to this class" data-i18n="roll">Roll</button>
</div>
<div class="class3_averagehp" style="display: inline;">
<button type="roll" name="roll_class3_averagehp" value="" title="" data-i18n="average">Average</button>
</div>
<input class="hpbylevel-toggle" type="checkbox" name="comp_hpbyLevel"/><span class="collapse">9</span>
<div class="class3_by-levels"></div>
</div>
<div>
<h3 class="class3_features_title choice" style="display: inline;" data-i18n="charmancer-class-features:">Class Features: </h3>
<button class="mancer_info" type="action" name="act_class3">i</button>
<div class="class3_features"></div>
<div class="subclass3_name"></div>
<div class="subclass3_warning"></div>
<div class="subclass3_selector">
<select class="choice subclass3" name="comp_class3_subclass">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
</div>
<div>
<h3 class="subclass3_features_title choice" style="display: inline;" data-i18n="charmancer-subclass-features:">Subclass Features: </h3>
<button class="mancer_info" type="action" name="act_class3_subclass">i</button>
<div class="subclass3_features"></div>
</div>
</div>
<div class="row">
<div style="display: inline;">
<div class="class4_selector">
<select class="multiclass_select" name="comp_class4" style="width: 85%;" value="">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
<div class="levels-message"><span class="class4_update"></span>
<input class="addinglevel" type="number" name="comp_class4_addlevel" min="0" max="19"/><span class="class4_tot"></span>
</div>
</div>
<div class="levels-hp-row">
<h2 data-i18n="hp-u">HP</h2>
<input type="number" name="comp_class4_addhp"/>
<input class="hp-flag" type="hidden" name="comp_class4_hp_flag" value="average"/>
<div class="class4_rollhp" style="display: inline;">
<button type="roll" name="roll_class4_rollhp" value="" title="You must input a level increase to this class" data-i18n="roll">Roll</button>
</div>
<div class="class4_averagehp" style="display: inline;">
<button type="roll" name="roll_class4_averagehp" value="" title="" data-i18n="average">Average</button>
</div>
<input class="hpbylevel-toggle" type="checkbox" name="comp_hpbyLevel"/><span class="collapse">9</span>
<div class="class4_by-levels"></div>
</div>
<div>
<h3 class="class4_features_title choice" style="display: inline;" data-i18n="charmancer-class-features:">Class Features: </h3>
<button class="mancer_info" type="action" name="act_class4">i</button>
<div class="class4_features"></div>
<div class="subclass4_name"></div>
<div class="subclass4_warning"></div>
<div class="subclass4_selector">
<select class="choice subclass4" name="comp_class4_subclass">
<option value="" data-i18n="choose">Choose</option>
</select>
</div>
</div>
<div>
<h3 class="subclass4_features_title choice" style="display: inline;" data-i18n="charmancer-subclass-features:">Subclass Features: </h3>
<button class="mancer_info" type="action" name="act_class4_subclass">i</button>
<div class="subclass4_features"></div>
</div>
</div>
<div>
<div class="multiclass_message"></div>
<button type="action" name="act_multiclass_add">+</button>
<button type="action" name="act_multiclass_remove">-</button>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-levels-info" accept="Rules:Classes"></div>
</div>
<div class="bottombar">
<button class="prev" type="back" value="lp-welcome" data-i18n="back">Back</button>
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next" type="back" value="lp-choices" data-i18n="next">Next</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="lp-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
<input type="hidden" name="comp_characterlevel"/>
<input type="hidden" name="comp_class1"/>
<input type="hidden" name="comp_class1_subclass"/>
<input type="hidden" name="comp_class1_currentlevel"/>
<input type="hidden" name="comp_class1_classlevel"/>
<input type="hidden" name="comp_class1_hitdie"/>
<input type="hidden" name="comp_class2"/>
<input type="hidden" name="comp_class2_subclass"/>
<input type="hidden" name="comp_class2_currentlevel"/>
<input type="hidden" name="comp_class2_classlevel"/>
<input type="hidden" name="comp_class2_hitdie"/>
<input type="hidden" name="comp_class3"/>
<input type="hidden" name="comp_class3_subclass"/>
<input type="hidden" name="comp_class3_currentlevel"/>
<input type="hidden" name="comp_class3_classlevel"/>
<input type="hidden" name="comp_class3_hitdie"/>
<input type="hidden" name="comp_class4"/>
<input type="hidden" name="comp_class4_subclass"/>
<input type="hidden" name="comp_class4_currentlevel"/>
<input type="hidden" name="comp_class4_classlevel"/>
<input type="hidden" name="comp_class4_hitdie"/>
<input type="hidden" name="comp_asi"/>
<input type="hidden" name="comp_spells"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-choices">
<div class="charmancer container">
<ol class="steps lp-steps">
<li>
<button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button>
</li>
</ol>
<div class="topbar-holder"></div>
<div class="choices"></div>
<div class="info">
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Classes"></div>
</div>
<div class="bottombar">
<button class="prev" type="back" value="lp-levels" data-i18n="back">Back</button>
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next" type="action" name="act_lp-choices_next" data-i18n="next">Next</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="lp-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-asi">
<div class="charmancer container">
<ol class="steps lp-steps">
<li>
<button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button>
</li>
</ol>
<div class="topbar-holder"></div><span class="spinner-toggle"></span>
<div class="spinnerloader"></div>
<div class="choices"></div>
<div class="info">
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Ability Scores"></div>
</div>
<div class="bottombar">
<button class="prev" type="back" value="lp-choices" data-i18n="back">Back</button>
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next" type="action" name="act_lp-asi_next" data-i18n="next">Next</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="lp-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-spells">
<div class="charmancer container">
<ol class="steps lp-steps">
<li>
<button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button>
</li>
</ol>
<div class="topbar-holder"></div><span class="pagelock" style="display: none"></span>
<div class="choices spellslide">
<p class="instructions" data-i18n="charmancer-leveler-spells-info">Listed here are the current spells your character already knows. If you gained access to more spells during your advancement you will be able to select them below. Check next to the spells you wish to learn. The Charactermancer will help you pick the right number of spells and from the correct list. If your class has the option to swap out spells, you can do so by unselecting currently known spells which will allow you to select new ones in their place.</p>
<p class="spells-summary"></p>
<div class="cantripinfo"></div>
<input type="hidden" name="comp_spell_loaded_data" value=""/><span class="locked--cantrips" style="display: none"></span><span class="locked--spells" style="display: none"></span>
<div class="row spelllist"></div>
<div class="spellloading"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/loading_spells.gif" alt="loading" width="55" height="37"/><span>Please Wait...</span></div>
</div>
<div class="info spellslide">
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Spells"></div>
</div>
<div class="bottombar">
<button class="prev" type="action" name="act_lp-spells_back" data-i18n="back">Back</button>
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next" type="back" value="lp-summary" data-i18n="next">Next</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="lp-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
<input type="hidden" name="comp_knowncantrips"/>
<input type="hidden" name="comp_knownspells"/>
<input type="hidden" name="comp_newcantrips"/>
<input type="hidden" name="comp_newspells"/>
<input type="hidden" name="comp_replace"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-summary">
<div class="charmancer container">
<ol class="steps lp-steps">
<li>
<button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button>
</li>
</ol>
<div class="topbar-holder"></div>
<div class="lp-summary summary-container">
<div class="before">
<h2 data-i18n="before-leveling">Before Leveling</h2>
<div></div>
</div>
<div class="after">
<h2 data-i18n="after-leveling">After Leveling</h2>
<div></div>
</div>
</div>
<div class="summary-container">
<div class="summary">
<p class="label" data-i18n="features">Features</p>
<div class="feature_info"></div>
</div>
<div class="summary">
<p class="label" data-i18n="feats"></p>
<div class="feat_info"></div>
</div>
<div class="summary">
<p class="label" data-i18n="spells">Spells</p>
<div class="spell_info"></div>
<div class="choice returnblock spells_button"><span data-i18n="return-to:">Return to: </span>
<button class="step" type="back" value="l1-spells" data-i18n="spells-u">SPELLS</button>
</div>
</div>
</div>
<div class="bottombar">
<button class="prev" type="action" name="act_lp-summary_back" data-i18n="back">Back</button>
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next choice apply_changes" type="finish" value="lp-mancer" data-i18n="apply-changes">Apply Changes</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="lp-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-spellchoice">
<div class="charmancer container">
<ol class="steps lp-steps">
<li>
<button class="exit" type="action" name="act_cancel" data-i18n="cancel-u">CANCEL</button>
</li>
</ol>
<div class="topbar-holder"></div><span class="pagelock" style="display: none"></span>
<div class="choices spellslide">
<p class="instructions"></p>
</div>
<div class="info spellslide">
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Spells"></div>
</div>
<div class="bottombar">
<button class="jump" type="button" data-scroll="#top" data-i18n="back-to-top">Back to Top</button>
<button class="next" type="action" name="act_lp-spellchoice_back" data-i18n="accept">Accept</button>
</div>
<div class="choice cancel-prompt">
<p class="label" data-i18n="charmancer-cancel-heading">Where would you like to go, Traveler?</p>
<button class="warning" type="cancel" value="lp-cancel" data-i18n="cancel_charactermancer">Discard and exit</button>
<button type="action" name="act_continue"><span data-i18n="continue-charmancer">Continue Charactermancer</span></button>
<p class="warning"><span data-i18n="charmancer-discard-warning">Discard and Exit will discard all choices you&rsquo;ve made in Charactermancer and let you edit your character sheet directly.</span></p>
</div>
<input type="hidden" name="comp_source"/>
<input type="hidden" name="comp_valid"/>
<input type="hidden" name="comp_newcantrips"/>
<input type="hidden" name="comp_newspells"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-lp-dataintegrity">
<div class="charmancer container"></div>
</charmancer>
<charmancer class="sheet-charmancer-utility-welcome">
<div class="charmancer container">
<div class="logos"><span class="charmancer-logo"></span><span class="compendium-logo"></span></div>
<p class="label" data-i18n="charmancer-welcome-utility">Welcome to the Charactermancer Level Up!</p>
<p data-i18n="charmancer-leveler-what-is-utility"></p>
<p class="label" data-i18n="charmancer-compendium"></p>
<p data-i18n="charmancer-compendium-detail-utility"></p>
<p class="label" data-i18n="get-started"></p>
<div class="sheet-stuff"></div>
<div class="bottombar">
<button class="prev" type="cancel" value="lp-cancel" data-i18n="opt-out-u">OPT OUT</button>
<button class="next" type="back" value="utility-newrace" data-i18n="next">Next</button>
</div>
<input type="hidden" name="comp_utility_data"/>
<input type="hidden" name="comp_lp-previous_level"/>
<input type="hidden" name="comp_spellinfo"/>
<input type="hidden" name="comp_previous_abilities"/>
<input type="hidden" name="comp_previous_attributes"/>
<input type="hidden" name="comp_previous_proficiencies"/>
<input type="hidden" name="comp_previous_repeating"/>
<input type="hidden" name="comp_lp-race"/>
<input type="hidden" name="comp_lp-subrace"/>
<input type="hidden" name="comp_lp-background"/>
</div>
</charmancer>
<charmancer class="sheet-charmancer-utility-newrace">
<div class="charmancer container">
<ol class="steps"></ol>
<div class="topbar-holder"></div>
<div class="choices">
<div class="row">
<input type="hidden" name="comp_has_tasha"/>
<div class="customize-origin">
<label><span data-i18n="customize-your-origin"></span><span data-i18n="customize-your-origin-info"></span></label>
<div class="toggle">
<input type="checkbox" name="comp_customize_origin" value="1"/>
<div class="knob"></div>
</div>
</div>
</div>
<div class="previous_race">
<div class="row previous_race_abilities">
<label><span data-i18n="previous-ability-modifiers"></span></label><span data-i18n="previous-ability-modifiers-desc"> </span>
</div>
<div class="row previous_race_skills">
<label><span data-i18n="previous-skills"></span></label><span data-i18n="previous-skills-desc"> </span>
</div>
<div class="row previous_race_other_proficiencies">
<label><span data-i18n="previous-proficiencies"></span></label><span data-i18n="previous-proficiencies-desc"></span>
</div>
<div class="row previous_race_traits">
<label><span data-i18n="previous-traits"></span></label><span data-i18n="previous-traits-desc"> </span>
</div>
<div class="row previous_race_spells">
<label><span data-i18n="previous-spells"></span></label><span data-i18n="previous-spells-desc"> </span>
</div>
</div>
<div class="row">
<label class="lineage-label"><span data-i18n="charmancer-chosen-race"></span>
<input type="hidden" name="comp_newrace"/>
</label>
<h2 class="lineage-name">
<!--
select(accept='Category:Races' name='comp_newrace')
option(data-i18n='choose' value='')
option.custom(data-i18n='custom' value='Rules:Races')
+buttonMancerInfo('race', false)
-->
</h2>
<div class="choice custom_race">
<label class="hilite" data-i18n="charmancer-custom-race-name"></label>
<input name="comp_race_name" placeholder="Custom Race" type="text"/>
</div>
</div>
<div class="row choice race_possible custom_race race_abilities">
<label>
<div class="choice tasha_race_enabled">
<input type="hidden" name="comp_tasha_race_ability_choice1_increase"/><span class="choice tasha_race_ability_choice1 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice1" name="comp_tasha_race_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice2_increase"/><span class="choice tasha_race_ability_choice2 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice2" name="comp_tasha_race_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice3_increase"/><span class="choice tasha_race_ability_choice3 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice3" name="comp_tasha_race_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice4_increase"/><span class="choice tasha_race_ability_choice4 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice4" name="comp_tasha_race_ability_choice4">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice5_increase"/><span class="choice tasha_race_ability_choice5 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice5" name="comp_tasha_race_ability_choice5">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_race_ability_choice6_increase"/><span class="choice tasha_race_ability_choice6 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice race_possible tasha_race_ability_choice6" name="comp_tasha_race_ability_choice6">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<div class="choice tasha_race_disabled"><span data-i18n="ability-score-increase"></span>
<p class="race_ability_score race_text"></p>
<p class="race_ability_choice_rule race_text" data-i18n="vr-ability-rules"></p>
<select class="choice race_possible race_ability_choice1" name="comp_race_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice race_possible race_ability_choice2" name="comp_race_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice race_possible race_ability_choice2" name="comp_race_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<label class="choice custom_race"><span data-i18n="strength"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_strength" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="dexterity"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_dexterity" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="constitution"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_constitution" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="intelligence"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_intelligence" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="wisdom"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_wisdom" type="number" value="0"/>
</label>
<label class="choice custom_race"><span data-i18n="charisma"></span>
<input class="choice custom_race" max="5" min="-5" name="comp_race_custom_charisma" type="number" value="0"/>
</label>
</label>
</div>
<div class="row choice race_always custom_race">
<label><span data-i18n="size"></span>
<p class="race_size race_text"></p>
<select class="choice custom_race" name="comp_size">
<option data-i18n="choose" value="">
<option data-i18n="tiny" value="Tiny"></option>
<option data-i18n="small" value="Small"></option>
<option data-i18n="medium" value="Medium"></option>
<option data-i18n="large" value="Large"></option>
<option data-i18n="huge" value="Huge"></option>
<option data-i18n="gargantuan" value="Gargantuan"></option>
</option>
</select>
</label>
</div>
<div class="row choice race_always custom_race">
<label><span data-i18n="speed"></span>
<p class="race_speed race_text"></p>
<input class="choice custom_race" min="0" name="comp_speed" type="number"/>
</label>
</div>
<div class="race_holder holder"></div>
<div class="choice subrace">
<div class="row">
<label class="hilite"><span data-i18n="subrace"></span>
<select class="choose_subrace" name="comp_newsubrace">
<option data-i18n="choose" value=""></option>
<option class="custom" data-i18n="custom" value="Rules:Races"></option>
</select>
<button class="mancer_info" name="act_info_subrace" type="action">i</button>
</label>
<div class="choice custom_subrace">
<label class="hilite"><span data-i18n="charmancer-custom-subrace-name"></span>
<input name="comp_subrace_name" placeholder="Custom Subrace" type="text"/>
</label>
</div>
</div>
<div class="choice subrace_options">
<div class="row choice subrace_possible subrace_abilities custom_subrace">
<label>
<div class="choice tasha_subrace_enabled">
<input type="hidden" name="comp_tasha_subrace_ability_choice1_increase"/><span class="choice tasha_subrace_ability_choice1 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice1" name="comp_tasha_subrace_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice2_increase"/><span class="choice tasha_subrace_ability_choice2 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice2" name="comp_tasha_subrace_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice3_increase"/><span class="choice tasha_subrace_ability_choice3 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice3" name="comp_tasha_subrace_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice4_increase"/><span class="choice tasha_subrace_ability_choice4 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice4" name="comp_tasha_subrace_ability_choice4">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice5_increase"/><span class="choice tasha_subrace_ability_choice5 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice5" name="comp_tasha_subrace_ability_choice5">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<input type="hidden" name="comp_tasha_subrace_ability_choice6_increase"/><span class="choice tasha_subrace_ability_choice6 tasha" data-i18n="ability-score-increase-tasha"></span>
<select class="choice subrace_possible tasha_subrace_ability_choice6" name="comp_tasha_subrace_ability_choice6">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<div class="choice tasha_subrace_disabled"><span data-i18n="subrace-ability-increase"></span>
<p class="subrace_ability_score subrace_text"></p>
<select class="choice subrace_possible subrace_ability_choice1" name="comp_subrace_ability_choice1">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice subrace_possible subrace_ability_choice1" name="comp_subrace_ability_choice2">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
<select class="choice subrace_possible subrace_ability_choice1" name="comp_subrace_ability_choice3">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</div>
<label class="choice custom_subrace"><span data-i18n="strength"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_strength" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="dexterity"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_dexterity" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="constitution"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_constitution" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="intelligence"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_intelligence" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="wisdom"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_wisdom" type="number" value="0"/>
</label>
<label class="choice custom_subrace"><span data-i18n="charisma"></span>
<input class="choice custom_subrace" max="5" min="-5" name="comp_subrace_custom_charisma" type="number" value="0"/>
</label>
</label>
</div>
<div class="row choice subrace_possible subrace_speed_row custom_subrace">
<label><span data-i18n="speed"></span>
<p class="subrace_speed subrace_text"></p>
<input class="choice custom_subrace" name="comp_speed" min="0" type="number"/>
</label>
</div>
<div class="subrace_holder holder"></div>
</div>
</div>
<div class="row choice race_possible custom_race custom_subrace">
<label><span data-i18n="innate-spellcasting:"></span>
<label><span data-i18n="spellcasting-ability"></span>
<select name="comp_custom_race_spell_ability">
<option data-i18n="choose" value=""></option>
<option data-i18n="strength" value="strength"></option>
<option data-i18n="dexterity" value="dexterity"></option>
<option data-i18n="constitution" value="constitution"></option>
<option data-i18n="intelligence" value="intelligence"></option>
<option data-i18n="wisdom" value="wisdom"></option>
<option data-i18n="charisma" value="charisma"></option>
</select>
</label>
<label class="hilite choice custom_spellcasting"><span data-i18n="number-of-cantrips"></span>
<input min="1" name="comp_custom_race_spell_number" type="number" value="1"/>
</label>
<label class="hilite choice custom_spellcasting"><span data-i18n="spell-list"></span>
<select name="comp_custom_race_spell_list">
<option data-i18n="choose" value=""></option>
<option data-i18n="artificer" value="artificer"></option>
<option data-i18n="bard" value="bard"></option>
<option data-i18n="cleric" value="cleric"></option>
<option data-i18n="druid" value="druid"></option>
<option data-i18n="paladin" value="paladin"></option>
<option data-i18n="ranger" value="ranger"></option>
<option data-i18n="sorcerer" value="sorcerer"></option>
<option data-i18n="warlock" value="warlock"></option>
<option data-i18n="wizard" value="wizard"></option>
</select>
</label>
</label>
</div>
</div>
<div class="info">
<div class="sheet-compendium-page sheet-race-info" accept="Rules:Races"></div>
</div>
<div class="bottombar">
<button class="prev" type="back" value="utility-welcome" data-i18n="back">Back</button>
<button class="next apply_changes" type="finish" value="utility-mancer" data-i18n="apply-changes">Apply Changes</button>
</div>
<input name="comp_has_subrace" type="hidden"/>
</div>
</charmancer>
<charmancer class="sheet-repeating-utility-select">
<input type="hidden" name="comp_default"/>
<select class="select" name="comp_selected">
<option>Choose</option>
</select>
</charmancer>
<charmancer class="sheet-repeating-utility-check">
<input type="hidden" name="comp_target"/>
<input class="check" type="checkbox" name="comp_checked" value="1"/><span class="check-label"></span>
</charmancer>
<!--
doctype html
head
title DnD 5e
link(rel="stylesheet" href="print.css")
body
-->
<div class="printable-pc-sheet printable">
<div class="printable-sheet__container printable">
<div class="header">
<div class="floating-data__container header__container header__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/header_background.svg"/><span class="attribute character-name__value" name="attr_character_name">10</span><span class="translation character-name__label" data-i18n="char-name-u">String</span>
<div class="meta">
<div class="cell"><span class="attribute character-class__value" name="attr_class_display">10</span><span class="translation character-class__label" data-i18n="class-level-u">String</span>
</div>
<div class="cell"><span class="attribute character-background__value" name="attr_background">10</span><span class="translation character-background__label" data-i18n="background-u">String</span>
</div>
<div class="cell"><span class="attribute character-player__value">&nbsp;</span><span class="translation character-player__label" data-i18n="player-name-u">String</span>
</div>
<div class="cell"><span class="attribute character-race__value" name="attr_race_display">10</span><span class="translation character-race__label" data-i18n="race-u">String</span>
</div>
<div class="cell"><span class="attribute character-alignment__value" name="attr_alignment">10</span><span class="translation character-alignment__label" data-i18n="alignment-u">String</span>
</div>
<div class="cell"><span class="attribute character-xp__value" name="attr_experience">10</span><span class="translation character-xp__label" data-i18n="exp-pts-u">String</span>
</div>
</div>
</div>
</div>
<div class="sections">
<div class="cols">
<div class="col col__1">
<div class="rolls">
<div class="abilties">
<div class="abilties__container">
<div class="abilties__container__background"></div>
<div class="floating-data__container strength__container ability__container strength__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/strength_background.svg"/><span class="translation ability__label" data-i18n="strength">String</span><span class="attribute ability__bonus" name="attr_strength_mod">10</span><span class="attribute ability__value" name="attr_strength">10</span>
</div>
<div class="floating-data__container dexterity__container ability__container dexterity__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/dexterity_background.svg"/><span class="translation ability__label" data-i18n="dexterity">String</span><span class="attribute ability__bonus" name="attr_dexterity_mod">10</span><span class="attribute ability__value" name="attr_dexterity">10</span>
</div>
<div class="floating-data__container constitution__container ability__container constitution__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/constitution_background.svg"/><span class="translation ability__label" data-i18n="constitution">String</span><span class="attribute ability__bonus" name="attr_constitution_mod">10</span><span class="attribute ability__value" name="attr_constitution">10</span>
</div>
<div class="floating-data__container intelligence__container ability__container intelligence__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/intelligence_background.svg"/><span class="translation ability__label" data-i18n="intelligence">String</span><span class="attribute ability__bonus" name="attr_intelligence_mod">10</span><span class="attribute ability__value" name="attr_intelligence">10</span>
</div>
<div class="floating-data__container wisdom__container ability__container wisdom__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/wisdom_background.svg"/><span class="translation ability__label" data-i18n="wisdom">String</span><span class="attribute ability__bonus" name="attr_wisdom_mod">10</span><span class="attribute ability__value" name="attr_wisdom">10</span>
</div>
<div class="floating-data__container charisma__container ability__container charisma__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/charisma_background.svg"/><span class="translation ability__label" data-i18n="charisma">String</span><span class="attribute ability__bonus" name="attr_charisma_mod">10</span><span class="attribute ability__value" name="attr_charisma">10</span>
</div>
</div>
</div>
<div class="inspiration">
<div class="floating-data__container inspiration__container styled-attribute__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/inspiration_background.svg"/><span class="translation styled-attribute__label" data-i18n="inspiration-u">String</span>
</div>
</div>
<div class="pb">
<div class="floating-data__container pb__container styled-attribute__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/pb_background.svg"/><span class="attribute styled-attribute__value" name="attr_pb">10</span><span class="translation styled-attribute__label" data-i18n="prof-bonus-u">String</span>
</div>
</div>
<div class="styled-box savings">
<div class="styled-box__content">
<div class="savings__container">
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_strength_save_prof" value="(@{pb})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_strength_save_bonus"/><span class="attribute pbl__bonus" name="attr_strength_save_bonus">10</span><span class="translation pbl__label truncate" data-i18n="strength">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_dexterity_save_prof" value="(@{pb})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_dexterity_save_bonus"/><span class="attribute pbl__bonus" name="attr_dexterity_save_bonus">10</span><span class="translation pbl__label truncate" data-i18n="dexterity">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_constitution_save_prof" value="(@{pb})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_constitution_save_bonus"/><span class="attribute pbl__bonus" name="attr_constitution_save_bonus">10</span><span class="translation pbl__label truncate" data-i18n="constitution">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_intelligence_save_prof" value="(@{pb})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_intelligence_save_bonus"/><span class="attribute pbl__bonus" name="attr_intelligence_save_bonus">10</span><span class="translation pbl__label truncate" data-i18n="intelligence">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_wisdom_save_prof" value="(@{pb})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_wisdom_save_bonus"/><span class="attribute pbl__bonus" name="attr_wisdom_save_bonus">10</span><span class="translation pbl__label truncate" data-i18n="wisdom">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_charisma_save_prof" value="(@{pb})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_charisma_save_bonus"/><span class="attribute pbl__bonus" name="attr_charisma_save_bonus">10</span><span class="translation pbl__label truncate" data-i18n="charisma">String</span>
</div>
</div><span class="translation box-label" data-i18n="saving-throws-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box skills">
<div class="styled-box__content">
<div class="skills__container">
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_acrobatics_prof" value="(@{pb}*@{acrobatics_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_acrobatics_bonus"/><span class="attribute pbl__bonus" name="attr_acrobatics_bonus">10</span><span class="translation pbl__label truncate" data-i18n="acrobatics-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_animal_handling_prof" value="(@{pb}*@{animal_handling_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_animal_handling_bonus"/><span class="attribute pbl__bonus" name="attr_animal_handling_bonus">10</span><span class="translation pbl__label truncate" data-i18n="animal_handling-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_arcana_prof" value="(@{pb}*@{arcana_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_arcana_bonus"/><span class="attribute pbl__bonus" name="attr_arcana_bonus">10</span><span class="translation pbl__label truncate" data-i18n="arcana-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_athletics_prof" value="(@{pb}*@{athletics_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_athletics_bonus"/><span class="attribute pbl__bonus" name="attr_athletics_bonus">10</span><span class="translation pbl__label truncate" data-i18n="athletics-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_deception_prof" value="(@{pb}*@{deception_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_deception_bonus"/><span class="attribute pbl__bonus" name="attr_deception_bonus">10</span><span class="translation pbl__label truncate" data-i18n="deception-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_history_prof" value="(@{pb}*@{history_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_history_bonus"/><span class="attribute pbl__bonus" name="attr_history_bonus">10</span><span class="translation pbl__label truncate" data-i18n="history-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_insight_prof" value="(@{pb}*@{insight_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_insight_bonus"/><span class="attribute pbl__bonus" name="attr_insight_bonus">10</span><span class="translation pbl__label truncate" data-i18n="insight-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_intimidation_prof" value="(@{pb}*@{intimidation_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_intimidation_bonus"/><span class="attribute pbl__bonus" name="attr_intimidation_bonus">10</span><span class="translation pbl__label truncate" data-i18n="intimidation-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_investigation_prof" value="(@{pb}*@{investigation_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_investigation_bonus"/><span class="attribute pbl__bonus" name="attr_investigation_bonus">10</span><span class="translation pbl__label truncate" data-i18n="investigation-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_medicine_prof" value="(@{pb}*@{medicine_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_medicine_bonus"/><span class="attribute pbl__bonus" name="attr_medicine_bonus">10</span><span class="translation pbl__label truncate" data-i18n="medicine-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_nature_prof" value="(@{pb}*@{nature_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_nature_bonus"/><span class="attribute pbl__bonus" name="attr_nature_bonus">10</span><span class="translation pbl__label truncate" data-i18n="nature-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_perception_prof" value="(@{pb}*@{perception_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_perception_bonus"/><span class="attribute pbl__bonus" name="attr_perception_bonus">10</span><span class="translation pbl__label truncate" data-i18n="perception-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_performance_prof" value="(@{pb}*@{performance_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_performance_bonus"/><span class="attribute pbl__bonus" name="attr_performance_bonus">10</span><span class="translation pbl__label truncate" data-i18n="performance-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_persuasion_prof" value="(@{pb}*@{persuasion_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_persuasion_bonus"/><span class="attribute pbl__bonus" name="attr_persuasion_bonus">10</span><span class="translation pbl__label truncate" data-i18n="persuasion-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_religion_prof" value="(@{pb}*@{religion_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_religion_bonus"/><span class="attribute pbl__bonus" name="attr_religion_bonus">10</span><span class="translation pbl__label truncate" data-i18n="religion-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_sleight_of_hand_prof" value="(@{pb}*@{sleight_of_hand_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_sleight_of_hand_bonus"/><span class="attribute pbl__bonus" name="attr_sleight_of_hand_bonus">10</span><span class="translation pbl__label truncate" data-i18n="sleight_of_hand-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_stealth_prof" value="(@{pb}*@{stealth_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_stealth_bonus"/><span class="attribute pbl__bonus" name="attr_stealth_bonus">10</span><span class="translation pbl__label truncate" data-i18n="stealth-core">String</span>
</div>
<div class="pbl__container">
<div class="checkbox pbl__prof">
<input class="checkbox__value" type="checkbox" name="attr_survival_prof" value="(@{pb}*@{survival_type})"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div>
<input class="hidden-attribute pbl__bonus__is-empty" type="hidden" name="attr_survival_bonus"/><span class="attribute pbl__bonus" name="attr_survival_bonus">10</span><span class="translation pbl__label truncate" data-i18n="survival-core">String</span>
</div>
</div><span class="translation box-label" data-i18n="skills-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
</div>
<div class="passive">
<div class="floating-data__container passive__container styled-attribute__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/passive_background.svg"/><span class="attribute styled-attribute__value" name="attr_passive_wisdom">10</span><span class="translation styled-attribute__label" data-i18n="pass-wis-u">String</span>
</div>
</div>
<div class="styled-box styled-box--3 other">
<div class="styled-box__content">
<div class="other__container">
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_tool"/>
<div class="other-type tools"><span><span class="translation proficiency-type" data-i18n="tool-u">String</span><span>: </span><span class="attribute proficiency-name" name="attr_other_tool">10</span></span></div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_languages"/>
<div class="other-type languages"><span><span class="translation proficiency-type" data-i18n="lang-u">String</span><span>: </span><span class="attribute proficiency-name" name="attr_other_languages">10</span></span></div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_armor"/>
<div class="other-type armor"><span><span class="translation proficiency-type" data-i18n="armor-u">String</span><span>: </span><span class="attribute proficiency-name" name="attr_other_armor">10</span></span></div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_weapon"/>
<div class="other-type weapons"><span><span class="translation proficiency-type" data-i18n="weapon-u">String</span><span>: </span><span class="attribute proficiency-name" name="attr_other_weapon">10</span></span></div>
<input class="hidden-attribute toggle-type" type="hidden" name="attr_other_other"/>
<div class="other-type other"><span><span class="translation proficiency-type" data-i18n="other-u">String</span><span>: </span><span class="attribute proficiency-name" name="attr_other_other">10</span></span></div>
<div class="notepad__container">
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
</div>
</div><span class="translation box-label" data-i18n="other-prof-langs-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
</div>
<div class="col col__2">
<div class="styled-box styled-box--5 vitals">
<div class="styled-box__content">
<div class="vitals__container">
<div class="floating-data__container combat__container combat__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/combat_background.svg"/>
<div class="vital__value"><span class="attribute vital__value__ac" name="attr_ac">10</span><span class="attribute vital__value__initiative" name="attr_initiative_bonus">10</span><span class="attribute vital__value__speed" name="attr_speed">10</span>
</div>
</div>
<div class="floating-data__container hp_current__container hp__container hp-current__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/hp_current_background.svg"/>
<div class="hp-max__container"><span class="translation hp-max__label" data-i18n="hp-max-u">String</span><span class="attribute hp-max__value" name="attr_hp_max">10</span>
</div><span class="translation hp__label hp-current__label" data-i18n="hp-current-u">String</span>
</div>
<div class="floating-data__container hp_temporary__container hp__container hp-temporary__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/hp_temporary_background.svg"/><span class="translation hp__label hp-temporary__label" data-i18n="hp-temp-u">String</span>
</div>
<div class="floating-data__container hd_ds__container hd-ds__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/hd_ds_background.svg"/><span class="attribute hd-max__value" name="attr_hit_dice_max">10</span>
</div>
</div>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box attack box styled-box--6">
<div class="styled-box__content">
<div class="attack__container">
<div class="table__header table__header--attack"><span class="translation header__label" data-i18n="name-u">String</span><span class="translation header__label" data-i18n="atk-u">String</span><span class="translation header__label" data-i18n="dmg-type-u">String</span>
</div>
<div class="repeating__container repeating__container--attack">
<fieldset class="repeating_attack">
<div class="repeating__row attack__row"><span class="attribute attack-name" name="attr_atkname">10</span>
<input class="hidden-attribute attack-bonus--valid" type="hidden" name="attr_atkbonus"/><span class="attribute attack-bonus" name="attr_atkbonus">10</span><span class="attribute attack-damage" name="attr_atkdmgtype">10</span>
</div>
</fieldset>
</div>
<div class="notepad__container">
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
</div>
</div><span class="translation box-label" data-i18n="atk-spellcasting-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--4 equipment">
<div class="styled-box__content">
<div class="money__container">
<div class="coin__container coin__container--cp"><span class="attribute coin__count" name="attr_cp">10</span><span class="coin__label">cp</span>
</div>
<div class="coin__container coin__container--sp"><span class="attribute coin__count" name="attr_sp">10</span><span class="coin__label">sp</span>
</div>
<div class="coin__container coin__container--ep"><span class="attribute coin__count" name="attr_ep">10</span><span class="coin__label">ep</span>
</div>
<div class="coin__container coin__container--gp"><span class="attribute coin__count" name="attr_gp">10</span><span class="coin__label">gp</span>
</div>
<div class="coin__container coin__container--pp"><span class="attribute coin__count" name="attr_pp">10</span><span class="coin__label">pp</span>
</div>
</div>
<div class="equipment__container">
<div class="repeating__container repeating__container--inventory">
<fieldset class="repeating_inventory">
<div class="repeating__row inventory__row"><span class="attribute equipment-count" name="attr_itemcount">10</span><span class="attribute equipment-name" name="attr_itemname">10</span>
</div>
</fieldset>
</div>
<div class="notepad__container">
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
</div>
</div><span class="translation box-label" data-i18n="equipment-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
</div>
<div class="col col__3">
<div class="styled-box styled-box--5 personality">
<div class="styled-box__content">
<div class="personality__container">
<div class="styled-box styled-box--7 personality__traits">
<div class="styled-box__content"><span class="attribute personality__characteristic" name="attr_personality_traits">10</span><span class="translation box-label" data-i18n="personality-traits-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--8 personality__characteristic">
<div class="styled-box__content"><span class="attribute personality__characteristic" name="attr_ideals">10</span><span class="translation box-label" data-i18n="ideals-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--8 personality__characteristic">
<div class="styled-box__content"><span class="attribute personality__characteristic" name="attr_bonds">10</span><span class="translation box-label" data-i18n="bonds-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--7 styled-box--rotated personality__characteristic">
<div class="styled-box__content"><span class="attribute personality__characteristic" name="attr_flaws">10</span><span class="translation box-label" data-i18n="flaws-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
</div>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--4 traits">
<div class="styled-box__content">
<div class="traits__container">
<div class="repeating__container repeating__container--traits">
<fieldset class="repeating_traits">
<div class="repeating__row traits__row"><span class="attribute trait-name" name="attr_name">10</span>
</div>
</fieldset>
</div>
</div><span class="translation box-label" data-i18n="feats-traits-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
</div>
</div>
</div>
</div>
<div class="expanded-info">
<div class="sections">
<div class="cols">
<div class="col col__1">
<div class="styled-box attack box styled-box--6">
<div class="styled-box__content">
<div class="attack__container">
<div class="table__header table__header--attack"><span class="translation header__label" data-i18n="name-u">String</span><span class="translation header__label" data-i18n="atk-u">String</span><span class="translation header__label" data-i18n="dmg-type-u">String</span>
</div>
<div class="repeating__container repeating__container--attack">
<fieldset class="repeating_attack">
<div class="repeating__row attack__row"><span class="attribute attack-name" name="attr_atkname">10</span>
<input class="hidden-attribute attack-bonus--valid" type="hidden" name="attr_atkbonus"/><span class="attribute attack-bonus" name="attr_atkbonus">10</span><span class="attribute attack-damage" name="attr_atkdmgtype">10</span>
</div>
</fieldset>
</div>
<div class="notepad__container">
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
</div>
</div><span class="translation box-label" data-i18n="atk-spellcasting-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
</div>
<div class="col col__2">
<div class="styled-box styled-box--4 equipment">
<div class="styled-box__content">
<div class="money__container">
<div class="coin__container coin__container--cp"><span class="attribute coin__count" name="attr_cp">10</span><span class="coin__label">cp</span>
</div>
<div class="coin__container coin__container--sp"><span class="attribute coin__count" name="attr_sp">10</span><span class="coin__label">sp</span>
</div>
<div class="coin__container coin__container--ep"><span class="attribute coin__count" name="attr_ep">10</span><span class="coin__label">ep</span>
</div>
<div class="coin__container coin__container--gp"><span class="attribute coin__count" name="attr_gp">10</span><span class="coin__label">gp</span>
</div>
<div class="coin__container coin__container--pp"><span class="attribute coin__count" name="attr_pp">10</span><span class="coin__label">pp</span>
</div>
</div>
<div class="equipment__container">
<div class="repeating__container repeating__container--inventory">
<fieldset class="repeating_inventory">
<div class="repeating__row inventory__row"><span class="attribute equipment-count" name="attr_itemcount">10</span><span class="attribute equipment-name" name="attr_itemname">10</span>
</div>
</fieldset>
</div>
<div class="notepad__container">
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
<div class="blank-line"></div>
</div>
</div><span class="translation box-label" data-i18n="equipment-u">String</span>
</div>
<div class="styled-box__border"></div>
</div>
</div>
<div class="col col__3">
<div class="resources">
<div class="main">
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_class_resource_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_class_resource_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_class_resource">10</span><span class="attribute resource-name" name="attr_class_resource_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_other_resource_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_other_resource_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_other_resource">10</span><span class="attribute resource-name" name="attr_other_resource_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
</div>
<div class="others">
<div class="repeating__container repeating__container--resource">
<fieldset class="repeating_resource">
<div class="repeating__row resource__row">
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_resource_left_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_resource_left_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_resource_left">10</span><span class="attribute resource-name" name="attr_resource_left_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_resource_right_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_resource_right_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_resource_right">10</span><span class="attribute resource-name" name="attr_resource_right_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
</div>
</fieldset>
</div>
</div>
<div class="blank">
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_class_resource_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_class_resource_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_class_resource">10</span><span class="attribute resource-name" name="attr_class_resource_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_class_resource_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_class_resource_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_class_resource">10</span><span class="attribute resource-name" name="attr_class_resource_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_class_resource_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_class_resource_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_class_resource">10</span><span class="attribute resource-name" name="attr_class_resource_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
<div class="styled-box styled-box--4 resource">
<div class="styled-box__content">
<div class="top">
<input class="hidden-attribute use-pb" type="hidden" name="attr_class_resource_use_pb"/><span class="translation" data-i18n="total">String</span><span class="attribute resource-pb" name="attr_pb">10</span><span class="attribute resource-max" name="attr_class_resource_max">10</span>
</div>
<div class="bottom"><span class="attribute resource-current" name="attr_class_resource">10</span><span class="attribute resource-name" name="attr_class_resource_name">10</span>
</div>
</div>
<div class="styled-box__border"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="spells printable">
<div class="spell-header">
<div class="floating-data__container spell_header__container spell-header__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_header_background.svg"/>
<!--+attribute("character_name").character-name__value-->
<div class="meta">
<select class="spellcasting-ability__value" name="attr_spellcasting_ability">
<option value="0*" data-i18n="none-u">NONE</option>
<option value="@{strength_mod}+" data-i18n="strength-u">STRENGTH</option>
<option value="@{dexterity_mod}+" data-i18n="dexterity-u">DEXTERITY</option>
<option value="@{constitution_mod}+" data-i18n="constitution-u">CONSTITUTION</option>
<option value="@{intelligence_mod}+" data-i18n="intelligence-u">INTELLIGENCE</option>
<option value="@{wisdom_mod}+" data-i18n="wisdom-u">WISDOM</option>
<option value="@{charisma_mod}+" data-i18n="charisma-u">CHARISMA</option>
</select><span class="attribute spell-save-dc__value" name="attr_spell_save_dc">10</span><span class="attribute spell-attack-bonus__value" name="attr_spell_attack_bonus">10</span>
</div>
</div>
</div>
<div class="spells__container">
<div class="col col-1">
<div class="spells-cantrip__container">
<div class="floating-data__container cantrip__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/cantrip_background.svg"/><span class="spell-level">0</span><span class="translation spell-label" data-i18n="cantrips-u">String</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-cantrip">
<div class="repeating__row spell-cantrip__row">
<div class="spell-description__container spell-cantrip-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="spells-1__container">
<div class="floating-data__container spell_with_header__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_with_header_background.svg"/><span class="spell-level">1</span><span class="attribute spell-slots" name="attr_lvl1_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-1">
<div class="repeating__row spell-1__row">
<div class="spell-description__container spell-1-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="spells-2__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">2</span><span class="attribute spell-slots" name="attr_lvl2_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-2">
<div class="repeating__row spell-2__row">
<div class="spell-description__container spell-2-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
</div>
<div class="col col-2">
<div class="spells-3__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">3</span><span class="attribute spell-slots" name="attr_lvl3_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-3">
<div class="repeating__row spell-3__row">
<div class="spell-description__container spell-3-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="spells-4__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">4</span><span class="attribute spell-slots" name="attr_lvl4_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-4">
<div class="repeating__row spell-4__row">
<div class="spell-description__container spell-4-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="spells-5__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">5</span><span class="attribute spell-slots" name="attr_lvl5_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-5">
<div class="repeating__row spell-5__row">
<div class="spell-description__container spell-5-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
</div>
<div class="col col-3">
<div class="spells-6__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">6</span><span class="attribute spell-slots" name="attr_lvl6_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-6">
<div class="repeating__row spell-6__row">
<div class="spell-description__container spell-6-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="spells-7__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">7</span><span class="attribute spell-slots" name="attr_lvl7_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-7">
<div class="repeating__row spell-7__row">
<div class="spell-description__container spell-7-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="spells-8__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">8</span><span class="attribute spell-slots" name="attr_lvl8_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-8">
<div class="repeating__row spell-8__row">
<div class="spell-description__container spell-8-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="spells-9__container">
<div class="floating-data__container spell__container"><img class="floating-data__container__background" src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/spell_background.svg"/><span class="spell-level">9</span><span class="attribute spell-slots" name="attr_lvl9_slots_total">10</span>
</div>
<div class="repeating__container">
<fieldset class="repeating_spell-9">
<div class="repeating__row spell-9__row">
<div class="spell-description__container spell-9-description__container">
<div class="checkbox">
<input class="checkbox__value" type="checkbox" name="attr_"/>
<div class="checkbox__display--checked"><img src="https://storage.googleapis.com/char-sheet-app-images-6e101411/D%26D5e/images/checkbox_background.svg"/></div>
<div class="checkbox__display--unchecked"></div>
</div><span class="attribute spell-name" name="attr_spellname">10</span>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</div>
</div>
<div class="full-traits printable">
<h2><span class="translation" data-i18n="feats-traits-u">String</span>
</h2>
<div class="full-traits__container">
<div class="repeating__container repeating__container--full-traits">
<fieldset class="repeating_traits">
<div class="repeating__row traits__row"><span class="attribute trait-name" name="attr_name">10</span><span class="attribute trait-description" name="attr_description">10</span>
</div>
</fieldset>
</div>
</div>
</div>
<div class="full-spells printable">
<h2><span class="translation" data-i18n="spells-u">String</span>
</h2>
<div class="full-spells__container">
<div class="spells-cantrip__container">
<!--+floatingDataOnTopOfImage(cantrip, ["styled-attribute__container"])-->
<div class="repeating__container">
<fieldset class="repeating_spell-cantrip">
<div class="repeating__row spell-cantrip__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-1__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-1">
<div class="repeating__row spell-1__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-2__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-2">
<div class="repeating__row spell-2__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-3__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-3">
<div class="repeating__row spell-3__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-4__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-4">
<div class="repeating__row spell-4__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-5__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-5">
<div class="repeating__row spell-5__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-6__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-6">
<div class="repeating__row spell-6__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-7__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-7">
<div class="repeating__row spell-7__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-8__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-8">
<div class="repeating__row spell-8__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
<div class="spells-9__container">
<!--+styleAttribute("spell", "prof-bonus-u", "pb")-->
<div class="repeating__container">
<fieldset class="repeating_spell-9">
<div class="repeating__row spell-9__row"><span class="attribute spell-name__label" name="attr_spellname">10</span>
<div class="spell-desc-row"><span class="spell-school" name="attr_spellschool" data-i18n-dynamic="data-i18n-dynamic"></span><span> </span><span class="attribute spell-school" name="attr_spelllevel">10</span>
<input class="toggle-something" type="checkbox" name="attr_spellritual" value="{{ritual=1}}"/><span class="tobe-toggled">(<span data-i18n="ritual-l">ritual</span>)</span>
</div>
<div class="spell-desc-row"><span class="translation casting-time__label" data-i18n="casting-time:">String</span><span> </span><span class="attribute casting-time__value" name="attr_spellcastingtime">10</span>
</div>
<div class="spell-desc-row"><span class="translation range__label" data-i18n="range:">String</span><span> </span><span class="attribute range__value" name="attr_spellrange">10</span>
</div>
<div class="spell-desc-row"><span class="translation target__label" data-i18n="target:">String</span><span> </span><span class="attribute range__value" name="attr_spelltarget">10</span>
</div>
<div class="spell-desc-row"><span class="translation components__label" data-i18n="components:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_v" value="{{v=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-verbal">V</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_s" value="{{s=1}}" checked="checked"/><span class="tobe-toggled" data-i18n="spell-component-somatic">S</span>
<input class="toggle-something" type="checkbox" name="attr_spellcomp_m" value="{{m=1}}" checked="checked"/><span class="spellcomp_m tobe-toggled" data-i18n="spell-component-material">M</span>
<input type="hidden" name="attr_spellcomp_materials" value=""/>
</div>
<div class="spell-desc-row"><span class="translation duration__label" data-i18n="duration:">String</span><span> </span>
<input class="toggle-something" type="checkbox" name="attr_spellconcentration" value="{{concentration=1}}"/><span class="tobe-toggled" data-i18n="concentration">Concentration</span><span name="attr_spellduration"></span>
</div>
<div class="spell-desc-row"><span class="bold description__label" data-i18n="description:"></span></div>
<div class="spell-desc-row"><span class="description__value" name="attr_spelldescription"></span></div>
<input class="toggle-something" type="hidden" name="attr_spellathigherlevels" value=""/>
<div class="spell-desc-row tobe-toggled"><span class="bold italics higher__label" data-i18n="at-higher-lvl"></span><span>: </span><span class="higher__value" name="attr_spellathigherlevels"></span></div>
</div>
</fieldset>
</div>
</div>
</div>
</div>
</div>
</web>
<!--mobileinclude mobile/5th Edition OGL by Roll20.pug
-->