sotc-damage-wizard : assistant de calcul des dégâts
This commit is contained in:
parent
ad63cafdc6
commit
84aa6b3437
7 changed files with 1532 additions and 0 deletions
263
sotc-battle-show-ability/module/main.js
Normal file
263
sotc-battle-show-ability/module/main.js
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/**
|
||||
* 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.");
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue