fix: harden SLA skill roster seeding and compat full installer fallback

This commit is contained in:
2026-03-04 19:28:03 +00:00
parent 51a7b93066
commit 0814054daa
3 changed files with 95 additions and 9 deletions
@@ -7,6 +7,38 @@ import { SLASkillPoints } from "../apps/sla-skill-points.mjs";
export class BRPactorItemDrop {
static _isSkillCategoryBrpid(brpid = "") {
return /^i\.skillcat\./i.test(String(brpid ?? "").trim());
}
static async _getFallbackSkillCategoryBrpid(actor) {
const actorSkillCat = actor?.items
?.filter((itm) => itm.type === "skillcat")
?.map((itm) => itm.flags?.[game.system.id]?.brpidFlag?.id ?? itm.flags?.brp?.brpidFlag?.id)
?.find((id) => this._isSkillCategoryBrpid(id));
if (actorSkillCat) return String(actorSkillCat);
try {
const cats = await game.system.api.brpid.fromBRPIDRegexBest({
brpidRegExp: /^i\.skillcat\./,
type: "i"
});
const preferred = ["mental", "physical", "combat", "perception", "communication", "technical"];
for (const needle of preferred) {
const match = cats.find((itm) => String(itm?.name ?? "").toLowerCase().includes(needle));
const brpid = match?.flags?.[game.system.id]?.brpidFlag?.id ?? match?.flags?.brp?.brpidFlag?.id;
if (this._isSkillCategoryBrpid(brpid)) return String(brpid);
}
for (const cat of cats) {
const brpid = cat?.flags?.[game.system.id]?.brpidFlag?.id ?? cat?.flags?.brp?.brpidFlag?.id;
if (this._isSkillCategoryBrpid(brpid)) return String(brpid);
}
} catch (err) {
console.warn("sla-industries-brp | Could not resolve fallback skill category BRPID", err);
}
return "";
}
// Change default on Drop Item Create routine for requirements (single items and folder drop)-----------------------------------------------------------------
static async _BRPonDropItemCreate(actor, itemData) {
const newItemData = [];
@@ -245,7 +277,15 @@ export class BRPactorItemDrop {
//Calculate Base Skill on Dropping the item on actor
static async _calcBase(itm, actor) {
//Check the skill Category exists
await this._checkSkillCat(itm, actor)
try {
await this._checkSkillCat(itm, actor)
} catch (err) {
console.warn("sla-industries-brp | Skill category check failed during base calc", {
skill: itm?.name,
category: itm?.system?.category,
err
})
}
if (itm.system.variable) {
let stat1 = itm.system.baseFormula[1].stat
let stat2 = itm.system.baseFormula[2].stat
@@ -742,20 +782,40 @@ export class BRPactorItemDrop {
}
static async _checkSkillCat(skill, actor) {
if (!skill || skill.type !== "skill") return
let categoryBrpid = String(skill?.system?.category ?? "").trim()
if (!this._isSkillCategoryBrpid(categoryBrpid)) {
categoryBrpid = await this._getFallbackSkillCategoryBrpid(actor)
if (categoryBrpid && skill?.system) {
skill.system.category = categoryBrpid
}
}
if (!this._isSkillCategoryBrpid(categoryBrpid)) return
//Check to see if the skill category already exists and if it does then do nothing
let newSkillCats = []
if (actor.items
.filter(dItm => (dItm.flags?.[game.system.id]?.brpidFlag?.id ?? dItm.flags?.brp?.brpidFlag?.id))
.filter(nitm => (nitm.flags?.[game.system.id]?.brpidFlag?.id ?? nitm.flags?.brp?.brpidFlag?.id) === skill.system.category).length > 0) {
.filter(nitm => (nitm.flags?.[game.system.id]?.brpidFlag?.id ?? nitm.flags?.brp?.brpidFlag?.id) === categoryBrpid).length > 0) {
return
}
//Get the best version of the skill category
let newSkillCat = (await game.system.api.brpid.fromBRPIDBest({ brpid: skill.system.category }))[0]
let newSkillCat = null
try {
newSkillCat = (await game.system.api.brpid.fromBRPIDBest({ brpid: categoryBrpid }))[0]
} catch (err) {
console.warn("sla-industries-brp | Failed resolving skill category by BRPID", {
skill: skill?.name,
categoryBrpid,
err
})
}
if (newSkillCat) {
newSkillCats.push(newSkillCat)
await Item.createDocuments(newSkillCats, { parent: actor })
} else {
let errMsg = game.i18n.format('BRP.noSkillCat', { skillCat: skill.system.category, skillName: skill.name })
let errMsg = game.i18n.format('BRP.noSkillCat', { skillCat: categoryBrpid, skillName: skill.name })
ui.notifications.warn(errMsg);
}
return
@@ -755,6 +755,10 @@ export class BRPActor extends Actor {
delete data.ownership
delete data.pack
data.system = data.system ?? {}
if (!BRPactorItemDrop._isSkillCategoryBrpid?.(data.system.category)) {
const fallbackCategory = await BRPactorItemDrop._getFallbackSkillCategoryBrpid?.(actor)
if (fallbackCategory) data.system.category = fallbackCategory
}
// Normalize specialty placeholders (e.g. Craft (Specialty)) even if source flags are missing.
if (!data.system.specialism && /\(\s*specialty\s*\)/i.test(skillName)) {
@@ -5446,11 +5446,33 @@ export class SLABPNToolkit {
const runAction = async (action = "close") => {
switch (String(action)) {
case "full-install":
return game.brp?.SLASeedImporter?.runFullSLAInstaller?.({
overwrite: true,
pruneCompendia: true,
notify: true
}) ?? { ok: false, reason: "full-installer-unavailable" };
if (typeof game.brp?.SLASeedImporter?.runFullSLAInstaller === "function") {
return game.brp.SLASeedImporter.runFullSLAInstaller({
overwrite: true,
pruneCompendia: true,
notify: true
});
}
// Backward-compatible fallback for clients still on older cached scripts.
if (!game.brp?.SLASeedImporter) {
return { ok: false, reason: "full-installer-unavailable" };
}
await this.ensureCoreJournals({ notify: false });
const fallbackSummary = {
ok: true,
mode: "compat-fallback",
base: await game.brp.SLASeedImporter.buildDraft2?.({ overwrite: true, syncCompendia: false }),
traits: await game.brp.SLASeedImporter.ensureSLA2Traits?.({ overwrite: true, notify: false }),
equipment: await this.seedWorldGeneralEquipment({ overwrite: true, notify: false }),
sync: await game.brp.SLASeedImporter.syncAllToCompendia?.({ overwrite: true, prune: true }),
migrate: await game.brp.SLASeedImporter.migrateLegacySLAAssetPaths?.({
includeActors: true,
includeCompendium: true,
notify: false
})
};
ui.notifications.info("SLA Full Install complete (compat mode).");
return fallbackSummary;
case "setup":
return this.ensureCoreJournals({ notify: true });
case "rulebook":