mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-31 04:02:29 +00:00
633 lines
19 KiB
TypeScript
633 lines
19 KiB
TypeScript
/* global getAttrs, setAttrs, getSectionIDs, generateRowID, on, removeRepeatingRow, _, getTranslationByKey */
|
|
|
|
/* Calculations */
|
|
const calculateSaves = () => {
|
|
getAttrs(
|
|
[
|
|
...attributes.map((attr) => `${attr}_mod`),
|
|
"level",
|
|
"luck_save",
|
|
"save_physical",
|
|
"save_mental",
|
|
"save_evasion",
|
|
"save_luck",
|
|
],
|
|
(v) => {
|
|
const base = 16 - (parseInt(v.level) || 1);
|
|
const setting: { [key: string]: any } = {
|
|
save_physical:
|
|
base -
|
|
(Math.max(parseInt(v.strength_mod), parseInt(v.constitution_mod)) ||
|
|
0),
|
|
save_mental:
|
|
base -
|
|
(Math.max(parseInt(v.charisma_mod), parseInt(v.wisdom_mod)) || 0),
|
|
save_evasion:
|
|
base -
|
|
(Math.max(parseInt(v.intelligence_mod), parseInt(v.dexterity_mod)) ||
|
|
0),
|
|
};
|
|
if (v.luck_save === "1") setting.save_luck = base;
|
|
mySetAttrs(setting, v);
|
|
}
|
|
);
|
|
};
|
|
|
|
const calculateEffort = () => {
|
|
getSectionIDs("repeating_psychic-skills", (idArray) => {
|
|
getAttrs(
|
|
[
|
|
...effortAttributes,
|
|
"psionics_total_effort",
|
|
...idArray.map((id) => `repeating_psychic-skills_${id}_skill`),
|
|
],
|
|
(v) => {
|
|
const attrBonus =
|
|
Math.max(parseInt(v.wisdom_mod), parseInt(v.constitution_mod)) || 0,
|
|
skillBonus = Math.max(
|
|
...skills.psionic.map((x) => parseInt(v[`skill_${x}`]) || 0),
|
|
...idArray.map(
|
|
(id) => parseInt(v[`repeating_psychic-skills_${id}_skill`]) || 0
|
|
)
|
|
);
|
|
const psionics_total_effort =
|
|
Math.max(1 + attrBonus + skillBonus, 1) +
|
|
parseInt(v.psionics_extra_effort) -
|
|
parseInt(v.psionics_committed_effort_current) -
|
|
parseInt(v.psionics_committed_effort_scene) -
|
|
parseInt(v.psionics_committed_effort_day) || 0;
|
|
mySetAttrs(
|
|
{
|
|
psionics_total_effort,
|
|
},
|
|
v
|
|
);
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
const calculateMagicEffort = () => {
|
|
getAttrs(
|
|
[
|
|
"magic_total_effort",
|
|
"magic_committed_effort_current",
|
|
"magic_committed_effort_scene",
|
|
"magic_committed_effort_day",
|
|
"magic_uncommitted_effort",
|
|
],
|
|
(v) => {
|
|
const magic_uncommitted_effort =
|
|
parseInt(v.magic_total_effort) -
|
|
(parseInt(v.magic_committed_effort_current) +
|
|
parseInt(v.magic_committed_effort_scene) +
|
|
parseInt(v.magic_committed_effort_day)) || 0;
|
|
mySetAttrs(
|
|
{
|
|
magic_uncommitted_effort,
|
|
},
|
|
v
|
|
);
|
|
}
|
|
);
|
|
};
|
|
|
|
const calculateProcessing = () => {
|
|
getSectionIDs("repeating_processing-nodes", (idArray) => {
|
|
const sourceAttrs = [
|
|
...idArray.map((id) => `repeating_processing-nodes_${id}_node_value`),
|
|
...idArray.map((id) => `repeating_processing-nodes_${id}_node_connected`),
|
|
"wisdom_mod",
|
|
"intelligence_mod",
|
|
"ai_extra_processing",
|
|
"ai_committed_processing_current",
|
|
"ai_committed_processing_scene",
|
|
"ai_committed_processing_day",
|
|
"ai_total_processing",
|
|
];
|
|
getAttrs(sourceAttrs, (v) => {
|
|
const maxProcessing =
|
|
(1 + Math.max(parseInt(v.wisdom_mod), parseInt(v.intelligence_mod)) ||
|
|
0) +
|
|
Math.max(
|
|
...idArray
|
|
.filter(
|
|
(id) =>
|
|
v[`repeating_processing-nodes_${id}_node_connected`] === "1"
|
|
)
|
|
.map(
|
|
(id) =>
|
|
parseInt(v[`repeating_processing-nodes_${id}_node_value`]) || 0
|
|
),
|
|
0
|
|
);
|
|
const ai_total_processing =
|
|
maxProcessing +
|
|
parseInt(v.ai_extra_processing) -
|
|
parseInt(v.ai_committed_processing_current) -
|
|
parseInt(v.ai_committed_processing_scene) -
|
|
parseInt(v.ai_committed_processing_day) || 0;
|
|
mySetAttrs(
|
|
{
|
|
ai_total_processing,
|
|
},
|
|
v
|
|
);
|
|
});
|
|
});
|
|
};
|
|
|
|
const calculateAC = () => {
|
|
getSectionIDs("repeating_armor", (idArray) => {
|
|
const sourceAttrs = [
|
|
...idArray.map((id) => `repeating_armor_${id}_armor_ac`),
|
|
...idArray.map((id) => `repeating_armor_${id}_armor_ranged_ac`),
|
|
...idArray.map((id) => `repeating_armor_${id}_armor_melee_ac`),
|
|
...idArray.map((id) => `repeating_armor_${id}_armor_active`),
|
|
...idArray.map((id) => `repeating_armor_${id}_armor_type`),
|
|
"system",
|
|
"npc",
|
|
"AC",
|
|
"innate_ac",
|
|
"dexterity_mod",
|
|
];
|
|
getAttrs(sourceAttrs, (v) => {
|
|
if (v.npc === "1") return;
|
|
|
|
const activeIDs = idArray.filter(
|
|
(id) => v[`repeating_armor_${id}_armor_active`] === "1"
|
|
);
|
|
const stackingType = (type: string) =>
|
|
type === "SHIELD" || type === "ACCESSORY";
|
|
|
|
const calculateArmorClass = (acType: string): number => {
|
|
return Math.max(
|
|
parseInt(v.innate_ac) || 0,
|
|
...activeIDs
|
|
.filter(
|
|
(id) => !stackingType(v[`repeating_armor_${id}_armor_type`])
|
|
)
|
|
.map((id) => parseInt(v[`repeating_armor_${id}_${acType}_ac`]) || 0)
|
|
);
|
|
};
|
|
|
|
const calculateStackedArmor = (acType: string): number => {
|
|
return Math.max(
|
|
0,
|
|
...activeIDs
|
|
.filter((id) => stackingType(v[`repeating_armor_${id}_armor_type`]))
|
|
.map((id) => parseInt(v[`repeating_armor_${id}_${acType}_ac`]) || 0)
|
|
);
|
|
};
|
|
|
|
const armor = calculateArmorClass("armor");
|
|
const shieldAC = calculateStackedArmor("armor");
|
|
|
|
const withShield = armor >= shieldAC ? armor + 1 : shieldAC;
|
|
const ac = shieldAC > 0 ? withShield : armor;
|
|
const AC = ac + (parseInt(v.dexterity_mod) || 0);
|
|
|
|
let update: { [key: string]: number } = { AC };
|
|
|
|
if (v.system === "cwn") {
|
|
const rangedBase = calculateArmorClass("armor_ranged");
|
|
const rangedStack = calculateStackedArmor("armor_ranged");
|
|
const armor_ranged_ac = rangedBase + rangedStack;
|
|
|
|
const meleeBase = calculateArmorClass("armor_melee");
|
|
const meleeStack = calculateStackedArmor("armor_melee");
|
|
const armor_melee_ac = meleeBase + meleeStack;
|
|
|
|
update = {
|
|
...update,
|
|
armor_ranged_ac,
|
|
armor_melee_ac,
|
|
};
|
|
}
|
|
|
|
mySetAttrs(update, v);
|
|
});
|
|
});
|
|
};
|
|
|
|
const calculateMaxStrain = () => {
|
|
getAttrs(["constitution", "strain_max"], (v) => {
|
|
mySetAttrs(
|
|
{
|
|
strain_max: parseInt(v.constitution) || 0,
|
|
},
|
|
v
|
|
);
|
|
});
|
|
};
|
|
|
|
const calculatePermanentStrain = () => {
|
|
getAttrs(
|
|
["cyberware_strain_total", "strain_permanent_extra", "strain_permanent"],
|
|
(v) => {
|
|
const permStrain =
|
|
parseInt(v.cyberware_strain_total) +
|
|
parseInt(v.strain_permanent_extra) || 0;
|
|
mySetAttrs(
|
|
{
|
|
strain_permanent: permStrain,
|
|
},
|
|
v
|
|
);
|
|
}
|
|
);
|
|
};
|
|
|
|
const calculateCyberwareStrain = () => {
|
|
//Loop through the cyberware and add up the strain.
|
|
getSectionIDs("repeating_cyberware", (idArray) => {
|
|
const sourceAttrs = [
|
|
...idArray.map((id) => `repeating_cyberware_${id}_cyberware_strain`),
|
|
"cyberware_strain_total",
|
|
];
|
|
getAttrs(sourceAttrs, (v) => {
|
|
const cyberwareStrain = idArray.reduce((m, id) => {
|
|
m += parseInt(v[`repeating_cyberware_${id}_cyberware_strain`]) || 0;
|
|
return m;
|
|
}, 0);
|
|
mySetAttrs(
|
|
{
|
|
cyberware_strain_total: cyberwareStrain,
|
|
},
|
|
v
|
|
);
|
|
});
|
|
});
|
|
};
|
|
|
|
const calculateStrain = () => {
|
|
//Add up all the strain
|
|
getAttrs(
|
|
[
|
|
"strain_permanent",
|
|
"strain_extra",
|
|
"strain",
|
|
"strain_max",
|
|
"strain_above_max",
|
|
],
|
|
(v) => {
|
|
const strain =
|
|
(parseInt(v.strain_permanent) || 0) + (parseInt(v.strain_extra) || 0);
|
|
const strain_above_max = strain > parseInt(v.strain_max) ? 1 : 0;
|
|
mySetAttrs(
|
|
{
|
|
strain: strain,
|
|
strain_above_max: strain_above_max,
|
|
},
|
|
v
|
|
);
|
|
}
|
|
);
|
|
};
|
|
|
|
const calculateAttr = (attr: string) => {
|
|
getAttrs([attr, `${attr}_base`, `${attr}_boosts`], (v) => {
|
|
const setting = {
|
|
[`${attr}`]: `${
|
|
(parseInt(v[`${attr}_base`]) || 10) +
|
|
(parseInt(v[`${attr}_boosts`]) || 0)
|
|
}`,
|
|
};
|
|
mySetAttrs(setting, v, null, () => {
|
|
calculateMod(attr);
|
|
});
|
|
});
|
|
};
|
|
|
|
const calculateMod = (attr: string) => {
|
|
getAttrs([attr, `${attr}_bonus`, `${attr}_mod`], (v) => {
|
|
const mod = ((value) => {
|
|
if (value >= 18) return 2;
|
|
else if (value >= 14) return 1;
|
|
else if (value >= 8) return 0;
|
|
else if (value >= 4) return -1;
|
|
else return -2;
|
|
})(parseInt(v[attr]) || 10);
|
|
|
|
const setting = {
|
|
[`${attr}_mod`]: `${mod + (parseInt(v[`${attr}_bonus`]) || 0)}`,
|
|
};
|
|
|
|
mySetAttrs(setting, v, null, () => {
|
|
calculateSaves();
|
|
generateWeaponDisplay();
|
|
calculateStrDexMod();
|
|
if (attr === "dexterity") calculateAC();
|
|
});
|
|
});
|
|
};
|
|
const calculateStrDexMod = () => {
|
|
getAttrs(["str_dex_mod", "strength_mod", "dexterity_mod"], (v) => {
|
|
const str_dex_mod = Math.max(
|
|
parseInt(v.strength_mod) || 0,
|
|
parseInt(v.dexterity_mod) || 0
|
|
);
|
|
mySetAttrs(
|
|
{
|
|
str_dex_mod,
|
|
},
|
|
v
|
|
);
|
|
});
|
|
};
|
|
|
|
const calculateShellAttrs = () => {
|
|
const physicalAttrs = ["strength", "dexterity", "constitution"];
|
|
getSectionIDs("repeating_shells", (idArray) => {
|
|
const sourceAttrs = [
|
|
...idArray.map((id) => `repeating_shells_${id}_shell_active`),
|
|
...idArray.map((id) => `repeating_shells_${id}_shell_strength`),
|
|
...idArray.map((id) => `repeating_shells_${id}_shell_dexterity`),
|
|
...idArray.map((id) => `repeating_shells_${id}_shell_constitution`),
|
|
...physicalAttrs,
|
|
"setting_transhuman_enable",
|
|
"setting_ai_enable",
|
|
];
|
|
getAttrs(sourceAttrs, (v) => {
|
|
if (
|
|
v.setting_transhuman_enable === "transhuman" ||
|
|
v.setting_ai_enable === "ai"
|
|
) {
|
|
let attributes: { [key: string]: string } = {};
|
|
physicalAttrs.forEach(
|
|
(attr) =>
|
|
(attributes[attr] =
|
|
idArray
|
|
.filter(
|
|
(id) => v[`repeating_shells_${id}_shell_active`] === "1"
|
|
)
|
|
.map((id) => v[`repeating_shells_${id}_shell_${attr}`])[0] ||
|
|
"None")
|
|
);
|
|
mySetAttrs(attributes, v, null, () => {
|
|
physicalAttrs
|
|
.filter((attr) => attributes[attr] !== "None")
|
|
.forEach((attr) => calculateMod(attr));
|
|
});
|
|
} else {
|
|
physicalAttrs.forEach((attr) => calculateAttr(attr));
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
const calculateNextLevelXP = () => {
|
|
const xp = [0, 3, 6, 12, 18, 27, 39, 54, 72, 93];
|
|
getAttrs(["level", "setting_xp_scheme"], (v) => {
|
|
const level = parseInt(v.level);
|
|
if (v.setting_xp_scheme === "xp") {
|
|
if (level < 10) {
|
|
setAttrs({
|
|
xp_next: xp[level],
|
|
});
|
|
} else {
|
|
setAttrs({
|
|
xp_next: 93 + (level - 9) * 24,
|
|
});
|
|
}
|
|
} else if (v.setting_xp_scheme === "money") {
|
|
setAttrs({
|
|
xp_next: 2500 * (2 * level),
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
const calculateGearReadiedStowed = () => {
|
|
const doCalc = (
|
|
gearIDs: string[],
|
|
weaponIDs: string[],
|
|
armorIDs: string[]
|
|
) => {
|
|
const attrs = [
|
|
...gearIDs.map((id) => `repeating_gear_${id}_gear_amount`),
|
|
...gearIDs.map((id) => `repeating_gear_${id}_gear_encumbrance`),
|
|
...gearIDs.map((id) => `repeating_gear_${id}_gear_status`),
|
|
...gearIDs.map((id) => `repeating_gear_${id}_gear_bundled`),
|
|
...armorIDs.map((id) => `repeating_armor_${id}_armor_encumbrance`),
|
|
...armorIDs.map((id) => `repeating_armor_${id}_armor_encumbrance_bonus`),
|
|
...armorIDs.map((id) => `repeating_armor_${id}_armor_status`),
|
|
...weaponIDs.map((id) => `repeating_weapons_${id}_weapon_encumbrance`),
|
|
...weaponIDs.map((id) => `repeating_weapons_${id}_weapon_status`),
|
|
"gear_readied",
|
|
"gear_stowed",
|
|
"strength",
|
|
"gear_readied_max",
|
|
"gear_readied_over",
|
|
"gear_stowed_over",
|
|
"gear_stowed_max",
|
|
];
|
|
getAttrs(attrs, (v) => {
|
|
const [gear_readied, gear_stowed] = armorIDs.reduce(
|
|
(m, id) => {
|
|
if (v[`repeating_armor_${id}_armor_status`] === "READIED") {
|
|
m[0] += parseInt(v[`repeating_armor_${id}_armor_encumbrance`]) || 0;
|
|
} else if (v[`repeating_armor_${id}_armor_status`] === "STOWED")
|
|
m[1] += parseInt(v[`repeating_armor_${id}_armor_encumbrance`]) || 0;
|
|
return m;
|
|
},
|
|
weaponIDs.reduce(
|
|
(m, id) => {
|
|
if (v[`repeating_weapons_${id}_weapon_status`] === "READIED")
|
|
m[0] +=
|
|
parseInt(v[`repeating_weapons_${id}_weapon_encumbrance`]) || 0;
|
|
else if (v[`repeating_weapons_${id}_weapon_status`] === "STOWED")
|
|
m[1] +=
|
|
parseInt(v[`repeating_weapons_${id}_weapon_encumbrance`]) || 0;
|
|
return m;
|
|
},
|
|
gearIDs.reduce(
|
|
(m, id) => {
|
|
const amount =
|
|
parseInt(v[`repeating_gear_${id}_gear_amount`]) || 0;
|
|
let packingFactor = 1;
|
|
if (v[`repeating_gear_${id}_gear_bundled`] === "on") {
|
|
packingFactor = 3;
|
|
}
|
|
if (v[`repeating_gear_${id}_gear_status`] === "READIED")
|
|
m[0] +=
|
|
Math.ceil(
|
|
(amount *
|
|
parseFloat(v[`repeating_gear_${id}_gear_encumbrance`])) /
|
|
packingFactor
|
|
) || 0;
|
|
else if (v[`repeating_gear_${id}_gear_status`] === "STOWED")
|
|
m[1] +=
|
|
Math.ceil(
|
|
(amount *
|
|
parseFloat(v[`repeating_gear_${id}_gear_encumbrance`])) /
|
|
packingFactor
|
|
) || 0;
|
|
return m;
|
|
},
|
|
[0, 0]
|
|
)
|
|
)
|
|
);
|
|
|
|
const armor_encumbrance_bonus = Math.max(
|
|
0,
|
|
...armorIDs
|
|
.filter((id) => v[`repeating_armor_${id}_armor_status`] === "READIED")
|
|
.map(
|
|
(id) =>
|
|
parseInt(v[`repeating_armor_${id}_armor_encumbrance_bonus`]) || 0
|
|
)
|
|
);
|
|
const gear_stowed_max =
|
|
parseInt(v.strength) + armor_encumbrance_bonus || 0;
|
|
const gear_readied_max = Math.floor(gear_stowed_max / 2);
|
|
const setting = {
|
|
gear_readied,
|
|
gear_stowed,
|
|
gear_readied_max,
|
|
gear_stowed_max,
|
|
gear_readied_over:
|
|
parseInt(v.gear_readied) > gear_readied_max ? "1" : "0",
|
|
gear_stowed_over: parseInt(v.gear_stowed) > gear_stowed_max ? "1" : "0",
|
|
};
|
|
|
|
mySetAttrs(setting, v, {
|
|
silent: true,
|
|
});
|
|
});
|
|
};
|
|
|
|
getSectionIDs("repeating_gear", (gearIDs) => {
|
|
getSectionIDs("repeating_weapons", (weaponIDs) => {
|
|
getSectionIDs("repeating_armor", (armorIDs) =>
|
|
doCalc(gearIDs, weaponIDs, armorIDs)
|
|
);
|
|
});
|
|
});
|
|
};
|
|
|
|
const generateWeaponDisplay = () => {
|
|
// Generates the weapon menu and sets the display of weapons
|
|
// in display mode in one go.
|
|
getSectionIDs("repeating_weapons", (idArray) => {
|
|
const prefixes = idArray.map((id) => `repeating_weapons_${id}`);
|
|
const sourceAttrs = [
|
|
...prefixes.map((prefix) => `${prefix}_weapon_attack`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_name`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_skill_bonus`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_attribute_mod`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_damage`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_shock`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_shock_damage`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_shock_ac`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_skill_to_damage`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_attack_display`),
|
|
...prefixes.map((prefix) => `${prefix}_weapon_damage_display`),
|
|
...attributes.map((attr) => `${attr}_mod`),
|
|
...weaponSkills,
|
|
"attack_bonus",
|
|
"str_dex_mod",
|
|
"macro_weapons",
|
|
];
|
|
getAttrs(sourceAttrs, (v) => {
|
|
const setting: { [key: string]: string } = {};
|
|
const baseAttackBonus = parseInt(v.attack_bonus) || 0;
|
|
prefixes.forEach((prefix) => {
|
|
const attrBonus =
|
|
parseInt(
|
|
v[(v[`${prefix}_weapon_attribute_mod`] || "").slice(2, -1)]
|
|
) || 0;
|
|
const skillBonus =
|
|
parseInt(v[(v[`${prefix}_weapon_skill_bonus`] || "").slice(2, -1)]) ||
|
|
parseInt(v[`${prefix}_weapon_skill_bonus`]) ||
|
|
0;
|
|
const damageBonus =
|
|
attrBonus +
|
|
(v[`${prefix}_weapon_skill_to_damage`] === "@{weapon_skill_bonus}"
|
|
? skillBonus
|
|
: 0);
|
|
const weaponDamage =
|
|
v[`${prefix}_weapon_damage`] === "0"
|
|
? ""
|
|
: v[`${prefix}_weapon_damage`];
|
|
const shockString =
|
|
v[`${prefix}_weapon_shock`] !== "0"
|
|
? `, ${
|
|
(parseInt(v[`${prefix}_weapon_shock_damage`]) || 0) +
|
|
damageBonus
|
|
}\xa0${translate("SHOCK").toString().toLowerCase()}${
|
|
v[`${prefix}_weapon_shock_ac`]
|
|
? ` ${translate("VS_AC_LEQ")} ${
|
|
v[`${prefix}_weapon_shock_ac`]
|
|
}`
|
|
: ""
|
|
}`
|
|
: "";
|
|
|
|
const attack =
|
|
baseAttackBonus +
|
|
(parseInt(v[`${prefix}_weapon_attack`]) || 0) +
|
|
(skillBonus === -1 ? -2 : skillBonus) +
|
|
attrBonus;
|
|
const damage =
|
|
weaponDamage +
|
|
(weaponDamage
|
|
? damageBonus === 0
|
|
? ""
|
|
: damageBonus > 0
|
|
? ` + ${damageBonus}`
|
|
: ` - ${-damageBonus}`
|
|
: damageBonus);
|
|
|
|
setting[`${prefix}_weapon_attack_display`] =
|
|
attack >= 0 ? `+${attack}` : attack.toString();
|
|
setting[`${prefix}_weapon_damage_display`] = `${
|
|
damage || 0
|
|
}\xa0${translate("DAMAGE").toString().toLowerCase()}${shockString}`;
|
|
});
|
|
setting.macro_weapons = prefixes
|
|
.map((prefix, index) => {
|
|
const label = `${v[`${prefix}_weapon_name`]} (${
|
|
setting[`${prefix}_weapon_attack_display`]
|
|
})`;
|
|
return buildLink(
|
|
label,
|
|
`${prefix}_attack`,
|
|
index === prefixes.length - 1
|
|
);
|
|
})
|
|
.join(" ");
|
|
mySetAttrs(setting, v, {
|
|
silent: true,
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
const handleAmmoAPI = (sName: string) => {
|
|
const formula = sName === "weapons" ? "[[-1 - @{weapon_burst}]]" : "-1";
|
|
getSectionIDs(`repeating_${sName}`, (idArray) => {
|
|
getAttrs(
|
|
[
|
|
"setting_use_ammo",
|
|
...idArray.map((id) => `repeating_${sName}_${id}_weapon_use_ammo`),
|
|
...idArray.map((id) => `repeating_${sName}_${id}_weapon_api`),
|
|
],
|
|
(v) => {
|
|
const setting = idArray.reduce((m: { [k: string]: string }, id) => {
|
|
m[`repeating_${sName}_${id}_weapon_api`] =
|
|
v.setting_use_ammo === "1" &&
|
|
v[`repeating_${sName}_${id}_weapon_use_ammo`] !== "0"
|
|
? `\n!modattr --mute --charid @{character_id} --repeating_${sName}_${id}_weapon_ammo|${formula}`
|
|
: "";
|
|
return m;
|
|
}, {});
|
|
mySetAttrs(setting, v, {
|
|
silent: true,
|
|
});
|
|
}
|
|
);
|
|
});
|
|
};
|