dh-ikonis/scripts/ikonis-data.js

194 lines
7.9 KiB
JavaScript

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 caches for resolved features to keep getters fast
const _featureCache = 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}`);
}
/**
* Robust feature fetching. Tries cache first, then async fromUuid.
*/
export async function getAttachedFeature(uuid) {
if (!uuid) return null;
if (_featureCache.has(uuid)) return _featureCache.get(uuid);
return await fromUuid(uuid);
}
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;
}
}
/**
* 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...");
const originalAllEffects = Actor.prototype.allApplicableEffects;
Actor.prototype.allApplicableEffects = function* (options) {
yield* originalAllEffects.call(this, options);
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;
// 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) {
if (effect.transfer) {
console.log(`DH-Ikonis | Injecting effect '${effect.name}' from augment '${feature.name}' on ${this.name}`);
// Clone the effect and set this actor as parent to ensure application
const effectData = effect.toObject();
effectData.disabled = false;
const ActiveEffectClass = getDocumentClass("ActiveEffect");
const effectInstance = new ActiveEffectClass(effectData, { parent: this });
yield effectInstance;
}
}
}
const bondedUuid = item.getFlag('dh-ikonis', 'bondedFeatureUuid');
if (bondedUuid) {
const feature = _featureCache.get(bondedUuid) || fromUuidSync(bondedUuid);
if (feature?.effects) {
for (const effect of feature.effects) {
if (effect.transfer) {
console.log(`DH-Ikonis | Injecting bonded effect '${effect.name}' from '${feature.name}' on ${this.name}`);
const effectData = effect.toObject();
effectData.disabled = false;
const ActiveEffectClass = getDocumentClass("ActiveEffect");
const effectInstance = new ActiveEffectClass(effectData, { parent: this });
yield effectInstance;
}
}
}
}
}
};
}
/**
* Synchronizes Ikonis Augments into the Daggerheart Homebrew settings.
* This makes them "Real" Weapon Features to the system.
*/
export async function syncIkonisToHomebrew() {
if (!game.user.isGM) return;
const MODULE_ID = 'dh-ikonis';
const homebrewKey = game.settings.settings.has('daggerheart.Homebrew') ? 'Homebrew' : 'homebrew';
const homebrew = game.settings.get('daggerheart', homebrewKey);
if (!homebrew.itemFeatures) homebrew.itemFeatures = { weaponFeatures: {}, armorFeatures: {} };
if (!homebrew.itemFeatures.weaponFeatures) homebrew.itemFeatures.weaponFeatures = {};
let updates = false;
const allAugments = getAugments();
for (const aug of allAugments) {
const feature = _featureCache.get(aug.featureUuid) || await fromUuid(aug.featureUuid);
if (feature && !homebrew.itemFeatures.weaponFeatures[aug.id]) {
console.log(`DH-Ikonis | Registering ${aug.name} as native weapon feature...`);
// Format actions for the system's Homebrew model
const actions = {};
if (feature.system.actions) {
for (const [id, action] of Object.entries(feature.system.actions)) {
actions[id] = action.toObject();
}
}
homebrew.itemFeatures.weaponFeatures[aug.id] = {
name: aug.name,
img: aug.img || feature.img,
description: feature.system.description,
actions: actions,
effects: Array.from(feature.effects || []).map(e => e.toObject())
};
updates = true;
}
}
if (updates) {
await game.settings.set('daggerheart', homebrewKey, homebrew);
console.log("DH-Ikonis | Homebrew settings synchronized.");
}
}
/**
* Patches the system's weapon data preparation to handle slot counts.
*/
export function patchIkonisLogic() {
// We no longer need to patch Actor.allApplicableEffects
// because the system handles native weapon features automatically.
}
/**
* Placeholder for character patching - no longer needed for virtual items
*/
export function patchDhCharacter(DhCharacter) {
// Injection is deprecated in favor of native weapon features
}
export function patchDHWeapon() {
// Future: Add damage type override logic here
}