implement feature caching and inject Ikonis augment effects into actor calculations
This commit is contained in:
parent
810afba204
commit
7c52be79c3
2 changed files with 81 additions and 22 deletions
|
|
@ -10,6 +10,9 @@ export const DEFAULT_AUGMENTS = [
|
||||||
{ id: "weight", name: "Gravity Plate", effect: "Weapon is Heavy (more damage)", cost: "4 Lead", precompile: 2 }
|
{ 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() {
|
export function getAugments() {
|
||||||
return game.settings.get('dh-ikonis', 'augmentsList') || DEFAULT_AUGMENTS;
|
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) {
|
export async function getAttachedFeature(uuid) {
|
||||||
if (!uuid) return null;
|
if (!uuid) return null;
|
||||||
|
if (_featureCache.has(uuid)) return _featureCache.get(uuid);
|
||||||
|
|
||||||
const timeout = new Promise((_, reject) =>
|
const timeout = new Promise((_, reject) =>
|
||||||
setTimeout(() => reject(new Error("Timeout fetching feature")), 2000)
|
setTimeout(() => reject(new Error("Timeout fetching feature")), 2000)
|
||||||
|
|
@ -42,6 +46,7 @@ export async function getAttachedFeature(uuid) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const item = await Promise.race([fromUuid(uuid), timeout]);
|
const item = await Promise.race([fromUuid(uuid), timeout]);
|
||||||
|
if (item) _featureCache.set(uuid, item);
|
||||||
return item;
|
return item;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`DH-Ikonis | Failed or timed out fetching feature [${uuid}]:`, err.message);
|
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() {
|
export function patchDHWeapon() {
|
||||||
console.log("DH-Ikonis | Patching DH Weapon system...");
|
console.log("DH-Ikonis | Patching DH Weapon system...");
|
||||||
|
// Future: Add damage type override logic here
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { patchIkonisSheet } from './ikonis-sheet.js';
|
||||||
import { IkonisAugmentConfig } from './ikonis-config.js';
|
import { IkonisAugmentConfig } from './ikonis-config.js';
|
||||||
|
|
||||||
|
|
@ -53,17 +53,35 @@ Hooks.once('init', () => {
|
||||||
|
|
||||||
Hooks.on('setup', () => {
|
Hooks.on('setup', () => {
|
||||||
patchDHWeapon();
|
patchDHWeapon();
|
||||||
|
patchIkonisLogic();
|
||||||
patchIkonisSheet();
|
patchIkonisSheet();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Watch for Tier/Ikonis changes and force a refresh
|
||||||
Hooks.on('updateItem', (item, changes, options, userId) => {
|
Hooks.on('updateItem', (item, changes, options, userId) => {
|
||||||
if (foundry.utils.hasProperty(changes, "system.tier") || foundry.utils.hasProperty(changes, "flags.dh-ikonis")) {
|
const isTierUpdate = foundry.utils.hasProperty(changes, "system.tier");
|
||||||
Object.values(item.apps || {}).forEach(app => { app.render?.(true); });
|
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 () => {
|
Hooks.once('ready', async () => {
|
||||||
// Perform Auto-Sync for Features
|
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
await syncIkonisFeatures();
|
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() {
|
async function syncIkonisFeatures() {
|
||||||
const pack = game.packs.get("dh-ikonis.ikonis-features");
|
const pack = game.packs.get("dh-ikonis.ikonis-features");
|
||||||
if (!pack) return;
|
if (!pack) return;
|
||||||
|
|
||||||
const index = await pack.getIndex();
|
const index = await pack.getIndex();
|
||||||
const currentAugs = game.settings.get(MODULE_ID, 'augmentsList') || [];
|
const currentAugs = game.settings.get(MODULE_ID, 'augmentsList') || [];
|
||||||
let updates = false;
|
let updates = false;
|
||||||
|
|
||||||
// 1. Sync Augments
|
|
||||||
const newAugs = currentAugs.map(aug => {
|
const newAugs = currentAugs.map(aug => {
|
||||||
if (!aug.featureUuid) {
|
if (!aug.featureUuid) {
|
||||||
const match = index.find(i => i.name === aug.name);
|
const match = index.find(i => i.name === aug.name);
|
||||||
if (match) {
|
if (match) {
|
||||||
aug.featureUuid = match.uuid;
|
aug.featureUuid = match.uuid;
|
||||||
updates = true;
|
updates = true;
|
||||||
console.log(`DH-Ikonis | Auto-linked augment [${aug.name}] to compendium feature.`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return aug;
|
return aug;
|
||||||
});
|
});
|
||||||
|
if (updates) await game.settings.set(MODULE_ID, 'augmentsList', newAugs);
|
||||||
if (updates) {
|
|
||||||
await game.settings.set(MODULE_ID, 'augmentsList', newAugs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Sync Bonded Feature
|
|
||||||
let bondedUuid = game.settings.get(MODULE_ID, 'defaultBondedUuid');
|
let bondedUuid = game.settings.get(MODULE_ID, 'defaultBondedUuid');
|
||||||
if (!bondedUuid) {
|
if (!bondedUuid) {
|
||||||
const bondMatch = index.find(i => i.name === "Ikonis Bond");
|
const bondMatch = index.find(i => i.name === "Ikonis Bond");
|
||||||
if (bondMatch) {
|
if (bondMatch) await game.settings.set(MODULE_ID, 'defaultBondedUuid', bondMatch.uuid);
|
||||||
await game.settings.set(MODULE_ID, 'defaultBondedUuid', bondMatch.uuid);
|
|
||||||
console.log(`DH-Ikonis | Auto-linked default Bonded feature.`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue