Skip to content

Commit

Permalink
v13.0.7-beta3 Fix spell sheet generation
Browse files Browse the repository at this point in the history
•	Fixed spells from feats, magic items, and race not appearing on the spell pages
•	Fixed issue with not being able to change the spellcasting ability score for magic items
•	Made the Choose Feature menu always display a source for each option
•	Improved compatibility between dynamic spell changes and spells gained from magic items
•	Improved display of source when adding notes through the `toNotesPage` attribute
•	Added option to disable upcasting of a singular spell (with the `spellChanges` attribute)
•	Added option to have the sheet ask which spellcasting ability to use (`abilitySpellcasting` can now be an array)
  • Loading branch information
morepurplemorebetter committed Jul 6, 2021
1 parent 7b266fd commit a3062a6
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 70 deletions.
3 changes: 2 additions & 1 deletion _functions/Functions1.js
Original file line number Diff line number Diff line change
Expand Up @@ -5808,8 +5808,9 @@ function MakeClassMenu(bKeepTempClassesKnown) {
}

// now make the menu entry
var oSrc = { source : feaObjA.source ? feaObjA.source : feaObj.source };
var mItem = {
cName : array[i] + extraNm + stringSource(feaObjA, "first,abbr", "\t [", "]"),
cName : array[i] + extraNm + stringSource(oSrc, "first,abbr", "\t [", "]"),
cReturn : classNm + "#" + featureNm + "#" + array[i] + "#" + extrareturn + "#" + removeStop + (moreReturn ? moreReturn : ""),
bMarked : isActive,
bEnabled : isEnabled
Expand Down
10 changes: 6 additions & 4 deletions _functions/Functions2.js
Original file line number Diff line number Diff line change
Expand Up @@ -8190,7 +8190,7 @@ function getHighestTotal(nmbrObj, notRound, replaceWalk, extraMods, prefix, with

// open a dialog with a number of lines of choices and return the choices in an array; if knownOpt === "radio", show radio buttons instead, and return the entry selected
// if notProficiencies is set to true, the optType will serve as the dialog header, and optSrc will serve as the multline explanatory text
function AskUserOptions(optType, optSrc, optSubj, knownOpt, notProficiencies) {
function AskUserOptions(optType, optSrc, optSubj, knownOpt, notProficiencies, sBottomMsg) {
if (!IsNotImport) return optSubj;
//first make the entry lines
var selectionLines = [];
Expand Down Expand Up @@ -8400,7 +8400,7 @@ function AskUserOptions(optType, optSrc, optSubj, knownOpt, notProficiencies) {
alignment : "align_fill",
item_id : "txtL",
wrap_name : true,
name : "You can always change what you set here at a later time by editing the corresponding field on the sheet. What you select here is not permanent.",
name : sBottomMsg ? sBottomMsg : "You can always change what you set here at a later time by editing the corresponding field on the sheet. What you select here is not permanent.",
char_width : 40
}, {
type : "ok"
Expand Down Expand Up @@ -8432,7 +8432,7 @@ function SetCreatureSize(prefix, sName, aSizes) {
oOptionsRef[sSizeName] = aSizes[i];
}
var sTooltip = sNamePl + " size " + (aOptions.length > 1 ? formatLineList("can be", aOptions, true) + "." : "is " + aOptions[0] + ".");
if(!prefix) sTooltip += "\n\nSelected size category will affect encumbrance on the second page.";
if (!prefix) sTooltip += "\n\nSelected size category will affect encumbrance on the second page.";
var bGoAsk = aSizes.length > 1;
if (bGoAsk) {
var sAsk = AskUserOptions("Select Size Category for " + sName, sNamePl + " can be multiple size categories. It is up to you to select which the sheet will use.", aOptions, "radio", true);
Expand Down Expand Up @@ -8466,7 +8466,9 @@ function processToNotesPage(AddRemove, items, type, mainObj, parentObj, namesArr
break;
case "race":
fallback.alertType = "Racial Traits section";
fallback.noteOrig = namesArr[1];
fallback.noteOrig = namesArr[1].capitalize();
if (mainObj.minlevel) fallback.noteOrig += " " + mainObj.minlevel;
if (!fallback.noteSrc) fallback.noteSrc = stringSource(CurrentRace, "first,abbr", ", ");
break;
case "background":
fallback.alertType = "Background Feature description";
Expand Down
38 changes: 31 additions & 7 deletions _functions/Functions3.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ function ApplyFeatureAttributes(type, fObjName, lvlA, choiceA, forceNonCurrent)
CurrentUpdates.types.push("spells");
var aCast = CurrentSpells[useSpCasting];
if (uObj.spellcastingAbility !== undefined) {
aCast.ability = uObj.spellcastingAbility;
aCast.ability = ReturnSpellcastingAbility(useSpCasting, uObj.spellcastingAbility);
aCast.abilityToUse = getSpellcastingAbility(useSpCasting);
}
if (uObj.fixedDC) aCast.fixedDC = Number(uObj.fixedDC);
Expand Down Expand Up @@ -694,6 +694,30 @@ function classFeaChoiceBackwardsComp() {
return returnStr;
}

// a function to return the spellcasting ability, ask the user which to use if it is an array
function ReturnSpellcastingAbility(sCast, vAbility) {
if (vAbility === undefined) return 0;
if (!isArray(vAbility)) return !isNaN(vAbility) || /^(class|race)$/i.test(vAbility) ? vAbility : 0;
var sCastName = CurrentSpells[sCast] ? CurrentSpells[sCast].name : sCast;
var aAbiOptions = [], oAbiRef = {};
vAbility.sort();
for (var i = 0; i < vAbility.length; i++) {
if (isNaN(vAbility[i]) && !/^(class|race)$/i.test(vAbility[i])) continue;
var sAbiTxt = !isNaN(vAbility[i]) ? AbilityScores.names[vAbility[i] - 1] : vAbility[i].toLowerCase() === "race" ? "Race: the same as the racial spellcasting ability score." : "Class: the same as the highest class spellcasting ability score.";
if (aAbiOptions.indexOf(sAbiTxt) === -1) {
aAbiOptions.push(sAbiTxt);
oAbiRef[sAbiTxt] = vAbility[i];
}
}
if (aAbiOptions.length < 2) return aAbiOptions.length ? aAbiOptions[0] : 0;
var sAsk = AskUserOptions("Select Spellcasting Ability Score for " + sCastName, "The spellcasting ability for " + sCastName + " can be one of multiple options. It is up to you to select which the sheet will use from now on.", aAbiOptions, "radio", true, 'You can always change the spellcasting ability for ' + sCastName + ' on the spell sheet once you generate the spell sheet. What you select here is the \"default\" spellcasting ability.');
if (oAbiRef[sAsk]) {
return oAbiRef[sAsk];
} else {
return oAbiRef[aAbiOptions[0]];
}
}

// a function to create the CurrentSpells global variable entry
function CreateCurrentSpellsEntry(type, fObjName, aChoice, forceNonCurrent) {
type = GetFeatureType(type);
Expand All @@ -720,7 +744,7 @@ function CreateCurrentSpellsEntry(type, fObjName, aChoice, forceNonCurrent) {
break;
case "race":
var fObj = CurrentRace;
var sObj = setCSobj(CurrentRace.known);
var sObj = setCSobj(fObjName);
sObj.name = fObj.name;
sObj.typeSp = "race";
sObj.level = fObj.level;
Expand Down Expand Up @@ -761,12 +785,12 @@ function CreateCurrentSpellsEntry(type, fObjName, aChoice, forceNonCurrent) {
}
}
}
if (!sObj.ability) sObj.ability = fObj.spellcastingAbility ? fObj.spellcastingAbility : fObj.abilitySave ? fObj.abilitySave : 0;
if (!sObj.ability) sObj.ability = ReturnSpellcastingAbility(fObjName, fObj.spellcastingAbility ? fObj.spellcastingAbility : fObj.abilitySave ? fObj.abilitySave : 0);
if (!sObj.fixedDC && fObj.fixedDC) sObj.fixedDC = Number(fObj.fixedDC);
if (!sObj.fixedSpAttack && fObj.fixedSpAttack) sObj.fixedSpAttack = Number(fObj.fixedSpAttack);
if (!sObj.allowUpCasting === undefined && fObj.allowUpCasting !== undefined) sObj.allowUpCasting = fObj.allowUpCasting;
if (fObjP) {
if (!sObj.ability) sObj.ability = fObjP.spellcastingAbility ? fObjP.spellcastingAbility : fObjP.abilitySave ? fObjP.abilitySave : 0;
if (!sObj.ability) sObj.ability = ReturnSpellcastingAbility(fObjName, fObjP.spellcastingAbility ? fObjP.spellcastingAbility : fObjP.abilitySave ? fObjP.abilitySave : 0);
if (!sObj.fixedDC && fObjP.fixedDC) sObj.fixedDC = Number(fObjP.fixedDC);
if (!sObj.fixedSpAttack && fObjP.fixedSpAttack) sObj.fixedSpAttack = Number(fObjP.fixedSpAttack);
if (!sObj.allowUpCasting === undefined && fObjP.allowUpCasting !== undefined) sObj.allowUpCasting = fObjP.allowUpCasting;
Expand Down Expand Up @@ -807,7 +831,7 @@ function processSpBonus(AddRemove, srcNm, spBon, type, parentName, choice, force
}
}
if (spAbility) {
sObj.ability = spAbility;
sObj.ability = ReturnSpellcastingAbility(useSpName, spAbility);
sObj.abilityToUse = getSpellcastingAbility(useSpName);
}
if (spFixedDC) sObj.fixedDC = spFixedDC;
Expand Down Expand Up @@ -1202,9 +1226,9 @@ function UpdateSheetDisplay() {
return;
}

if (!ChangesDialogSkip) {
if (ChangesDialogSkip.chXP === undefined) {
var cDialogFld = What("ChangesDialogSkip.Stringified");
ChangesDialogSkip = cDialogFld ? eval(cDialogFld) : {
ChangesDialogSkip = cDialogFld !== "({})" ? eval(cDialogFld) : {
chXP : false, // experience points
chAS : false, // ability scores
chHP : false, // hit points
Expand Down
49 changes: 25 additions & 24 deletions _functions/FunctionsSpells.js
Original file line number Diff line number Diff line change
Expand Up @@ -3250,12 +3250,6 @@ function AskUserSpellSheet() {
if (CurrentCasters.incl.indexOf(classesArray[i]) === -1 && CurrentCasters.excl.indexOf(classesArray[i]) === -1) CurrentCasters.incl.push(classesArray[i]);
}
}
//now see if anything has been defined for adding a glossary or not
if (CurrentCasters.glossary === undefined) CurrentCasters.glossary = false;
//now see if anything has been defined for putting a EM dash in empty fields or not
if (CurrentCasters.emptyFields === undefined) CurrentCasters.emptyFields = false;
//now see if anything has been defined for showing the full or applied version of cantrip/spell descriptions (i.e. never for full lists or manually added spells)
if (CurrentCasters.amendSpDescr === undefined) CurrentCasters.amendSpDescr = false;

//convert the incl and excl CurrentSpells arrays to their named counterparts
var exclNames = [];
Expand All @@ -3270,7 +3264,7 @@ function AskUserSpellSheet() {
//now add them to the dialog and let the user make a decision
spDias.sheetOrder.bExcL = exclNames;
spDias.sheetOrder.bIncL = inclNames;
spDias.sheetOrder.glossary = CurrentCasters.glossary;
spDias.sheetOrder.glossary = CurrentCasters.glossary ? true : false;
spDias.sheetOrder.dashEmptyFields = CurrentCasters.emptyFields ? false : true;
spDias.sheetOrder.amendSpellDescriptions = CurrentCasters.amendSpDescr || CurrentCasters.amendSpDescr === undefined ? true : false;
spDias.sheetOrder.allowSpellAdd = CurrentCasters.allowSpellAdd || CurrentCasters.allowSpellAdd === undefined ? true : false;
Expand Down Expand Up @@ -4859,29 +4853,36 @@ function HideSpellSheetElement(theTarget) {
// When changing the spellcasting ability of a manual header, save it to the remember field so that the element can be recreated when inserting/deleting rows (field blur)
// If this is a header linked to a CurrentSpells object, change its spellcasting ability and offer to re-generate the sheet
function SaveSpellcastingAbility() {
var base = event.target && event.target.name ? event.target.name : "";
if (!event.target) return;
var base = event.target.name;
var caster = What(base.replace("ability", "class"));
var selAbi = event.target.currentValueIndices;
if (caster && CurrentSpells[caster] && !CurrentSpells[caster].fixedDC) {
if (caster && CurrentSpells[caster] && !(CurrentSpells[caster].fixedDC && !CurrentSpells[caster].ability)) {
var spCast = CurrentSpells[caster];
if (selAbi) {
spCast.abilityBackup = spCast.ability;
spCast.ability = selAbi;
spCast.abilityToUse = getSpellcastingAbility(caster);
} else if (spCast.abilityBackup) {
if (!selAbi && spCast.abilityBackup) {
spCast.ability = spCast.abilityBackup;
delete spCast.abilityBackup;
spCast.abilityToUse = getSpellcastingAbility(caster);
PickDropdown(base, spCast.abilityToUse[0]);
} else if (selAbi && spCast.ability != selAbi) {
if (!spCast.abilityBackup) spCast.abilityBackup = spCast.ability;
spCast.ability = selAbi;
spCast.abilityToUse = getSpellcastingAbility(caster);
var origSpAbi = !isNaN(spCast.abilityBackup) ? AbilityScores.names[spCast.abilityBackup - 1] : spCast.abilityBackup.toLowerCase() === "race" ? "the same as the race" : "the same as a class, highest score if multiple eligible";
// Warn the user that to update the spellcasting ability requires regenerating the spells sheets
var redosheets = app.alert({
cMsg : "Please know that you can reset the spellcasting ability to its original (" + origSpAbi + ") by selecting the first (empty) option in the dropdown box." + (CurrentCasters.amendSpDescr ? "\n\nYou will need to regenerate the spell sheets if you want this change in spellcasting ability to be applied to the spells. If you have no spells that incorporate your spellcasting ability, than there is no reason the regenerate the spell sheets.\n\nDo you want to generate new spell sheets now?" : ""),
cTitle : "Spellcasting Ability Changed",
nIcon : 3,
nType : CurrentCasters.amendSpDescr ? 2 : 0 // 0: OK; 2: Yes-No
});
if (redosheets === 4) {
SetStringifieds("spells");
GenerateSpellSheet();
return;
}
}
// Warn the user that to update the spellcasting ability requires regenerating the spells sheets
var redosheets = app.alert({
cMsg : "Please know that you can reset the spellcasting ability to its original by selecting the first (empty) option in the dropdown box." + (CurrentCasters.amendSpDescr ? "\n\nYou will need to regenerate the spell sheets if you want this change in spellcasting ability to be applied to the spells. If you have no spells that incorporate your spellcasting ability, than there is no reason the regenerate the spell sheets.\n\nDo you want to generate new spell sheets now?" : ""),
cTitle : "Spellcasting Ability Changed",
nIcon : 3,
nType : CurrentCasters.amendSpDescr ? 2 : 0 // 0: OK; 2: Yes-No
});
if (redosheets === 4) GenerateSpellSheet();
SetStringifieds("spells");
tDoc.calculateNow();
} else if (!caster || !CurrentSpells[caster]) {
var prefix = base.substring(0, base.indexOf("spells"));
var SSfrontPrefix = What("Template.extras.SSfront").split(",")[1];
Expand Down Expand Up @@ -5518,7 +5519,7 @@ function getSpellcastingAbility(theCast) {
var curAbiScore = 0;
var spObj = CurrentSpells[theCast];
var casterArray = [];
var testFixedDC = false;
var testFixedDC = (/race|class/i).test(spObj.abilityBackup);
if (spObj && spObj.ability && !isNaN(spObj.ability)) {
spAbility = Number(spObj.ability);
} else if (spObj && spObj.ability == "class") {
Expand Down
3 changes: 2 additions & 1 deletion _variables/Lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ var CurrentProfs = { // Also change field defaultValue!
};

var thermoCount = [], thermoDur = {};
var calcStartSet = false, thermoStopSet = false, ChangesDialogSkip;
var calcStartSet = false, thermoStopSet = false;
var ChangesDialogSkip = {};
var IsSubclassException = {};
var IsNotReset = true;
var IsNotImport = true;
Expand Down
18 changes: 9 additions & 9 deletions _variables/ListsClasses.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,11 @@ var Base_ClassList = {
calcChanges : {
atkCalc : [
function (fields, v, output) {
if (v.isMeleeWeapon && classes.known.barbarian && classes.known.barbarian.level && (/\brage\b/i).test(v.WeaponText)) {
if (v.isMeleeWeapon && classes.known.barbarian && classes.known.barbarian.level && (/\brage\b/i).test(v.WeaponTextName)) {
output.extraDmg += classes.known.barbarian.level < 9 ? 2 : classes.known.barbarian.level < 16 ? 3 : 4;
}
},
"If I include the word 'Rage' in a melee weapon's name or description, the calculation will add my Rage's bonus damage to it."
"If I include the word 'Rage' in a melee weapon's name, the calculation will add my Rage's bonus damage to it."
]
}
},
Expand Down Expand Up @@ -1986,13 +1986,13 @@ var Base_ClassList = {
calcChanges : {
atkAdd : [
function (fields, v) {
if (v.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponText))) fields.Description += (fields.Description ? '; ' : '') + '+Cha mod necrotic damage (included above)';
if (v.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponTextName))) fields.Description += (fields.Description ? '; ' : '') + '+Cha mod necrotic damage (included above)';
},
"If I include the word 'Pact' in a melee or magic weapon's name or description, the calculation will add my Charisma modifier to its damage. However, it won't say in the damage box that this added damage is of the necrotic type, as it can only display a single damage type."
"If I include the word 'Pact' in a melee or magic weapon's name, the calculation will add my Charisma modifier to its damage. However, it won't say in the damage box that this added damage is of the necrotic type, as it can only display a single damage type."
],
atkCalc : [
function (fields, v, output) {
if (v.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponText))) output.extraDmg += What('Cha Mod');
if (v.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponTextName))) output.extraDmg += What('Cha Mod');
}, ""
]
},
Expand Down Expand Up @@ -2236,14 +2236,14 @@ var Base_ClassList = {
calcChanges : {
atkCalc : [
function (fields, v, output) {
if (v.theWea.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponText))) {
if (v.theWea.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponTextName))) {
v.pactWeapon = true;
}
}, ""
],
atkAdd : [
function (fields, v) {
if (v.pactWeapon || v.theWea.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponText))) {
if (v.pactWeapon || v.theWea.pactWeapon || ((v.isMeleeWeapon || v.theWea.isMagicWeapon || v.thisWeapon[1]) && (/\bpact\b/i).test(v.WeaponTextName))) {
v.pactWeapon = true;
fields.Proficiency = true;
if (!v.theWea.isMagicWeapon && !v.thisWeapon[1] && !(/counts as( a)? magical/i).test(fields.Description)) fields.Description += (fields.Description ? '; ' : '') + 'Counts as magical';
Expand Down Expand Up @@ -2828,11 +2828,11 @@ var Base_ClassSubList = {
calcChanges : {
atkCalc : [
function (fields, v, output) {
if (classes.known.paladin && classes.known.paladin.level > 2 && !v.isSpell && (/^(?=.*sacred)(?=.*weapon).*$/i).test(v.WeaponText)) {
if (classes.known.paladin && classes.known.paladin.level > 2 && !v.isSpell && (/^(?=.*sacred)(?=.*weapon).*$/i).test(v.WeaponTextName)) {
output.extraHit += What('Cha Mod');
};
},
"If I include the words 'Sacred Weapon' in the name or description of a weapon, it gets my Charisma modifier added to its To Hit."
"If I include the words 'Sacred Weapon' in the name of a weapon, it gets my Charisma modifier added to its To Hit."
]
},
spellcastingExtra : ["protection from evil and good", "sanctuary", "lesser restoration", "zone of truth", "beacon of hope", "dispel magic", "freedom of movement", "guardian of faith", "commune", "flame strike"]
Expand Down
6 changes: 6 additions & 0 deletions _variables/ListsGear.js
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,12 @@ var Base_GearList = {
amount : "",
weight : 1
},
"animal feed (1 day)" : {
infoname : "Animal feed (1 day) [5 cp]",
name : "Animal feed, days of",
amount : 1,
weight : 10
},
"arrows (20)" : {
infoname : "Arrows (20) [1 gp]",
name : "Arrows",
Expand Down
Loading

0 comments on commit a3062a6

Please sign in to comment.