import { getAugments, getSlotCount } from './ikonis-data.js'; /** * Patches the Daggerheart Weapon sheet to include the Ikonis tab. * This is more robust than extending the class in Foundry V14. */ export function patchIkonisSheet() { const { Weapon } = game.system.api.applications.sheets.items || {}; if (!Weapon) { console.error("DH-Ikonis | Weapon sheet class not found in system API!"); return; } console.log("DH-Ikonis | Patching Weapon sheet prototype..."); // 1. Add the Ikonis Tab to TABS (on the static class) if (Weapon.TABS?.primary) { const hasTab = Weapon.TABS.primary.tabs.some(t => t.id === 'motherboard'); if (!hasTab) { Weapon.TABS.primary.tabs.push({ id: 'motherboard', label: 'DAGGERHEART.ITEMS.Ikonis.Motherboard', icon: 'fa-solid fa-microchip' }); } } // 2. Add the Motherboard Part to PARTS if (!Weapon.PARTS.motherboard) { Weapon.PARTS.motherboard = { template: 'modules/dh-ikonis/templates/ikonis-motherboard.hbs', scrollable: ['.motherboard-content'] }; } // 3. Patch _prepareContext const originalPrepare = Weapon.prototype._prepareContext; Weapon.prototype._prepareContext = async function(options) { let context = await originalPrepare.call(this, options); try { const doc = this.document; if (!doc) return context; const weaponFeatures = doc.system.weaponFeatures || []; const allAugmentsList = getAugments() || []; const processedAugments = []; let bondedFeature = null; // Identify "Bonded" features by name const allNativeFeatures = CONFIG.DH.ITEM.allWeaponFeatures() || {}; const bondedIds = Object.keys(allNativeFeatures).filter(k => allNativeFeatures[k].name === "Ikonis: Bonded"); // Auto-install if missing (check by name/presence of any bonded ID) const hasBonded = weaponFeatures.some(f => bondedIds.includes(f.value)); if (bondedIds.length > 0 && !hasBonded) { console.log(`DH-Ikonis | Auto-installing Bonded feature on ${doc.name}`); const bondedId = bondedIds.includes("ikonis-bonded") ? "ikonis-bonded" : bondedIds[0]; const newFeatures = [...weaponFeatures, { value: bondedId }]; doc.update({ "system.weaponFeatures": newFeatures }); } for (const featureRef of weaponFeatures) { const nativeId = featureRef.value; // Special handling for Bonded if (bondedIds.includes(nativeId)) { const feature = allNativeFeatures[nativeId]; bondedFeature = { id: nativeId, name: "Bonded", fullName: feature.name, effect: feature.description ? feature.description.replace(/<[^>]*>?/gm, '').substring(0, 100) + "..." : "Primary module", installed: true }; continue; } const base = allAugmentsList.find(a => String(a.id) === String(nativeId)); if (!base) continue; processedAugments.push({ ...base, installed: true }); } context.ikonis = { enabled: true, augments: processedAugments, bonded: bondedFeature, isGM: game.user?.isGM || false }; context.maxSlots = getSlotCount(doc); context.usedSlots = processedAugments.length; // Bonded doesn't count // Explicitly pass the active tab to the template context.activeTab = this.tabGroups?.primary || ""; return context; } catch (err) { console.error("DH-Ikonis | Error in patched _prepareContext:", err); return context; } }; // 4. Patch _onClickAction const originalClick = Weapon.prototype._onClickAction; Weapon.prototype._onClickAction = function(event, target) { const action = target.dataset.action; if (action === "addAugment") return this._onAddAugment(event, target); if (action === "removeAugment") return this._onRemoveAugment(event, target); return originalClick.call(this, event, target); }; // 5. Add custom methods Weapon.prototype._onAddAugment = async function(event, target) { const weaponFeatures = this.document.system.weaponFeatures || []; const allAugments = getAugments(); const augmentIds = allAugments.map(a => a.id); // Identify all features that are recognized as augments (and NOT "Bonded") const allNativeFeatures = CONFIG.DH.ITEM.allWeaponFeatures() || {}; const bondedIds = Object.keys(allNativeFeatures).filter(k => allNativeFeatures[k].name === "Ikonis: Bonded"); const usedSlotsCount = weaponFeatures.filter(f => { if (bondedIds.includes(f.value)) return false; return augmentIds.includes(f.value); }).length; const maxSlots = getSlotCount(this.document); if (usedSlotsCount >= maxSlots) { ui.notifications.warn(`No more augment slots available! (Max: ${maxSlots})`); return; } const available = allAugments.filter(a => !weaponFeatures.some(f => f.value === a.id)); const content = `