implement pre-loading cache for Ikonis features and override system currency settings
This commit is contained in:
parent
077d284987
commit
b62824cd54
2 changed files with 96 additions and 49 deletions
|
|
@ -12,12 +12,37 @@ export const DEFAULT_AUGMENTS = [
|
||||||
|
|
||||||
// Global caches for resolved features to keep getters fast
|
// Global caches for resolved features to keep getters fast
|
||||||
const _featureCache = new Map();
|
const _featureCache = new Map();
|
||||||
const _itemObjectCache = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
export function getSlotCount(item) {
|
||||||
const flags = item.getFlag('dh-ikonis') || {};
|
const flags = item.getFlag('dh-ikonis') || {};
|
||||||
if (typeof flags.slotOverride === "number") return flags.slotOverride;
|
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.
|
* Patches the system to ensure Ikonis effects are applied to the actor.
|
||||||
*/
|
*/
|
||||||
export function patchIkonisLogic() {
|
export function patchIkonisLogic() {
|
||||||
console.log("DH-Ikonis | Patching Actor for Ikonis effect injection...");
|
console.log("DH-Ikonis | Patching Actor for Ikonis effect injection...");
|
||||||
|
|
||||||
// 1. Patch allApplicableEffects to inject Active Effects from Augments
|
|
||||||
const originalAllEffects = Actor.prototype.allApplicableEffects;
|
const originalAllEffects = Actor.prototype.allApplicableEffects;
|
||||||
Actor.prototype.allApplicableEffects = function* () {
|
Actor.prototype.allApplicableEffects = function* (options) {
|
||||||
yield* originalAllEffects.call(this);
|
yield* originalAllEffects.call(this, options);
|
||||||
|
|
||||||
for (const item of this.items) {
|
for (const item of this.items) {
|
||||||
if (item.type !== 'weapon' || !item.system.equipped) continue;
|
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));
|
const aug = allAugs.find(a => String(a.id) === String(id));
|
||||||
if (!aug?.featureUuid) continue;
|
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;
|
if (!feature || !feature.effects) continue;
|
||||||
|
|
||||||
for (const effect of feature.effects) {
|
for (const effect of feature.effects) {
|
||||||
|
|
@ -86,7 +90,7 @@ export function patchIkonisLogic() {
|
||||||
|
|
||||||
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
|
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
|
||||||
if (bondedUuid) {
|
if (bondedUuid) {
|
||||||
const feature = fromUuidSync(bondedUuid);
|
const feature = _featureCache.get(bondedUuid) || fromUuidSync(bondedUuid);
|
||||||
if (feature?.effects) {
|
if (feature?.effects) {
|
||||||
for (const effect of feature.effects) {
|
for (const effect of feature.effects) {
|
||||||
if (effect.transfer) yield effect;
|
if (effect.transfer) yield effect;
|
||||||
|
|
@ -101,10 +105,13 @@ export function patchIkonisLogic() {
|
||||||
* Patches the Character Data Model to show features in the UI lists.
|
* Patches the Character Data Model to show features in the UI lists.
|
||||||
*/
|
*/
|
||||||
export function patchDhCharacter(DhCharacter) {
|
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');
|
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;
|
const originalSheetLists = descriptor.get;
|
||||||
Object.defineProperty(DhCharacter.prototype, 'sheetLists', {
|
Object.defineProperty(DhCharacter.prototype, 'sheetLists', {
|
||||||
|
|
@ -112,33 +119,32 @@ export function patchDhCharacter(DhCharacter) {
|
||||||
const lists = originalSheetLists.call(this);
|
const lists = originalSheetLists.call(this);
|
||||||
const ikonisFeatures = [];
|
const ikonisFeatures = [];
|
||||||
|
|
||||||
// Find equipped Ikonis weapons
|
if (!this.parent) return lists;
|
||||||
|
|
||||||
for (const item of this.parent.items) {
|
for (const item of this.parent.items) {
|
||||||
if (item.type !== 'weapon' || !item.system.equipped) continue;
|
if (item.type !== 'weapon' || !item.system.equipped) continue;
|
||||||
|
|
||||||
const installedIds = item.getFlag('dh-ikonis', 'installedAugments') || [];
|
const installedIds = item.getFlag('dh-ikonis', 'installedAugments') || [];
|
||||||
const allAugs = getAugments();
|
|
||||||
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
|
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
|
||||||
|
|
||||||
// Collect Bonded Feature
|
|
||||||
if (bondedUuid) {
|
if (bondedUuid) {
|
||||||
const feature = fromUuidSync(bondedUuid);
|
const feature = _featureCache.get(bondedUuid) || fromUuidSync(bondedUuid);
|
||||||
if (feature) ikonisFeatures.push(feature);
|
if (feature) ikonisFeatures.push(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect Augments
|
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) continue;
|
if (!aug?.featureUuid) continue;
|
||||||
|
|
||||||
const feature = fromUuidSync(aug.featureUuid);
|
const feature = _featureCache.get(aug.featureUuid) || fromUuidSync(aug.featureUuid);
|
||||||
if (feature) ikonisFeatures.push(feature);
|
if (feature) ikonisFeatures.push(feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ikonisFeatures.length > 0) {
|
if (ikonisFeatures.length > 0) {
|
||||||
// Ensure unique features
|
|
||||||
const uniqueFeatures = Array.from(new Set(ikonisFeatures));
|
const uniqueFeatures = Array.from(new Set(ikonisFeatures));
|
||||||
|
console.log(`DH-Ikonis | Injecting ${uniqueFeatures.length} features for ${this.parent.name}`);
|
||||||
|
|
||||||
lists.ikonis = {
|
lists.ikonis = {
|
||||||
title: "Ikonis Augments",
|
title: "Ikonis Augments",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { patchDHWeapon, patchIkonisLogic, patchDhCharacter, DEFAULT_AUGMENTS } from './ikonis-data.js';
|
import { patchDHWeapon, patchIkonisLogic, patchDhCharacter, loadIkonisFeatures, 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';
|
||||||
|
|
||||||
|
|
@ -82,27 +82,68 @@ Hooks.on('updateItem', (item, changes, options, userId) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.once('ready', async () => {
|
Hooks.once('ready', async () => {
|
||||||
|
console.log("DH-Ikonis | Ready hook triggered.");
|
||||||
|
|
||||||
|
// Sync features from compendium if needed
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
await syncIkonisFeatures();
|
await syncIkonisFeatures();
|
||||||
|
await overrideCurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
const actorsApi = game.system.api.models.actors || {};
|
// Load features into memory for sync getters
|
||||||
const DhCharacter = actorsApi.DhCharacter || actorsApi.character;
|
await loadIkonisFeatures();
|
||||||
|
|
||||||
|
// Patch Character Data Model
|
||||||
|
const DhCharacter = game.system.api?.models?.actors?.DhCharacter || CONFIG.Actor.dataModels?.character;
|
||||||
if (DhCharacter) {
|
if (DhCharacter) {
|
||||||
// Apply visual injection patch
|
|
||||||
patchDhCharacter(DhCharacter);
|
patchDhCharacter(DhCharacter);
|
||||||
|
} else {
|
||||||
Object.defineProperty(DhCharacter.prototype, 'primaryWeapon', {
|
console.warn("DH-Ikonis | Could not find DhCharacter class for patching visual features.");
|
||||||
get: function() { return this.parent.items.find(x => x.type === 'weapon' && x.system.equipped && !x.system.secondary); },
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
Object.defineProperty(DhCharacter.prototype, 'secondaryWeapon', {
|
|
||||||
get: function() { return this.parent.items.find(x => x.type === 'weapon' && x.system.equipped && x.system.secondary); },
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force re-render of open character sheets to show newly patched features
|
||||||
|
Object.values(ui.windows).forEach(w => {
|
||||||
|
if (w.document?.type === 'character') w.render(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function overrideCurrency() {
|
||||||
|
const homebrew = game.settings.get('daggerheart', 'homebrew');
|
||||||
|
// Only override if not already set to Quantum to avoid spamming updates
|
||||||
|
if (homebrew.currency?.title !== "Quantum") {
|
||||||
|
console.log("DH-Ikonis | Overriding currency settings to Quantum...");
|
||||||
|
|
||||||
|
// We must work with a plain object for settings updates
|
||||||
|
const newHomebrew = homebrew.toObject();
|
||||||
|
|
||||||
|
newHomebrew.currency = {
|
||||||
|
title: "Quantum",
|
||||||
|
coins: {
|
||||||
|
enabled: true,
|
||||||
|
label: "Quantum",
|
||||||
|
icon: "fa-solid fa-atom"
|
||||||
|
},
|
||||||
|
handfuls: {
|
||||||
|
enabled: false,
|
||||||
|
label: "Handfuls",
|
||||||
|
icon: "fa-solid fa-coins"
|
||||||
|
},
|
||||||
|
bags: {
|
||||||
|
enabled: false,
|
||||||
|
label: "Bags",
|
||||||
|
icon: "fa-solid fa-sack"
|
||||||
|
},
|
||||||
|
chests: {
|
||||||
|
enabled: false,
|
||||||
|
label: "Chests",
|
||||||
|
icon: "fa-solid fa-treasure-chest"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await game.settings.set('daggerheart', 'homebrew', newHomebrew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue