From 7c52be79c3ca01c6530102006375e564aaeb273b Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 26 Apr 2026 17:45:12 +0200 Subject: [PATCH] implement feature caching and inject Ikonis augment effects into actor calculations --- scripts/ikonis-data.js | 58 +++++++++++++++++++++++++++++++++++++++++- scripts/main.js | 45 +++++++++++++++++--------------- 2 files changed, 81 insertions(+), 22 deletions(-) diff --git a/scripts/ikonis-data.js b/scripts/ikonis-data.js index cbb8700..2029640 100644 --- a/scripts/ikonis-data.js +++ b/scripts/ikonis-data.js @@ -10,6 +10,9 @@ export const DEFAULT_AUGMENTS = [ { id: "weight", name: "Gravity Plate", effect: "Weapon is Heavy (more damage)", cost: "4 Lead", precompile: 2 } ]; +// Global cache for resolved features to keep generators fast +const _featureCache = new Map(); + export function getAugments() { return game.settings.get('dh-ikonis', 'augmentsList') || DEFAULT_AUGMENTS; } @@ -31,10 +34,11 @@ export function getSlotCount(item) { } /** - * Robust feature fetching with timeout to prevent sheet hangs. + * 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) @@ -42,6 +46,7 @@ export async function getAttachedFeature(uuid) { 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); @@ -49,6 +54,57 @@ export async function getAttachedFeature(uuid) { } } +/** + * 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 standard effects + yield* originalAllEffects.call(this); + + // Inject effects from equipped Ikonis weapons + for (const item of this.items) { + if (item.type !== 'weapon' || !item.system.equipped) continue; + + const installedIds = item.getFlag('dh-ikonis', 'installedAugments') || []; + const allAugs = getAugments(); + + for (const id of installedIds) { + const aug = allAugs.find(a => String(a.id) === String(id)); + if (!aug?.featureUuid) continue; + + // We use sync lookup here because generators must be synchronous. + // The features should already be in the cache from the sheet rendering. + const feature = fromUuidSync(aug.featureUuid); + if (!feature || !feature.effects) continue; + + for (const effect of feature.effects) { + // Only yield transfer effects (those meant to apply to the actor) + if (effect.transfer) yield effect; + } + } + + // Also check Bonded feature + const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid'); + if (bondedUuid) { + const feature = fromUuidSync(bondedUuid); + if (feature?.effects) { + for (const effect of feature.effects) { + if (effect.transfer) yield effect; + } + } + } + } + }; + + console.log("DH-Ikonis | Actor patches complete."); +} + export function patchDHWeapon() { console.log("DH-Ikonis | Patching DH Weapon system..."); + // Future: Add damage type override logic here } diff --git a/scripts/main.js b/scripts/main.js index af1f4af..c4fc899 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -1,4 +1,4 @@ -import { patchDHWeapon, DEFAULT_AUGMENTS } from './ikonis-data.js'; +import { patchDHWeapon, patchIkonisLogic, DEFAULT_AUGMENTS } from './ikonis-data.js'; import { patchIkonisSheet } from './ikonis-sheet.js'; import { IkonisAugmentConfig } from './ikonis-config.js'; @@ -53,17 +53,35 @@ Hooks.once('init', () => { Hooks.on('setup', () => { patchDHWeapon(); + patchIkonisLogic(); patchIkonisSheet(); }); +// Watch for Tier/Ikonis changes and force a refresh Hooks.on('updateItem', (item, changes, options, userId) => { - if (foundry.utils.hasProperty(changes, "system.tier") || foundry.utils.hasProperty(changes, "flags.dh-ikonis")) { - Object.values(item.apps || {}).forEach(app => { app.render?.(true); }); + const isTierUpdate = foundry.utils.hasProperty(changes, "system.tier"); + const isIkonisUpdate = foundry.utils.hasProperty(changes, "flags.dh-ikonis"); + const isEquipUpdate = foundry.utils.hasProperty(changes, "system.equipped"); + + if (isTierUpdate || isIkonisUpdate || isEquipUpdate) { + console.log(`DH-Ikonis | Update detected for ${item.name}.`); + + // Refresh sheets + Object.values(item.apps || {}).forEach(app => { + if (app.render) app.render(true); + }); + + // If it's on an actor, we need to force the actor to recalculate its effects + if (item.actor) { + console.log(`DH-Ikonis | Resetting actor data for ${item.actor.name}...`); + item.actor.prepareData(); + // Force sheet re-render for the actor + item.actor.sheet?.render(false); + } } }); Hooks.once('ready', async () => { - // Perform Auto-Sync for Features if (game.user.isGM) { await syncIkonisFeatures(); } @@ -82,41 +100,26 @@ Hooks.once('ready', async () => { } }); -/** - * Automatically links augments in the settings to features in the compendium by name. - */ async function syncIkonisFeatures() { const pack = game.packs.get("dh-ikonis.ikonis-features"); if (!pack) return; - const index = await pack.getIndex(); const currentAugs = game.settings.get(MODULE_ID, 'augmentsList') || []; let updates = false; - - // 1. Sync Augments const newAugs = currentAugs.map(aug => { if (!aug.featureUuid) { const match = index.find(i => i.name === aug.name); if (match) { aug.featureUuid = match.uuid; updates = true; - console.log(`DH-Ikonis | Auto-linked augment [${aug.name}] to compendium feature.`); } } return aug; }); - - if (updates) { - await game.settings.set(MODULE_ID, 'augmentsList', newAugs); - } - - // 2. Sync Bonded Feature + if (updates) await game.settings.set(MODULE_ID, 'augmentsList', newAugs); let bondedUuid = game.settings.get(MODULE_ID, 'defaultBondedUuid'); if (!bondedUuid) { const bondMatch = index.find(i => i.name === "Ikonis Bond"); - if (bondMatch) { - await game.settings.set(MODULE_ID, 'defaultBondedUuid', bondMatch.uuid); - console.log(`DH-Ikonis | Auto-linked default Bonded feature.`); - } + if (bondMatch) await game.settings.set(MODULE_ID, 'defaultBondedUuid', bondMatch.uuid); } }