export const DEFAULT_AUGMENTS = [ { id: "force", name: "Kinetic Amplifier", effect: "+1 Damage on Melee attacks", cost: "2 Iron", precompile: 1 }, { id: "fire", name: "Thermal Core", effect: "Deals Fire damage instead of Physical", cost: "1 Blaze Glass", precompile: 1 }, { id: "shock", name: "Static Coil", effect: "Targets hit are Dazed", cost: "3 Copper", precompile: 2 }, { id: "shield", name: "Reactive Plating", effect: "+1 Armor while equipped", cost: "2 Steel", precompile: 1 }, { id: "range", name: "Long-Range Optics", effect: "Increases Range by 1 step", cost: "1 Lens", precompile: 2 }, { id: "crit", name: "Precision Chip", effect: "+1 to Crit range", cost: "1 Gold", precompile: 3 }, { id: "multi", name: "Burst Module", effect: "Can target 2 enemies (Half damage)", cost: "2 Gears", precompile: 4 }, { id: "drain", name: "Siphon Link", effect: "Recover 1 Hope on kill", cost: "1 Soul Gem", precompile: 4 }, { 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; } export function getSlotCount(item) { const flags = item.getFlag('dh-ikonis') || {}; if (typeof flags.slotOverride === "number") return flags.slotOverride; let tier = item.system?.tier?.value; if (tier === undefined) tier = item.system?.tier; const tierNum = parseInt(tier) || 1; const settingKey = `slotsTier${tierNum}`; try { return game.settings.get('dh-ikonis', settingKey); } catch (e) { return tierNum >= 2 ? 3 : 2; } } /** * 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 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 }