mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 03:40:32 +00:00
Merge pull request #15144 from skydawn1/master
[Esper Genesis 5E] Update to new version.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 332 KiB After Width: | Height: | Size: 325 KiB |
+3684
-1632
File diff suppressed because it is too large
Load Diff
+1429
-4872
File diff suppressed because one or more lines are too long
@@ -13,7 +13,7 @@
|
||||
|
||||
include src/mixins/mixins.pug
|
||||
|
||||
input(name='attr_version' type='hidden' value!='3.0')
|
||||
input(name='attr_version' type='hidden' value!='4.2')
|
||||
input.monster_confirm_flag(name='attr_monster_confirm_flag' type='hidden')
|
||||
input.mancer_confirm_flag(name='attr_mancer_confirm_flag' type='hidden')
|
||||
input.licensedsheet(name='attr_licensedsheet' type='hidden')
|
||||
@@ -23,22 +23,23 @@ input.licensedsheet(name='attr_licensedsheet' type='hidden')
|
||||
input.npcspellcastingflag(name='attr_npcspellcastingflag' type='hidden' value!='0')
|
||||
input.sheet-pc-type(name='attr_pc_sheet_type' type='hidden' value='pc')
|
||||
input.npcspellcastingflag(name='attr_npcspellcastingflag' type='hidden')
|
||||
input.toggleflag(name='attr_rtype' type='hidden')
|
||||
.advantagetoggle
|
||||
input.toggle-left(type='radio' name='attr_advantagetoggle' value!='{{query=1}} {{advantage=1}} {{r2=[[@{d20}')
|
||||
span(data-i18n='adv-u') ADVANTAGE
|
||||
input.toggle-center(type='radio' name='attr_advantagetoggle' value!='{{query=1}} {{normal=1}} {{r2=[[0d20' checked='checked')
|
||||
span(data-i18n='norm-u') NORMAL
|
||||
input.toggle-right(type='radio' name='attr_advantagetoggle' value!='{{query=1}} {{disadvantage=1}} {{r2=[[@{d20}')
|
||||
span(data-i18n='disadv-u') DISADVANTAGE
|
||||
input.toggleflag(name='attr_wtype' type='hidden')
|
||||
.advantagetoggle.whispertoggle
|
||||
input.toggle-left(name='attr_whispertoggle' type='radio' value!='' checked='checked')
|
||||
span(data-i18n='public:-u') PUBLIC
|
||||
input.toggle-right(name='attr_whispertoggle' type='radio' value!='/w gm ')
|
||||
span(data-i18n='to-gm:-u')
|
||||
| TO
|
||||
span.togm GM
|
||||
.top-bar
|
||||
input.toggleflag(name='attr_rtype' type='hidden')
|
||||
.advantagetoggle
|
||||
input.toggle-left(type='radio' id='adv-toggle-adv' name='attr_advantagetoggle' value!='{{query=1}} {{advantage=1}} {{r2=[[@{d20}')
|
||||
label(for='adv-toggle-adv' data-i18n='adv-u') ADVANTAGE
|
||||
input.toggle-center(type='radio' id='adv-toggle-norm' name='attr_advantagetoggle' value!='{{query=1}} {{normal=1}} {{r2=[[0d20' checked='checked')
|
||||
label(for='adv-toggle-norm' data-i18n='norm-u') NORMAL
|
||||
input.toggle-right(type='radio' id='adv-toggle-disadv' name='attr_advantagetoggle' value!='{{query=1}} {{disadvantage=1}} {{r2=[[@{d20}')
|
||||
label(for='adv-toggle-disadv' data-i18n='disadv-u') DISADVANTAGE
|
||||
input.toggleflag(name='attr_wtype' type='hidden')
|
||||
.advantagetoggle.whispertoggle
|
||||
input.toggle-left(id='whisper-toggle-pub' name='attr_whispertoggle' type='radio' value!='' checked='checked')
|
||||
label(for='whisper-toggle-pub' data-i18n='public:-u') PUBLIC
|
||||
input.toggle-right(id='whisper-toggle-gm' name='attr_whispertoggle' type='radio' value!='/w gm ')
|
||||
label(for='whisper-toggle-gm' data-i18n='to-gm:-u') WHISPER
|
||||
input.npc-mode-mirror(type='checkbox' name='attr_npc' value='1')
|
||||
label.settings-modal-btn(for='settings-modal-toggle') ⚙ SHEET SETTINGS
|
||||
|
||||
include src/npc/npc.pug
|
||||
.container.pc
|
||||
@@ -69,10 +70,9 @@ input.licensedsheet(name='attr_licensedsheet' type='hidden')
|
||||
.mancer_confirm
|
||||
span(style='line-height: 25px' data-i18n='do_you_want_mancer') How do you want to create this character?
|
||||
div(style='margin-top: 20px;')
|
||||
//
|
||||
div(style='position: relative; display: inline-block; width:130px;')
|
||||
input.yes(type='checkbox' style='width:130px;' name='attr_mancer_confirm')
|
||||
span.yes(style='width:130px;' data-i18n='mancer-confirm-yes') Use the Charactermancer
|
||||
div(style='position: relative; display: inline-block; width:130px;')
|
||||
input.yes(type='checkbox' style='width:130px;' name='attr_mancer_confirm')
|
||||
span.yes(style='width:130px;' data-i18n='mancer-confirm-yes') Use the Charactermancer
|
||||
div(style='position: relative; display: inline-block; width:80px;')
|
||||
input.yes(style='width:80px;' type='checkbox' name='attr_mancer_npc')
|
||||
span.yes(style='width:80px;' data-i18n='npc-new') Create an NPC
|
||||
@@ -84,13 +84,13 @@ input.licensedsheet(name='attr_licensedsheet' type='hidden')
|
||||
include src/rolltemplates/rolltemplates.html
|
||||
|
||||
//- CHARACTERMANCER REPEATING SECTIONS
|
||||
include src/charactermancer/repeating/repeatingsections.html
|
||||
include src/charactermancer/repeating/repeatingsections.html
|
||||
|
||||
//- CHARACTERMANCER LEVEL 1
|
||||
include src/charactermancer/l1/l1.pug
|
||||
include src/charactermancer/l1/l1.pug
|
||||
|
||||
//- CHARACTERMANCER LEVEL UP
|
||||
include src/charactermancer/lp/lp.pug
|
||||
include src/charactermancer/lp/lp.pug
|
||||
|
||||
//- HIDDEN INPUTS
|
||||
include src/hiddeninputs/hiddeninputs.pug
|
||||
|
||||
+3520
-1703
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 332 KiB After Width: | Height: | Size: 325 KiB |
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"html": "EG_Sheet.html",
|
||||
"css": "EG_Sheet.css",
|
||||
"authors": "Rich Lescouflair (original sheet by Steve K. and Phil B.)",
|
||||
"authors": "Skydawn Game Studios",
|
||||
"roll20userid": "1471558",
|
||||
"preview": "EG Sheet Roll20.png",
|
||||
"compendium": "espergenesis",
|
||||
"compendium": "dnd5e",
|
||||
"instructions": "This sheet is for use with the Esper Genesis 5E RPG. It is adapted from The D&D 5th Edition Legacy sheet.",
|
||||
"useroptions": [
|
||||
{
|
||||
@@ -261,6 +261,83 @@
|
||||
"description": "Character OTHER PROFICIENCIES & LANGUAGES section defaults to the complex style that is compatible with the Roll20 5E Compendium. The Simple option provides a text field for players who want more manual control over the sheet.",
|
||||
"descriptiontranslationkey": "simple-proficiencies-info"
|
||||
},
|
||||
{
|
||||
"attribute": "use_hero_points",
|
||||
"displayname": "HERO POINTS",
|
||||
"displaytranslationkey": "hero-points-u",
|
||||
"type": "select",
|
||||
"options": [
|
||||
"Off|0",
|
||||
"On|1"
|
||||
],
|
||||
"optiontranslationkeys": [
|
||||
"off",
|
||||
"on"
|
||||
],
|
||||
"default": "0",
|
||||
"description": "When enabled, shows a Hero Points section on the character sheet. Characters start with 5 Hero Points + half their level (rounded down), up to 15 at level 20. Spend a Hero Point to roll 1D6 and add the result to any ability check, attack roll, or saving throw after seeing the roll.",
|
||||
"descriptiontranslationkey": "hero-points-info"
|
||||
},
|
||||
{
|
||||
"attribute": "use_rest_buttons",
|
||||
"displayname": "SHORT/LONG REST BUTTONS",
|
||||
"displaytranslationkey": "rest-buttons-u",
|
||||
"type": "select",
|
||||
"options": [
|
||||
"On|1",
|
||||
"Off|0"
|
||||
],
|
||||
"optiontranslationkeys": [
|
||||
"on",
|
||||
"off"
|
||||
],
|
||||
"default": "1",
|
||||
"description": "Adds Short Rest and Long Rest buttons to the Resources section. Short rest clears death saves and resets short-rest resources. Long rest fully restores HP, hit dice (half level), talent points, and all resources.",
|
||||
"descriptiontranslationkey": "rest-buttons-info"
|
||||
},
|
||||
{
|
||||
"attribute": "reliable_talent",
|
||||
"displayname": "RELIABLE TALENT:",
|
||||
"displaytranslationkey": "reliable-talent:-u",
|
||||
"type": "select",
|
||||
"options": [
|
||||
"Off|0",
|
||||
"On|10"
|
||||
],
|
||||
"optiontranslationkeys": [
|
||||
"off",
|
||||
"on"
|
||||
],
|
||||
"default": "0",
|
||||
"description": "When enabled, any skill check roll for a skill this character is proficient in will treat a d20 result lower than 10 as a 10. Represents the Specialist class feature of the same name (level 11).",
|
||||
"descriptiontranslationkey": "reliable-talent-info"
|
||||
},
|
||||
{
|
||||
"attribute": "powerful_build",
|
||||
"displayname": "POWERFUL BUILD",
|
||||
"displaytranslationkey": "powerful_build-u",
|
||||
"type": "checkbox",
|
||||
"value": "1",
|
||||
"description": "When checked, doubles the character's carrying capacity by applying a x2 multiplier to the size-based weight limit. Represents the racial trait of the same name.",
|
||||
"descriptiontranslationkey": "powerful-build-info"
|
||||
},
|
||||
{
|
||||
"attribute": "currency_mode",
|
||||
"displayname": "CURRENCY:",
|
||||
"displaytranslationkey": "currency:-u",
|
||||
"type": "select",
|
||||
"options": [
|
||||
"EG Currency|eg",
|
||||
"5e Currency|5e"
|
||||
],
|
||||
"optiontranslationkeys": [
|
||||
"eg-currency",
|
||||
"5e-currency"
|
||||
],
|
||||
"default": "eg",
|
||||
"description": "Toggle between EG currency (Cubil CU, Cubil Coin CN, Cusara CS, Orim OM) and standard 5e currency (CP, SP, EP, GP, PP). EG is the default; 5e mode is provided for GMs running hybrid games.",
|
||||
"descriptiontranslationkey": "currency-mode-info"
|
||||
},
|
||||
{
|
||||
"attribute": "npc_name_flag",
|
||||
"displayname": "NPC NAME IN ROLLS:",
|
||||
|
||||
@@ -225,10 +225,10 @@ on("sheet:compendium-drop", function() {
|
||||
update_initiative();
|
||||
break;
|
||||
case "intelligence":
|
||||
update_skills(["astrophysics", "lore", "investigation", "xenobiology", "computers"]);
|
||||
update_skills(["astrophysics", "lore", "investigation", "xenobiology", "computers", "arcana", "history", "nature", "religion"]);
|
||||
break;
|
||||
case "wisdom":
|
||||
update_skills(["mechanics", "insight", "medicine", "perception", "survival"]);
|
||||
update_skills(["mechanics", "insight", "medicine", "perception", "survival", "animal_handling"]);
|
||||
break;
|
||||
case "charisma":
|
||||
update_skills(["deception", "intimidation", "performance", "persuasion"]);
|
||||
@@ -256,8 +256,9 @@ on("change:death_save_mod", function(eventinfo) {
|
||||
update_save("death");
|
||||
});
|
||||
|
||||
['acrobatics','astrophysics','athletics','computers','deception','insight','intimidation','investigation','lore',
|
||||
'mechanics','medicine','perception','performance','persuasion','piloting','sleight_of_hand','stealth','survival','xenobiology'].forEach(attr => {
|
||||
['acrobatics','animal_handling','arcana','astrophysics','athletics','computers','deception','history','insight',
|
||||
'intimidation','investigation','lore','mechanics','medicine','nature','perception','performance','persuasion',
|
||||
'piloting','religion','sleight_of_hand','stealth','survival','xenobiology'].forEach(attr => {
|
||||
on(`change:${attr}_prof change:${attr}_type change:${attr}_flat`, function(eventinfo) {
|
||||
if(eventinfo.sourceType === "sheetworker") {return;};
|
||||
update_skills([`${attr}`]);
|
||||
@@ -413,7 +414,7 @@ on("change:repeating_inventory:itemname change:repeating_inventory:itempropertie
|
||||
});
|
||||
});
|
||||
|
||||
on("change:repeating_inventory:itemweight change:repeating_inventory:itemcount change:cp change:sp change:ep change:cu change:cn change:encumberance_setting change:size change:carrying_capacity_mod", function() {
|
||||
on("change:repeating_inventory:itemweight change:repeating_inventory:itemcount change:cp change:sp change:ep change:cu change:cn change:encumberance_setting change:size change:carrying_capacity_mod change:powerful_build", function() {
|
||||
update_weight();
|
||||
});
|
||||
|
||||
@@ -519,6 +520,29 @@ on("change:base_level change:multiclass1_flag change:multiclass1 change:multicla
|
||||
set_level();
|
||||
});
|
||||
|
||||
// Cybermancer Talent Slot auto-calculation.
|
||||
// Fires when class or level changes; sets slots_max, slot_rank_max, and slots_used
|
||||
// from the Cybermancer level progression table (analogous to Warlock pact slots).
|
||||
var cybermancerSlotTable = [
|
||||
// [slots, rank] index = level - 1
|
||||
[1,1],[2,1],[2,2],[2,2],[2,3],[2,3],[2,4],[2,4],[2,5],[2,5],
|
||||
[3,5],[3,5],[3,5],[3,5],[3,5],[3,5],[4,5],[4,5],[4,5],[4,5]
|
||||
];
|
||||
var update_cybermancer_slots = function() {
|
||||
getAttrs(["class", "base_level"], function(values) {
|
||||
var className = (values["class"] || "").toLowerCase().trim();
|
||||
if(className !== "cybermancer") return;
|
||||
var level = parseInt(values["base_level"], 10) || 1;
|
||||
level = Math.min(Math.max(level, 1), 20);
|
||||
var entry = cybermancerSlotTable[level - 1];
|
||||
setAttrs({ slots_max: entry[0], slot_rank_max: entry[1], slots_used: entry[0] });
|
||||
});
|
||||
};
|
||||
on("change:class change:base_level", function(eventinfo) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === "sheetworker") return;
|
||||
update_cybermancer_slots();
|
||||
});
|
||||
|
||||
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", function(eventinfo) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === "sheetworker") {
|
||||
return;
|
||||
@@ -554,6 +578,13 @@ on("change:dtype", function(eventinfo) {
|
||||
update_npc_action("all");
|
||||
});
|
||||
|
||||
on("change:default_critical_range", function(eventinfo) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === "sheetworker") {
|
||||
return;
|
||||
}
|
||||
update_attacks("all");
|
||||
});
|
||||
|
||||
on("change:jack_of_all_trades", function(eventinfo) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === "sheetworker") {
|
||||
return;
|
||||
@@ -562,6 +593,40 @@ on("change:jack_of_all_trades", function(eventinfo) {
|
||||
update_all_ability_checks();
|
||||
});
|
||||
|
||||
on("change:reliable_talent", function(eventinfo) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === "sheetworker") {
|
||||
return;
|
||||
}
|
||||
update_skills(["athletics","acrobatics","sleight_of_hand","stealth","astrophysics","lore","investigation","xenobiology","computers","mechanics","insight","medicine","perception","survival","deception","intimidation","performance","persuasion","animal_handling","arcana","history","nature","religion"]);
|
||||
});
|
||||
|
||||
on("change:show_5e_skills", function(eventinfo) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === "sheetworker") {return;}
|
||||
update_skills(["animal_handling","arcana","history","nature","religion"]);
|
||||
});
|
||||
|
||||
// ---- Feature: Hero Points ----
|
||||
var updateHeroPoints = function(level) {
|
||||
getAttrs(["use_hero_points", "level"], values => {
|
||||
if (!values["use_hero_points"] || values["use_hero_points"] != "1") return;
|
||||
const lvl = level !== undefined ? parseInt(level) : (parseInt(values["level"]) || 0);
|
||||
setAttrs({ hero_points: Math.floor(lvl / 2) + 5 }, { silent: true });
|
||||
});
|
||||
};
|
||||
on("change:consumed_hero_points", () => {
|
||||
getAttrs(["hero_points"], values => {
|
||||
const hp = values["hero_points"] ? parseInt(values["hero_points"]) : 0;
|
||||
setAttrs({ hero_points: Math.max(hp - 1, 0) }, { silent: true });
|
||||
});
|
||||
});
|
||||
on("change:hero_points", eventInfo => {
|
||||
const hp = eventInfo.newValue ? Math.max(parseInt(eventInfo.newValue), 0) : 0;
|
||||
setAttrs({ hero_points: hp }, { silent: true });
|
||||
});
|
||||
on("change:use_hero_points", eventInfo => {
|
||||
if (eventInfo.newValue && eventInfo.newValue == "1") updateHeroPoints();
|
||||
});
|
||||
|
||||
on("change:initmod change:init_tiebreaker", function(eventinfo) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === "sheetworker") {
|
||||
return;
|
||||
@@ -914,12 +979,13 @@ var update_all_saves = function() {
|
||||
|
||||
var update_all_ability_checks = function(){
|
||||
update_initiative();
|
||||
update_skills(["athletics", "acrobatics", "sleight_of_hand", "stealth", "astrophysics", "lore", "investigation", "xenobiology", "computers", "mechanics", "insight", "medicine", "perception", "survival", "deception", "intimidation", "performance", "persuasion", "piloting"]);
|
||||
update_skills(["athletics", "acrobatics", "sleight_of_hand", "stealth", "astrophysics", "lore", "investigation", "xenobiology", "computers", "mechanics", "insight", "medicine", "perception", "survival", "deception", "intimidation", "performance", "persuasion", "piloting", "animal_handling", "arcana", "history", "nature", "religion"]);
|
||||
};
|
||||
|
||||
var update_skills = function (skills_array) {
|
||||
var skill_parent = {athletics: "strength", acrobatics: "dexterity", sleight_of_hand: "dexterity", stealth: "dexterity", astrophysics: "intelligence", lore: "intelligence", investigation: "intelligence", xenobiology: "intelligence", computers: "intelligence", mechanics: "wisdom", insight: "wisdom", medicine: "wisdom", perception: "wisdom", survival: "wisdom", deception: "charisma", intimidation: "charisma", performance: "charisma", persuasion: "charisma", piloting: "dexterity"};
|
||||
var attrs_to_get = ["pb","pb_type","jack_of_all_trades","jack"];
|
||||
var skill_parent = {athletics: "strength", acrobatics: "dexterity", sleight_of_hand: "dexterity", stealth: "dexterity", astrophysics: "intelligence", lore: "intelligence", investigation: "intelligence", xenobiology: "intelligence", computers: "intelligence", mechanics: "wisdom", insight: "wisdom", medicine: "wisdom", perception: "wisdom", survival: "wisdom", deception: "charisma", intimidation: "charisma", performance: "charisma", persuasion: "charisma", piloting: "dexterity",
|
||||
animal_handling: "wisdom", arcana: "intelligence", history: "intelligence", nature: "intelligence", religion: "intelligence"};
|
||||
var attrs_to_get = ["pb","pb_type","jack_of_all_trades","jack","reliable_talent"];
|
||||
var update = {};
|
||||
var callbacks = [];
|
||||
|
||||
@@ -989,6 +1055,10 @@ var update_skills = function (skills_array) {
|
||||
|
||||
};
|
||||
update[s + "_bonus"] = total;
|
||||
// Reliable Talent: floor skill die at 10 for proficient skills (Specialist class feature)
|
||||
const isProficient = v[s + "_prof"] && v[s + "_prof"] != 0;
|
||||
const hasReliableTalent = v["reliable_talent"] && parseInt(v["reliable_talent"]) === 10;
|
||||
update[s + "_d20"] = (isProficient && hasReliableTalent) ? "{@{d20},0d20+10}k1" : "@{d20}";
|
||||
});
|
||||
|
||||
setAttrs(update, {silent: true}, function() {callbacks.forEach(function(callback) {callback(); })} );
|
||||
@@ -1573,7 +1643,7 @@ const update_attacks = (update_id, source) => {
|
||||
};
|
||||
|
||||
const do_update_attack = (attack_array, source) => {
|
||||
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"];
|
||||
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","dmgflag","dmgbase","dmgattr","dmgmod","dmgtype","dmg2flag","dmg2base","dmg2attr","dmg2mod","dmg2type","dmgcustcrit","dmg2custcrit","saveflag","savedc","saveeffect","saveflat","hldmg","spellid","spelllevel","atkrange","itemid","ammo",].forEach((attr) => {
|
||||
attack_attribs.push(`repeating_attack_${attackid}_${attr}`)
|
||||
@@ -1787,10 +1857,17 @@ const do_update_attack = (attack_array, source) => {
|
||||
let globaldamagecrit = `[[${v.global_damage_mod_crit || 0}]]`;
|
||||
|
||||
//Assamble Roll Templates
|
||||
// Use default_critical_range if set and lower than the per-attack range (e.g. Warrior subclass expanded crit)
|
||||
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>@{atkcritrange}${hbonus}]]}} ${r2}cs>@{atkcritrange}${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} @{charname_output} {{licensedsheet=@{licensedsheet}}}`;
|
||||
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} @{charname_output} {{licensedsheet=@{licensedsheet}}}`;
|
||||
} 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>@{atkcritrange}${hbonus}]]}} ${r2}cs>@{atkcritrange}${hbonus}]]}} {{range=@{atkrange}}} {{desc=@{atk_desc}}} {{spelllevel=@{spelllevel}}} {{innate=@{spell_innate}}} {{globalattack=@{global_attack_mod}}} ammo=@{ammo} @{charname_output} {{licensedsheet=@{licensedsheet}}}`;
|
||||
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} @{charname_output} {{licensedsheet=@{licensedsheet}}}`;
|
||||
} 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} @{charname_output} {{licensedsheet=@{licensedsheet}}}`;
|
||||
} else {
|
||||
@@ -2066,7 +2143,7 @@ const remove_resource = (id) => {
|
||||
const update_weight = function() {
|
||||
let update = {};
|
||||
let wtotal = 0;
|
||||
let weight_attrs = ["cp","sp","ep","cu","cn","encumberance_setting","strength","size","carrying_capacity_mod"];
|
||||
let weight_attrs = ["cp","sp","ep","cu","cn","encumberance_setting","strength","size","carrying_capacity_mod","powerful_build"];
|
||||
getSectionIDs("repeating_inventory", (idarray) => {
|
||||
_.each(idarray, (currentID, i) => {
|
||||
weight_attrs.push(`repeating_inventory_${currentID}_itemweight`);
|
||||
@@ -2095,6 +2172,7 @@ const update_weight = function() {
|
||||
size_multiplier =
|
||||
(size === "tiny") ? .5 : (size === "large") ? 2 : (size === "huge") ? 4 : (size === "gargantuan") ? 8 : size_multiplier;
|
||||
};
|
||||
if (v["powerful_build"] && v["powerful_build"] == 1) { size_multiplier *= 2; }
|
||||
let str = str_base*size_multiplier;
|
||||
// Parse the carrying capacitiy modificator if any
|
||||
if (v.carrying_capacity_mod) {
|
||||
@@ -2356,10 +2434,16 @@ const set_level = function() {
|
||||
let hitdie_final = (multiclass) ? `?{Hit Die Class|${charclass},@{hitdietype}` : "@{hitdietype}";
|
||||
const subclass = (v[`subclass`]) ? v[`subclass`] + " " : "";
|
||||
let class_display = `${subclass}${charclass} ${finallevel}`;
|
||||
const baselevel = finallevel;
|
||||
|
||||
// This nested array is used to determine the overall spellcasting level for the character.
|
||||
var classes = [ [finalclass.toLowerCase(), v[`base_level`]] ];
|
||||
|
||||
// Reset all multiclass HD slots; active ones will be populated in the forEach below.
|
||||
update["multiclass1_hitdietype"] = ""; update["multiclass1_hit_dice_max"] = "0";
|
||||
update["multiclass2_hitdietype"] = ""; update["multiclass2_hit_dice_max"] = "0";
|
||||
update["multiclass3_hitdietype"] = ""; update["multiclass3_hit_dice_max"] = "0";
|
||||
|
||||
["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;
|
||||
@@ -2368,6 +2452,8 @@ const set_level = function() {
|
||||
hitdie_final = `${hitdie_final}|` + v[`${multiclass}`].charAt(0).toUpperCase() + v[`${multiclass}`].slice(1) + "," + checkHitDie(v[`${multiclass}`]);
|
||||
classes.push([ v[`${multiclass}`], multiclasslevel ]);
|
||||
class_display = `${class_display}, ${subclass}` + v[`${multiclass}`] + ` ${multiclasslevel}`;
|
||||
update[`${multiclass}_hitdietype`] = checkHitDie(v[`${multiclass}`]);
|
||||
update[`${multiclass}_hit_dice_max`] = multiclasslevel;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -2376,14 +2462,31 @@ const set_level = function() {
|
||||
update["hitdie_final"] = (multiclass) ? `${hitdie_final}}` : hitdie_final;
|
||||
update["level"] = finallevel;
|
||||
update["caster_level"] = casterlevel;
|
||||
// Set flag: does this class use power slots (not TP-only or Talent Slot system)?
|
||||
var slotClasses = ["engineer","full","hunter","sentinel","half","third"];
|
||||
var usesSlots = classes.some(function(c) {
|
||||
var cn = c[0].toLowerCase();
|
||||
return slotClasses.indexOf(cn) !== -1
|
||||
|| (cn === "fighter" && v["arcane_fighter"] === "1")
|
||||
|| (cn === "rogue" && v["arcane_rogue"] === "1")
|
||||
|| (cn === "warrior" && v["arcane_fighter"] === "1")
|
||||
|| (cn === "specialist" && v["arcane_rogue"] === "1");
|
||||
});
|
||||
update["uses_power_slots"] = usesSlots ? "1" : "0";
|
||||
update["class_display"] = class_display;
|
||||
|
||||
if(!v["level_calculations"] || v["level_calculations"] === "on") {
|
||||
update["hit_dice_max"] = finallevel;
|
||||
update["hit_dice_max"] = baselevel;
|
||||
callbacks.push(() => {update_spell_slots();} );
|
||||
}
|
||||
callbacks.push(() => {update_pb();} );
|
||||
callbacks.push(() => {update_leveler_display();} );
|
||||
callbacks.push(() => {updateHeroPoints(finallevel);} );
|
||||
callbacks.push(() => {
|
||||
getAttrs(["use_spell_points"], v => {
|
||||
if (v["use_spell_points"] && v["use_spell_points"] == "1") setSpellPoints();
|
||||
});
|
||||
});
|
||||
setAttrs(update, {silent: true},() => {callbacks.forEach((callback) => {callback(); })} );
|
||||
});
|
||||
};
|
||||
@@ -2403,7 +2506,8 @@ var isMultiCaster = function(classes, arcane_fighter, arcane_rogue) {
|
||||
};
|
||||
|
||||
var getCasterType = function(class_string, levels, arcane_fighter, arcane_rogue) {
|
||||
var full = ["adept","cybermancer","engineer","melder","full"];
|
||||
// Full slot casters: Engineer only (Melder/Adept use TP; Cybermancer uses Talent Slots)
|
||||
var full = ["engineer","full"];
|
||||
var half = ["hunter","sentinel","half"];
|
||||
class_string = class_string.toLowerCase();
|
||||
if(full.indexOf(class_string) != -1) {
|
||||
@@ -2411,7 +2515,12 @@ var getCasterType = function(class_string, levels, arcane_fighter, arcane_rogue)
|
||||
} 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" || (class_string === "fighter" && arcane_fighter === "1") || (class_string === "rogue" && arcane_rogue === "1")) {
|
||||
} else if(class_string === "third"
|
||||
|| (class_string === "fighter" && arcane_fighter === "1")
|
||||
|| (class_string === "rogue" && arcane_rogue === "1")
|
||||
|| (class_string === "warrior" && arcane_fighter === "1")
|
||||
|| (class_string === "specialist" && arcane_rogue === "1")
|
||||
) {
|
||||
return (levels == 1 || levels == 2) ? 0 : (1/3);
|
||||
} else {
|
||||
return 0;
|
||||
@@ -2465,8 +2574,13 @@ var update_leveler_display = function () {
|
||||
};
|
||||
|
||||
var update_spell_slots = function() {
|
||||
getAttrs(["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"], function(v) {
|
||||
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"], function(v) {
|
||||
var update = {};
|
||||
if (v["use_spell_points"] && v["use_spell_points"] == "1") {
|
||||
for (var i = 1; i <= 9; i++) update[`lvl${i}_slots_total`] = 0;
|
||||
setAttrs(update, {silent: true});
|
||||
return;
|
||||
}
|
||||
var lvl = v["caster_level"] && !isNaN(parseInt(v["caster_level"], 10)) ? parseInt(v["caster_level"], 10) : 0;
|
||||
var l1 = v["lvl1_slots_mod"] && !isNaN(parseInt(v["lvl1_slots_mod"], 10)) ? parseInt(v["lvl1_slots_mod"], 10) : 0;
|
||||
var l2 = v["lvl2_slots_mod"] && !isNaN(parseInt(v["lvl2_slots_mod"], 10)) ? parseInt(v["lvl2_slots_mod"], 10) : 0;
|
||||
@@ -2531,7 +2645,7 @@ var update_pb = function() {
|
||||
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", "astrophysics", "lore", "investigation", "xenobiology", "computers", "mechanics", "insight", "medicine", "perception", "survival", "deception", "intimidation", "performance", "persuasion", "piloting"]);} );
|
||||
callbacks.push( function() {update_skills(["athletics", "acrobatics", "sleight_of_hand", "stealth", "astrophysics", "lore", "investigation", "xenobiology", "computers", "mechanics", "insight", "medicine", "perception", "survival", "deception", "intimidation", "performance", "persuasion", "piloting", "animal_handling", "arcana", "history", "nature", "religion"]);} );
|
||||
|
||||
setAttrs(update, {silent: true}, function() {callbacks.forEach(function(callback) {callback(); })} );
|
||||
});
|
||||
@@ -3216,7 +3330,7 @@ var upgrade_to_2_0 = function(doneupdating) {
|
||||
update_mod("wisdom");
|
||||
update_mod("charisma");
|
||||
update_all_saves();
|
||||
update_skills(["athletics", "acrobatics", "sleight_of_hand", "stealth", "astrophysics", "lore", "investigation", "xenobiology", "computers", "mechanics", "insight", "medicine", "perception", "survival", "deception", "intimidation", "performance", "persuasion", "piloting"]);
|
||||
update_skills(["athletics", "acrobatics", "sleight_of_hand", "stealth", "astrophysics", "lore", "investigation", "xenobiology", "computers", "mechanics", "insight", "medicine", "perception", "survival", "deception", "intimidation", "performance", "persuasion", "piloting", "animal_handling", "arcana", "history", "nature", "religion"]);
|
||||
update_tool("all")
|
||||
update_attacks("all");
|
||||
update_pb();
|
||||
@@ -3679,8 +3793,8 @@ on("change:repeating_weapon-systems", eventinfo => {
|
||||
if(values[`${pre}_damage`]) {
|
||||
const damage = addShipDamageType(values[`${pre}_damage`], values[`${pre}_dmgtype`]);
|
||||
const damagec = addShipDamageType(calcShipAtkdmgc(values[`${pre}_damage`]), values[`${pre}_dmgtype`]);
|
||||
const atkdmg = `@{wtype}&{template:dmg} {{rname=@{weapon}}} {{attack=1}} {{range=@{range}}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[${damage}]]}} {{dmg1type=${values[`${pre}_dmgtype`]}}} {{desc=@{special}}} @{pc_ship_name} {{licensedsheet=@{licensedsheet}}}`;
|
||||
const atkdmgc = `@{wtype}&{template:dmg} {{rname=@{weapon}}} {{attack=1}} {{range=@{range}}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[${damagec}]]}} {{dmg1type=${values[`${pre}_dmgtype`]}}} {{desc=@{special}}} @{pc_ship_name} {{licensedsheet=@{licensedsheet}}}`;
|
||||
const atkdmg = `@{wtype}&{template:dmg} {{rname=@{weapon}}} {{attack=1}} {{range=@{range}}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[${damage}]]}} {{dmg1type=${values[`${pre}_dmgtype`]}}} {{desc=@{special}}} @{character_name} {{licensedsheet=@{licensedsheet}}}`;
|
||||
const atkdmgc = `@{wtype}&{template:dmg} {{rname=@{weapon}}} {{attack=1}} {{range=@{range}}} {{damage=1}} {{dmg1flag=1}} {{dmg1=[[${damagec}]]}} {{dmg1type=${values[`${pre}_dmgtype`]}}} {{desc=@{special}}} @{character_name} {{licensedsheet=@{licensedsheet}}}`;
|
||||
const update = {
|
||||
[`${pre}_atkdmg`]: atkdmg,
|
||||
[`${pre}_atkdmgc`]: atkdmgc,
|
||||
@@ -3691,4 +3805,344 @@ on("change:repeating_weapon-systems", eventinfo => {
|
||||
});
|
||||
|
||||
|
||||
// ---- Feature: Spell Points Variant ----
|
||||
const createResource = function(name, max, current) {
|
||||
getAttrs(["other_resource_name"], v => {
|
||||
if (!v.other_resource_name || v.other_resource_name === "" || v.other_resource_name === name) {
|
||||
setAttrs({ other_resource_name: name, other_resource_max: max, other_resource: current });
|
||||
} else {
|
||||
getSectionIDs("repeating_resource", ids => {
|
||||
if (ids.length === 0) {
|
||||
const newid = generateRowID();
|
||||
setAttrs({
|
||||
[`repeating_resource_${newid}_resource_left_name`]: name,
|
||||
[`repeating_resource_${newid}_resource_left_max`]: max,
|
||||
[`repeating_resource_${newid}_resource_left`]: current
|
||||
});
|
||||
return;
|
||||
}
|
||||
const nameAttrs = ids.map(id => `repeating_resource_${id}_resource_left_name`)
|
||||
.concat(ids.map(id => `repeating_resource_${id}_resource_right_name`));
|
||||
getAttrs(nameAttrs, nv => {
|
||||
const existingKey = Object.keys(nv).find(k => nv[k] === name);
|
||||
const side = existingKey && existingKey.includes("_right_") ? "right" : "left";
|
||||
const existingID = existingKey
|
||||
? existingKey.replace("repeating_resource_", "").split("_resource_")[0]
|
||||
: generateRowID();
|
||||
setAttrs({
|
||||
[`repeating_resource_${existingID}_resource_${side}_name`]: name,
|
||||
[`repeating_resource_${existingID}_resource_${side}_max`]: max,
|
||||
[`repeating_resource_${existingID}_resource_${side}`]: current
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const formatPrintedTools = () => {
|
||||
getSectionIDs("tool", (ids) => {
|
||||
const attrs = [
|
||||
...ids.map((id) => `repeating_tool_${id}_toolbonus_base`),
|
||||
...ids.map((id) => `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) => `repeating_proficiencies_${id}_prof_type`),
|
||||
...ids.map((id) => `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(", "),
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const deleteResource = function(name) {
|
||||
getAttrs(["other_resource_name"], v => {
|
||||
if (v.other_resource_name === name) {
|
||||
setAttrs({ other_resource_name: "", other_resource_max: "", other_resource: "" });
|
||||
} else {
|
||||
getSectionIDs("repeating_resource", ids => {
|
||||
if (ids.length === 0) return;
|
||||
const nameAttrs = ids.map(id => `repeating_resource_${id}_resource_left_name`)
|
||||
.concat(ids.map(id => `repeating_resource_${id}_resource_right_name`));
|
||||
getAttrs(nameAttrs, nv => {
|
||||
const existingKey = Object.keys(nv).find(k => nv[k] === name);
|
||||
if (!existingKey) return;
|
||||
const existingID = existingKey.replace("repeating_resource_", "").split("_resource_")[0];
|
||||
const side = existingKey.includes("_right_") ? "right" : "left";
|
||||
const otherSide = side === "left" ? "right" : "left";
|
||||
if (!nv[`repeating_resource_${existingID}_resource_${otherSide}_name`]) {
|
||||
removeRepeatingRow(`repeating_resource_${existingID}`);
|
||||
} else {
|
||||
setAttrs({
|
||||
[`repeating_resource_${existingID}_resource_${side}_name`]: "",
|
||||
[`repeating_resource_${existingID}_resource_${side}_max`]: "",
|
||||
[`repeating_resource_${existingID}_resource_${side}`]: ""
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const getSpellPoints = function(callback) {
|
||||
const classAttrs = ["base_level","class","multiclass1_flag","multiclass1","multiclass1_lvl",
|
||||
"multiclass2_flag","multiclass2","multiclass2_lvl","multiclass3_flag","multiclass3","multiclass3_lvl",
|
||||
"arcane_fighter","arcane_rogue"];
|
||||
const pointsPerLevel = [0,4,6,14,17,27,32,38,44,57,64,73,78,83,88,94,100,107,114,123,133];
|
||||
getAttrs(classAttrs, v => {
|
||||
const classes = [[v["class"] || "", v["base_level"] || 0]].concat(
|
||||
[1,2,3].filter(i => v[`multiclass${i}_flag`] === "1")
|
||||
.map(i => [v[`multiclass${i}`] || "", v[`multiclass${i}_lvl`] || 0])
|
||||
);
|
||||
const multicaster = isMultiCaster(classes, v["arcane_fighter"], v["arcane_rogue"]);
|
||||
let totalCasterLevel = 0;
|
||||
classes.forEach(function([cls, lvl]) {
|
||||
const lvlInt = parseInt(lvl) || 0;
|
||||
const fraction = getCasterType(cls, lvlInt, v["arcane_fighter"], v["arcane_rogue"]);
|
||||
const contribution = lvlInt * fraction;
|
||||
totalCasterLevel += multicaster ? Math.floor(contribution) : Math.ceil(contribution);
|
||||
});
|
||||
const level = Math.min(Math.max(Math.round(totalCasterLevel), 0), 20);
|
||||
callback(pointsPerLevel[level] || 0);
|
||||
});
|
||||
};
|
||||
const setSpellPoints = function() {
|
||||
getSpellPoints(points => {
|
||||
if (points > 0) createResource("Spell Points", points, points);
|
||||
});
|
||||
};
|
||||
on("change:use_spell_points", eventInfo => {
|
||||
if (eventInfo.newValue && eventInfo.newValue == "1") {
|
||||
setSpellPoints();
|
||||
} else {
|
||||
deleteResource("Spell Points");
|
||||
}
|
||||
update_spell_slots();
|
||||
});
|
||||
|
||||
// ---- Feature: Short/Long Rest Buttons ----
|
||||
class Rest {
|
||||
static resetResources(types) {
|
||||
if (!types.includes("long")) return;
|
||||
getSectionIDs("resource", ids => {
|
||||
const attributes = [
|
||||
"class_resource_max", "class_resource_use_pb",
|
||||
"other_resource_max", "other_resource_use_pb",
|
||||
"pb"
|
||||
];
|
||||
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 pb = parseInt(values["pb"]) || 0;
|
||||
const resetOne = (currentKey, maxKey, pbKey) => {
|
||||
const usePB = values[pbKey] === "1";
|
||||
const max = usePB ? pb : parseInt(values[maxKey]);
|
||||
if (!isNaN(max) && max > 0) update[currentKey] = max;
|
||||
};
|
||||
resetOne("class_resource", "class_resource_max", "class_resource_use_pb");
|
||||
resetOne("other_resource", "other_resource_max", "other_resource_use_pb");
|
||||
ids.forEach(id => {
|
||||
resetOne(`repeating_resource_${id}_resource_left`, `repeating_resource_${id}_resource_left_max`, `repeating_resource_${id}_resource_left_use_pb`);
|
||||
resetOne(`repeating_resource_${id}_resource_right`, `repeating_resource_${id}_resource_right_max`, `repeating_resource_${id}_resource_right_use_pb`);
|
||||
});
|
||||
setAttrs(update);
|
||||
});
|
||||
});
|
||||
}
|
||||
static resetHP() {
|
||||
getAttrs(["hp_max"], values => {
|
||||
if(!isNaN(values["hp_max"])) setAttrs({hp: values["hp_max"]});
|
||||
});
|
||||
}
|
||||
static resetHitDice() {
|
||||
getAttrs(["level", "hit_dice", "hit_dice_max"], values => {
|
||||
const update = {};
|
||||
const level = parseInt(values["level"]) || 0;
|
||||
const maxHitDice = Math.max(Math.floor(level / 2), 1);
|
||||
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);
|
||||
});
|
||||
}
|
||||
// Restores the Talent Points pool (tp). Used by Adept, Cybermancer, Melder on long rest.
|
||||
static resetTalentPoints() {
|
||||
getAttrs(["tp_max"], values => {
|
||||
if(!isNaN(values["tp_max"])) setAttrs({tp: values["tp_max"]});
|
||||
});
|
||||
}
|
||||
// Restores Cybermancer Talent Slots (slots_used = remaining slots) to max.
|
||||
// Cybermancer is EG's Warlock equivalent: talent slots recover on both short and long rest.
|
||||
static resetCybermancerSlots() {
|
||||
get_class_level("cybermancer", cybermancerLvl => {
|
||||
if(cybermancerLvl === 0 || cybermancerLvl === "0") return;
|
||||
getAttrs(["slots_max"], values => {
|
||||
if(!isNaN(values["slots_max"])) setAttrs({slots_used: values["slots_max"]});
|
||||
});
|
||||
});
|
||||
}
|
||||
// Restores power slots (lvl1–9) to their calculated totals on long rest.
|
||||
static resetPowerSlots() {
|
||||
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);
|
||||
});
|
||||
}
|
||||
// Clears Esper Mastery used-flags on long rest.
|
||||
static resetEsperMastery() {
|
||||
setAttrs({
|
||||
mastery_6th_used: "",
|
||||
mastery_7th_used: "",
|
||||
mastery_8th_used: "",
|
||||
mastery_9th_used: ""
|
||||
});
|
||||
}
|
||||
static resetDeathSave() {
|
||||
const update = {};
|
||||
for(let i = 1; i <= 3; i++) {
|
||||
update[`deathsave_succ${i}`] = "";
|
||||
update[`deathsave_fail${i}`] = "";
|
||||
}
|
||||
setAttrs(update);
|
||||
}
|
||||
static decreaseExhaustionLevels() {
|
||||
getAttrs(["exhaustion_toggle", "exhaustion_level"], 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 long() {
|
||||
Rest.resetResources(["short", "long"]);
|
||||
Rest.resetHP();
|
||||
Rest.resetHitDice();
|
||||
Rest.resetTalentPoints();
|
||||
Rest.resetCybermancerSlots();
|
||||
Rest.resetPowerSlots();
|
||||
Rest.resetEsperMastery();
|
||||
Rest.decreaseExhaustionLevels();
|
||||
Rest.resetDeathSave();
|
||||
}
|
||||
static short() {
|
||||
Rest.resetResources(["short"]);
|
||||
Rest.resetCybermancerSlots();
|
||||
Rest.resetDeathSave();
|
||||
}
|
||||
}
|
||||
|
||||
on("clicked:short_rest", () => {
|
||||
console.log("Taking a short rest...");
|
||||
Rest.short();
|
||||
});
|
||||
on("clicked:long_rest", () => {
|
||||
console.log("Taking a long rest...");
|
||||
Rest.long();
|
||||
});
|
||||
|
||||
const sanitizeShipNum = (val) => {
|
||||
const raw = (val || "").trim().replace(/^\+/, "");
|
||||
const n = parseInt(raw, 10);
|
||||
return isNaN(n) ? 0 : n;
|
||||
};
|
||||
|
||||
on("change:pc_ship_piloting_maneuver_check", () => {
|
||||
getAttrs(["pc_ship_piloting_maneuver_check"], function(v) {
|
||||
setAttrs({ pc_ship_piloting_num: sanitizeShipNum(v["pc_ship_piloting_maneuver_check"]) }, { silent: true });
|
||||
});
|
||||
});
|
||||
|
||||
const update_pc_ship_ac = () => {
|
||||
getAttrs(["pc_ship_base_armor_class", "pc_ship_maneuver_bonus", "pc_ship_pilot_dex_modifier", "pc_ship_misc_mod_bonus"], function(v) {
|
||||
const ac = sanitizeShipNum(v["pc_ship_base_armor_class"])
|
||||
+ sanitizeShipNum(v["pc_ship_maneuver_bonus"])
|
||||
+ sanitizeShipNum(v["pc_ship_pilot_dex_modifier"])
|
||||
+ sanitizeShipNum(v["pc_ship_misc_mod_bonus"]);
|
||||
setAttrs({ pc_ship_armor_class: ac }, { silent: true });
|
||||
});
|
||||
};
|
||||
|
||||
const update_pc_ship_md = () => {
|
||||
getAttrs(["pc_ship_proficiency_bonus", "pc_ship_pilot_wis_modifier", "pc_ship_captain_cha_modifier"], function(v) {
|
||||
const md = 8
|
||||
+ sanitizeShipNum(v["pc_ship_proficiency_bonus"])
|
||||
+ sanitizeShipNum(v["pc_ship_pilot_wis_modifier"])
|
||||
+ sanitizeShipNum(v["pc_ship_captain_cha_modifier"]);
|
||||
setAttrs({ pc_ship_base_maneuver_defense: 8, pc_ship_maneuver_defense_bonus: md }, { silent: true });
|
||||
});
|
||||
};
|
||||
|
||||
on("change:pc_ship_base_armor_class change:pc_ship_maneuver_bonus change:pc_ship_pilot_dex_modifier change:pc_ship_misc_mod_bonus", update_pc_ship_ac);
|
||||
on("change:pc_ship_proficiency_bonus change:pc_ship_pilot_wis_modifier change:pc_ship_captain_cha_modifier", update_pc_ship_md);
|
||||
|
||||
on("sheet:opened", () => {
|
||||
update_pc_ship_ac();
|
||||
update_pc_ship_md();
|
||||
});
|
||||
|
||||
// ── Damage / Heal buttons ──────────────────────────────────────────────────
|
||||
// Minus (−): subtract hp_change from current HP, floor at 0
|
||||
on("clicked:damage", function() {
|
||||
getAttrs(["hp", "hp_change"], function(v) {
|
||||
const current = parseInt(v.hp) || 0;
|
||||
const change = parseInt(v.hp_change) || 0;
|
||||
const newHp = Math.max(0, current - change);
|
||||
setAttrs({ hp: newHp });
|
||||
});
|
||||
});
|
||||
|
||||
// Plus (+): add hp_change to current HP, ceiling at hp_max
|
||||
on("clicked:heal", function() {
|
||||
getAttrs(["hp", "hp_max", "hp_change"], function(v) {
|
||||
const current = parseInt(v.hp) || 0;
|
||||
const max = parseInt(v.hp_max) || 0;
|
||||
const change = parseInt(v.hp_change) || 0;
|
||||
const newHp = Math.min(max, current + change);
|
||||
setAttrs({ hp: newHp });
|
||||
});
|
||||
});
|
||||
|
||||
//# sourceURL=espergenesis.js
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Classes"></div>
|
||||
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Esper Classes"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-race" data-i18n="back">Back</button>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<label class="hilite"><span data-i18n="charmancer-choose-race">Choose a race:</span>
|
||||
<select name="comp_race" accept="Category:Races" required>
|
||||
<option value="" data-i18n="choose">Choose</option>
|
||||
<option class="custom" value="Rules:Races" data-i18n="custom">Custom</option>
|
||||
<option class="custom" value="Rules:Species" data-i18n="custom">Custom</option>
|
||||
</select>
|
||||
<button type="action" name="act_info_race" class="mancer_info">i</button>
|
||||
</label>
|
||||
@@ -165,7 +165,7 @@
|
||||
<label class="hilite"><span data-i18n="subrace">Subrace:</span>
|
||||
<select class="choose_subrace" name="comp_subrace" required>
|
||||
<option value="" data-i18n="choose">Choose</option>
|
||||
<option class="custom" value="Rules:Races" data-i18n="custom">Custom</option>
|
||||
<option class="custom" value="Rules:Species" data-i18n="custom">Custom</option>
|
||||
</select>
|
||||
<button type="action" name="act_info_subrace" class="mancer_info">i</button>
|
||||
</label>
|
||||
@@ -292,7 +292,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="sheet-compendium-page sheet-race-info" accept="Rules:Races"></div>
|
||||
<div class="sheet-compendium-page sheet-race-info" accept="Rules:Species"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-welcome" data-i18n="back">Back</button>
|
||||
@@ -454,7 +454,7 @@
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Classes"></div>
|
||||
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Esper Classes"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-race" data-i18n="back">Back</button>
|
||||
@@ -621,7 +621,7 @@
|
||||
<label class="hilite"><span data-i18n="choose-background">Choose a Background :</span>
|
||||
<select name="comp_background" accept="Category:Backgrounds" required>
|
||||
<option value="" data-i18n="choose">Choose</option>
|
||||
<option class="custom" value="Rules:Backgrounds" data-i18n="custom">Custom</option>
|
||||
<option class="custom" value="Rules:Personality and Background" data-i18n="custom">Custom</option>
|
||||
</select>
|
||||
</label>
|
||||
<div class="choice custom_background">
|
||||
@@ -699,7 +699,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="sheet-compendium-page sheet-background-info" accept="Rules:Backgrounds"></div>
|
||||
<div class="sheet-compendium-page sheet-background-info" accept="Rules:Personality and Background"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-abilities" data-i18n="back">Back</button>
|
||||
@@ -861,7 +861,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="sheet-compendium-page sheet-background-info" accept="Rules:Equipment"></div>
|
||||
<div class="sheet-compendium-page sheet-background-info" accept="Rules:Gear and Equipment"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-background" data-i18n="back">Back</button>
|
||||
@@ -890,7 +890,7 @@
|
||||
<div class="choices spell_holder">
|
||||
</div>
|
||||
<div class="info spell-info-container choice">
|
||||
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Spells"></div>
|
||||
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Esper Powers"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-equipment" data-i18n="back">Back</button>
|
||||
@@ -1087,7 +1087,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info feat-info-container choice yes_feat">
|
||||
<div class="sheet-compendium-page sheet-feat-info" accept="Rules:Feats"></div>
|
||||
<div class="sheet-compendium-page sheet-feat-info" accept="Rules:Feats (EG)"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-spells" data-i18n="back">Back</button>
|
||||
@@ -1151,7 +1151,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="sheet-compendium-page sheet-race-info" accept="Rules:Races"></div>
|
||||
<div class="sheet-compendium-page sheet-race-info" accept="Rules:Species"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="l1-feat" data-i18n="back">Back</button>
|
||||
@@ -1223,12 +1223,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary">
|
||||
<p class="label" data-i18n="spells">Spells</p>
|
||||
<div class="spell_info">
|
||||
<p class="label" data-i18n="powers">Powers</p>
|
||||
<div class="spells_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>
|
||||
<button class="step" type="back" value="l1-spells" data-i18n="powers-u">POWERS</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary">
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
if(mancerdata["l1-race"] && mancerdata["l1-race"].values.race == "Rules:Races") {
|
||||
if(mancerdata["l1-race"] && mancerdata["l1-race"].values.race == "Rules:Species") {
|
||||
_.each(abilityList, function(ability){
|
||||
var custom = mancerdata["l1-race"].values["race_custom_" + ability.toLowerCase()] || false;
|
||||
if(custom) {
|
||||
@@ -115,7 +115,7 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
if(mancerdata["l1-race"] && mancerdata["l1-race"].values.subrace == "Rules:Races") {
|
||||
if(mancerdata["l1-race"] && mancerdata["l1-race"].values.subrace == "Rules:Species") {
|
||||
_.each(abilityList, function(ability){
|
||||
var custom = mancerdata["l1-race"].values["subrace_custom_" + ability.toLowerCase()] || false;
|
||||
if(custom) {
|
||||
@@ -356,15 +356,15 @@
|
||||
var additionalList = {};
|
||||
var blobs = getRelevantBlobs(mancerdata, "1", "l1");
|
||||
|
||||
//First look through all Blobs for any blob.Powers
|
||||
//First look through all Blobs for any blob.Spells or blob.Powers
|
||||
_.each(blobs.all, function(blob) {
|
||||
if(blob.Powers) {
|
||||
var spells = JSON.parse(blob.Powers);
|
||||
if(blob.Spells || blob.Powers) {
|
||||
var spells = JSON.parse(blob.Spells || blob.Powers);
|
||||
//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"];
|
||||
expandedList[spell.Level || spell.Rank] = expandedList[spell.Level || spell.Rank] ? expandedList[spell.Level || spell.Rank].concat(spell["Expanded List"]) : spell["Expanded List"];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -388,12 +388,12 @@
|
||||
//Looks through the blobs for Spells attribute
|
||||
//class.Powers 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}.Powers will contain KNOWN, CHOSEN, or EXPANDED LIST for extra spells granted by these sections
|
||||
if(blob.Powers) {
|
||||
var spells = JSON.parse(blob.Powers);
|
||||
if(blob.Spells || blob.Powers) {
|
||||
var spells = JSON.parse(blob.Spells || blob.Powers);
|
||||
allSpells[parentsection] = allSpells[parentsection] || {known: [], choices: []}
|
||||
_.each(spells, function(spell) {
|
||||
var numSpells = 0;
|
||||
expandedList[spell.Level] = expandedList[spell.Level] || [];
|
||||
expandedList[spell.Level || spell.Rank] = expandedList[spell.Level || spell.Rank] || [];
|
||||
//class.Powers 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) {
|
||||
@@ -405,17 +405,17 @@
|
||||
//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)};
|
||||
var thisspell = {Level: parseInt(spell.Level || spell.Rank)};
|
||||
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];
|
||||
if(parentsection == "class") thisspell["Expanded List"] = expandedList[spell.Level || spell.Rank];
|
||||
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)};
|
||||
var thisspell = {Name: known, Level: parseInt(spell.Level || spell.Rank)};
|
||||
thisspell.Ability = spell.Ability ? spell.Ability : classAbility;
|
||||
thisspell.Source = parentsection;
|
||||
allSpells[parentsection].known.push(thisspell);
|
||||
@@ -1250,21 +1250,12 @@
|
||||
};
|
||||
|
||||
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;
|
||||
return false;
|
||||
return false; // Tasha's Cauldron rules not used in Esper Genesis
|
||||
};
|
||||
|
||||
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);
|
||||
return false;
|
||||
});
|
||||
// Tasha's Cauldron rules not used in Esper Genesis
|
||||
return false;
|
||||
};
|
||||
|
||||
var clearTasha = (eventinfo, subraceOnly, callback) => {
|
||||
@@ -1317,7 +1308,7 @@
|
||||
clearRepeatingSections("subrace_holder");
|
||||
}
|
||||
|
||||
if(eventinfo.newValue === "Rules:Races") {
|
||||
if(eventinfo.newValue === "Rules:Species") {
|
||||
//Clears saved data for this field
|
||||
getCompendiumPage("");
|
||||
setAttrs(reset, function() {
|
||||
@@ -1420,7 +1411,7 @@
|
||||
clearRepeatingSections("subrace_holder");
|
||||
}
|
||||
|
||||
if(eventinfo.newValue === "Rules:Races") {
|
||||
if(eventinfo.newValue === "Rules:Species") {
|
||||
//Clears saved data for this field
|
||||
getCompendiumPage("");
|
||||
setAttrs(reset, function() {
|
||||
@@ -2244,7 +2235,7 @@
|
||||
var mancerdata = getCharmancerData();
|
||||
var known = knownSpells().known;
|
||||
_.each(mancerdata["l1-spells"].repeating, function(id) {
|
||||
disableCharmancerOptions(id + "_choice", known, {category: "Powers"});
|
||||
disableCharmancerOptions(id + "_choice", known, {category: "Spells"});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2310,7 +2301,7 @@
|
||||
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";
|
||||
spell_name = data["l1-spells"].values[eventinfo.sourceSection + "_choice"] ? data["l1-spells"].values[eventinfo.sourceSection + "_choice"] : "Rules:Esper Powers";
|
||||
card = data["l1-spells"].values[eventinfo.sourceSection + "_choice"] ? card : "";
|
||||
}
|
||||
changeCompendiumPage("sheet-spells-info", spell_name, card);
|
||||
@@ -2572,7 +2563,7 @@
|
||||
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] = "Powers:" + spell;
|
||||
toset[rowid + "_known" + index] = "Spells:" + spell;
|
||||
knownspells.push(thisSpell);
|
||||
});
|
||||
repupdate[rowid + " label p"] = knownspells.join(", ");
|
||||
@@ -2584,8 +2575,8 @@
|
||||
var toset = {};
|
||||
var lists = [spell.List];
|
||||
if(spell.AddList) lists = lists.concat(spell.AddList);
|
||||
var choices = "Category:Powers Classes:*" + lists.join("|*") + " Rank:" + spell.Level;
|
||||
var settings = {category: "Powers", disable: spellData.known};
|
||||
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);
|
||||
@@ -2741,7 +2732,7 @@
|
||||
for(let i=0; i<spells.length; i++) {
|
||||
const spellIndex = i+1;
|
||||
const selector = `comp_feat_spell_choice${spellIndex}`;
|
||||
const query = `Category:Powers ${spells[i]['List']}`;
|
||||
const query = `Category:Spells ${spells[i]['List']}`;
|
||||
showList.push(`feat_spell_choice${spellIndex}`);
|
||||
setCharmancerOptions(selector, query);
|
||||
}
|
||||
@@ -2757,7 +2748,7 @@
|
||||
|
||||
/* BIO PAGE */
|
||||
on("page:l1-bio", function(eventinfo) {
|
||||
const attrs = ["age", "character_name", "eyes", "hair", "height", "weight", "skin"];
|
||||
const attrs = ["age", "character_name", "height", "weight", "character_appearance"];
|
||||
const mancerdata = getCharmancerData();
|
||||
const biodata = mancerdata["l1-bio"] || undefined;
|
||||
const race = getName("race");
|
||||
@@ -2911,36 +2902,30 @@
|
||||
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.character_appearance) {
|
||||
set["bio_info"] += '<p>' + mancerdata["l1-bio"].values.character_appearance + '</p>';};
|
||||
|
||||
if (mancerdata["l1-bio"].values.previous_race != racename) {
|
||||
setCharmancerText({
|
||||
race_warning: '<p class="sheet-warning">Each race has unique look and different names. You might want to adjust your choices to reflect your race.</p>'
|
||||
race_warning: '<p class="sheet-warning">Each species has a unique look and different names. You might want to adjust your choices to reflect your species.</p>'
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if(mancerdata["l1-race"] && racename) {
|
||||
set["race_info"] = '<p>Your race is ' + racename + '</p>';
|
||||
set["race_info"] = '<p>Your species is ' + racename + '</p>';
|
||||
if(!mancerdata["l1-race"].values.alignment) set["race_info"] += '<p class="sheet-warning">You haven\'t chosen your alignment.</p>';
|
||||
set["race_info"] += handleMissing("race");
|
||||
if(mancerdata["l1-race"].values["has_subrace"] == "true") {
|
||||
if(subracename) {
|
||||
set["race_info"] += '<p>Your subrace is ' + subracename + '</p>';
|
||||
set["race_info"] += '<p>Your archetype 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="sheet-warning sheet-needed">You need to pick a name for your custom subrace!</p>';
|
||||
if(mancerdata["l1-race"].values.subrace == "Rules:Species" && !mancerdata["l1-race"].values.subrace_name) {
|
||||
set["race_info"] = '<p class="sheet-warning sheet-needed">You need to pick a name for your custom archetype!</p>';
|
||||
} else {
|
||||
set["race_info"] += '<p class="sheet-warning sheet-needed">You have not selected a subrace!</p>';
|
||||
set["race_info"] += '<p class="sheet-warning sheet-needed">You have not selected an archetype!</p>';
|
||||
}
|
||||
ready = false;
|
||||
showChoices(["race_button"]);
|
||||
@@ -2956,15 +2941,15 @@
|
||||
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="sheet-warning">You have not selected a size for your custom race!</p>';
|
||||
if(!mancerdata["l1-race"].values.speed) set["race_info"] += '<p class="sheet-warning">You have not selected a walking speed for your custom race!</p>';
|
||||
if(mancerdata["l1-race"].values.race == "Rules:Species") {
|
||||
if(!mancerdata["l1-race"].values.size) set["race_info"] += '<p class="sheet-warning">You have not selected a size for your custom species!</p>';
|
||||
if(!mancerdata["l1-race"].values.speed) set["race_info"] += '<p class="sheet-warning">You have not selected a walking speed for your custom species!</p>';
|
||||
}
|
||||
} else {
|
||||
if(mancerdata["l1-race"] && mancerdata["l1-race"].values.race == "Rules:Races" && !mancerdata["l1-race"].values.race_name) {
|
||||
set["race_info"] = '<p class="sheet-warning sheet-needed">You need to pick a name for your custom race!</p>';
|
||||
if(mancerdata["l1-race"] && mancerdata["l1-race"].values.race == "Rules:Species" && !mancerdata["l1-race"].values.race_name) {
|
||||
set["race_info"] = '<p class="sheet-warning sheet-needed">You need to pick a name for your custom species!</p>';
|
||||
} else {
|
||||
set["race_info"] = '<p class="sheet-warning sheet-needed">You have not selected a race!</p>';
|
||||
set["race_info"] = '<p class="sheet-warning sheet-needed">You have not selected a species!</p>';
|
||||
}
|
||||
ready = false;
|
||||
showChoices(["race_button"]);
|
||||
@@ -3131,7 +3116,7 @@
|
||||
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>';
|
||||
set["spells_info"] += '<p>Cosmic forces are 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) {
|
||||
@@ -3141,7 +3126,7 @@
|
||||
toDelete.push("race_level1_choice" + x);
|
||||
}
|
||||
if(raceSpells > 0 && mancerdata["l1-spells"].values.race_number > 0) {
|
||||
set["spells_info"] += '<p class="sheet-warning">Your racial spells were reset because your options have changed.</p>';
|
||||
set["spells_info"] += '<p class="sheet-warning">Your species powers were reset because your options have changed.</p>';
|
||||
}
|
||||
}
|
||||
if(prevClass != currentClass || mancerdata["l1-spells"].values.class_number > classSpells) {
|
||||
@@ -3159,7 +3144,7 @@
|
||||
spellsready = false;
|
||||
showChoices(["spells_button"]);
|
||||
} else {
|
||||
set["spells_info"] = '<p>The arcane is a mystery to you.</p>';
|
||||
set["spells_info"] = '<p>Cosmic forces are a mystery to you.</p>';
|
||||
spellsready = false
|
||||
}
|
||||
|
||||
@@ -3223,7 +3208,7 @@
|
||||
/* 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";
|
||||
var race = data["l1-race"] && data["l1-race"].values.race ? data["l1-race"].values.race : "Rules:Species";
|
||||
changeCompendiumPage("sheet-race-info", race);
|
||||
});
|
||||
|
||||
@@ -3337,7 +3322,7 @@
|
||||
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"]);
|
||||
set["cu"] += parseInt(page.data["data-Starting Gold"]);
|
||||
}
|
||||
allPageData.push(page);
|
||||
});
|
||||
@@ -3410,15 +3395,15 @@
|
||||
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", "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 clearattrs = ["hp", "hp_max", "size", "speed", "cu", "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 set = {cu: 0};
|
||||
var allDrops = [];
|
||||
var currentDrop = 0;
|
||||
var totalDrops = 1;
|
||||
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);
|
||||
var eraseSections = ["attack", "inventory", "traits", "resource", "proficiencies", "tool", "damagemod", "spell-cantrip", "hpmod", "acmod", "tohitmod", "savemod", "skillmod"];
|
||||
var eraseSections = ["attack", "inventory", "traits", "resource", "proficiencies", "tool", "damagemod", "spell-prime", "hpmod", "acmod", "tohitmod", "savemod", "skillmod"];
|
||||
for(var x=1; x<=9; x++) {
|
||||
eraseSections.push("spell-" + x);
|
||||
}
|
||||
@@ -3667,10 +3652,10 @@
|
||||
|
||||
//Next, add spell drops
|
||||
_.each(spells.all, function(spell) {
|
||||
var spelldata = {name: "Powers:" + spell.Name};
|
||||
var spelldata = {name: "Spells:" + spell.Name};
|
||||
spelldata.data = {"spellcasting_ability": spell.Ability};
|
||||
if(spell.Source == "race") {
|
||||
spelldata.data.spellclass = "Racial";
|
||||
spelldata.data.spellclass = "Species";
|
||||
} else {
|
||||
spelldata.data.spellclass = classname;
|
||||
};
|
||||
@@ -3707,13 +3692,13 @@
|
||||
}
|
||||
});
|
||||
|
||||
set["gp"] = parseInt(data["l1-equipment"].values["background_starting_gold"]) || 0;
|
||||
set["cu"] = 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"]);
|
||||
set["cu"] = 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) {
|
||||
_.each(["character_name", "age", "height", "weight", "character_appearance"], function(type) {
|
||||
if(data["l1-bio"].values[type]) {
|
||||
set[type] = data["l1-bio"].values[type] || "";
|
||||
}
|
||||
@@ -3771,15 +3756,12 @@
|
||||
getAllPages(allDrops, function() {
|
||||
setCharmancerText({"mancer_progress" :'<div style="width: 20%"></div>'});
|
||||
doAllDrops(allPageData, function() {
|
||||
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");
|
||||
var endTime = Date.now();
|
||||
console.log(`Elapsed time: ${(endTime-startTime)/1000}`);
|
||||
finishCharactermancer();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ charmancer.sheet-charmancer-l1-race
|
||||
span(data-i18n='charmancer-choose-race')
|
||||
select(accept='Category:Races' name='comp_race' required)
|
||||
option(data-i18n='choose' value='')
|
||||
option.custom(data-i18n='custom' value='Rules:Races')
|
||||
option.custom(data-i18n='custom' value='Rules:Species')
|
||||
+buttonMancerInfo('race')
|
||||
.choice.custom_race
|
||||
label.hilite(data-i18n='charmancer-custom-race-name')
|
||||
@@ -145,7 +145,7 @@ charmancer.sheet-charmancer-l1-race
|
||||
span(data-i18n='subrace')
|
||||
select.choose_subrace(name='comp_subrace' required)
|
||||
option(data-i18n='choose' value='')
|
||||
option.custom(data-i18n='custom' value='Rules:Races')
|
||||
option.custom(data-i18n='custom' value='Rules:Species')
|
||||
+buttonMancerInfo('subrace')
|
||||
.choice.custom_subrace
|
||||
label.hilite
|
||||
@@ -211,7 +211,7 @@ charmancer.sheet-charmancer-l1-race
|
||||
span(data-i18n='spell-list')
|
||||
select(name='comp_custom_race_spell_list')
|
||||
+classOptions()
|
||||
+infoDiv('race','Rules:Races')
|
||||
+infoDiv('race','Rules:Species')
|
||||
+bottomBar('welcome', 'class')
|
||||
+cancelPrompt('race')
|
||||
input(name='comp_has_subrace' type='hidden')
|
||||
@@ -409,7 +409,7 @@ charmancer.sheet-charmancer-l1-equipment
|
||||
select.choice.background_possible(class=`background_equipment_choice${backEquipInt}` name=`comp_background_equipment_choice${backEquipInt}`)
|
||||
option(data-i18n='choose' value='')
|
||||
- backEquipInt++
|
||||
+infoDiv('background','Rules:Equipment')
|
||||
+infoDiv('background','Rules:Gear and Equipment')
|
||||
+bottomBar('background', 'spells')
|
||||
+cancelPrompt('equipment')
|
||||
|
||||
@@ -526,7 +526,7 @@ charmancer.sheet-charmancer-l1-feat
|
||||
select.feat_possible.other_options_and_features_choice1(name='comp_other_options_and_features_choice2')
|
||||
option(data-i18n='choose' value='')
|
||||
.info.feat-info-container.choice.yes_feat
|
||||
.sheet-compendium-page.sheet-feat-info(accept='Rules:Feats')
|
||||
.sheet-compendium-page.sheet-feat-info(accept='Rules:Feats (EG)')
|
||||
+bottomBar('spells', 'bio')
|
||||
+cancelPrompt('feat')
|
||||
|
||||
@@ -537,14 +537,18 @@ charmancer.sheet-charmancer-l1-bio
|
||||
.topbar-holder
|
||||
.choices
|
||||
input(name='comp_previous_race' type='hidden' value='')
|
||||
each val in ['Name', 'Age', 'Height', 'Weight', 'Eyes', 'Hair', 'Skin']
|
||||
each val in ['Name', 'Age', 'Height', 'Weight']
|
||||
- const attributeName = val === 'Name' ? 'character_name' : val.toLowerCase();
|
||||
- const datai18n = val === 'Name' ? 'char-name-u' : val === 'Eyes' ? `eye-u` : `${val.toLowerCase()}-u`;
|
||||
- const datai18n = val === 'Name' ? 'char-name-u' : `${val.toLowerCase()}-u`;
|
||||
.row
|
||||
label
|
||||
div(data-i18n=datai18n)
|
||||
input(name=`comp_${attributeName}` type='text')
|
||||
+infoDiv('race','Rules:Races')
|
||||
.row
|
||||
label
|
||||
div(data-i18n='char-appearance-u')
|
||||
textarea(name='comp_character_appearance' style='width:100%; min-height:80px; resize:vertical;')
|
||||
+infoDiv('race','Rules:Species')
|
||||
+bottomBar('feat', 'summary')
|
||||
+cancelPrompt('bio')
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Classes"></div>
|
||||
<div class="sheet-compendium-page sheet-class-info" accept="Rules:Esper Classes"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="back" value="lp-levels" data-i18n="back">Back</button>
|
||||
@@ -347,7 +347,7 @@
|
||||
</div>
|
||||
|
||||
<div class="info spellslide">
|
||||
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Powers"></div>
|
||||
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Esper Powers"></div>
|
||||
</div>
|
||||
<div class="bottombar">
|
||||
<button class="prev" type="action" name="act_lp-spells_back" data-i18n="back">Back</button>
|
||||
@@ -427,7 +427,7 @@
|
||||
</div>
|
||||
|
||||
<div class="info spellslide">
|
||||
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Powers"></div>
|
||||
<div class="sheet-compendium-page sheet-spells-info" accept="Rules:Esper Powers"></div>
|
||||
</div>
|
||||
|
||||
<div class="bottombar">
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
/****** LEVEL UP MANCER WORKERS ******/
|
||||
/*********************************************************************/
|
||||
|
||||
// Safe JSON parse — replaces bare JSON.parse() calls throughout LP workers.
|
||||
// Returns fallback (default: null) on invalid input instead of throwing.
|
||||
var safeParseJSON = function(str, fallback) {
|
||||
if (typeof str !== "string" || str === "") return fallback !== undefined ? fallback : null;
|
||||
try { return JSON.parse(str); } catch(e) { return fallback !== undefined ? fallback : null; }
|
||||
};
|
||||
|
||||
//Helper function to update the text in the top bar
|
||||
var recalcLpData = function(blobs) {
|
||||
var mancerdata = getCharmancerData();
|
||||
@@ -81,7 +88,7 @@
|
||||
var leveldata = getLevelingData(mancerdata);
|
||||
var abilities = abilities || getAbilityTotals(mancerdata, blobs);
|
||||
var previous = mancerdata["lp-welcome"].values["previous_attributes"] ? JSON.parse(mancerdata["lp-welcome"].values["previous_attributes"]) : {};
|
||||
var prev_repeat = JSON.parse(mancerdata["lp-welcome"].values["previous_repeating"]), hpmod = prev_repeat["hpmod"];
|
||||
var prev_repeat = safeParseJSON(mancerdata["lp-welcome"].values["previous_repeating"], {}), hpmod = prev_repeat["hpmod"];
|
||||
var newhp = 0;
|
||||
var additionalhp = 0;
|
||||
var totalpreviouslevel = parseInt(previous["base_level"]);
|
||||
@@ -166,7 +173,7 @@
|
||||
return x[0].toUpperCase() + x.substr(1, x.length);
|
||||
}).join(" ");
|
||||
};
|
||||
var spellSections = ["spell-cantrip"];
|
||||
var spellSections = ["spell-prime"];
|
||||
var allSkills = ["athletics", "acrobatics", "sleight_of_hand", "stealth", "arcana", "history", "investigation", "nature", "religion", "animal_handling", "insight", "medicine", "perception", "survival","deception", "intimidation", "performance", "persuasion"];
|
||||
var skillget = [];
|
||||
for(var x=1; x<=9; x++) {
|
||||
@@ -853,7 +860,9 @@
|
||||
}
|
||||
_.each(thisclass.class.blobs, function(blob) {
|
||||
if(blob["Spell Slots"] && parseInt(blob.Level) <= (thisclass.currentlevel + thisclass.addlevel)) {
|
||||
_.each(JSON.parse(blob["Spell Slots"]), function(slots, name) {
|
||||
var rawSlots = blob["Spell Slots"];
|
||||
var parsedSlots = (typeof rawSlots === "string") ? safeParseJSON(rawSlots, {}) : (rawSlots || {});
|
||||
_.each(parsedSlots, function(slots, name) {
|
||||
if(parseInt(_.last(name.split(" "))) > maxspell) maxspell = parseInt(_.last(name.split(" ")));
|
||||
});
|
||||
}
|
||||
@@ -1081,7 +1090,6 @@
|
||||
});
|
||||
|
||||
const restorePreviousASI = function(prevData, callback) {
|
||||
console.log('Restoring previous state...');
|
||||
if(!SheetUtils.checkPath(prevData, "['lp-asi']['values']")) {
|
||||
callback();
|
||||
return;
|
||||
@@ -1153,7 +1161,7 @@
|
||||
_.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.spellclass && spell.spellclass.toLowerCase() == "racial") spelldata[spell.spellname].known = "Species";
|
||||
if(spell.spellsource) spelldata[spell.spellname].known = spell.spellsource;
|
||||
});
|
||||
_.each(leveldata, function(level) {
|
||||
@@ -1182,8 +1190,8 @@
|
||||
}
|
||||
_.each(data.blobs, function(blob, blobname) {
|
||||
if(blob.Multiclass != "no" && ((parseInt(blob.Level) <= thislevel || blob.Level == "all") && !blob.Group) || choices.includes(blobname)) {
|
||||
if(blob.Powers) {
|
||||
var blobspells = JSON.parse(blob.Powers);
|
||||
if(blob.Spells || blob.Powers) {
|
||||
var blobspells = safeParseJSON(blob.Spells || blob.Powers, []);
|
||||
_.each(blobspells, function(blobspell) {
|
||||
if(blobspell.Known) {
|
||||
_.each(blobspell.Known, function(spell) {
|
||||
@@ -1191,8 +1199,8 @@
|
||||
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);
|
||||
spelldata[spell].known = data.Category == "Races" ? "Species" : data.Category.substring(0, data.Category.length - 1);
|
||||
spelldata[spell].spellclass = data.Category == "Races" ? "Species" : data.Category.substring(0, data.Category.length - 1);
|
||||
}
|
||||
if(blobspell.Ability) {
|
||||
spelldata[spell].ability = blobspell.Ability;
|
||||
@@ -1211,7 +1219,7 @@
|
||||
//Gater spells from class features
|
||||
_.each(page.values, function(value, name) {
|
||||
if(name.substr(-18) === "_utilityrow_powers") {
|
||||
spelldata = _.extend(spelldata, JSON.parse(value));
|
||||
spelldata = _.extend(spelldata, safeParseJSON(value, {}));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1220,6 +1228,88 @@
|
||||
return spelldata;
|
||||
};
|
||||
|
||||
// ---- Feature 7: LP Spell List Helpers (ported from 5e OGL) ----
|
||||
// Returns the known spells as a flat array with name merged in.
|
||||
var getKnownLPSpellList = function() {
|
||||
var list = [];
|
||||
var known = getKnownLpSpells();
|
||||
Object.keys(known).forEach(function(spell) {
|
||||
var newSpell = _.extend({name: spell}, known[spell]);
|
||||
if (newSpell.level && String(newSpell.level).toLowerCase() === "cantrip") newSpell.level = 0;
|
||||
list.push(newSpell);
|
||||
});
|
||||
return list;
|
||||
};
|
||||
|
||||
// Returns true if a spell with the given name and level exists in the known list.
|
||||
var isExistingSpell = function(name, level) {
|
||||
var known = getKnownLPSpellList().filter(function(spell) {
|
||||
return spell.hasOwnProperty("name") && spell.name == name &&
|
||||
spell.hasOwnProperty("level") && spell.level == level;
|
||||
});
|
||||
return known.length > 0;
|
||||
};
|
||||
|
||||
// Returns spells the player has checked/selected on the lp-spells page.
|
||||
// Adapted for EG's repeating section pattern (not 5e OGL's key-based pattern).
|
||||
var getSelectedSpells = function(filters) {
|
||||
var spells = [];
|
||||
var spellpage = getCharmancerData()["lp-spells"];
|
||||
if (!spellpage || !spellpage.repeating) return spells;
|
||||
_.each(spellpage.repeating, function(repid) {
|
||||
if (spellpage.values[repid + "_checked"]) {
|
||||
spells.push({
|
||||
name: spellpage.values[repid + "_name"],
|
||||
level: spellpage.values[repid + "_level"],
|
||||
ability: spellpage.values[repid + "_ability"],
|
||||
source: spellpage.values[repid + "_source"],
|
||||
spellclass: spellpage.values[repid + "_class"],
|
||||
existing: spellpage.values[repid + "_existing"] || false
|
||||
});
|
||||
}
|
||||
});
|
||||
if (typeof filters === "object") {
|
||||
Object.keys(filters).forEach(function(filterBy) {
|
||||
spells = spells.filter(function(spell) {
|
||||
return spell.hasOwnProperty(filterBy) && spell[filterBy] == filters[filterBy];
|
||||
});
|
||||
});
|
||||
}
|
||||
return spells;
|
||||
};
|
||||
|
||||
// Groups selected spells by level. Pass an optional filter object.
|
||||
var getSelectedSpellsByLevel = function(filter) {
|
||||
var byLevel = {};
|
||||
var selectedSpells = getSelectedSpells(filter);
|
||||
selectedSpells.forEach(function(spell) {
|
||||
if (!byLevel[spell.level]) byLevel[spell.level] = [];
|
||||
byLevel[spell.level].push(spell);
|
||||
});
|
||||
return byLevel;
|
||||
};
|
||||
|
||||
// Returns known spells annotated with whether they were removed (unchecked) on the spells page.
|
||||
var getKnownLPSpellListWithRemoved = function() {
|
||||
var list = [];
|
||||
var selectedSpells = getSelectedSpells();
|
||||
var knownSpells = getKnownLPSpellList();
|
||||
knownSpells.forEach(function(known) {
|
||||
var filtered = selectedSpells.filter(function(selected) {
|
||||
return selected.name == known.name && selected.level == known.level;
|
||||
});
|
||||
if (filtered.length === 0) list.push(_.extend({removed: true}, known));
|
||||
else list.push(_.extend({removed: false}, known));
|
||||
});
|
||||
return list;
|
||||
};
|
||||
|
||||
// Returns a stable, CSS-safe attribute key for a spell name + level combination.
|
||||
var getSanitizedSpellName = function(name, level) {
|
||||
return "spell_" + level + "_" + name.toLowerCase().replace(/[^a-z]/g, "");
|
||||
};
|
||||
// ---- End Feature 7 helpers ----
|
||||
|
||||
var getNewSpells = function(mancerdata, leveldata, blobs) {
|
||||
mancerdata = mancerdata || getCharmancerData();
|
||||
leveldata = leveldata || getCharmancerData();
|
||||
@@ -1237,16 +1327,15 @@
|
||||
thisclass.newspells = 0;
|
||||
_.each(["class", "subclass"], function(section) {
|
||||
if(level[section]) {
|
||||
var currCantripBlobLevel = 0;
|
||||
var prevCantripBlobLevel = 0;
|
||||
_.each(level[section].blobs, function(blob) {
|
||||
var blobLvl = parseInt(blob.Level);
|
||||
if(blob.Level == thislevel) {
|
||||
if(blob["Talents Known"]) {
|
||||
currentknown = parseInt(blob["Talents 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;
|
||||
}
|
||||
@@ -1256,10 +1345,17 @@
|
||||
prevknown = prevlevel === 0 ? 0 : parseInt(blob["Talents Known"]);
|
||||
if(prevlevel === 0) thisclass.newspells = parseInt(blob["Talents Known"]);
|
||||
}
|
||||
if(blob.Cantrips) {
|
||||
thisclass.cantrips = true;
|
||||
prevcantrips = prevlevel === 0 ? 0 : parseInt(blob.Cantrips);
|
||||
}
|
||||
}
|
||||
// Carry-forward: current Cantrips
|
||||
if(!isNaN(blobLvl) && blobLvl <= thislevel && blobLvl > currCantripBlobLevel && blob.Cantrips) {
|
||||
currCantripBlobLevel = blobLvl;
|
||||
thisclass.cantrips = true;
|
||||
currentcantrips = parseInt(blob.Cantrips);
|
||||
}
|
||||
// Carry-forward: prev Cantrips
|
||||
if(prevlevel > 0 && !isNaN(blobLvl) && blobLvl <= prevlevel && blobLvl > prevCantripBlobLevel && blob.Cantrips) {
|
||||
prevCantripBlobLevel = blobLvl;
|
||||
prevcantrips = parseInt(blob.Cantrips);
|
||||
}
|
||||
if(level[section]["data-Spell Add"]) {
|
||||
spelladd = parseInt(level[section]["data-Spell Add"]);
|
||||
@@ -1277,7 +1373,7 @@
|
||||
if(blobs.sorted[`class${level.classnumber}${section}`]) {
|
||||
_.each(blobs.sorted[`class${level.classnumber}${section}`], function(blob) {
|
||||
if(blob.Spells) {
|
||||
let spells = JSON.parse(blob.Spells);
|
||||
let spells = safeParseJSON(blob.Spells, []);
|
||||
_.each(spells, function(spell) {
|
||||
if(spell.Choose) {
|
||||
result = true;
|
||||
@@ -1329,26 +1425,24 @@
|
||||
var currentcantrips = 0;
|
||||
var spelladd = 0;
|
||||
thisclass.list = [thisclass.class];
|
||||
thisclass.hasSpellReplace = !!level.class["data-Spell Replace"];
|
||||
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"];
|
||||
// Carry-forward trackers for sparse class tables (EG compendium only
|
||||
// lists a field when its value changes, not at every level).
|
||||
var maxRankBlobLevel = 0; // highest blob level seen with "Maximum Talent Rank" ≤ thislevel
|
||||
var currCantripBlobLevel = 0; // highest blob level seen with "Cantrips" ≤ thislevel
|
||||
var prevCantripBlobLevel = 0; // highest blob level seen with "Cantrips" ≤ prevlevel
|
||||
_.each(level[section].blobs, function(blob) {
|
||||
var blobLvl = parseInt(blob.Level);
|
||||
if(blob.Level == thislevel) {
|
||||
if(blob["Talents Known"]) {
|
||||
var maxRank = JSON.parse(blob["Maximum Talent Rank"]);
|
||||
thisclass.maxlevel = parseInt(maxRank);
|
||||
spellmaxlevel = Math.max(thisclass.maxlevel, spellmaxlevel);
|
||||
}
|
||||
if(blob["Talents Known"]) {
|
||||
currentknown = parseInt(blob["Talents 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;
|
||||
@@ -1367,11 +1461,46 @@
|
||||
prevknown = prevlevel === 0 ? 0 : parseInt(blob["Talents Known"]);
|
||||
if(prevlevel === 0) thisclass.newspells = parseInt(blob["Talents Known"]);
|
||||
}
|
||||
if(blob.Cantrips) {
|
||||
thisclass.cantrips = true;
|
||||
prevcantrips = prevlevel === 0 ? 0 : parseInt(blob.Cantrips);
|
||||
if(knowncantrips < prevcantrips) prevcantrips = knowncantrips;
|
||||
}
|
||||
// Carry-forward: max available power rank.
|
||||
// Primary path (5e compendium standard): derive from "Spell Slots" JSON —
|
||||
// find the highest slot level key (e.g. "Level 2" → 2).
|
||||
// Override path (EG custom field): "Maximum Talent Rank" takes precedence
|
||||
// if present, allowing the compendium to explicitly set the value.
|
||||
// Both use carry-forward so sparse tables work correctly.
|
||||
// Defensive parse: blob fields may arrive as JSON strings or pre-parsed objects.
|
||||
if(!isNaN(blobLvl) && blobLvl <= thislevel && blobLvl > maxRankBlobLevel) {
|
||||
if(blob["Spell Slots"]) {
|
||||
maxRankBlobLevel = blobLvl;
|
||||
var rawSlots = blob["Spell Slots"];
|
||||
var slots = (typeof rawSlots === "string") ? safeParseJSON(rawSlots, {}) : (rawSlots || {});
|
||||
var slotKeys = _.keys(slots).sort();
|
||||
if(slotKeys.length > 0) {
|
||||
var derivedRank = parseInt(_.last(_.last(slotKeys).split(" ")));
|
||||
if(!isNaN(derivedRank)) {
|
||||
thisclass.maxlevel = derivedRank;
|
||||
spellmaxlevel = Math.max(thisclass.maxlevel, spellmaxlevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(blob["Maximum Talent Rank"]) {
|
||||
maxRankBlobLevel = blobLvl;
|
||||
var rawRank = blob["Maximum Talent Rank"];
|
||||
thisclass.maxlevel = parseInt((typeof rawRank === "string") ? safeParseJSON(rawRank, rawRank) : rawRank);
|
||||
spellmaxlevel = Math.max(thisclass.maxlevel, spellmaxlevel);
|
||||
}
|
||||
}
|
||||
// Carry-forward: current Cantrips — most recent entry at or below thislevel
|
||||
if(!isNaN(blobLvl) && blobLvl <= thislevel && blobLvl > currCantripBlobLevel && blob.Cantrips) {
|
||||
currCantripBlobLevel = blobLvl;
|
||||
thisclass.cantrips = true;
|
||||
currentcantrips = parseInt(blob.Cantrips);
|
||||
}
|
||||
// Carry-forward: prev Cantrips — most recent entry at or below prevlevel
|
||||
if(prevlevel > 0 && !isNaN(blobLvl) && blobLvl <= prevlevel && blobLvl > prevCantripBlobLevel && blob.Cantrips) {
|
||||
prevCantripBlobLevel = blobLvl;
|
||||
prevcantrips = 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"]);
|
||||
@@ -1380,6 +1509,12 @@
|
||||
spelladd = parseInt(level[section]["data-Spell Add"]);
|
||||
}
|
||||
});
|
||||
// Fallback: if carry-forward didn't set maxlevel (e.g. no Spell Slots field found
|
||||
// in blobs), use the pre-computed maxspell from getLevelingData.
|
||||
if(!thisclass.maxlevel && level.maxspell > 0) {
|
||||
thisclass.maxlevel = level.maxspell;
|
||||
spellmaxlevel = Math.max(thisclass.maxlevel, spellmaxlevel);
|
||||
}
|
||||
}
|
||||
});
|
||||
if(spelladd) {
|
||||
@@ -1394,9 +1529,9 @@
|
||||
if(blobs.sorted[`class${level.classnumber}${section}`]) {
|
||||
_.each(blobs.sorted[`class${level.classnumber}${section}`], function(blob) {
|
||||
if(blob.Spells) {
|
||||
let spells = JSON.parse(blob.Spells);
|
||||
let spells = safeParseJSON(blob.Spells, []);
|
||||
_.each(spells, function(spell) {
|
||||
if(spell.Choose && spell.Level === "0") {
|
||||
if(spell.Choose && (spell.Level || spell.Rank) === "0") {
|
||||
thisclass.newcantrips += parseInt(spell.Choose);
|
||||
}
|
||||
});
|
||||
@@ -1423,7 +1558,7 @@
|
||||
_.each(data.blobs, function(blob, blobname) {
|
||||
if((parseInt(blob.Level) <= thislevel || blob.Level == "every") && blob.Multiclass != "no") {
|
||||
if(blob.Spells) {
|
||||
var blobspells = JSON.parse(blob.Spells);
|
||||
var blobspells = safeParseJSON(blob.Spells, []);
|
||||
_.each(blobspells, function(thisspell) {
|
||||
if(parseInt(thisspell.Level) <= spellmaxlevel) {
|
||||
if(thisspell["Expanded List"]) {
|
||||
@@ -1438,15 +1573,15 @@
|
||||
}
|
||||
});
|
||||
if(knownlist) {
|
||||
queries.push("Category:Powers Name:" + knownlist.join("|"));
|
||||
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:Powers Classes:*" + _.uniq(thisclass.list).join("|*") + " Rank:";
|
||||
if(!thisclass.prepared || !thisclass.hasSpellReplace) 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;
|
||||
@@ -1571,7 +1706,7 @@
|
||||
}
|
||||
});
|
||||
} else {
|
||||
update[levelrow + " .sheet-spellstext .sheet-summary"] = "<p>You can add " + newspells + " new spells.</p>";
|
||||
update[levelrow + " .sheet-spellstext .sheet-summary"] = "<p>You can add " + newspells + " new powers.</p>";
|
||||
}
|
||||
}
|
||||
if(replace > 0) {
|
||||
@@ -1631,7 +1766,7 @@
|
||||
toSet[spellid + "_checked"] = "1";
|
||||
toSet[spellid + "_existing"] = "1";
|
||||
toSet[spellid + "_source"] = thisspell.known;
|
||||
update[spellid + " .sheet-classes"] = "<span class=\"known\">" + thisspell.known + " Spell</span>";
|
||||
update[spellid + " .sheet-classes"] = "<span class=\"known\">" + thisspell.known + " Power</span>";
|
||||
update[spellid + " .sheet-hardlock"] = "locked";
|
||||
}
|
||||
if(thisspell.spellclass) toSet[spellid + "_class"] = thisspell.spellclass;
|
||||
@@ -1735,16 +1870,16 @@
|
||||
update[cantripsection + " label .sheet-number"] = (known + newspells) + " / " + (known + allowed);
|
||||
} else {
|
||||
if(thispage != "lp-spellchoice") {
|
||||
update[cantripsection + " .sheet-spellstext .sheet-total"] = newspells + " / " + (allowed + replaced) + " spells chosen.";
|
||||
update[cantripsection + " .sheet-spellstext .sheet-total"] = newspells + " / " + (allowed + replaced) + " powers chosen.";
|
||||
if(replaced > 0) {
|
||||
update[cantripsection + " .sheet-spellstext .sheet-total"] += "<br>(" + allowed + " new, " + replaced + " replaced)";
|
||||
}
|
||||
if(allowedreplace > 0) {
|
||||
update[cantripsection + " .sheet-replacetext .sheet-total"] = replaced + " / " + allowedreplace + " spells replaced.";
|
||||
update[cantripsection + " .sheet-replacetext .sheet-total"] = replaced + " / " + allowedreplace + " powers replaced.";
|
||||
}
|
||||
}
|
||||
_.each(perlevel, function(info, level) {
|
||||
if(thispage != "lp-spellchoice") update[cantripsection + " .sheet-spellstext .sheet-levels"] += "<p>" + info.number + " Level " + level + " spells added.</p>";
|
||||
if(thispage != "lp-spellchoice") update[cantripsection + " .sheet-spellstext .sheet-levels"] += "<p>" + info.number + " Level " + level + " powers added.</p>";
|
||||
update[info.section + " label .sheet-number"] = "";
|
||||
if(info.number > 0) update[info.section + " label .sheet-number"] = "+" + info.number;
|
||||
if(info.replaced) {
|
||||
@@ -1829,14 +1964,14 @@
|
||||
const id = SheetUtils.getUID(eventinfo.sourceSection);
|
||||
if(showFeats) {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === 'player') resetASI(id, ()=> {recalcLpData();});
|
||||
changeCompendiumPage("sheet-class-info", 'Rules:Feats');
|
||||
changeCompendiumPage("sheet-class-info", 'Rules:Feats (EG)');
|
||||
setAttrs({[`repeating_${id}_asi-row_switch`]:'feat'}, {silent: true});
|
||||
} else {
|
||||
if(eventinfo.sourceType && eventinfo.sourceType === 'player') resetFeats(id, ()=> {recalcLpData();});
|
||||
changeCompendiumPage("sheet-class-info", 'Rules:Ability Scores');
|
||||
setAttrs({[`repeating_${id}_asi-row_switch`]:'asi'}, {silent: true});
|
||||
}
|
||||
} else if(trigger === 'feat' && eventinfo.sourceType === 'woker') {
|
||||
} else if(trigger === 'feat' && eventinfo.sourceType === 'worker') {
|
||||
|
||||
} else if(globalAttributesByCategory.abilitiesWithAllOptionals.indexOf(trigger) > -1) {
|
||||
updateAbilityLock(eventinfo.sourceSection);
|
||||
@@ -1973,7 +2108,7 @@
|
||||
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:Powers ${spells[i]['List']}`;
|
||||
const query = `Category:Spells ${spells[i]['List']}`;
|
||||
showList.push(`feat_spell_choice${spellIndex}`);
|
||||
setCharmancerOptions(selector, query);
|
||||
}
|
||||
@@ -2138,8 +2273,8 @@
|
||||
update["feat_info"] = (feats.length > 0) ? `<p>You've selected ${feats.join(', ')}` : noFeatsMsg;
|
||||
//Spells
|
||||
let selectedSpells = getGainedSpells();
|
||||
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>';
|
||||
update["spell_info"] = "<p>You haven't gained any powers</p>";
|
||||
if(selectedSpells.length > 0) update["spell_info"] = '<p>You have learnt these powers: '+ selectedSpells.sort().join(", ") +'</p>';
|
||||
|
||||
let gainedFeatures = getGainedFeatures();
|
||||
update["feature_info"] = (gainedFeatures.length > 0) ? '<p>You have gained these class features: '+ gainedFeatures.sort().join(", ") +'</p>' :
|
||||
@@ -2232,13 +2367,13 @@
|
||||
if(!querysettings.Level || (querysettings.Level && querysettings.Level == spell.level)) spellnames.push(spellname);
|
||||
});
|
||||
if(spellnames.length > 0) {
|
||||
query = "Category:Powers Name:" + spellnames.join("|");
|
||||
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:Powers";
|
||||
query = "Category:Spells";
|
||||
if(querysettings.Level) {
|
||||
let levels = [querysettings.Level];
|
||||
if(querysettings.Level == "max") {
|
||||
@@ -2359,13 +2494,13 @@
|
||||
if(!querysettings.Level || (querysettings.Level && querysettings.Level == spell.level)) spellnames.push(spellname);
|
||||
});
|
||||
if(spellnames.length > 0) {
|
||||
query = "Category:Powers Name:" + spellnames.join("|");
|
||||
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:Powers";
|
||||
query = "Category:Spells";
|
||||
if(querysettings.Level) {
|
||||
let levels = [querysettings.Level];
|
||||
if(querysettings.Level == "max") {
|
||||
@@ -2468,7 +2603,7 @@
|
||||
let newspells = false;
|
||||
let spellsettings = {};
|
||||
if(mancerdata["lp-choices"].values[`${source}_info`]) {
|
||||
spellsettings = JSON.parse(mancerdata["lp-choices"].values[`${source}_info`]);
|
||||
spellsettings = safeParseJSON(mancerdata["lp-choices"].values[`${source}_info`], {});
|
||||
}
|
||||
_.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`]);
|
||||
@@ -2498,9 +2633,6 @@
|
||||
});
|
||||
|
||||
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) {
|
||||
@@ -2832,7 +2964,7 @@
|
||||
//Set up spell drops
|
||||
if(data["lp-spells"] && data["lp-spells"].repeating) {
|
||||
_.each(data["lp-spells"].repeating, function(repid) {
|
||||
if(data["lp-spells"].values[repid + "_checked"]) {
|
||||
if(data["lp-spells"].values[repid + "_checked"] && !data["lp-spells"].values[repid + "_existing"]) {
|
||||
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"];
|
||||
@@ -2856,12 +2988,12 @@
|
||||
_.each(blobs.sorted, function(section, name) {
|
||||
_.each(section, function(blob) {
|
||||
if(blob.Spells) {
|
||||
spells = JSON.parse(blob.Spells);
|
||||
spells = safeParseJSON(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["lp-levels"].values[name.split("_")[0]].split(":")) : "Racial";
|
||||
var thisclass = name.substring(0,5) === "class" ? _.last(data["lp-levels"].values[name.split("_")[0]].split(":")) : "Species";
|
||||
if(spell.Ability) thisspell.data["spellcasting_ability"] = spell.Ability;
|
||||
if(spell.Source) thisspell.data["spellsource"] = spell.Source;
|
||||
thisspell.data["spellclass"] = thisclass;
|
||||
@@ -2913,8 +3045,6 @@
|
||||
update_attacks("all");
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -2955,4 +3085,4 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
//# sourceURL=dnd5e.js
|
||||
//# sourceURL=EGSheet-lp.js
|
||||
@@ -159,7 +159,7 @@ charmancer.sheet-charmancer-lp-choices
|
||||
+lpSteps()
|
||||
.choices
|
||||
.info
|
||||
.sheet-compendium-page.sheet-class-info(accept='Rules:Classes')
|
||||
.sheet-compendium-page.sheet-class-info(accept='Rules:Esper Classes')
|
||||
+bottomBar({back: {type:'back', value: 'lp-levels'}, next: {action: true, name: 'act_lp-choices_next'}})
|
||||
+cancelPrompt()
|
||||
|
||||
@@ -180,7 +180,7 @@ charmancer.sheet-charmancer-lp-spells
|
||||
span.pagelock(style='display: none')
|
||||
.choices.spellslide
|
||||
p.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.
|
||||
| Listed here are the current powers your character already knows. If you gained access to more powers during your advancement you will be able to select them below. Check next to the powers you wish to learn. The Charactermancer will help you pick the right number of powers and from the correct list. If your class has the option to swap out powers, you can do so by unselecting currently known powers which will allow you to select new ones in their place.
|
||||
p.spells-summary
|
||||
.cantripinfo
|
||||
.info.spellslide
|
||||
|
||||
@@ -233,7 +233,7 @@ class Trait extends RepeatingFieldset {
|
||||
break;
|
||||
case "Races":
|
||||
case "Subraces":
|
||||
return "Racial"
|
||||
return "Species"
|
||||
break;
|
||||
case "Classes":
|
||||
case "Subclasses":
|
||||
|
||||
@@ -30,7 +30,6 @@ var handle_drop = function(category, eventinfo) {
|
||||
return;
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
console.log("cleaning last ID");
|
||||
lastDropID = "";
|
||||
}, 2000);
|
||||
};
|
||||
@@ -219,7 +218,8 @@ var processDrop = function(page, currentData, repeating, looped) {
|
||||
const processSpells = () => {
|
||||
try {
|
||||
if(page.name) {
|
||||
const spellLevel = page.data["Rank"] && page.data["Rank"] > 0 ? page.data["Rank"] : "prime";
|
||||
const rankOrLevel = page.data["Rank"] !== undefined ? page.data["Rank"] : (page.data["Level"] !== undefined ? parseInt(page.data["Level"]) : 0);
|
||||
const spellLevel = rankOrLevel > 0 ? rankOrLevel : "prime";
|
||||
const section = `spell-${spellLevel}`
|
||||
let existing = {id: false};
|
||||
|
||||
@@ -234,7 +234,7 @@ var processDrop = function(page, currentData, repeating, looped) {
|
||||
|
||||
const dropSheetAssocitation = {
|
||||
spelldescription: page.data['data-description'],
|
||||
spellschool: page.data['Domain'].toLowerCase(),
|
||||
spellschool: page.data['Domain'] ? page.data['Domain'].toLowerCase() : undefined,
|
||||
spellclass: page.data['spellclass'],
|
||||
spellsource: page.data['spellsource'],
|
||||
spellritual: page.data['Ritual'],
|
||||
@@ -382,8 +382,7 @@ var processDrop = function(page, currentData, repeating, looped) {
|
||||
if (page.data["data-MAdesc"]) {
|
||||
newObject.attributes.npc_mythic_actions_desc = page.data["data-MAdesc"];
|
||||
} else {
|
||||
//TODO UC1098
|
||||
newObject.attributes.npc_legendary_actions_desc = ``;
|
||||
newObject.attributes.npc_mythic_actions_desc = ``;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,9 +672,7 @@ var processDrop = function(page, currentData, repeating, looped) {
|
||||
if (page.data.multiclass) {
|
||||
update[`${page.data.multiclass}_subclass`] = page.name;
|
||||
classlevel = parseInt(currentData[`${page.data.multiclass}_lvl`]);
|
||||
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;
|
||||
@@ -760,6 +757,7 @@ var processDrop = function(page, currentData, repeating, looped) {
|
||||
processProficiencies();
|
||||
break;
|
||||
case "Powers":
|
||||
case "Spells":
|
||||
processSpells();
|
||||
break;
|
||||
case "Subclasses":
|
||||
@@ -1003,7 +1001,6 @@ var processDrop = function(page, currentData, repeating, looped) {
|
||||
}
|
||||
}
|
||||
|
||||
//Testing this funtion to find an empty resource
|
||||
const findEmptyResource = () => {
|
||||
if (currentData["class_resource_name"].length === 0) {
|
||||
return "class_resource"
|
||||
|
||||
@@ -7,6 +7,9 @@ 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_multiclass1_hitdietype' value!='')
|
||||
input(type='hidden' name='attr_multiclass2_hitdietype' value!='')
|
||||
input(type='hidden' name='attr_multiclass3_hitdietype' 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')
|
||||
@@ -62,6 +65,11 @@ 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_xenobiology_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_history_bonus' value!='0')
|
||||
input(type='hidden' name='attr_nature_bonus' value!='0')
|
||||
input(type='hidden' name='attr_religion_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')
|
||||
|
||||
@@ -16,6 +16,40 @@ mixin abilityList()
|
||||
each ability in ABILTIES
|
||||
+ability(ability)
|
||||
|
||||
mixin abilityBarCol(name, abbr)
|
||||
.ability-col
|
||||
.ability-header
|
||||
button(type='roll' name=`roll_${name}` value!=`@{wtype}&{template:simple} {{rname=^{${name}-u}}} {{mod=@{${name}_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{${name}_mod}@{jack_attr}[${abbr}]]]}} @{rtype}+@{${name}_mod}@{jack_attr}[${abbr}]]]}} @{charname_output} {{licensedsheet=@{licensedsheet}}}` data-i18n=`${abbr.toLowerCase()}-u`)= abbr
|
||||
input.baseattr(type='text' name=`attr_${name}_base` title=`@{${name}}` value!='10')
|
||||
input(type='hidden' name=`attr_${name}` value!='10')
|
||||
input.attr-flag(type='hidden' name=`attr_${name}_flag` value!='0')
|
||||
span.finalattr(name=`attr_${name}`)
|
||||
.ability-modifiers
|
||||
.ability-mod-block
|
||||
.ability-val-box
|
||||
input(type='hidden' name=`attr_${name}_mod` value='0')
|
||||
button(type='roll' name=`roll_${name}` value!=`@{wtype}&{template:simple} {{rname=^{${name}-u}}} {{mod=@{${name}_mod}@{jack_bonus}}} {{r1=[[@{d20}+@{${name}_mod}@{jack_attr}[${abbr}]]]}} @{rtype}+@{${name}_mod}@{jack_attr}[${abbr}]]]}} @{charname_output} {{licensedsheet=@{licensedsheet}}}`)
|
||||
span.attr(name=`attr_${name}_mod` title=`@{${name}_mod}`) 0
|
||||
span.ab-label Mod
|
||||
.ability-save-block
|
||||
.ability-val-box
|
||||
input(type='hidden' name=`attr_${name}_save_bonus` value='0')
|
||||
button(type='roll' name=`roll_${name}_save` value!=`@{wtype}&{template:simple} {{rname=^{${name}-save-u}}} {{mod=@{${name}_save_bonus}}} {{r1=[[@{d20}+@{${name}_save_bonus}@{pbd_safe}]]}} @{rtype}+@{${name}_save_bonus}@{pbd_safe}]]}} {{global=@{global_save_mod}}} @{charname_output} {{licensedsheet=@{licensedsheet}}}`)
|
||||
span.save-val(name=`attr_${name}_save_bonus` title=`@{${name}_save_bonus}`) 0
|
||||
input(type='checkbox' name=`attr_${name}_save_prof` title=`@{${name}_save_prof}` value!='(@{pb})')
|
||||
span.ab-label Save
|
||||
|
||||
mixin abilitiesBar()
|
||||
.abilities-bar
|
||||
+abilityBarCol('strength', 'STR')
|
||||
+abilityBarCol('dexterity', 'DEX')
|
||||
+abilityBarCol('constitution', 'CON')
|
||||
+abilityBarCol('intelligence', 'INT')
|
||||
+abilityBarCol('wisdom', 'WIS')
|
||||
+abilityBarCol('charisma', 'CHA')
|
||||
.ab-section-label
|
||||
span(data-i18n='abilities-u') ABILITIES
|
||||
|
||||
mixin powerHeader(rank, tp)
|
||||
span.rank
|
||||
span(data-i18n='max-rank-u')
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
span.bold(data-i18n='hp') Hit Points
|
||||
span.num3(name='attr_hp_max')
|
||||
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.parens(name='attr_npc_hpformula' style='font-size: 12px; font-weight: normal;')
|
||||
span.parens(name='attr_npc_hpformula')
|
||||
.row.red
|
||||
span.bold(data-i18n='speed') Speed
|
||||
span(name='attr_npc_speed')
|
||||
@@ -624,17 +624,20 @@
|
||||
input.num(type='text' name='attr_npc_piloting_base' placeholder='0')
|
||||
.spacer
|
||||
.row
|
||||
span(data-i18n='ship-attributes-u') ATTRIBUTES (PILOT/CREW)
|
||||
span(data-i18n='ship-attributes-u') ATTRIBUTES (P=PILOT T=TECHNICIAN G=GUNNER)
|
||||
.row
|
||||
span(data-i18n='ship-dex-u') DEX—[PILOT]
|
||||
span(data-i18n='ship-dex-u') P-DEX
|
||||
input.num(type='text' name='attr_dexterity_base' value!='10')
|
||||
span(data-i18n='ship-int-u') INT—[TECH]
|
||||
span(data-i18n='ship-cha-u') P-WIS
|
||||
input.num(type='text' name='attr_charisma_base' value!='10')
|
||||
span(data-i18n='ship-int-u') T-INT
|
||||
input.num(type='text' name='attr_intelligence_base' value!='10')
|
||||
span(data-i18n='ship-wis-u') WIS—[TECH]
|
||||
span(data-i18n='ship-wis-u') T-WIS
|
||||
input.num(type='text' name='attr_wisdom_base' value!='10')
|
||||
input(type='hidden' name='attr_strength_base' value!='0')
|
||||
input(type='hidden' name='attr_constitution_base' value!='0')
|
||||
input(type='hidden' name='attr_charisma_base' value!='0')
|
||||
span(data-i18n='ship-str-u') G-DEX
|
||||
input.num(type='text' name='attr_strength_base' value!='10')
|
||||
span(data-i18n='ship-con-u') G-WIS
|
||||
input.num(type='text' name='attr_constitution_base' value!='10')
|
||||
.spacer
|
||||
.row
|
||||
span(data-i18n='saves-u') SAVES
|
||||
@@ -805,7 +808,7 @@
|
||||
span.bold(data-i18n='hlp') Hull Points
|
||||
span.num3(name='attr_hp_max')
|
||||
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.parens(name='attr_npc_hpformula' style='font-size: 12px; font-weight: normal;')
|
||||
span.parens(name='attr_npc_hpformula')
|
||||
.row.red
|
||||
span.bold(data-i18n='structural-integrity') Structural Integrity
|
||||
span(name='attr_ship_si')
|
||||
@@ -815,41 +818,41 @@
|
||||
.triangle
|
||||
.row.red
|
||||
.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.bold(data-i18n='str-u') STR
|
||||
span(name='attr_strength')
|
||||
input.negativeflag(type='hidden' name='attr_npc_str_negative' value!='0')
|
||||
span.npcattr(name='attr_strength_mod')
|
||||
.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.bold(data-i18n='dex-u') DEX
|
||||
button(type='roll' name='roll_npc_dex' value!='@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{npc-ship-dex}}} {{mod=[[[[@{dexterity_mod}]][P-DEX]]]}} {{r1=[[@{d20}+[[@{dexterity_mod}]][P-DEX]]]}} @{rtype}+[[@{dexterity_mod}]][P-DEX]]]}} {{type=Ability}}')
|
||||
span.bold(data-i18n='ship-dex-u') P-DEX
|
||||
span(name='attr_dexterity')
|
||||
input.negativeflag(type='hidden' name='attr_npc_dex_negative' value!='0')
|
||||
span.npcattr(name='attr_dexterity_mod')
|
||||
.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.bold(data-i18n='con-u') CON
|
||||
span(name='attr_constitution')
|
||||
input.negativeflag(type='hidden' name='attr_npc_con_negative' value!='0')
|
||||
span.npcattr(name='attr_constitution_mod')
|
||||
button(type='roll' name='roll_npc_cha' value!='@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{npc-ship-cha}}} {{mod=[[[[@{charisma_mod}]][P-WIS]]]}} {{r1=[[@{d20}+[[@{charisma_mod}]][P-WIS]]]}} @{rtype}+[[@{charisma_mod}]][P-WIS]]]}} {{type=Ability}}')
|
||||
span.bold(data-i18n='ship-cha-u') P-WIS
|
||||
span(name='attr_charisma')
|
||||
input.negativeflag(type='hidden' name='attr_npc_cha_negative' value!='0')
|
||||
span.npcattr(name='attr_charisma_mod')
|
||||
.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.bold(data-i18n='int-u') INT
|
||||
button(type='roll' name='roll_npc_int' value!='@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{npc-ship-int}}} {{mod=[[[[@{intelligence_mod}]][T-INT]]]}} {{r1=[[@{d20}+[[@{intelligence_mod}]][T-INT]]]}} @{rtype}+[[@{intelligence_mod}]][T-INT]]]}} {{type=Ability}}')
|
||||
span.bold(data-i18n='ship-int-u') T-INT
|
||||
span(name='attr_intelligence')
|
||||
input.negativeflag(type='hidden' name='attr_npc_int_negative' value!='0')
|
||||
span.npcattr(name='attr_intelligence_mod')
|
||||
.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.bold(data-i18n='wis-u') WIS
|
||||
button(type='roll' name='roll_npc_wis' value!='@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{npc-ship-wis}}} {{mod=[[[[@{wisdom_mod}]][T-WIS]]]}} {{r1=[[@{d20}+[[@{wisdom_mod}]][T-WIS]]]}} @{rtype}+[[@{wisdom_mod}]][T-WIS]]]}} {{type=Ability}}')
|
||||
span.bold(data-i18n='ship-wis-u') T-WIS
|
||||
span(name='attr_wisdom')
|
||||
input.negativeflag(type='hidden' name='attr_npc_wis_negative' value!='0')
|
||||
span.npcattr(name='attr_wisdom_mod')
|
||||
.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.bold(data-i18n='cha-u') CHA
|
||||
span(name='attr_charisma')
|
||||
input.negativeflag(type='hidden' name='attr_npc_cha_negative' value!='0')
|
||||
span.npcattr(name='attr_charisma_mod')
|
||||
button(type='roll' name='roll_npc_str' value!='@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{npc-ship-str}}} {{mod=[[[[@{strength_mod}]][G-DEX]]]}} {{r1=[[@{d20}+[[@{strength_mod}]][G-DEX]]]}} @{rtype}+[[@{strength_mod}]][G-DEX]]]}} {{type=Ability}}')
|
||||
span.bold(data-i18n='ship-str-u') G-DEX
|
||||
span(name='attr_strength')
|
||||
input.negativeflag(type='hidden' name='attr_npc_str_negative' value!='0')
|
||||
span.npcattr(name='attr_strength_mod')
|
||||
.attr-container
|
||||
button(type='roll' name='roll_npc_con' value!='@{wtype}&{template:npc} @{npc_name_flag} {{rname=^{npc-ship-con}}} {{mod=[[[[@{constitution_mod}]][G-WIS]]]}} {{r1=[[@{d20}+[[@{constitution_mod}]][G-WIS]]]}} @{rtype}+[[@{constitution_mod}]][G-WIS]]]}} {{type=Ability}}')
|
||||
span.bold(data-i18n='ship-con-u') G-WIS
|
||||
span(name='attr_constitution')
|
||||
input.negativeflag(type='hidden' name='attr_npc_con_negative' value!='0')
|
||||
span.npcattr(name='attr_constitution_mod')
|
||||
.triangle
|
||||
.row.skills.red(style='position:relative;')
|
||||
span.bold(data-i18n='piloting') Piloting Bonus
|
||||
|
||||
+982
-494
File diff suppressed because it is too large
Load Diff
@@ -27,31 +27,53 @@ mixin armorClassOrManeuverEntry(name)
|
||||
span(data-i18n=`${name.replace(/_/g,'-')}`)
|
||||
|
||||
.sheet-pc-ship
|
||||
input.tab-button.type-pc(name='attr_pc_sheet_type' type='radio' value!='pc' checked='checked')
|
||||
span(data-i18n='pc-u') PC
|
||||
input.tab-button.type-starship(name='attr_pc_sheet_type' type='radio' value!='starship')
|
||||
span(data-i18n='starship-u') STARSHIP
|
||||
input.tab-button.type-pc(id='tab-type-pc-ship' name='attr_pc_sheet_type' type='radio' value!='pc' checked='checked')
|
||||
input.tab-button.type-starship(id='tab-type-starship-ship' name='attr_pc_sheet_type' type='radio' value!='starship')
|
||||
|
||||
.sheet-top
|
||||
.sheet-flex-container.sheet-logo
|
||||
h2
|
||||
span(data-i18n='starship-statistics')
|
||||
div
|
||||
img(src='https://raw.githubusercontent.com/skydawn1/esper_genesis/master/EG_Logo.png' alt='Esper Genesis Logo')
|
||||
.sheet-ship-top-layout
|
||||
.sheet-token-frame
|
||||
img.sheet-char-avatar(name='attr_character_avatar')
|
||||
.sheet-ship-info
|
||||
.sheet-flex-container
|
||||
label(for='attr_character_name')
|
||||
span(data-i18n='pc_ship_name')
|
||||
input(type='text' name='attr_character_name')
|
||||
+headerEntry('owner')
|
||||
.sheet-flex-container
|
||||
+headerEntry('size_category')
|
||||
label(for='attr_pc_ship_weight')
|
||||
span(data-i18n='pc_ship_weight')
|
||||
input.sheet-ship-compact(type='text' name='attr_pc_ship_weight')
|
||||
label(for='attr_pc_ship_hull_type')
|
||||
span(data-i18n='pc_ship_hull_type')
|
||||
input.sheet-ship-compact(type='text' name='attr_pc_ship_hull_type')
|
||||
+headerEntry('crew_capacity')
|
||||
+headerEntry('cargo_capacity')
|
||||
|
||||
.sheet-crew-roles
|
||||
h3
|
||||
span(data-i18n='crewmember-roles') Crewmember Roles
|
||||
.sheet-flex-container
|
||||
+headerEntry('name')
|
||||
+headerEntry('owner')
|
||||
.sheet-flex-container
|
||||
+headerEntry('size_category')
|
||||
+headerEntry('weight')
|
||||
+headerEntry('hull_type')
|
||||
.sheet-flex-container
|
||||
+headerEntry('crew_capacity')
|
||||
+headerEntry('cargo_capacity')
|
||||
+headerEntry('length')
|
||||
+headerEntry('width')
|
||||
+headerEntry('height')
|
||||
label.sheet-crew-field
|
||||
input(type='text' name='attr_pc_ship_crew_pilot_name')
|
||||
span PILOT
|
||||
label.sheet-crew-field
|
||||
input(type='text' name='attr_pc_ship_crew_tech1_name')
|
||||
span TECHNICIAN 1
|
||||
label.sheet-crew-field
|
||||
input(type='text' name='attr_pc_ship_crew_tech2_name')
|
||||
span TECHNICIAN 2
|
||||
label.sheet-crew-field
|
||||
input(type='text' name='attr_pc_ship_crew_copilot_name')
|
||||
span CO-PILOT
|
||||
label.sheet-crew-field
|
||||
input(type='text' name='attr_pc_ship_crew_captain_name')
|
||||
span CAPTAIN
|
||||
|
||||
.ship-tab-row
|
||||
label.tab-label.type-pc(for='tab-type-pc-ship') PC
|
||||
label.tab-label.type-starship(for='tab-type-starship-ship') STARSHIP
|
||||
|
||||
.sheet-middle.sheet-grid-container
|
||||
.sheet-col-left
|
||||
@@ -86,23 +108,27 @@ mixin armorClassOrManeuverEntry(name)
|
||||
|
||||
.sheet-col-center
|
||||
.sheet-piloting-maneuver-check
|
||||
+subtitle('piloting-maneuver-check')
|
||||
h3
|
||||
button(type='roll' name='roll_pc_ship_piloting' value!="@{wtype}&{template:npc} {{name=@{character_name}}} {{rname=^{piloting-check}}} {{mod=[[0+@{pc_ship_piloting_num}]]}} {{r1=[[@{d20}+@{pc_ship_piloting_num}]]}} @{rtype}+@{pc_ship_piloting_num}]]}} {{type=Vehicle}}")
|
||||
span(data-i18n='piloting-maneuver-check')
|
||||
.sheet-fancy-input
|
||||
img(src=`${imagePath}/piloting_maneuver_check.png` alt='Piloting maneuver Check')
|
||||
input(type='hidden' name='attr_pc_ship_piloting_num' value='0')
|
||||
input(type='text' name='attr_pc_ship_piloting_maneuver_check')
|
||||
|
||||
p
|
||||
span(data-i18n='piloting-maneuver-check-info')
|
||||
|
||||
.sheet-command-dice
|
||||
h3
|
||||
span(data-i18n='command-dice')
|
||||
label(for='attr_pc_ship_command_dice')
|
||||
span(data-i18n='type-u')
|
||||
select(name='attr_pc_ship_command_dice')
|
||||
options(value='d4') d4
|
||||
options(value='d6') d6
|
||||
options(value='d4') d8
|
||||
options(value='d6') d12
|
||||
options(value='d6') d20
|
||||
option(value='') None
|
||||
option(value='d6') d6
|
||||
option(value='d8') d8
|
||||
option(value='d10') d10
|
||||
|
||||
.sheet-grid-container
|
||||
label(for='attr_pc_ship_total_dice')
|
||||
@@ -140,20 +166,65 @@ mixin armorClassOrManeuverEntry(name)
|
||||
span(data-i18n='maneuver-save-dc-info')
|
||||
|
||||
|
||||
.sheet-special-features
|
||||
+subtitle('special-features-and-options')
|
||||
+repeatingSection('ship-features')
|
||||
textarea(name='attr_feature' placeholder='Enter feature description...' required)
|
||||
span(name='attr_feature')
|
||||
.sheet-weapon-systems
|
||||
.sheet-grid-container.sheet-max-weapon-payload
|
||||
label(for='attr_pc_ship_max_weapon_payload')
|
||||
span(data-i18n='max-weapon-payload')
|
||||
div
|
||||
input(type='text' name='attr_pc_ship_max_weapon_payload')
|
||||
|
||||
.sheet-ship-cargo
|
||||
+subtitle('ship-cargo')
|
||||
+repeatingSection('ship-cargo')
|
||||
.sheet-grid-container
|
||||
div
|
||||
input(type='text' name='attr_name')
|
||||
div
|
||||
input(type='text' name='attr_weight')
|
||||
+subtitle('weapon-systems')
|
||||
+repeatingSection('weapon-systems')
|
||||
input(type='hidden' name='attr_rollbase' value!='@{wtype}&{template:atkdmg} {{attack=1}} {{damage=1}} {{dmg1flag=1}} {{mod=[[0@{tohit}]]}} {{rname=@{weapon}}} {{r1=[[@{d20}@{tohit}]]}} {{normal=1}} {{r2=[[0d20]]}} {{range=@{range}}} {{dmg1=[[@{damage}+0]]}} {{dmg1type=@{dmgtype}}} {{desc=@{special}}} ammo=@{ammo} {{licensedsheet=@{licensedsheet}}}')
|
||||
input(type='hidden' name='attr_atkdmg' value!='')
|
||||
input(type='hidden' name='attr_atkdmgc' value!='')
|
||||
.sheet-weapon-action
|
||||
input.sheet-weapon-flag(type='checkbox' name='attr_weapon_options_flag' checked='checked')
|
||||
span y
|
||||
.sheet-weapon-options
|
||||
.sheet-weapon-row
|
||||
span WEAPON:
|
||||
input(type='text' name='attr_weapon')
|
||||
span GUNNER NAME:
|
||||
input(type='text' name='attr_gunner')
|
||||
.sheet-weapon-row
|
||||
span RANGE:
|
||||
input(type='text' name='attr_range')
|
||||
span TO HIT:
|
||||
input(type='text' name='attr_tohit')
|
||||
.sheet-weapon-row
|
||||
span ON HIT:
|
||||
input(type='text' name='attr_damage')
|
||||
input(type='text' name='attr_dmgtype' placeholder='damage type')
|
||||
.sheet-weapon-row
|
||||
span SPECIAL:
|
||||
input(type='text' name='attr_special')
|
||||
.sheet-weapon-row
|
||||
span AMMO:
|
||||
input(type='text' name='attr_ammo')
|
||||
span RECHARGE:
|
||||
input(type='checkbox' name='attr_recharge' value='3')
|
||||
input(type='checkbox' name='attr_recharge' value='2')
|
||||
input(type='checkbox' name='attr_recharge' value='1')
|
||||
.sheet-weapon-display
|
||||
button.pick(type='roll' value!='@{rollbase}' name='roll_attack')
|
||||
span.sheet-weapon-name(name='attr_weapon')
|
||||
.sheet-weapon-meta
|
||||
span Gunner:
|
||||
span(name='attr_gunner')
|
||||
span | Range:
|
||||
span(name='attr_range')
|
||||
span | To Hit:
|
||||
span(name='attr_tohit')
|
||||
span | Hit:
|
||||
span(name='attr_damage')
|
||||
span
|
||||
span(name='attr_dmgtype')
|
||||
button.hidden(type='roll' value!='@{atkdmg}' name='roll_atkdmg')
|
||||
button.hidden(type='roll' value!='@{atkdmgc}' name='roll_atkdmgc')
|
||||
|
||||
.sheet-weapon-systems-info
|
||||
span(data-i18n='weapon-systems-info')
|
||||
|
||||
|
||||
.sheet-bottom
|
||||
@@ -183,7 +254,10 @@ mixin armorClassOrManeuverEntry(name)
|
||||
input(type='text' name='attr_pc_ship_maneuver_defense_bonus')
|
||||
|
||||
.sheet-grid-container
|
||||
+armorClassOrManeuverEntry('base_maneuver_defense')
|
||||
div
|
||||
span.sheet-static-num 8
|
||||
label
|
||||
span(data-i18n='base-maneuver-defense')
|
||||
+armorClassOrManeuverEntry('proficiency_bonus')
|
||||
+armorClassOrManeuverEntry('pilot_wis_modifier')
|
||||
+armorClassOrManeuverEntry('captain_cha_modifier')
|
||||
@@ -247,66 +321,26 @@ mixin armorClassOrManeuverEntry(name)
|
||||
.sheet-fancy-input
|
||||
img(src=`${imagePath}/structural_integrity.png` alt='Structural Integrity')
|
||||
input(type='text' name='attr_pc_ship_structural_integrity')
|
||||
span(data-i18n='structural-integrity-info')
|
||||
|
||||
label
|
||||
input(type='text' name='attr_pc_remaining_si')
|
||||
span(data-i18n='remaining-si')
|
||||
span(data-i18n='structural-integrity-info')
|
||||
|
||||
.sheet-weapon-systems
|
||||
.sheet-grid-container.sheet-max-weapon-payload
|
||||
label(for='attr_pc_ship_max_weapon_payload')
|
||||
span(data-i18n='max-weapon-payload')
|
||||
div
|
||||
input(type='text' name='attr_pc_ship_max_weapon_payload')
|
||||
|
||||
+subtitle('weapon-systems')
|
||||
.sheet-grid-container.table-header
|
||||
.sheet-gunner
|
||||
span(data-i18n='pc-ship-gunner')
|
||||
.sheet-weapon
|
||||
span(data-i18n='pc-ship-weapon')
|
||||
.sheet-range
|
||||
span(data-i18n='pc-ship-range')
|
||||
.sheet-to-hit
|
||||
span(data-i18n='pc-ship-to-hit')
|
||||
.sheet-damage
|
||||
span(data-i18n='pc-ship-damage')
|
||||
.sheet-damage-type
|
||||
span(data-i18n='pc-ship-damage-type')
|
||||
.sheet-grid-container.sheet-features-cargo-row
|
||||
.sheet-special-features
|
||||
+subtitle('special-features-and-options')
|
||||
+repeatingSection('ship-features')
|
||||
textarea(name='attr_feature' placeholder='Enter feature description...' required)
|
||||
span(name='attr_feature')
|
||||
|
||||
+repeatingSection('weapon-systems')
|
||||
input(type='hidden' name='attr_rollbase' value!='@{wtype}&{template:atk} {{mod=@{tohit}}} {{rname=[@{weapon}](~repeating_weapon-systems_atkdmg)}} {{rnamec=[@{weapon}](~repeating_weapon-systems_atkdmgc)}} {{r1=[[@{d20} + @{tohit}]]}} {{normal=1}} {{r2=[[0d20]]}} {{range=@{range}}} {{desc=@{special}}} ammo=@{ammo} @{pc_ship_name} {{licensedsheet=@{licensedsheet}}}')
|
||||
input(type='hidden' name='attr_atkdmg' value!='')
|
||||
input(type='hidden' name='attr_atkdmgc' value!='')
|
||||
.sheet-grid-container.table-body
|
||||
.sheet-gunner
|
||||
input(type='text' name='attr_gunner')
|
||||
.sheet-weapon
|
||||
input(type='text' name='attr_weapon')
|
||||
.sheet-range
|
||||
input(type='text' name='attr_range')
|
||||
.sheet-to-hit
|
||||
input(type='text' name='attr_tohit')
|
||||
.sheet-damage
|
||||
input(type='text' name='attr_damage')
|
||||
.sheet-damage-type
|
||||
input(type='text' name='attr_dmgtype')
|
||||
.sheet-grid-container.table-meta
|
||||
label.sheet-special
|
||||
span(data-i18n='pc-ship-special')
|
||||
input(type='text' name='attr_special')
|
||||
label.sheet-ammo
|
||||
span(data-i18n='pc-ship-ammo')
|
||||
input(type='text' name='attr_ammo')
|
||||
label.sheet-recharge
|
||||
span(data-i18n='pc-ship-recharge')
|
||||
input(type='checkbox' name='attr_recharge' value='3')
|
||||
input(type='checkbox' name='attr_recharge' value='2')
|
||||
input(type='checkbox' name='attr_recharge' value='1')
|
||||
button.hidden(type='roll' value!='@{atkdmg}' name='roll_atkdmg')
|
||||
button.hidden(type='roll' value!='@{atkdmgc}' name='roll_atkdmgc')
|
||||
button(type='roll' value!='@{rollbase}' name='roll_attack')
|
||||
|
||||
div
|
||||
span(data-i18n='weapon-systems-info')
|
||||
.sheet-ship-cargo
|
||||
+subtitle('ship-cargo')
|
||||
+repeatingSection('ship-cargo')
|
||||
.sheet-grid-container
|
||||
div
|
||||
input(type='text' name='attr_name')
|
||||
div
|
||||
input(type='text' name='attr_weight')
|
||||
|
||||
.sheet-ship-logo-footer
|
||||
img.sheet-ship-logo-img(src='https://raw.githubusercontent.com/skydawn1/esper_genesis/master/EG_Logo.png' alt='Esper Genesis Logo')
|
||||
@@ -221,9 +221,9 @@ const dndUpdates = [
|
||||
};
|
||||
// Set all spells without a given modifier to 'spell'
|
||||
var spgetList = [];
|
||||
getSectionIDs("spell-cantrip", function(secIds0) {
|
||||
getSectionIDs("spell-prime", function(secIds0) {
|
||||
_.each(secIds0, function(x) {
|
||||
spgetList.push("repeating_spell-cantrip_" + x + "_spell_ability");
|
||||
spgetList.push("repeating_spell-prime_" + x + "_spell_ability");
|
||||
});
|
||||
getSectionIDs("spell-1", function(secIds1) {
|
||||
_.each(secIds1, function(x) {
|
||||
@@ -935,4 +935,136 @@ const dndUpdates = [
|
||||
}
|
||||
),
|
||||
|
||||
// --- Updates added from OGL v4.2 base sheet ---
|
||||
|
||||
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,{{normal=1}} {{r2=[[0d20|Advantage,{{advantage=1}} {{r2=[[@{d20}|Disadvantage,{{disadvantage=1}} {{r2=[[@{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 {
|
||||
// EG uses update_skills in place of OGL updateSkillRolls
|
||||
update_skills(["acrobatics", "astrophysics", "athletics", "computers", "deception",
|
||||
"insight", "intimidation", "investigation", "lore", "mechanics",
|
||||
"medicine", "perception", "performance", "persuasion",
|
||||
"sleight_of_hand", "stealth", "survival", "xenobiology"]);
|
||||
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) {
|
||||
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 => entry.includes("itemname")).forEach(key => {
|
||||
if(armorsWithStealthDisadvantage.indexOf(v[key].toLowerCase()) > 1) {
|
||||
const modifiersKey = key.replace("_itemname", "_itemmodifiers");
|
||||
let modifiers = 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
),
|
||||
|
||||
];
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"npc-ship-wis": "Wisdom (Tech)",
|
||||
"npc-ship-str": "Dexterity (Gunner)",
|
||||
"npc-ship-con": "Wisdom (Gunner)",
|
||||
"crewmember-roles": "Crewmember Roles",
|
||||
"sensor-u": "SENSOR RANGE",
|
||||
"structural-integrity-u": "STRUCTURAL INTEGRITY",
|
||||
"saves-u": "SAVES",
|
||||
@@ -263,7 +264,7 @@
|
||||
"class-level-u": "CLASS & LEVEL",
|
||||
"background-u": "BACKGROUND",
|
||||
"background": "Background",
|
||||
"race-u": "RACE",
|
||||
"race-u": "SPECIES",
|
||||
"alignment-u": "ALIGNMENT",
|
||||
"alignment": "Alignment:",
|
||||
"exp-pts-u": "EXPERIENCE POINTS",
|
||||
@@ -365,6 +366,16 @@
|
||||
"cubil-cred-u": "CU",
|
||||
"cubil-cusara-u": "CS",
|
||||
"cubil-orim-u": "OM",
|
||||
"cu-u": "CU",
|
||||
"cn-u": "CN",
|
||||
"cs-u": "CS",
|
||||
"om-u": "OM",
|
||||
"cp-u": "CP",
|
||||
"sp-u": "SP",
|
||||
"ep-u": "EP",
|
||||
"gp-u": "GP",
|
||||
"pp-u": "PP",
|
||||
"starting-gold:": "Starting Money",
|
||||
"warning-armor-sets-u": "<input type=\"checkbox\" name=\"attr_armorwarning\" value=\"hide\">WARNING - AC IS NOT INCORRECT. UNEQUIP EXTRA ARMOR OR SHIELD FROM ITEM DETAILS.",
|
||||
"item-name-u": "ITEM NAME",
|
||||
"equipped-u": "EQUIPPED",
|
||||
@@ -372,6 +383,8 @@
|
||||
"prop:-u": "PROP:",
|
||||
"mods:-u": "MODS:",
|
||||
"total-weight-u": "TOTAL WEIGHT",
|
||||
"general-information-u": "GENERAL INFORMATION",
|
||||
"power-settings-u": "ESPER POWER FEATURES",
|
||||
"immobile-u": "IMMOBILE",
|
||||
"heavily-encumbered-u": "HEAVILY ENCUMBERED",
|
||||
"encumbered-u": "ENCUMBERED",
|
||||
@@ -392,12 +405,9 @@
|
||||
"eye-u": "EYES",
|
||||
"skin-u": "SKIN",
|
||||
"hair-u": "HAIR",
|
||||
"char-appearance-u": "CHARACTER APPEARANCE",
|
||||
"char-backstory-u": "CHARACTER BACKSTORY",
|
||||
"allies-orgs-u": "ALLIES & ORGANIZATIONS",
|
||||
"add-feats-traits-u": "ADDITIONAL FEATURES & TRAITS",
|
||||
"char-appearance-u": "CHARACTER DESCRIPTION",
|
||||
"char-backstory-u": "CHARACTER HISTORY",
|
||||
"feat-u": "FEAT",
|
||||
"treasure-u": "TREASURE",
|
||||
"spell-save-dc-u": "POWER SAVE DC",
|
||||
"primes-u": "PRIME POWERS",
|
||||
"ritual-u": "CONVENTIONAL",
|
||||
@@ -412,17 +422,18 @@
|
||||
"glob-atk-mod:-u": "GLOBAL POWER ATTACK MODIFIER:",
|
||||
"pass-perc-mod:-u": "PASSIVE PERCEPTION MODIFIER:",
|
||||
"death-save-mod:-u": "DEATH SAVE MODIFIER:",
|
||||
"magic-caster-lvl:-u": "CHANNELER/FORGER LEVEL:",
|
||||
"magic-caster-lvl:-u": "ESPER LEVEL:",
|
||||
"halfling-luck-u": "HALFLING LUCK",
|
||||
"arcane-fighter-u": "JUSTICAR",
|
||||
"arcane-rogue-u": "ARTIFICE",
|
||||
"class-options-u": "CLASS OPTIONS (<input type=\"text\" name=\"attr_class_label\" value=\"@{class}\" disabled=\"true\">)",
|
||||
"class:-u": "CLASS:",
|
||||
"subclass:-u": "SUBCLASS:",
|
||||
"race:-u": "RACE:",
|
||||
"subrace:-u": "SUBRACE:",
|
||||
"subrace": "Subrace:",
|
||||
"subrace-ability-increase": "Subrace Ability Score Increase:",
|
||||
"race:-u": "SPECIES:",
|
||||
"subrace:-u": "ARCHETYPE:",
|
||||
"subrace": "Species Archetype:",
|
||||
"subrace-ability-increase": "Species Ability Score Increase:",
|
||||
"subrace-speed:": "Species Speed:",
|
||||
"2nd-class:-u": "2ND CLASS:",
|
||||
"lvl:-u": "LEVEL:",
|
||||
"3rd-class:-u": "3RD CLASS:",
|
||||
@@ -437,8 +448,10 @@
|
||||
"spell-third-u": "THIRD (Artifice, Justicar)",
|
||||
"tech-slots-u": "TECH SLOTS",
|
||||
"esper-powers-u": "ESPER POWERS",
|
||||
"powers": "Powers",
|
||||
"tp-u": "TALENT POINTS",
|
||||
"tp-max-u": "Talent Point Maximum",
|
||||
"tp-current-u": "TALENT POINTS REMAINING",
|
||||
"tp-current-u": "TP REMAINING",
|
||||
"talent-slots-label": "TALENT SLOTS (Cybermancer)",
|
||||
"talent-slots-u": "Max Talent Slots",
|
||||
"used-talent-slots": "Slots Remaining",
|
||||
@@ -450,6 +463,7 @@
|
||||
"normal": "Normal",
|
||||
"expirtise": "Expertise",
|
||||
"skill-opts-u": "SKILL OPTIONS",
|
||||
"show-5e-skills-u": "SHOW 5E SKILLS?",
|
||||
"version": "v",
|
||||
"always-roll-adv": "Always Roll Advantage",
|
||||
"toggle-roll-adv": "Advantage Toggle",
|
||||
@@ -469,13 +483,8 @@
|
||||
"encumbrance:-u": "ENCUMBRANCE:",
|
||||
"ammo-tracking:-u": "AMMO TRACKING:",
|
||||
"general-opts-u": "GENERAL OPTIONS",
|
||||
"transition-text": "Clicking one of the images below will reformat the data from your current sheet to this sheet. These changes are permanent and cannot be undone.",
|
||||
"transition-text-highlight": "Please make a copy of your game before you use this tool.",
|
||||
"choose-sheet-transfer": "CHOOSE THE SHEET YOU ARE TRANSFERING FROM",
|
||||
"community-sheet-u": "COMMUNITY SHEET",
|
||||
"shaped-sheet-u": "SHAPED SHEET",
|
||||
"transition-opts-u": "TRANSITION OPTIONS",
|
||||
"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",
|
||||
"portions-sheet-utilize": "Portions of this sheet utilize content from the System Reference Document 5.x under the Creative Commons Attribution 4.0 License.",
|
||||
"api-required-title": "Try it now with easy one click API installation available with your Pro subscription.",
|
||||
"api-required-u": "(API REQUIRED)",
|
||||
"spell_dc_mod:-u": "POWER SAVE DC MOD:",
|
||||
@@ -489,7 +498,6 @@
|
||||
"inventory-info": "Character Inventories default to the complex style that is compatible with the Roll20 5E Compendium. This includes granular item amounts/weights, weight and encumbrance tracking, sorting, AC calculations, automatic attack generation, and more. The Simple option provides a text field for item lists for players who want more manual control over the sheet.",
|
||||
"encumbrance-info": "The sheet uses the variant encumbrance rules on page 160 of the Core Manual. The Off option disables the variant rules and only uses a basic over-limit inventory weight check.",
|
||||
"ammo-tracking-info": "Provides automatic ammo tracking with the <a src='https://wiki.roll20.net/5th_Edition_OGL_by_Roll20#Utilizing_the_5th_Edition_OGL_Companion_API_Script'>5th Edition OGL by Roll20 Companion API Script</a>.",
|
||||
"dungeons-and-dragons-styling": "DUNGEONS AND DRAGONS STYLING",
|
||||
"dungeons-and-dragons-styling-info": "Turns on Dungeons and Dragons styling for both the character sheet and roll templates. This setting is automatically applied to all character sheets and cannot be selectively used.",
|
||||
"cd-bar1-value:-u": "BAR 1 VALUE:",
|
||||
"cd-bar1-max:-u": "BAR 1 MAX:",
|
||||
@@ -516,7 +524,7 @@
|
||||
"npc-name-in-rolls-desc:-u": "In NPC roll results, show or hide the name of the NPC in the roll result card.",
|
||||
"ship-name-in-rolls-desc:-u": "In SHIP roll results, show or hide the name of the SHIP in the roll result card.",
|
||||
"public:-u": "PUBLIC",
|
||||
"to-gm:-u": "TO <span class='togm'>GM</span>",
|
||||
"to-gm:-u": "WHISPER",
|
||||
"toggle-roll-whisper": "Whisper Toggle",
|
||||
"attribute-opts-u": "ATTRIBUTE OPTIONS",
|
||||
"save-opts-u": "SAVE OPTIONS",
|
||||
@@ -573,12 +581,12 @@
|
||||
"global-skill-info": "Creates a custom global skill modifier field, underneath the SKILLS section of the character sheet. Anything put there will be applied to any save roll. Perfect for regular but not permanent bonuses like the Amplify Ability power.",
|
||||
"global-attack-info": "Creates a custom global attack modifier field, underneath the ATTACKS AND ESPER POWERS section of the character sheet. Anything put there will be applied to any attack to-hit roll. Perfect for regular but not permanent bonuses to attack like the Sensory Boost power.",
|
||||
"global-damage-info": "Creates a custom global damage modifier field, underneath the ATTACKS AND ESPER POWERS section of the character sheet. Anything put there will be applied to any attack damage roll. Perfect for regular but not permanent bonuses to damage like the Deft Strike ability.",
|
||||
"auto-level-calc-info": "By default changing character levels will automatically set spell slots and hit dice. Changing this setting to off prevents this behavior.",
|
||||
"auto-level-calc-info": "By default changing character levels will automatically set power slots and hit dice. Changing this setting to off prevents this behavior.",
|
||||
"simple-traits-info": "Character FEATURES AND TRAITS section defaults to the complex style that is compatible with the Roll20 5E Compendium. The Simple option provides a text field for players who want more manual control over the sheet.",
|
||||
"simple-proficiencies-info": "Character OTHER PROFICIENCIES & LANGUAGES section defaults to the complex style that is compatible with the Roll20 5E Compendium. The Simple option provides a text field for players who want more manual control over the sheet.",
|
||||
"init-tiebreaker-info": "Adds the character's dexterity score as a decimal to the end of the initiative bonus for purposes of breaking ties.",
|
||||
"b-custom-info": "When the Proficiency Bonus type is set to Custom, the character sheet will use this value for the character's Proficiency Bonus.",
|
||||
"footer-wiki-link": "This sheet was created from the 5th Edition OGL character sheet by Roll20. For game information, visit http://espergenesis.com",
|
||||
"footer-wiki-link": "This sheet was adapted from the 5th Edition OGL character sheet by Roll20. For game information, visit http://espergenesis.com",
|
||||
"accepting-drop-u": "ACCEPTING DROP FROM COMPENDIUM",
|
||||
"are_you_sure": "Are you sure you want to turn this character into a ",
|
||||
"yes": "Yes",
|
||||
@@ -594,15 +602,16 @@
|
||||
"get-started": "Get started by clicking Next.",
|
||||
"opt-out-u": "OPT OUT",
|
||||
"charmancer-compendium": "Your Compendium Awaits.",
|
||||
"charmancer-compendium-detail": "The Charactermancer currently supports the Esper Genesis SRD. You can add the Core Manual to your compendium in the Marketplace, or the game owner can share their compendium with everyone in the game.",
|
||||
"charmancer-choose-race": "Choose a Race:",
|
||||
"charmancer-custom-race-name": "Custom Race Name:",
|
||||
"charmancer-custom-subclass-name:": "Custom sublass Name:",
|
||||
"charmancer-compendium-detail": "The Charactermancer currently supports the Esper Genesis RPG. You can add the Core Manual to your compendium in the Marketplace, or the game owner can share their compendium with everyone in the game.",
|
||||
"charmancer-choose-race": "Choose a Species:",
|
||||
"charmancer-custom-race-name": "Custom Species Name:",
|
||||
"charmancer-custom-subclass-name:": "Custom Subclass Name:",
|
||||
"charmancer-additional-primes": "Additional Cantrips",
|
||||
"charmancer-additional-lvl-1-spells": "Additional Level 1 Spells",
|
||||
"charmancer-additional-lvl-1-spells": "Additional Level 1 Powers",
|
||||
"suggested-abilities": "Suggested Abilities: ",
|
||||
"class-spellcasting": "Class Spellcasting Ability: ",
|
||||
"class-spellcasting": "Class Esper Ability: ",
|
||||
"ability-score-increase": "Ability Score Increase:",
|
||||
"ability-score-increase:": "Ability Score Increase:",
|
||||
"lawful-good": "Lawful Good",
|
||||
"neutral-good": "Neutral Good",
|
||||
"chaotic-good": "Chaotic Good",
|
||||
@@ -624,7 +633,7 @@
|
||||
"equipment-from-class": "Equipment from Class",
|
||||
"class-equipment": "Class Equipment",
|
||||
"starting-wealth": "Starting Wealth (Core)",
|
||||
"starting-cubil:": "Starting Cubil:",
|
||||
"starting-cubil:": "Starting Money:",
|
||||
"no_class_warning-u": "NO CLASS HAS BEEN SELECTED OR SELECTED CLASS DOES NOT HAVE STANDARD EQUIPMENT. CLASS EQUIPMENT CANNOT BE CHOSEN AT THIS TIME.",
|
||||
"standard-array": "Standard Array",
|
||||
"roll-for-stats": "Roll for Stats",
|
||||
@@ -661,7 +670,7 @@
|
||||
"charmancer-leveler-what-is": "The Charactermancer is a step-by-step process that makes leveling up a character simple and streamlined. Changes are saved as you progress, but the character sheet is only updated when the Charactermancer has been completed.",
|
||||
"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.",
|
||||
"charmancer-leveler-level-info2": "In each class section you'll have the option to take the average number of hit points or to roll randomly, choose class features for the levels you're advancing, and choose your subclass if appropriate.",
|
||||
"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.",
|
||||
"charmancer-leveler-spells-info": "Listed here are the current powers your character already knows. If you gained access to more powers during your advancement you will be able to select them below. Check next to the powers you wish to learn. The Charactermancer will help you pick the right number of powers and from the correct list. If your class has the option to swap out powers, you can do so by unselecting currently known powers which will allow you to select new ones in their place.",
|
||||
"total-level-u": "TOTAL LEVEL",
|
||||
"main-class-level-u": "MAIN CLASS LEVEL",
|
||||
"class-2-level-u": "CLASS 2 LEVEL",
|
||||
@@ -672,7 +681,7 @@
|
||||
"charmancer-leveler-custom-class-warning": "The Level Up tool does not support custom classes at this time.",
|
||||
"charmancer-welcome-lp": "Welcome to Charactermancer Level Up!",
|
||||
"charmancer-leveler-what-is-lp": "Look at you, with all that experience. Now it's time to claim your reward. You'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'll need to revisit the steps and choices that come after.",
|
||||
"charmancer-compendium-detail-lp": "The Charactermancer currently supports the Esper Genesis SRD. You can add the Core Manual to your compendium in the Marketplace, or the game owner can share their compendium with everyone in the game.",
|
||||
"charmancer-compendium-detail-lp": "The Charactermancer currently supports the Esper Genesis RPG. You can add the Core Manual to your compendium in the Marketplace, or the game owner can share their compendium with everyone in the game.",
|
||||
"roll:-u": "ROLL:",
|
||||
"roll": "Roll",
|
||||
"prerequisite:": "Prerequisite:",
|
||||
@@ -697,25 +706,26 @@
|
||||
"innate-spellcasting:": "Innate Powers:",
|
||||
"spellcasting-ability": "Esper Ability:",
|
||||
"number-of-primes": "Number of Primes:",
|
||||
"number-of-lvl-1-spells": "Number of Level 1 Spells:",
|
||||
"number-of-lvl-1-spells": "Number of Rank 1 Powers:",
|
||||
"spell-list": "Powers List:",
|
||||
"done": "Done",
|
||||
"ability-score-method": "Ability Score Method:",
|
||||
"select-your-choices": "Select Your Choices:",
|
||||
"input-your-scores": "Input Your Scores:",
|
||||
"choose-background": "Choose a Background:",
|
||||
"choose-a-feat:": "Choose a Feat:",
|
||||
"personality-traits": "Personality Traits",
|
||||
"ideal": "Ideal",
|
||||
"bond": "Bond",
|
||||
"flaw": "Flaw",
|
||||
"you-have-chosen-starting-cubil": "You have chosen starting cubil and do not get equipment from your background.",
|
||||
"you-have-chosen-starting-gold": "You have chosen starting money and do not get equipment from your background.",
|
||||
"you-have-created-custom-class": "You have created a custom class; you can add equipment to the character sheet after you have finished the Charactermancer.",
|
||||
"arcane-is-mystery": "Cosmic forces are a mystery to you.",
|
||||
"your-character-dont-have-spells-this-lvl": "Your character doesn't have powers at this level. Click “Next” to review your character!",
|
||||
"charmancer-a-feat-represents": "A feat represents accomplishment, grit, determination, and expertise. It is the reward for hard work.",
|
||||
"you-really-havent-done": "You really haven't done that much yet",
|
||||
"review-your-choices": "Review your choices:",
|
||||
"race": "Race",
|
||||
"race": "Species",
|
||||
"abilities": "Abilities",
|
||||
"bio": "Bio",
|
||||
"name-background": "Name:",
|
||||
@@ -729,7 +739,7 @@
|
||||
"you-can-increase-two-abilites": "You can increase two of your abilities by 1, or one of your abilities by 2",
|
||||
"before-leveling": "Before Leveling",
|
||||
"after-leveling": "After Leveling",
|
||||
"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.",
|
||||
"charmancer-removed-spells-marked-pt1": "Removed powers will be marked with a <span>r</span> symbol so you can find it easily if you change your mind.",
|
||||
"pc_ship_name": "Ship/Station Name",
|
||||
"pc_ship_owner": "Owner",
|
||||
"pc_ship_size_category": "Size Category",
|
||||
@@ -758,9 +768,9 @@
|
||||
"intercept-speed": "Intercept Speed",
|
||||
"startdrive": "Stardrive",
|
||||
"pc_ship_sensors": "Sensors",
|
||||
"pc_ship_sensors_info": "Short / Extended Range",
|
||||
"pc_ship_initiative": "Initiative Bonus",
|
||||
"pc_ship_initiative_info": "= Pilot's DEX modifier or Captain's WIS modifier",
|
||||
"pc_ship_sensors_info": "Short / Extended",
|
||||
"pc_ship_initiative": "Initiative",
|
||||
"pc_ship_initiative_info": "= Pilot's DEX mod or Captain's WIS mod",
|
||||
"maneuver-save-dc": "Maneuver Save DC",
|
||||
"maneuver-save-dc-info": "= 8 + Pilot's Proficiency Bonus + Pilot's Dex Modifier + Captain's CHA Modifier",
|
||||
"special-features-and-options": "Special Features and Options",
|
||||
@@ -792,32 +802,58 @@
|
||||
"patch-repair-info": "(Max of two HD per repair)",
|
||||
"die-type": "Die Type",
|
||||
"int-mod": "Int Mod",
|
||||
"used": "Used#",
|
||||
"used": "# Used",
|
||||
"damage-threshold": "Damage Threshold",
|
||||
"temp-hp": "Temp HP",
|
||||
"damage": "Damage",
|
||||
"remaining-si": "Remaining SI",
|
||||
"structural-integrity-info": "Max SI - Base SI + Technician’s WIS Mod",
|
||||
"structural-integrity-info": "Max SI = Base SI + Technician’s WIS Mod",
|
||||
"pc-u": "PC",
|
||||
"starship-u": "STARSHIP",
|
||||
"sheet-type-u": "SHEET TYPE:",
|
||||
"starship-statistics": "Starship Statistics",
|
||||
"max-weapon-payload": "Max Weapon Payload",
|
||||
"weapon-systems-info": "To Hit = (Gunner’s Proficiency Bonus + Gunner’s DEX modifier + Captain’s WIS modifier)",
|
||||
"honor-u": "HONOR",
|
||||
"honor-save-u": "HONOR SAVE",
|
||||
"honor": "Honor",
|
||||
"honor-info": "Enables the optional Honor Attribute.",
|
||||
"honor-save": "Honor Save",
|
||||
"honor-setting-u": "HONOR SCORE:",
|
||||
"sanity-u": "SANITY",
|
||||
"sanity-save-u": "SANITY SAVE",
|
||||
"sanity": "Sanity",
|
||||
"sanity-info": "Enables the optional Sanity Attribute.",
|
||||
"sanity-save": "Sanity Save",
|
||||
"sanity-setting-u": "SANITY SCORE:",
|
||||
"customize-your-origin": "Customize Your Origin",
|
||||
"customize-your-origin-info": "If you’d like your character to follow their own path, you may ignore your Ability Score Increase trait and assign ability score increases tailored to your character.",
|
||||
"ability-score-increase-tasha": "Ability Score increase",
|
||||
"ship-trait-desc-place": "Ship trait description"
|
||||
}
|
||||
"ship-trait-desc-place": "Ship trait description",
|
||||
"short-rest": "SHORT REST",
|
||||
"long-rest": "LONG REST",
|
||||
"rest-buttons-u": "SHORT/LONG REST BUTTONS",
|
||||
"rest-buttons-info": "Adds Short Rest and Long Rest buttons to the Resources section. Short rest clears death saves and resets short-rest resources. Long rest fully restores HP, hit dice (half level), talent points, and all resources.",
|
||||
"default-critical-range:-u": "CRITICAL RANGE:",
|
||||
"reliable-talent:-u": "RELIABLE TALENT:",
|
||||
"reliable-talent-info": "When enabled, any skill check roll for a skill this character is proficient in will treat a d20 result lower than 10 as a 10. Represents the Specialist class feature of the same name (level 11).",
|
||||
"powerful_build-u": "POWERFUL BUILD",
|
||||
"powerful-build-info": "When checked, doubles the character's carrying capacity by applying a x2 multiplier to the size-based weight limit. Represents the species trait of the same name.",
|
||||
"hero-points-u": "HERO POINTS",
|
||||
"hero-points-info": "Enables the Hero Points panel in the character sheet. When enabled, hero points are automatically calculated as (level / 2) + 5 and can be spent by clicking the button to roll 1d6.",
|
||||
"none-left-u": "NONE LEFT",
|
||||
"spell-points-variant:-u": "SPELL POINTS VARIANT",
|
||||
"spell-points-info": "Enables the Spell Points variant rule (DMG p.288). When enabled, power slots are hidden and replaced with a Spell Points resource automatically calculated from caster level. Points replenish on long rest.",
|
||||
"animal_handling-u": "ANIMAL HANDLING",
|
||||
"animal_handling-core": "Animal Handling <span>(Wis)</span>",
|
||||
"animal_handling": "Animal Handling",
|
||||
"arcana-u": "ARCANA",
|
||||
"arcana-core": "Arcana <span>(Int)</span>",
|
||||
"arcana": "Arcana",
|
||||
"history-u": "HISTORY",
|
||||
"history-core": "History <span>(Int)</span>",
|
||||
"history": "History",
|
||||
"nature-u": "NATURE",
|
||||
"nature-core": "Nature <span>(Int)</span>",
|
||||
"nature": "Nature",
|
||||
"religion-u": "RELIGION",
|
||||
"religion-core": "Religion <span>(Int)</span>",
|
||||
"religion": "Religion",
|
||||
"skill-set:-u": "SKILL SET:",
|
||||
"eg-skills": "EG Skills",
|
||||
"5e-skills": "5e Skills",
|
||||
"all-skills": "All Skills",
|
||||
"skill-mode-info": "Toggle between Esper Genesis skills (Astrophysics, Computers, Lore, Mechanics, Xenobiology), standard 5e skills (Animal Handling, Arcana, History, Nature, Religion), or All Skills (both sets visible simultaneously for hybrid games).",
|
||||
"currency:-u": "CURRENCY:",
|
||||
"eg-currency": "EG Currency",
|
||||
"5e-currency": "5e Currency",
|
||||
"currency-mode-info": "Toggle between EG currency (CU Cubil digital, CN Cubil coin, CS Cusara = 100 CU, OM Orim = 1000 CU) and standard 5e currency (CP, SP, EP, GP, PP). EG Currency is the default. Use 5e Currency for hybrid games or when running 5e modules."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user