refactor: move recursion guard to actor instance and implement feature caching for ikonis virtual items
This commit is contained in:
parent
fec1a6c1ef
commit
d7fd36d24e
1 changed files with 23 additions and 12 deletions
|
|
@ -127,36 +127,46 @@ export function patchIkonisLogic() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global recursion guard for feature injection
|
|
||||||
let _isInjecting = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal helper to generate and cache virtual features for an actor.
|
* Internal helper to generate and cache virtual features for an actor.
|
||||||
* Safe to call multiple times; uses a cache to keep it fast.
|
* Safe to call multiple times; uses a cache to keep it fast.
|
||||||
*/
|
*/
|
||||||
function _injectIkonisFeatures(actor) {
|
function _injectIkonisFeatures(actor) {
|
||||||
if (!actor || _isInjecting) return [];
|
if (!actor || actor._isIkonisInjecting) return [];
|
||||||
|
|
||||||
// Use a short-lived cache for the current render cycle
|
// Initialize the cache if it doesn't exist
|
||||||
if (!actor._ikonisCache) actor._ikonisCache = new Map();
|
if (!actor._ikonisCache) actor._ikonisCache = new Map();
|
||||||
|
|
||||||
const ikonisFeatures = [];
|
const ikonisFeatures = [];
|
||||||
_isInjecting = true;
|
actor._isIkonisInjecting = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const weapons = actor.items.filter(i => i.type === 'weapon');
|
const weapons = actor.items.filter(i => i.type === 'weapon');
|
||||||
|
const allAugs = getAugments();
|
||||||
|
|
||||||
for (const item of weapons) {
|
for (const item of weapons) {
|
||||||
const isEquipped = item.system.equipped;
|
const isEquipped = item.system.equipped;
|
||||||
const installedIds = item.getFlag('dh-ikonis', 'installedAugments') || [];
|
const installedIds = item.getFlag('dh-ikonis', 'installedAugments') || [];
|
||||||
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
|
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
|
||||||
|
|
||||||
const processFeature = (uuid, type) => {
|
const processFeature = (uuid, type) => {
|
||||||
|
if (!uuid) return;
|
||||||
|
|
||||||
|
// Extract original feature ID from UUID to build our virtual ID
|
||||||
|
const featureId = uuid.split('.').pop();
|
||||||
|
const virtualId = `ikonis-${item.id}-${featureId}`;
|
||||||
|
|
||||||
|
// Check cache first
|
||||||
|
if (actor._ikonisCache.has(virtualId)) {
|
||||||
|
if (isEquipped) ikonisFeatures.push(actor._ikonisCache.get(virtualId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve original and clone
|
||||||
const feature = _featureCache.get(uuid) || fromUuidSync(uuid);
|
const feature = _featureCache.get(uuid) || fromUuidSync(uuid);
|
||||||
if (feature) {
|
if (feature) {
|
||||||
const featureClone = feature.clone({ parent: actor }, { keepId: true });
|
const featureClone = feature.clone({ parent: actor }, { keepId: true });
|
||||||
featureClone.system.type = "ikonis";
|
featureClone.system.type = "ikonis";
|
||||||
|
|
||||||
const virtualId = `ikonis-${item.id}-${feature.id}`;
|
|
||||||
Object.defineProperty(featureClone, "id", { value: virtualId, enumerable: true });
|
Object.defineProperty(featureClone, "id", { value: virtualId, enumerable: true });
|
||||||
|
|
||||||
actor._ikonisCache.set(virtualId, featureClone);
|
actor._ikonisCache.set(virtualId, featureClone);
|
||||||
|
|
@ -165,7 +175,6 @@ function _injectIkonisFeatures(actor) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (bondedUuid) processFeature(bondedUuid, "bonded");
|
if (bondedUuid) processFeature(bondedUuid, "bonded");
|
||||||
const allAugs = getAugments();
|
|
||||||
for (const id of installedIds) {
|
for (const id of installedIds) {
|
||||||
const aug = allAugs.find(a => String(a.id) === String(id));
|
const aug = allAugs.find(a => String(a.id) === String(id));
|
||||||
if (aug?.featureUuid) processFeature(aug.featureUuid, "augment");
|
if (aug?.featureUuid) processFeature(aug.featureUuid, "augment");
|
||||||
|
|
@ -174,10 +183,10 @@ function _injectIkonisFeatures(actor) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("DH-Ikonis | Error during feature injection:", err);
|
console.error("DH-Ikonis | Error during feature injection:", err);
|
||||||
} finally {
|
} finally {
|
||||||
_isInjecting = false;
|
actor._isIkonisInjecting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(new Set(ikonisFeatures));
|
return ikonisFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -218,7 +227,9 @@ export function patchDhCharacter(DhCharacter) {
|
||||||
const originalGetEmbedded = Actor.prototype.getEmbeddedDocument;
|
const originalGetEmbedded = Actor.prototype.getEmbeddedDocument;
|
||||||
Actor.prototype.getEmbeddedDocument = function(embeddedName, id, options) {
|
Actor.prototype.getEmbeddedDocument = function(embeddedName, id, options) {
|
||||||
if (embeddedName === "Item" && typeof id === "string" && id.startsWith("ikonis-")) {
|
if (embeddedName === "Item" && typeof id === "string" && id.startsWith("ikonis-")) {
|
||||||
if (!this._ikonisCache?.has(id)) _injectIkonisFeatures(this);
|
if (!this._ikonisCache?.has(id) && !this._isIkonisInjecting) {
|
||||||
|
_injectIkonisFeatures(this);
|
||||||
|
}
|
||||||
return this._ikonisCache?.get(id);
|
return this._ikonisCache?.get(id);
|
||||||
}
|
}
|
||||||
return originalGetEmbedded.call(this, embeddedName, id, options);
|
return originalGetEmbedded.call(this, embeddedName, id, options);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue