Skip to content

Commit

Permalink
v13.1.14 armorAdd/weaponsAdd revamp
Browse files Browse the repository at this point in the history
• Changed how attack entries are recalculated (e.g. on level change) so that more manual changes are preserved.
• Fixed issues with the interaction between attack entries and the cleric 8th-level domain features, Divine Strike, Potent Spellcasting, and Blessed Strikes.
• The `calcChanges` for Potent Spellcasting can now be referenced from the built-in variables by using
` GenericClassFeatures["potent spellcasting"].calcChanges`. This allows future changes to be more easily implemented without having to go through add-on scripts.
• Changed how recalculation of attack entries work (after a level-up for example) to improve manual changes not being overwritten. However, when a `calcChanges.atkAdd` is added or removed, all attack entries will still be reset to purely what the animation sets.
• Fixed issue with certain attack-altering features not working as intended when the required wording was added to an attack without anything in the description field (e.g. “Rage Unarmed Strike” didn’t add Rage bonus damage).
• Improved detection (slightly) in the adventuring gear section so that the correct weight is automatically filled out.
• Fixed issue with the name in the drop-down box of feats and magic items not updating after selecting one of its variants, i.e. one of the `choices` options (thanks Ratatoskr/Reading Toskr for reporting).
• Stuff selected through `chooseGear` is now added to the relevant drop-down box(es) (feature request MFR-149).
• `weaponOptions `and `armorOptions` have a new optional attribute, `selectNow`. Setting this to `true` will cause the weapon or armour to be immediately added to the sheet. Thus, a separate `weaponsAdd` or `armorAdd` attribute is no longer necessary.
• Edited `weaponsAdd` and `armorAdd` attributes to also allow adding options to the relevant drop-down menus.
• Companions now add their attack options to the drop-down boxes of their attack section.
• Fixed Eldritch Spear and Repelling Blast not affecting Eldritch Blast if it wasn’t selected as a Warlock cantrip (MBUG-129).
• Changed the check for off-hand weapons to also work for ranged weapons (i.e. add the text “off-hand” to a ranged weapon’s name will stop the ability modifier from being added to damage).
  • Loading branch information
