import { getAugments, getSlotCount, getAttachedFeature } 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 // We REMOVE the 'tab' property here to prevent Foundry from automatically hiding it // instead, we will handle visibility in the template. 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 installedIds = doc.getFlag('dh-ikonis', 'installedAugments') || []; const allAugmentsList = getAugments() || []; const processedAugments = []; for (const id of installedIds) { const base = allAugmentsList.find(a => String(a.id) === String(id)); if (!base) continue; const aug = { ...base, installed: true }; if (aug.featureUuid) { const item = await getAttachedFeature(aug.featureUuid); if (item) aug.feature = { name: item.name, img: item.img, uuid: item.uuid }; } processedAugments.push(aug); } const bondedUuid = doc.getFlag('dh-ikonis', 'bondedFeatureUuid') || game.settings.get('dh-ikonis', 'defaultBondedUuid'); const bonded = { enabled: true, feature: null }; if (bondedUuid) { const item = await getAttachedFeature(bondedUuid); if (item) bonded.feature = { name: item.name, img: item.img, uuid: item.uuid }; } context.ikonis = { enabled: true, augments: processedAugments, bonded: bonded, isGM: game.user?.isGM || false }; context.maxSlots = getSlotCount(doc); context.usedSlots = processedAugments.length; // 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 installedIds = this.document.getFlag('dh-ikonis', 'installedAugments') || []; const allAugments = getAugments(); const validInstalled = installedIds.filter(id => allAugments.some(a => String(a.id) === String(id))); const maxSlots = getSlotCount(this.document); if (validInstalled.length >= maxSlots) { ui.notifications.warn(`No more augment slots available! (Max: ${maxSlots})`); return; } const available = allAugments.filter(a => !validInstalled.includes(String(a.id))); const content = `
${available.map(a => `
${a.name} ${a.effect} Cost: ${a.cost}
`).join('')}
`; let selectedId = null; const res = await foundry.applications.api.DialogV2.wait({ window: { title: "Install Tech" }, content: content, buttons: [ { action: "ok", label: "Install", icon: "fa-solid fa-download", callback: () => selectedId }, { action: "cancel", label: "Cancel", icon: "fa-solid fa-times" } ], render: (e, app) => { const search = app.element.querySelector('#augment-search'); if (search) search.focus(); search?.addEventListener('input', (event) => { const query = event.target.value.toLowerCase(); app.element.querySelectorAll('.picker-item').forEach(el => { el.style.display = el.innerText.toLowerCase().includes(query) ? 'block' : 'none'; }); }); app.element.querySelectorAll('.picker-item').forEach(el => { el.addEventListener('click', () => { app.element.querySelectorAll('.picker-item').forEach(i => { i.style.borderColor = "#2d3436"; i.style.background = "#1a1a2e"; i.classList.remove('selected'); }); el.style.borderColor = "#00d2ff"; el.style.background = "rgba(0, 210, 255, 0.15)"; el.classList.add('selected'); selectedId = el.dataset.id; }); }); } }); if (res && res !== "cancel") { const newIds = [...validInstalled, String(res)]; await this.document.update({ "flags.dh-ikonis.installedAugments": newIds }); this.render(true); } }; Weapon.prototype._onRemoveAugment = async function(event, target) { const installedIds = this.document.getFlag('dh-ikonis', 'installedAugments') || []; const newIds = installedIds.filter(id => id !== String(target.dataset.id)); await this.document.update({ "flags.dh-ikonis.installedAugments": newIds }); this.render(true); }; console.log("DH-Ikonis | Weapon sheet patched successfully."); }