/** * MGT2 – Commandes /sector et /subsector * * Ouvre l'application interactive SectorMapApp (IFRAME Traveller Map). * Compatible Foundry VTT v13 et v14 */ import { SectorMapApp } from './SectorMapApp.js'; import { postWorldCardToChat } from './worldCard.js'; import { CommerceDialog } from './CommerceDialog.js'; const MODULE_ID = 'mgt2-compendium-amiral-denisov'; const ChatLogV2 = foundry.applications?.sidebar?.tabs?.ChatLog; let _pendingHandle = false; /* ───── Fonctions partagées ───── */ async function openMap(sector, subsector) { const app = new SectorMapApp(sector, subsector); await app.render({ force: true }); } async function handleSectorCommand(sector, subsector) { if (_pendingHandle) return; _pendingHandle = true; try { if (!game.user?.isGM) { ui.notifications.error('Seul le MJ peut utiliser cette commande'); return; } await openMap(sector?.trim()); } catch (err) { console.error(`${MODULE_ID} | Erreur /sector :`, err); ui.notifications.error(`Erreur : ${err.message}`); } finally { _pendingHandle = false; } } async function handleSystemCommand(sector, hex) { if (!sector || !hex) { ui.notifications.warn('Usage : /system (ex: /system "Spinward Marches" 1910)'); return; } await postWorldCardToChat(sector, hex); } async function handleSubsectorCommand(raw) { if (_pendingHandle) return; _pendingHandle = true; try { const parts = raw?.trim().split(/\s+/); if (!parts || parts.length < 2) { ui.notifications.warn('Usage : /subsector (ex: /subsector "Spinward Marches" C)'); return; } const sub = parts.pop(); const sector = parts.join(' '); if (!sector || !sub) { ui.notifications.warn('Usage : /subsector '); return; } if (!game.user?.isGM) { ui.notifications.error('Seul le MJ peut utiliser cette commande'); return; } await openMap(sector.trim(), sub.trim()); } catch (err) { console.error(`${MODULE_ID} | Erreur /subsector :`, err); ui.notifications.error(`Erreur : ${err.message}`); } finally { _pendingHandle = false; } } /* ───── Commande /sector ───── */ if (ChatLogV2?.CHAT_COMMANDS) { ChatLogV2.CHAT_COMMANDS['sector'] = { rgx: /^\/sector(?:\s+(.*))?$/i, fn: function() { const raw = arguments[1]?.[1]?.trim?.(); handleSectorCommand(raw || undefined); return false; }, }; console.log(`${MODULE_ID} | Commande /sector enregistrée`); ChatLogV2.CHAT_COMMANDS['subsector'] = { rgx: /^\/subsector(?:\s+(.*))?$/i, fn: function() { const raw = arguments[1]?.[1]?.trim?.(); if (raw) { handleSubsectorCommand(raw); return false; } return true; }, }; console.log(`${MODULE_ID} | Commande /subsector enregistrée`); ChatLogV2.CHAT_COMMANDS['system'] = { rgx: /^\/system\s+(.+?)\s+(\d{4})\s*$/i, fn: function() { const sector = arguments[1]?.[1]?.trim?.(); const hex = arguments[1]?.[2]?.trim?.(); if (sector && hex) { handleSystemCommand(sector, hex); return false; } return true; }, }; console.log(`${MODULE_ID} | Commande /system enregistrée`); } /* ───── Hooks de secours (v13 / fallback v14) ───── */ Hooks.on('preCreateChatMessage', (message, data, options) => { const c = message.content?.trim(); let m = c?.match(/^\/sector(?:\s+(.*))?$/i); if (m) { handleSectorCommand(m[1]?.trim()); return false; } m = c?.match(/^\/subsector(?:\s+(.*))?$/i); if (m) { handleSubsectorCommand(m[1]?.trim()); return false; } m = c?.match(/^\/system\s+(.+?)\s+(\d{4})\s*$/i); if (m) { handleSystemCommand(m[1]?.trim(), m[2]?.trim()); return false; } }); /* ───── Socket (synchronisation MJ → joueurs) ───── */ Hooks.once('ready', () => { game.socket.on(`module.${MODULE_ID}`, (data) => { if (data?.type !== 'sectorMapSync') return; if (game.user?.isGM) return; openMap(data.sector, data.subsector); }); // Clics sur les liens de monde dans les journals de trajet document.addEventListener('click', (event) => { const link = event.target.closest('.mgt2-world-link'); if (!link) return; event.preventDefault(); const sector = link.dataset.sector; const hex = link.dataset.hex; if (sector && hex) { handleSystemCommand(sector, hex); } }); // Clics sur le bouton Commerce dans les cartes de monde document.addEventListener('click', (event) => { const btn = event.target.closest('.mgt2-world-commerce'); if (!btn) return; event.preventDefault(); const uwp = btn.dataset.uwp; const zone = btn.dataset.zone; const name = btn.dataset.name; const sector = btn.dataset.sector; const hex = btn.dataset.hex; if (uwp) { const existing = Object.values(ui.windows).find(w => w.id === 'mgt2-commerce'); if (existing) { existing.bringToTop(); return; } const dialog = new CommerceDialog({ defaultWorld: { uwp, zone, name, sector, hex }, initialTab: 'trade', }); dialog.render({ force: true }); } }); }); Hooks.on('chatMessage', (...args) => { let msg; if (args[0]?.content !== undefined) msg = args[0].content; else if (typeof args[1] === 'string') msg = args[1]; else return; let m = msg?.trim()?.match(/^\/sector(?:\s+(.*))?$/i); if (m) { handleSectorCommand(m[1]?.trim()); return false; } m = msg?.trim()?.match(/^\/subsector(?:\s+(.*))?$/i); if (m) { handleSubsectorCommand(m[1]?.trim()); return false; } m = msg?.trim()?.match(/^\/system\s+(.+?)\s+(\d{4})\s*$/i); if (m) { handleSystemCommand(m[1]?.trim(), m[2]?.trim()); return false; } });