morepurplemorebetter committed Jun 14, 2024
1 parent 9eced81 commit a455175
Show file tree
Hide file tree
Showing 15 changed files with 689 additions and 340 deletions.
67 changes: 47 additions & 20 deletions _functions/Functions1.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ function ResetAll(GoOn, noTempl, deleteImports) {
CurrentUpdates = {types : []};
GetStringifieds(keepImports);

if (keepImports) { // remove the imports and reset the sources
if (keepImports) { // keep the imports and reset the sources
SetStringifieds("sources");
SetStringifieds("scriptfiles");
Value("User Script", userScriptString);
Expand Down Expand Up @@ -2939,7 +2939,9 @@ function ReCalcWeapons(justProfs, force) {
};
};

function SetWeaponsdropdown(forceTooltips) {
// Fill the attack drop-down boxes.
// `aCompPrefixes` - Optional - Pass (array of) prefix of companion pages to update drop-down boxes. Omit to update every drop-down.
function SetWeaponsdropdown(forceTooltips, aCompPrefixes) {
var tempString = "Type in the name of the attack (or select it from the drop-down menu) and all its attributes will be filled out automatically, provided that its a recognized attack.";
tempString += "\n\n" + toUni("Magic bonus") + '\nAny magical bonus you type in this field is added to both the to hit and damage (e.g. type " +2Longsword").';
tempString += "\n\n" + toUni("Off-hand weapons") + '\nIf the name or description fields include the word "off-hand", "secondary", "spell", or "cantrip", the ability modifier will only be added to the to hit bonus, and not to the damage.';
Expand Down Expand Up @@ -2988,47 +2990,67 @@ function SetWeaponsdropdown(forceTooltips) {
}
};

if (CurrentVars.extraWeaponsDisplay) {
for (var key in CurrentVars.extraWeaponsDisplay) {
weaponlists.startlist.push(CurrentVars.extraWeaponsDisplay[key]);
}
};

// make the definitive list of weapons for the dropdown box
var setweapons = [];
var addWeaList = function (weArr, addFirst, noSort, addAtStart) {
var addWeaList = function (setweapons, weArr, addFirst, noSort, addAtStart) {
if (!weArr) return setweapons;
if (!noSort) weArr.sort();
if (addFirst) weArr.unshift(addFirst);
if (weArr.length) {
weArr.unshift("");
setweapons = !addAtStart ? setweapons.concat(weArr) : weArr.concat(setweapons);
}
return setweapons;
};
addWeaList(weaponlists.melee.concat(weaponlists.ranged), "Unarmed Strike"); // add the natural weapons
addWeaList(weaponlists.improvised, "Improvised Weapon"); // add the improvised weapons
addWeaList(weaponlists.spell, "Spell Attack"); // add the spells/cantrips
addWeaList(weaponlists.endlist, false, true); // add the endlist weapons
setweapons = addWeaList(setweapons, weaponlists.melee.concat(weaponlists.ranged), "Unarmed Strike"); // add the natural weapons
setweapons = addWeaList(setweapons, weaponlists.improvised, "Improvised Weapon"); // add the improvised weapons
setweapons = addWeaList(setweapons, weaponlists.spell, "Spell Attack"); // add the spells/cantrips

// now add any lists that are not preset
otherLists.sort();
for (var i = 0; i < otherLists.length; i++) addWeaList(weaponlists[otherLists[i]]);

setweapons = addWeaList(setweapons, weaponlists.endlist, false, true); // add the endlist weapons

var listToSource = setweapons.toSource();

// first set the companion sheets attack dropdowns
var AScompA = What("Template.extras.AScomp").split(",");
var listToSource = setweapons.toSource();
if (aCompPrefixes && !isArray(aCompPrefixes)) aCompPrefixes = [aCompPrefixes];
for (var i = 0; i < AScompA.length; i++) {
var prefix = AScompA[i];
if (aCompPrefixes && aCompPrefixes.indexOf(prefix) === -1) continue; // only do selected
// Create new list of attacks, at the start
var compAtkList = !CurrentCompRace[prefix] || !CurrentCompRace[prefix].attacks ? false : CurrentCompRace[prefix].attacks.map(function (atk) { return atk.name; })
var setweaponsComp = addWeaList(setweapons, compAtkList, false, true, true);
var listToSourceComp = !compAtkList ? listToSource : setweaponsComp.toSource();
// Apply list to drop-downs
for (var c = 1; c <= 3; c++) {
var theFld = prefix + "Comp.Use.Attack." + c + ".Weapon Selection";
var theFldSuNm = prefix + "Comp.Use.Attack." + c + ".Proficiency";
if (tDoc.getField(theFldSuNm).submitName === listToSource) {
if (tDoc.getField(theFldSuNm).submitName === listToSourceComp) {
if (forceTooltips) AddTooltip(theFld, tempString);
continue; // no changes, so no reason to set this field
}
tDoc.getField(theFldSuNm).submitName = listToSource;
tDoc.getField(theFldSuNm).submitName = listToSourceComp;
var theFldVal = What(theFld);
IsNotWeaponMenu = false;
tDoc.getField(theFld).setItems(setweapons);
tDoc.getField(theFld).setItems(setweaponsComp);
IsNotWeaponMenu = true;
if (theFldVal !== What(theFld)) Value(theFld, theFldVal, tempString);
};
}

if (aCompPrefixes) return; // Only doing a specific companion page, so stop here

// now add the special weapons added by features, as we only want those on the first page
addWeaList(weaponlists.startlist, false, false, true);
setweapons = addWeaList(setweapons, weaponlists.startlist, false, false, true);
listToSource = setweapons.toSource();

// lastly set this array for the attack dropdowns on the first page
Expand Down Expand Up @@ -3075,6 +3097,12 @@ function SetArmordropdown(forceTooltips) {
}
};

if (CurrentVars.extraArmourDisplay) {
for (var key in CurrentVars.extraArmourDisplay) {
aLists.startlist.push(CurrentVars.extraArmourDisplay[key]);
}
};

// now create the final array element to set to the armour field
var setarmours = [];

Expand Down Expand Up @@ -3215,7 +3243,7 @@ function ParseGear(input) {
if (!input) return false;
var foundLen = 0, testLen = 0;
var result = false;
var tempString = removeDiacritics(input.toLowerCase());
var tempString = clean(input.toLowerCase(), false, true);
var tempStrLen = tempString.length;

//see if it is a magic item
Expand Down Expand Up @@ -4941,7 +4969,6 @@ function ApplyFeat(input, FldNmbr) {
var ArrayNmbr = FldNmbr - 1;
var oldFeat = CurrentFeats.known[ArrayNmbr];
var oldFeatVar = CurrentFeats.choices[ArrayNmbr];
var setFieldValueTo;
var failedChoice = false;

var doNotCommit = function(toSetVal) {
Expand Down Expand Up @@ -4974,7 +5001,7 @@ function ApplyFeat(input, FldNmbr) {
if (!selectFeatVar) selectFeatVar = AskUserOptions("Select " + aFeat.name + " Type", "The '" + aFeat.name + "' feat has several forms. Select which form you want to add to the sheet at this time.\n\nYou can change the selected form with the little square button in the feat line that this feat is in.", parseResult[2], "radio", true);
newFeatVar = selectFeatVar.toLowerCase();
aFeatVar = aFeat[newFeatVar];
setFieldValueTo = aFeatVar.name ? aFeatVar.name : aFeat.name + " [" + selectFeatVar + "]";
event.target.setValPrepared = aFeatVar.name ? aFeatVar.name : aFeat.name + " [" + selectFeatVar + "]";
}
} else if (!IsNotImport) {
failedChoice = true;
Expand All @@ -5001,7 +5028,7 @@ function ApplyFeat(input, FldNmbr) {
}

if (oldFeat === newFeat && oldFeatVar === newFeatVar) {
if (setFieldValueTo) event.target.setVal = setFieldValueTo;
if (event.target.setValPrepared) event.target.setVal = event.target.setValPrepared;
return; // No changes were made
}

Expand All @@ -5015,7 +5042,7 @@ function ApplyFeat(input, FldNmbr) {
aFeatVar = "";
} else {
var theFeat = {
name : aFeatVar.name ? aFeatVar.name : setFieldValueTo ? setFieldValueTo : input
name : aFeatVar.name ? aFeatVar.name : event.target.setValPrepared ? event.target.setValPrepared : input
}
var FeatAttr = ["source", "description", "descriptionFull", "calculate", "prerequisite", "prereqeval"];
for (var a = 0; a < FeatAttr.length; a++) {
Expand Down Expand Up @@ -5089,9 +5116,6 @@ function ApplyFeat(input, FldNmbr) {
};
};

// if a feat variant was chosen, make sure this field will show that selection, now that it can't be cancelled anymore due to not meeting a prerequisite
if (setFieldValueTo) event.target.setVal = setFieldValueTo;

calcStop(); // Now stop the calculations

// Remove previous feat at the same field
Expand Down Expand Up @@ -5160,6 +5184,9 @@ function ApplyFeat(input, FldNmbr) {
);
}

// if a feat variant was chosen, make sure this field will show that selection (important that this is the last step)
if (event.target.setValPrepared) event.target.setVal = event.target.setValPrepared;

thermoM(thermoTxt, true); // Stop progress bar
};

Expand Down
58 changes: 31 additions & 27 deletions _functions/Functions2.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ function ApplyCompRace(newRace, prefix, sCompType) {
thermoM(3/10); //increment the progress dialog's progress

//add any weapons the creature possesses
SetWeaponsdropdown(false, prefix);
for (var a = 0; a < aCrea.attacks.length; a++) {
AddWeapon(aCrea.attacks[a].name, undefined, prefix);
}
Expand Down Expand Up @@ -4619,13 +4620,12 @@ function UpdateDropdown(type, weapon) {
if (minVer || !IsNotUserScript) return;
IsSetDropDowns = true;
type = type ? type.toLowerCase() : "all";
var notAll, forceTT = false;
var forceTT = false;
calcStop();
switch (type) {
case "tooltips" :
forceTT = true;
case "resources" :
notAll = true;
case "all" :
SetRacesdropdown(forceTT);
SetBackgrounddropdown(forceTT);
Expand All @@ -4636,10 +4636,8 @@ function UpdateDropdown(type, weapon) {
SetWildshapeDropdown(forceTT);
SetArmordropdown(forceTT);
SetAmmosdropdown(forceTT);
if (notAll) {
SetWeaponsdropdown(forceTT);
break;
}
SetWeaponsdropdown(forceTT);
break;
case "attack" :
case "attacks" :
case "weapon" :
Expand Down Expand Up @@ -4693,9 +4691,9 @@ function UpdateDropdown(type, weapon) {
case "creatures" :
case "wildshape" :
case "wildshapes" :
SetWildshapeDropdown();
case "companiononly" :
SetCompDropdown();
if (type !== "companiononly") SetWildshapeDropdown();
break;
};
IsSetDropDowns = false;
Expand Down Expand Up @@ -5668,19 +5666,21 @@ function ApplyWeapon(inputText, fldName, isReCalc, onlyProf, forceRedo) {
for (var attr in aWea) theWea[attr] = aWea[attr];

thermoTxt = thermoM("Applying the weapon's features...", false); //change the progress dialog text
var curDescr = What(fldBase + "Description");
var curRange = What(fldBase + "Range");
fields.Description = theWea.description ? theWea.description : ""; //add description
fields.Description_Tooltip = theWea.tooltip ? theWea.tooltip : ""; //add the tooltip for the description
fields.Range = theWea.range; //add range
fields.Damage_Type = theWea.damage[2]; //add Damage Type

//add proficiency checkmark
fields.Proficiency = !QI ? true : isProficientWithWeapon(WeaponName, theWea);

//add Weight
fields.Weight = isReCalc ? What(fldBaseBT + "Weight") :
theWea.weight ? theWea.weight : "";

//add Damage Die
fields.Damage_Die = theWea.damage[0] + (parseFloat(theWea.damage[1]) ? "d" + theWea.damage[1] : "");
var weaponDamageDie = theWea.damage[0] + (parseFloat(theWea.damage[1]) ? "d" + theWea.damage[1] : "");
fields.Damage_Die = weaponDamageDie;

//add To Hit Bonus
fields.To_Hit_Bonus = isReCalc ? What(fldBaseBT + "To Hit Bonus") :
Expand All @@ -5691,14 +5691,9 @@ function ApplyWeapon(inputText, fldName, isReCalc, onlyProf, forceRedo) {
fields.Damage_Bonus = isReCalc ? What(fldBaseBT + "Damage Bonus") :
theWea.modifiers && theWea.modifiers[1] ? theWea.modifiers[1] : 0;

//add proficiency checkmark
fields.Proficiency = !QI ? true : isProficientWithWeapon(WeaponName, theWea);

//add mod
var StrDex = What(QI ? "Str" : prefix + "Comp.Use.Ability.Str.Score") < What(QI ? "Dex" : prefix + "Comp.Use.Ability.Dex.Score") ? 2 : 1;
fields.Mod = isReCalc && !theWea.ability ? tDoc.getField(fldBase + "Mod").currentValueIndices :
(/finesse/i).test(theWea.description) ? StrDex : theWea.ability;

var weaponMod = /finesse/i.test(theWea.description) ? StrDex : theWea.ability;
//change mod if this is concerning a spell/cantrip
var forceUseSpellcastingMod = theWea.useSpellcastingAbility === undefined ? false : theWea.useSpellcastingAbility ? "y" : "n";
if ((thisWeapon[3] || forceUseSpellcastingMod == "y") && forceUseSpellcastingMod != "n") {
Expand All @@ -5719,10 +5714,11 @@ function ApplyWeapon(inputText, fldName, isReCalc, onlyProf, forceRedo) {
if (!abiArr[i] || abiDone.indexOf(abiArr[i]) !== -1) continue;
abiDone.push(abiArr[i]);
var thisMod = What(AbilityScores.abbreviations[abiArr[i] - 1]);
if (thisMod > Math.max.apply(Math, abiModArr)) fields.Mod = abiArr[i];
if (thisMod > Math.max.apply(Math, abiModArr)) weaponMod = abiArr[i];
abiModArr.push(thisMod);
}
}
fields.Mod = weaponMod;

if (theWea.ammo) fields.Ammo = theWea.ammo; //add ammo

Expand Down Expand Up @@ -5777,10 +5773,20 @@ function ApplyWeapon(inputText, fldName, isReCalc, onlyProf, forceRedo) {
}
}
};
// if this is a field recalculation and no custom eval changed the description or range, just use the one from the field so that manual changes are preserved
// if this is a field recalculation and no custom eval changed specific parts, just use the one from the field so that manual changes are preserved
if (isReCalc && !forceRedo) {
if (fields.Description === theWea.description) fields.Description = curDescr;
if (fields.Range === theWea.range) fields.Range = curRange;
if (fields.Description === theWea.description) {
fields.Description = What(fldBase + "Description"); }
if (fields.Range === theWea.range) {
fields.Range = What(fldBase + "Range"); }
if (fields.Damage_Type === theWea.damage[2]) {
fields.Damage_Type = What(fldBase + "Damage Type"); }
if (fields.Range === theWea.range) {
fields.Range = What(fldBase + "Range"); }
if (fields.Damage_Die === weaponDamageDie) {
fields.Damage_Die = What(fldBaseBT + "Damage Die"); }
if (fields.Mod === weaponMod) {
fields.Mod = tDoc.getField(fldBase + "Mod").currentValueIndices; }
}
};

Expand Down Expand Up @@ -5863,7 +5869,8 @@ function CalcAttackDmgHit(fldName) {
var thisWeapon = QI ? CurrentWeapons.known[ArrayNmbr] : CurrentWeapons.compKnown[prefix][ArrayNmbr];
var WeaponName = thisWeapon[0];
var aWea = QI || isNaN(parseFloat(WeaponName)) ? WeaponsList[WeaponName] : !QI && !isNaN(parseFloat(WeaponName)) && CurrentCompRace[prefix] && CurrentCompRace[prefix].attacks ? CurrentCompRace[prefix].attacks[WeaponName] : false;
var WeaponText = QI ? CurrentWeapons.field[ArrayNmbr] : CurrentWeapons.compField[prefix][ArrayNmbr];
var WeaponTextName = QI ? CurrentWeapons.field[ArrayNmbr] : CurrentWeapons.compField[prefix][ArrayNmbr];
var WeaponText = WeaponText + (fields.Description ? " " + fields.Description : "");
var theWea = {};
if (aWea && aWea.baseWeapon && WeaponsList[aWea.baseWeapon]) {
for (var attr in WeaponsList[aWea.baseWeapon]) theWea[attr] = WeaponsList[aWea.baseWeapon][attr];
Expand All @@ -5872,16 +5879,13 @@ function CalcAttackDmgHit(fldName) {
var fixedCaster = theWea.useSpellMod && CurrentSpells[theWea.useSpellMod] ? CurrentSpells[theWea.useSpellMod] : false;
var aWeaNoAbi = theWea.ability === 0 || (fixedCaster && fixedCaster.fixedDC && fixedCaster.abilityToUse && !fixedCaster.abilityToUse[0]);

if (!WeaponText || ((/^(| |empty)$/).test(fields.Mod) && !aWeaNoAbi)) {
if (!WeaponTextName || ((/^(| |empty)$/).test(fields.Mod) && !aWeaNoAbi)) {
Value(fldBase + "Damage", "");
Value(fldBase + "To Hit", "");
if (QI) CurrentWeapons.offHands[ArrayNmbr] = false;
return;
};

// only add the description part now, so we don't test against it above
if (fields.Description) WeaponText += " " + fields.Description;

// get the damage bonuses from the selected modifier, magic, and the blueText field
var output = {
prof : !fields.Proficiency ? 0 : (QI ? (tDoc.getField("Proficiency Bonus Dice").isBoxChecked(0) ? 0 : Number(How("Proficiency Bonus"))) : (tDoc.getField(prefix + "BlueText.Comp.Use.Proficiency Bonus Dice").isBoxChecked(0) ? 0 : What(prefix + "Comp.Use.Proficiency Bonus"))),
Expand All @@ -5907,7 +5911,7 @@ function CalcAttackDmgHit(fldName) {
var isThrownWeapon = isWeapon && /\bthrown\b/i.test(fields.Description) && /\d ?(ft|m)\.?($|[^)])/i.test(fields.Range);

// see if this is a off-hand attack and the modToDmg shouldn't be use
var isOffHand = isMeleeWeapon && (/^(?!.*(spell|cantrip))(?=.*(off.{0,3}hand|secondary)).*$/i).test(WeaponText);
var isOffHand = /^(?!.*(spell|cantrip))(?=.*(off.{0,3}hand|secondary)).*$/i.test(WeaponText);
if (isOffHand) output.modToDmg = output.mod < 0;
// Add the off-hand attack action (only for attacks on the first page)
if (QI && CurrentWeapons.offHands[ArrayNmbr] !== isOffHand) {
Expand All @@ -5920,7 +5924,7 @@ function CalcAttackDmgHit(fldName) {

var gatherVars = {
WeaponText : WeaponText,
WeaponTextName : WeaponText.replace(" " + fields.Description, ""),
WeaponTextName : WeaponTextName,
isDC : isDC,
isSpell : isSpell,
isWeapon : isWeapon,
Expand Down
Loading

0 comments on commit a455175

Please sign in to comment.