263 lines
8.3 KiB
JavaScript
263 lines
8.3 KiB
JavaScript
/**
|
|
* sotc-battle-show-ability — main.js
|
|
* Point d'entrée du module.
|
|
*/
|
|
|
|
import { SpeedDiePanelApp } from "./SpeedDiePanelApp.js";
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Initialisation */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
Hooks.once("init", () => {
|
|
console.log("sotc-battle-show-ability | Initialisation du module");
|
|
|
|
loadTemplates([
|
|
"modules/sotc-battle-show-ability/templates/speed-die-panel.hbs"
|
|
]);
|
|
|
|
// Note : le helper Handlebars "eq" est déjà enregistré par le système SotC.
|
|
});
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Menu contextuel dans le Combat Tracker */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
Hooks.on("getCombatTrackerEntryContext", (html, options) => {
|
|
if (game.system.id !== "sotc") return;
|
|
|
|
options.push({
|
|
name: game.i18n.localize("SOTC_BSA.Panel.Title"),
|
|
icon: '<i class="fas fa-dice-d6"></i>',
|
|
condition: (li) => {
|
|
if (!game.combat) return false;
|
|
const combatantId = li.data("combatant-id");
|
|
const combatant = game.combat.combatants.get(combatantId);
|
|
if (!combatant) return false;
|
|
return combatant.isOwner || game.user.isGM;
|
|
},
|
|
callback: (li) => {
|
|
const combatantId = li.data("combatant-id");
|
|
_openSpeedDiePanel(combatantId);
|
|
}
|
|
});
|
|
});
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Ouverture du panneau */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
function _openSpeedDiePanel(combatantId) {
|
|
const existing = Object.values(ui.windows).find(
|
|
w => w instanceof SpeedDiePanelApp && w.combatantId === combatantId
|
|
);
|
|
|
|
if (existing) {
|
|
existing.render(true);
|
|
existing.bringToTop();
|
|
return;
|
|
}
|
|
|
|
const panel = new SpeedDiePanelApp(combatantId);
|
|
|
|
const combatant = game.combat?.combatants.get(combatantId);
|
|
if (combatant) {
|
|
const numDice = combatant.actor?.system?.speed_dice?.num_dice ?? 1;
|
|
// Index visuel : position dans le tri par initiative décroissante
|
|
const sorted = game.combat.combatants
|
|
.filter(c => c.tokenId === combatant.tokenId)
|
|
.sort((a, b) => (b.initiative ?? -Infinity) - (a.initiative ?? -Infinity));
|
|
const dieIndex = sorted.findIndex(c => c.id === combatant.id) + 1;
|
|
panel.options.title = numDice > 1
|
|
? `${game.i18n.localize("SOTC_BSA.Panel.SpeedDie")} #${dieIndex} — ${combatant.name}`
|
|
: `${game.i18n.localize("SOTC_BSA.Panel.Title")} — ${combatant.name}`;
|
|
}
|
|
|
|
panel.render(true);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Clic droit sur les marqueurs PIXI de SIO */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
function _installSIORightClickHandler() {
|
|
const board = document.getElementById("board");
|
|
if (!board) return;
|
|
|
|
if (board._bsaRightClickHandler) {
|
|
board.removeEventListener("mousedown", board._bsaRightClickHandler);
|
|
}
|
|
|
|
const handler = async (event) => {
|
|
if (event.button !== 2) return;
|
|
if (!game.combat?.active) return;
|
|
|
|
const rect = board.getBoundingClientRect();
|
|
const screenX = event.clientX - rect.left;
|
|
const screenY = event.clientY - rect.top;
|
|
|
|
for (const token of canvas.tokens.placeables) {
|
|
const markers = token._initiativeMarkers;
|
|
if (!markers?.length) continue;
|
|
|
|
for (let i = 0; i < markers.length; i++) {
|
|
const m = markers[i];
|
|
const b = m.getBounds();
|
|
|
|
const inside =
|
|
screenX >= b.x &&
|
|
screenX <= b.x + b.width &&
|
|
screenY >= b.y &&
|
|
screenY <= b.y + b.height;
|
|
|
|
if (!inside) continue;
|
|
|
|
const combatants = game.combat.combatants
|
|
.filter(c => c.tokenId === token.document.id)
|
|
.sort((a, b) => (b.initiative ?? -Infinity) - (a.initiative ?? -Infinity));
|
|
|
|
const combatant = combatants[i];
|
|
if (!combatant) return;
|
|
|
|
if (!combatant.isOwner && !game.user.isGM) return;
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
_openSpeedDiePanel(combatant.id);
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
board._bsaRightClickHandler = handler;
|
|
board.addEventListener("mousedown", handler);
|
|
|
|
if (!board._bsaContextMenuHandler) {
|
|
board._bsaContextMenuHandler = (event) => {
|
|
const rect = board.getBoundingClientRect();
|
|
const screenX = event.clientX - rect.left;
|
|
const screenY = event.clientY - rect.top;
|
|
for (const token of canvas.tokens.placeables) {
|
|
const markers = token._initiativeMarkers;
|
|
if (!markers?.length) continue;
|
|
for (const m of markers) {
|
|
const b = m.getBounds();
|
|
if (screenX >= b.x && screenX <= b.x + b.width &&
|
|
screenY >= b.y && screenY <= b.y + b.height) {
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
board.addEventListener("contextmenu", board._bsaContextMenuHandler);
|
|
}
|
|
}
|
|
|
|
Hooks.on("canvasReady", () => {
|
|
_installSIORightClickHandler();
|
|
});
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Réinitialisation au début d'un nouveau round */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
async function _resetAllCombatants(combat) {
|
|
if (!game.user.isGM) return;
|
|
|
|
const updates = [];
|
|
|
|
for (const combatant of combat.combatants) {
|
|
const actor = combatant.actor;
|
|
if (!actor?.system?.speed_dice) continue;
|
|
|
|
updates.push({
|
|
_id: combatant.id,
|
|
"flags.sotc-battle-show-ability.dieState": 0,
|
|
"flags.sotc-battle-show-ability.assignedSkillId": null,
|
|
"flags.sotc.used": false
|
|
});
|
|
}
|
|
|
|
if (updates.length > 0) {
|
|
await combat.updateEmbeddedDocuments("Combatant", updates);
|
|
console.log(`sotc-battle-show-ability | ${updates.length} dé(s) réinitialisé(s)`);
|
|
}
|
|
|
|
_refreshOpenPanels();
|
|
}
|
|
|
|
Hooks.on("combatRound", async (combat, roundData, options) => {
|
|
if (game.system.id !== "sotc") return;
|
|
console.log("sotc-battle-show-ability | Nouveau round — réinitialisation des dés de vitesse");
|
|
await _resetAllCombatants(combat);
|
|
});
|
|
|
|
Hooks.on("deleteCombat", async (combat, options, userId) => {
|
|
// Fermer les panneaux ouverts
|
|
for (const win of Object.values(ui.windows)) {
|
|
if (win instanceof SpeedDiePanelApp) win.close();
|
|
}
|
|
// Reset des flags (utile si le combat est supprimé sans être terminé)
|
|
console.log("sotc-battle-show-ability | Fin du combat — réinitialisation des dés de vitesse");
|
|
await _resetAllCombatants(combat);
|
|
});
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Rafraîchissement réactif des panneaux ouverts */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
function _refreshOpenPanels() {
|
|
for (const win of Object.values(ui.windows)) {
|
|
if (win instanceof SpeedDiePanelApp) {
|
|
if (!win._checkStillValid()) continue;
|
|
win.render(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
Hooks.on("updateCombatant", (combatant, changes, options, userId) => {
|
|
if (game.system.id !== "sotc") return;
|
|
|
|
for (const win of Object.values(ui.windows)) {
|
|
if (win instanceof SpeedDiePanelApp && win.combatantId === combatant.id) {
|
|
win.render(false);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
Hooks.on("updateActor", (actor, changes, options, userId) => {
|
|
if (game.system.id !== "sotc") return;
|
|
|
|
for (const win of Object.values(ui.windows)) {
|
|
if (win instanceof SpeedDiePanelApp && win.actor?.id === actor.id) {
|
|
win.render(false);
|
|
}
|
|
}
|
|
});
|
|
|
|
Hooks.on("deleteCombatant", (combatant, options, userId) => {
|
|
for (const win of Object.values(ui.windows)) {
|
|
if (win instanceof SpeedDiePanelApp && win.combatantId === combatant.id) {
|
|
win.close();
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* API publique */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
Hooks.once("ready", () => {
|
|
game.modules.get("sotc-battle-show-ability").api = {
|
|
openSpeedDiePanel: _openSpeedDiePanel,
|
|
SpeedDiePanelApp
|
|
};
|
|
|
|
console.log("sotc-battle-show-ability | Module prêt.");
|
|
});
|