diff --git a/RolemasterUnified_Official/rolemasterunified.html b/RolemasterUnified_Official/rolemasterunified.html
index 602d7e3c16..540840740c 100644
--- a/RolemasterUnified_Official/rolemasterunified.html
+++ b/RolemasterUnified_Official/rolemasterunified.html
@@ -6559,7 +6559,7 @@ Sheet Version:
-Revision e4295d24baee7bde9d32235b4c46b457402d3f3b
+Revision 071aaeb51fba31488c8ccd15c39ed1f6dd9fb343
@@ -7766,7 +7766,7 @@ function miscBonusHighest(miscstr, lowest) {
function miscAttrSet(attr, name, value) {
let toget = attr;
if (!attr.endsWith("_misc")) {
- rmuerror("Use of default _misc on ", attr);
+ rmuerror("Use of default _misc on miscAttrSet ", attr, name, value);
toget = attr + "_misc";
}
getAttrsPending([toget], (ev) => {
@@ -7779,7 +7779,7 @@ function miscAttrSet(attr, name, value) {
function miscAttrRemove(attr, name) {
let toget = attr;
if (!attr.endsWith("_misc")) {
- rmuerror("Use of default _misc on ", attr);
+ rmuerror("Use of default _misc on miscAttrRemove ", attr, name, value);
toget = attr + "_misc";
}
getAttrsPending([toget], (ev) => {
@@ -7928,6 +7928,25 @@ function getSectionIDsPending(section, func) {
});
}
+function getSectionIDsListPending(sections, func) {
+ const res = [];
+ let waiting = 1; // Start at one to force something
+ rmuasync ++;
+
+ sections.forEach((section) => {
+ waiting ++;
+ getSectionIDs(section, (ids) => {
+ for (id of ids) {
+ res.push(section + "_" + id)
+ }
+ waiting --;
+ if (waiting == 0) { waiting = -99; func(res); rmuasync --; checkPending(); }
+ });
+ });
+ waiting --;
+ if (waiting == 0) { waiting = -99; func(res); rmuasync --; checkPending(); }
+}
+
function getSectionIDsOrdered(sectionName, callback) {
'use strict';
getAttrs([`_reporder_${sectionName}`], function (v) {
@@ -8558,7 +8577,7 @@ function lookupSkillResult(skill, skillinfo, umroll, total) {
console.log(statCache_);
// FIXME: Should make this prettier
miscAttrSet("scr_misc", "Realm Stat", statCache_.rs);
- if (donefn && typeof donefn === 'function') {
+ if (donefn && typeof(donefn) === 'function') {
donefn();
}
});
@@ -8849,7 +8868,9 @@ function lookupSkillResult(skill, skillinfo, umroll, total) {
catForEachSkill(cat, func, () => {
catcount --;
if (catcount == 0) {
- donefn();
+ if (donefn && typeof(donefn) == "function") {
+ donefn();
+ }
}
});
});
@@ -9368,11 +9389,11 @@ function _spellDoQuery(prevranks, listtype, filter, subtarget, cost, listranks)
}
}
- addRepeatingSectionPending(`cmspelllistspell${subtarget}`, 'spelllist', `${subtarget}spelllist`,
+ addRepeatingSection(`cmspelllistspell${subtarget}`, 'spelllist', `${subtarget}spelllist`,
(sectionname) => {
cmPopulateListOptions(`${sectionname}_cmranks`, costarray,
prevranks[list.name] || costarray[0]);
- setAttrsPending({[`${sectionname}_listname`]: list.name,
+ setAttrs({[`${sectionname}_listname`]: list.name,
[`${sectionname}_listranks`]: listranks[list.name]?.ranks || '-'});
});
}
@@ -9488,34 +9509,34 @@ onCheck("mancerchange:repeating_otherbaseownrealmspelllist", changeSkillPurchase
function prepareGraceTrickery(gracebonus, trickerybonus) {
getSectionIDsPending(`repeating_specializationgrace`, (graceids) => {
- let toget = []
- getSectionIDsPending(`repeating_specializationspelltrickery`, (trickeryids) => {
- for (const id of trickeryids) {
- const basename = `repeating_specializationspelltrickery_${id}`
- toget.push(basename + "_bonus");
- toget.push(basename + "_name");
- }
- for (const id of graceids) {
- const basename = `repeating_specializationgrace_${id}`
- toget.push(basename + "_bonus");
- toget.push(basename + "_name");
- }
- getAttrsPending(toget, (perks) => {
- for (const id of graceids) {
- const basename = `repeating_specializationgrace_${id}`
- if (perks[basename + "_name"]) {
- gracebonus[perks[basename + "_name"]] = perks[basename + "_bonus"]
- }
+ let toget = []
+ getSectionIDsPending(`repeating_specializationspelltrickery`, (trickeryids) => {
+ for (const id of trickeryids) {
+ const basename = `repeating_specializationspelltrickery_${id}`
+ toget.push(basename + "_bonus");
+ toget.push(basename + "_name");
+ }
+ for (const id of graceids) {
+ const basename = `repeating_specializationgrace_${id}`
+ toget.push(basename + "_bonus");
+ toget.push(basename + "_name");
+ }
+ getAttrsPending(toget, (perks) => {
+ for (const id of graceids) {
+ const basename = `repeating_specializationgrace_${id}`
+ if (perks[basename + "_name"]) {
+ gracebonus[perks[basename + "_name"]] = perks[basename + "_bonus"]
}
- for (const id of trickeryids) {
- const basename = `repeating_specializationspelltrickery_${id}`
- if (perks[basename + "_name"]) {
- trickerybonus[perks[basename + "_name"]] = perks[basename + "_bonus"]
- }
+ }
+ for (const id of trickeryids) {
+ const basename = `repeating_specializationspelltrickery_${id}`
+ if (perks[basename + "_name"]) {
+ trickerybonus[perks[basename + "_name"]] = perks[basename + "_bonus"]
}
- console.log("Trickery & Grace", trickerybonus, gracebonus);
- });
- });
+ }
+ console.log("Trickery & Grace", trickerybonus, gracebonus);
+ });
+ });
});
}
@@ -9754,7 +9775,7 @@ function getSubtlePenalty(realm, type, trickery, subtle, voice, hands, enc, meta
epen += encdelta * pentable.armorenc;
log += ` -${epen} [(${enc} - ${metalenc} Non-metal Enc% * ${pentable.armorenc}]`;
epen += metalenc * pentable.metalenc;
- log += ` -${epen} [${metalenc} Metal Enc% * ${pentable.metalenc}]`;
+ log += ` -${metalenc * pentable.metalenc} [${metalenc} Metal Enc% * ${pentable.metalenc}]`;
}
// Now apply transcendance
if (transcend && epen > 0) {
@@ -10744,35 +10765,80 @@ onCheck("page:levelupreview", levelUpReview);
* results = { "riding": [prefix:'sss', category:animal, aname: 'riding', name:"Horse",ranks:7,bonus:44] .. };
*/
function getSpecialisedSkillRanks(results) {
+ const sections = [];
+ const skilltocat = {}
+
categories.forEach((cat) => {
cat.skills.forEach((skill) => {
if (!skill.dynamicspecializations) return;
let prefix = `repeating_specialization${skill.aname}`
- getSectionIDs(prefix, (ids) => {
- if (ids.length == 0) return;
- let specskill = [];
- results[skill.aname] = specskill;
- ids.forEach(id => {
- let toget = [];
- toget.push(prefix + '_' + id + "_name");
- toget.push(prefix + '_' + id + "_ranks");
- toget.push(prefix + '_' + id + "_bonus");
- toget.push(prefix + '_' + id + "_usecount");
- toget.push(prefix + '_' + id + "_usecounttotal");
- getAttrs(toget, data => {
- specskill.push({
- category: cat.aname,
- id:id,
- prefix:prefix + '_' + id,
- aname:skill.aname,
- name:data[prefix + '_' + id + "_name"],
- bonus:data[prefix + '_' + id + "_bonus"],
- usecount:data[prefix + '_' + id + "_usecount"],
- usecounttotal:data[prefix + '_' + id + "_usecounttotal"],
- ranks:data[prefix + '_' + id+ "_ranks"]});
- });
- });
- });
+ skilltocat[skill.aname] = cat;
+ sections.push(prefix);
+ });
+ });
+
+ getSectionIDsListPending(sections, (prefixes) => {
+ const toget = [];
+ prefixes.forEach(prefix => {
+ toget.push(prefix + "_name");
+ toget.push(prefix + "_ranks");
+ toget.push(prefix + "_bonus");
+ toget.push(prefix + "_usecount");
+ toget.push(prefix + "_usecounttotal");
+ });
+ getAttrsPending(toget, (attrs) => {
+ for (prefix of prefixes) {
+ let matches = prefix.match(/^repeating_specialization([^_]*)/);
+ let skill = matches[1] || "Error";
+ item = {
+ aname: skill,
+ category: skilltocat[skill].aname,
+ name: attrs[prefix + "_name"],
+ ranks: attrs[prefix + "_ranks"] || 0,
+ bonus: attrs[prefix + "_bonus"] || 0,
+ usecount: attrs[prefix + "_usecount"] || 0,
+ usecounttotal: attrs[prefix + "_usecounttotal"] || 0,
+ prefix: prefix,
+ };
+ if (!results[skill]) {
+ results[skill] = [];
+ }
+ results[skill].push(item)
+ }
+ });
+ });
+}
+
+/**
+ * Gets the ranks that the character currently has in spell lists.
+ *
+ Returns in results (async)
+ Organ Arcana : {ranks: 2, prefix: 'repeating_spelllistspellotherbaseownrealm_-ozihd609zzaws1qrpev'}
+Sense Molding : {ranks: 12, prefix: 'repeating_spelllistspellownbase_-ovjjkh0a4-zn4izjdcw'}
+*/
+function getSpellListRanks(results) {
+ const spelltypes = [
+ 'repeating_spelllistspellownbase',
+ 'repeating_spelllistspellopen',
+ 'repeating_spelllistspellclosed',
+ 'repeating_spelllistspellotherbaseownrealm'];
+ getSectionIDsListPending(spelltypes, (listids) => {
+ console.log(listids)
+ let listtofetch = []
+ for (list of listids) {
+ listtofetch.push(list + '_name');
+ listtofetch.push(list + '_ranks');
+ }
+ getAttrsPending(listtofetch, (listdata) => {
+ console.log("list fetched", listdata);
+ for (list of listids) {
+ let name = listdata[list + "_name"];
+ results[name] = {
+ ranks: listdata[list + "_ranks"],
+ prefix: list,
+ };
+ }
+ console.log("GetSpellListRaks done", results);
});
});
}
@@ -10794,9 +10860,10 @@ function levelUpStart(_) {
// If we are at any level but 0, we get 2 free statgains.
const level = parseIntDefault(ev.level, 0);
if (level > 0){
- showChoices(["levelup_statgain_free"]);
- // Create the list of options dynamically so we can add temp / pot
- updateStatgainList(['comp_statgain1', 'comp_statgain2']);
+ showChoices(["levelup_statgain_free"]);
+ // Create the list of options dynamically so we can add temp / pot
+ // FIXME: This si not pending
+ updateStatgainList(['comp_statgain1', 'comp_statgain2']);
}
// New level
@@ -10901,15 +10968,10 @@ function levelUpStart(_) {
savedskills.push(prefix);
continue;
}
- if (!match || match.length < 2) {
- rmuerror("Coulnd't match regex against ", prefix, index);
- continue;
- }
}
}
});
-
addPendingFunction('Levelup: Create specialisations from ranks', () => {
for (const skillname in specialisedranks) {
const specials = specialisedranks[skillname];
@@ -10922,7 +10984,7 @@ function levelUpStart(_) {
const prefix = `${sectionname}_sheetprefix`;
cmPopulateListOptions(rname, costarray, 0)
- setAttrsPending({[sname]: skill.name, [prefix] : skill.prefix});
+ setAttrs({[sname]: skill.name, [prefix] : skill.prefix});
let uses = skill.usecount || '-';
let usestotal = skill.usecounttotal || '-';
let ranks = skill.ranks || '-';
@@ -10937,63 +10999,19 @@ function levelUpStart(_) {
});
- addPendingFunction('Talents add', () => {
- const data = getCharmancerData();
- console.log("data.level.values.current_level");
- if (previous_level != parseIntDefault(data.levelup?.values?.current_level, 0)) {
- return;
- }
- for (obj of savedtalents) {
- addRepeatingSection('added_talents', 'talent', (id) => {
- setCharmancerOptions(`${id}_talent`, "Category:Talent",
- { selected: data?.levelup?.data[obj] });
- });
- }
+
+ const listRanks = {};
+
+ addPendingFunction("Levelup: DEBUGGG@!", () => {
+ console.log("Debug point!");
});
-
- // FIXME: This is duplicated below. I should make it function
- const listRanks = {};
- {
- const spellRanks = {}
- const listIdsToGet = [];
- addPendingFunction('Levelup: Get Ranks for spells', () => {
- // So two pieces here. Get all the lists with ranks. Put in an array indexed
- // by spell name.
- // FIXME: This is in RMUSkills.lua, should sync
- const spelltypes = ['spellownbase', 'spellopen', 'spellclosed', 'spellotherbaseownrealm'];
- spelltypes.forEach(spelltype => {
- getSectionIDsPending('spelllist' + spelltype, (ids) => {
- ids.forEach(id => {
- listIdsToGet.push('repeating_spelllist' + spelltype + '_' + id + '_name');
- listIdsToGet.push('repeating_spelllist' + spelltype + '_' + id + '_ranks');
- });
- });
- });
- });
- // Immediataly after the prior; now get the damn things
- addPendingFunction("Levelup: Do the damn fetch", () => {
- getAttrsPending(listIdsToGet, (nr) => {
- // find all the _name and get the ranks from that.
- for (nameid in nr) {
- if (!nameid.endsWith("_name")) {
- continue;
- }
- const ranksid = nameid.replace(/_name$/, "_ranks");
- const prefix = nameid.replace(/_name$/, "");
- // So we grabbed the name, now get the basename
- listRanks[nr[nameid]] = {
- ranks: nr[ranksid],
- prefix: prefix
- }
- }
- });
- });
- }
+ addPendingFunction('Levelup: Spell List add', () => {
+ console.log("List Ranks is ", listRanks);
- addPendingFunction('Spell List add', () => {
const data = getCharmancerData();
+ // FIXME: Already got the damn costws - don't need to get them again
getAttrsPending(["realm", "profession", "spellownbase_cost",
"spellopen_cost", "spellclosed_cost", "spellrestricted_cost",
"spellarcane_cost"], (info) => {
@@ -11050,7 +11068,6 @@ function levelUpStart(_) {
setCharmancerText(updates);
});
});
-
// Specialisations; get existing ones and ones from cultures page
addPendingFunction('Levelup Init Done', () => {
console.log("****** Finished levelup function *****");});
@@ -11156,6 +11173,9 @@ onCheck("mancerfinish:levelup", (ev) => {
// Handled later
continue;
}
+ if (purchase === "current_level" || purchase === "new_level") {
+ continue;
+ }
const [ranks, cost] = optionDecodeIntPair(values[purchase]);
if (!ranks || ranks < 1) {
@@ -11287,9 +11307,11 @@ onCheck("mancerfinish:levelup", (ev) => {
});
addPendingFunction("LevelupFinish: Save DP / New Level", () => {
- miscAttrSet("dp_used_misc", levelstr, dpspent);
setAttrsPending({"level": newlevel});
- })
+ });
+ addPendingFunction("LevelupFinish: Save DP Used", () => {
+ miscAttrSet("dp_used_misc", levelstr, dpspent);
+ });
addPendingFunction("LevelupFinish: update all skills", RMUSkills.updateAllSkills);
addPendingFunction("LevelupFinish: Set HP/PP", () => {
@@ -11332,6 +11354,7 @@ function levelUpReview() {
levelstr = `Level ${newlevel}`;
const levelup = getLevelUpSummary(cmdata);
+ console.log("level up summary", levelup);
for (const statgain in levelup.statgains) {
const sg = getTranslationByKey(statgain)
@@ -11360,10 +11383,21 @@ function levelUpReview() {
}
}
+ const grouptopretty = {
+ ownbase: "Own Base",
+ open: "Open",
+ closed: "Closed",
+ otherbaseownrealm: "Other Base (Own Realm)",
+ openother: "Open Other Realm",
+ closedother: "Closed Other Realm",
+ otherbaseother: "Other Base Other Realm",
+ arcane: "Arcane"
+ }
for (let spell in levelup.spelllists) {
const list = levelup.spelllists[spell];
+ let pretty = grouptopretty[list.group] || list.group;
addRepeatingSection('levelupreviewspelllists', 'levelupreviewlistitem', (id) => {
- setCharmancerText({[`${id} .text`]: `${list.list}: ${list.ranks} Ranks`});
+ setCharmancerText({[`${id} .text`]: `${list.list} (${pretty}): ${list.ranks} Ranks`});
});
}
@@ -11412,11 +11446,14 @@ function getLevelUpSummary(cmdata) {
}
result.talents.push({talent: name, tier: tier});
}
+
+ // Spell Lists
} else if (purchase.includes("spelllist")) {
// skip if not ranks
if (purchase.endsWith("_listranks") || purchase.endsWith("_listname")) {
continue;
}
+ console.log("List", purchase);
const [ranks, cost] = optionDecodeIntPair(values[purchase]);
if (ranks == 0) {
continue;
@@ -11424,7 +11461,13 @@ function getLevelUpSummary(cmdata) {
// List name:
const namekey = purchase.replace(/_cmranks/, "_listname");
const list = values[namekey];
- result.spelllists.push({list: list, ranks: ranks})
+ const groupmatch = purchase.match(/_([a-zA-Z]*)spelllist_cmranks/);
+ let group = "Unknown";
+ if (groupmatch && groupmatch[1]) {
+ group = groupmatch[1];
+ }
+ result.spelllists.push({list: list, ranks: ranks, group: group})
+
} else if (purchase.startsWith("tp")) {
console.log("Traing Package Purchase");
} else if (purchase.startsWith("lasterror")) {
@@ -11810,7 +11853,7 @@ RMUStats.createStatsFinalize = function(cmdata) {
let st = svals[str90];
st.temp = 90;
st.pot = Math.max(Math.min(st.pot + 10, 90), 100);
- miscRawSetAbsolute(st.misc, `Raise hightest to ${st.temp} (Pot ${st.pot})`, st.temp);
+ miscRawSetAbsolute(st.misc, `Raise highest to ${st.temp} (Pot ${st.pot})`, st.temp);
console.log(`Raise ${str90} to 90`);
break;
}
@@ -11822,7 +11865,7 @@ RMUStats.createStatsFinalize = function(cmdata) {
let st = svals[str85];
st.temp = 85;
st.pot = Math.max(Math.min(st.pot + 10, 85), 100);
- miscRawSetAbsolute(st.misc, `Raise hightest to ${st.temp} (Pot ${st.pot})`, st.temp);
+ miscRawSetAbsolute(st.misc, `Raise highest to ${st.temp} (Pot ${st.pot})`, st.temp);
break;
}
case 'statgain':
@@ -14656,7 +14699,7 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
}
// If Armor need to make a %
update.weight = `${update.weight}%`;
- update.itemmaterial = ia.inventoryaddarmormaterial;
+ update.material = ia.inventoryaddarmormaterial;
} else if (update.type == 'Shield') {
system['Attacks Blocked'] = ia.inventoryaddshieldattacks;
system['DB Bonus'] = ia.inventoryaddshielddb;
@@ -14739,7 +14782,7 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
update.inventoryaddweaponmaterials = material.includes('s') ? 'on' : 'off';
update.inventoryaddweaponmaterialm = material.includes('m') ? 'on' : 'off';
update.inventoryaddweaponobbonus = parseIntDefault(sys.OB, 0);
- update.inventoryaddweaponskill = sys['Attack Skill'];
+ update.inventoryaddweaponskill = sys['Attack Skill'] || "";
}
//
@@ -14750,7 +14793,7 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
if (sys.AT) {
update.inventoryaddarmorat = sys.AT;
}
- update.inventoryaddarmormaterial = item[`${basename}_itemmaterial`];
+ update.inventoryaddarmormaterial = item[`${basename}_itemmaterial`] || "metal";
update.inventoryaddarmordbbonus = parseIntDefault(sys.DB, 0);
}
{//
@@ -14760,7 +14803,6 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
if (sys['DB Bonus']) {
update.inventoryaddshielddb = sys['DB Bonus'];
}
- update.inventoryaddarmormaterial = item[`${basename}_itemmaterial`];
}
// Ideally this should be the same as the above function
diff --git a/RolemasterUnified_Official/sheet.json b/RolemasterUnified_Official/sheet.json
index f02b06d6ec..423e760348 100644
--- a/RolemasterUnified_Official/sheet.json
+++ b/RolemasterUnified_Official/sheet.json
@@ -36,5 +36,5 @@
"description": "Show FX when available based on the sheets guess of the attack"
}
],
- "version": "1758176124"
+ "version": "1758728339"
}
diff --git a/RolemasterUnified_Official/updates.md b/RolemasterUnified_Official/updates.md
index 01b9ae32ff..20bdb669c4 100644
--- a/RolemasterUnified_Official/updates.md
+++ b/RolemasterUnified_Official/updates.md
@@ -1,3 +1,26 @@
+# 2025-09-23
+
+- Creature Law (new upload)
+ - Fix level variance in listing
+ - Fix Variance in listings
+ - Fix variance in display
+- Banecroft (new upload)
+ - Adventure text uploaded
+ - Creatures updated to new standard
+ - First Tokens created
+- Validation
+ - Support Body,Head,Arms,Legs format for AT
+ - Validate attack sizes for creatures
+- Stuff: Fix error in itemmaterial not saving
+- Levelup:
+ - Don't try and levelup current_level/new_level
+ - Spell lists show the group
+ - Recreate previous skill ranks correctly
+- Misc: Make one of the errors more verbose
+- Spells: Fix display of mixed armor penalties
+ - calculation was correct, display showed culmulative error
+- Create: fix highest message (@ixs)
+
# 2025-09-18
- Fix stacking of the overlays