implement pre-loading cache for Ikonis features and override system currency settings

This commit is contained in:
CPTN Cosmo 2026-04-26 17:52:44 +02:00
parent 077d284987
commit b62824cd54
2 changed files with 96 additions and 49 deletions

View file

@ -12,12 +12,37 @@ export const DEFAULT_AUGMENTS = [
// Global caches for resolved features to keep getters fast
const _featureCache = new Map();
const _itemObjectCache = new Map();
export function getAugments() {
return game.settings.get('dh-ikonis', 'augmentsList') || DEFAULT_AUGMENTS;
}
/**
* Pre-loads all features from the compendium into memory.
* This is necessary because sheetLists getter is synchronous.
*/
export async function loadIkonisFeatures() {
console.log("DH-Ikonis | Pre-loading features into cache...");
const allAugs = getAugments();
const bondedUuid = game.settings.get('dh-ikonis', 'defaultBondedUuid');
const uuids = new Set(allAugs.map(a => a.featureUuid).filter(Boolean));
if (bondedUuid) uuids.add(bondedUuid);
for (const uuid of uuids) {
try {
const item = await fromUuid(uuid);
if (item) {
_featureCache.set(uuid, item);
console.log(`DH-Ikonis | Cached feature: ${item.name} [${uuid}]`);
}
} catch (e) {
console.error(`DH-Ikonis | Failed to load feature ${uuid}`, e);
}
}
console.log(`DH-Ikonis | Cache size: ${_featureCache.size}`);
}
export function getSlotCount(item) {
const flags = item.getFlag('dh-ikonis') || {};
if (typeof flags.slotOverride === "number") return flags.slotOverride;
@ -34,37 +59,15 @@ export function getSlotCount(item) {
}
}
/**
* Robust feature fetching with timeout and caching.
*/
export async function getAttachedFeature(uuid) {
if (!uuid) return null;
if (_featureCache.has(uuid)) return _featureCache.get(uuid);
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout fetching feature")), 2000)
);
try {
const item = await Promise.race([fromUuid(uuid), timeout]);
if (item) _featureCache.set(uuid, item);
return item;
} catch (err) {
console.warn(`DH-Ikonis | Failed or timed out fetching feature [${uuid}]:`, err.message);
return null;
}
}
/**
* Patches the system to ensure Ikonis effects are applied to the actor.
*/
export function patchIkonisLogic() {
console.log("DH-Ikonis | Patching Actor for Ikonis effect injection...");
// 1. Patch allApplicableEffects to inject Active Effects from Augments
const originalAllEffects = Actor.prototype.allApplicableEffects;
Actor.prototype.allApplicableEffects = function* () {
yield* originalAllEffects.call(this);
Actor.prototype.allApplicableEffects = function* (options) {
yield* originalAllEffects.call(this, options);
for (const item of this.items) {
if (item.type !== 'weapon' || !item.system.equipped) continue;
@ -76,7 +79,8 @@ export function patchIkonisLogic() {
const aug = allAugs.find(a => String(a.id) === String(id));
if (!aug?.featureUuid) continue;
const feature = fromUuidSync(aug.featureUuid);
// Try cache first, then sync
const feature = _featureCache.get(aug.featureUuid) || fromUuidSync(aug.featureUuid);
if (!feature || !feature.effects) continue;
for (const effect of feature.effects) {
@ -86,7 +90,7 @@ export function patchIkonisLogic() {
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
if (bondedUuid) {
const feature = fromUuidSync(bondedUuid);
const feature = _featureCache.get(bondedUuid) || fromUuidSync(bondedUuid);
if (feature?.effects) {
for (const effect of feature.effects) {
if (effect.transfer) yield effect;
@ -101,44 +105,46 @@ export function patchIkonisLogic() {
* Patches the Character Data Model to show features in the UI lists.
*/
export function patchDhCharacter(DhCharacter) {
console.log("DH-Ikonis | Patching DhCharacter data model for UI lists...");
console.log("DH-Ikonis | Applying DhCharacter.sheetLists patch...");
const descriptor = Object.getOwnPropertyDescriptor(DhCharacter.prototype, 'sheetLists');
if (!descriptor) return;
if (!descriptor) {
console.error("DH-Ikonis | FAILED to find sheetLists descriptor on DhCharacter prototype!");
return;
}
const originalSheetLists = descriptor.get;
Object.defineProperty(DhCharacter.prototype, 'sheetLists', {
get: function() {
const lists = originalSheetLists.call(this);
const ikonisFeatures = [];
if (!this.parent) return lists;
// Find equipped Ikonis weapons
for (const item of this.parent.items) {
if (item.type !== 'weapon' || !item.system.equipped) continue;
const installedIds = item.getFlag('dh-ikonis', 'installedAugments') || [];
const allAugs = getAugments();
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
// Collect Bonded Feature
if (bondedUuid) {
const feature = fromUuidSync(bondedUuid);
const feature = _featureCache.get(bondedUuid) || fromUuidSync(bondedUuid);
if (feature) ikonisFeatures.push(feature);
}
// Collect Augments
const allAugs = getAugments();
for (const id of installedIds) {
const aug = allAugs.find(a => String(a.id) === String(id));
if (!aug?.featureUuid) continue;
const feature = fromUuidSync(aug.featureUuid);
const feature = _featureCache.get(aug.featureUuid) || fromUuidSync(aug.featureUuid);
if (feature) ikonisFeatures.push(feature);
}
}
if (ikonisFeatures.length > 0) {
// Ensure unique features
const uniqueFeatures = Array.from(new Set(ikonisFeatures));
console.log(`DH-Ikonis | Injecting ${uniqueFeatures.length} features for ${this.parent.name}`);
lists.ikonis = {
title: "Ikonis Augments",