38d0ba2734
Utiliser les différents repères de cases: - coordonnées TMR A5 - oddq pour les coordonnées de case (ligne, colonne) - axial (q,r) pour effectuer les calculs de distance utiliser x, y rend la distinction de positions de pixels vs position dans la grille parfois ardue. Utilisation des coordonnées axiales pour le calcul de distance.
403 lines
16 KiB
JavaScript
403 lines
16 KiB
JavaScript
/* -------------------------------------------- */
|
|
|
|
import { DialogCreateSigneDraconique } from "./dialog-create-signedraconique.js";
|
|
import { DialogStress } from "./dialog-stress.js";
|
|
import { Grammar } from "./grammar.js";
|
|
import { RdDItemCompetence } from "./item-competence.js";
|
|
import { Misc } from "./misc.js";
|
|
import { RdDCarac } from "./rdd-carac.js";
|
|
import { RdDDice } from "./rdd-dice.js";
|
|
import { RdDNameGen } from "./rdd-namegen.js";
|
|
import { RdDResolutionTable } from "./rdd-resolution-table.js";
|
|
import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js";
|
|
import { RdDRollTables } from "./rdd-rolltables.js";
|
|
import { RdDUtility } from "./rdd-utility.js";
|
|
import { TMRRencontres } from "./tmr-rencontres.js";
|
|
import { TMRUtility } from "./tmr-utility.js";
|
|
|
|
const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
|
|
|
|
/* -------------------------------------------- */
|
|
export class RdDCommands {
|
|
|
|
static init() {
|
|
if (!game.system.rdd.commands) {
|
|
const rddCommands = new RdDCommands();
|
|
rddCommands.registerCommand({ path: ["/aide"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
|
|
rddCommands.registerCommand({ path: ["/help"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
|
|
rddCommands.registerCommand({ path: ["/table", "queues"], func: (content, msg, params) => RdDRollTables.getQueue(true), descr: "Tire une Queue de Dragon" });
|
|
rddCommands.registerCommand({ path: ["/table", "ideefixe"], func: (content, msg, params) => RdDRollTables.getIdeeFixe(true), descr: "Tire une Idée fixe" });
|
|
rddCommands.registerCommand({ path: ["/table", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant(true), descr: "Tire un Désir Lancinant" });
|
|
rddCommands.registerCommand({ path: ["/table", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre(true), descr: "Tire une Ombre de Dragon" });
|
|
rddCommands.registerCommand({ path: ["/table", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR(true), descr: "Tire une Tête de Dragon pour Hauts Revants" });
|
|
rddCommands.registerCommand({ path: ["/table", "tete"], func: (content, msg, params) => RdDRollTables.getTete(true), descr: "Tire une Tête de Dragon" });
|
|
rddCommands.registerCommand({ path: ["/table", "souffle"], func: (content, msg, params) => RdDRollTables.getSouffle(true), descr: " Tire un Souffle de Dragon" });
|
|
rddCommands.registerCommand({ path: ["/table", "comp"], func: (content, msg, params) => RdDRollTables.getCompetence(true), descr: "Tire une compétence au hasard" });
|
|
rddCommands.registerCommand({ path: ["/table", "tarot"], func: (content, msg, params) => RdDRollTables.getTarot(true), descr: "Tire une carte du Tarot Draconique" });
|
|
rddCommands.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.getName(msg, params), descr: "Génère un nom aléatoire" });
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/tmra"], func: (content, msg, params) => rddCommands.getTMRAleatoire(msg, params),
|
|
descr: `Tire une case aléatoire des Terres médianes
|
|
<br><strong>/tmra forêt</strong> détermine une 'forêt' aléatoire
|
|
<br><strong>/tmra</strong> détermine une case aléatoire dans toutes les TMR` });
|
|
rddCommands.registerCommand({
|
|
path: ["/tmr"], func: (content, msg, params) => rddCommands.findTMR(msg, params),
|
|
descr: `Cherche où se trouve une case des Terres médianes
|
|
<br><strong>/tmr? sordide</strong> indique que la cité Sordide est en D13
|
|
<br><strong>/tmr? foret</strong> donne la liste des TMR dont le nom contient "foret" (donc, toutes les forêts)` });
|
|
rddCommands.registerCommand({
|
|
path: ["/tmrr"], func: (content, msg, params) => rddCommands.getRencontreTMR(params),
|
|
descr: `Détermine une rencontre dans un type de case
|
|
<br><strong>/tmrr foret</strong> lance un d100 et détermine la rencontre correspondante en 'forêt'
|
|
<br><strong>/tmrr forêt 47</strong> détermine la rencontre en 'forêt' pour un jet de dé de 47`
|
|
});
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/xp", "comp"], func: (content, msg, params) => rddCommands.getCoutXpComp(msg, params),
|
|
descr: `Détermine le coût d'expérience pour augmenter une compétence. Exemples:
|
|
<br>/xp comp -6 1: pour passer de -6 à +1
|
|
<br>/xp comp +4: pour atteindre le niveau 4 (depuis +3)`
|
|
});
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/xp", "carac"], func: (content, msg, params) => rddCommands.getCoutXpCarac(msg, params),
|
|
descr: `Détermine le coût d'expérience pour augmenter une caractéristique. Exemples:
|
|
<br>/xp carac 15: coût pour atteindre 15 (depuis 14)`
|
|
});
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/rdd"], func: (content, msg, params) => rddCommands.rollRdd(msg, params),
|
|
descr: `Effectue un jet de dés dans la table de résolution. Exemples:
|
|
<br><strong>/rdd</strong> ouvre la table de résolution
|
|
<br><strong>/rdd 10 3</strong> effectue un jet 10 à +3
|
|
<br><strong>/rdd 15 -2</strong> effectue un jet 15 à -2
|
|
<br><strong>/rdd 15 0 s</strong> effectue un jet 15 à 0, avec significative requise
|
|
<br><strong>/rdd Vue Vigilance -2</strong> effectue un jet de Vue/Vigilance à -2 pour les tokens sélectionnés
|
|
<br><strong>/rdd vol déser +2</strong> effectue un jet de Volonté/Survie en désert à +2 pour les tokens sélectionnés
|
|
`
|
|
});
|
|
rddCommands.registerCommand({ path: ["/ddr"], func: (content, msg, params) => rddCommands.rollDeDraconique(msg), descr: "Lance un Dé Draconique" });
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/payer"], func: (content, msg, params) => RdDUtility.afficherDemandePayer(params[0], params[1]),
|
|
descr: `Permet de payer un montant. Exemples:
|
|
<br><strong>/payer 5s 10d</strong> permet d'envoyer un message pour payer 5 sols et 10 deniers
|
|
<br><strong>/payer 10d</strong> permet d'envoyer un message pour payer 10 deniers`
|
|
});
|
|
rddCommands.registerCommand({
|
|
path: ["/astro"], func: (content, msg, params) => RdDUtility.afficherHeuresChanceMalchance(Misc.join(params, ' ')),
|
|
descr: `Affiche les heures de chance et de malchance selon l'heure de naissance donnée en argument. Exemples pour l'heure de la Lyre:
|
|
<br><strong>/astro 7</strong>
|
|
<br><strong>/astro Lyre</strong>
|
|
<br><strong>/astro Lyr</strong>`
|
|
});
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/signe", "+"], func: (content, msg, params) => rddCommands.creerSignesDraconiques(),
|
|
descr: "Crée un signe draconique et l'ajoute aux haut-rêvants choisis."
|
|
});
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/signe", "-"], func: (content, msg, params) => rddCommands.supprimerSignesDraconiquesEphemeres(),
|
|
descr: "Supprime les signes draconiques éphémères"
|
|
});
|
|
|
|
rddCommands.registerCommand({
|
|
path: ["/stress"], func: (content, msg, params) => rddCommands.distribuerStress(params),
|
|
descr: `Distribue du stress aux personnages. Exemples:
|
|
<br><strong>/stress</strong> : Ouvre une fenêtre pour donner du stress ou de l'expérience à un ensemble de personnages
|
|
<br><strong>/stress 6</strong> : Distribue 6 points des Stress à tout les personnages joueurs, sans raison renseignée
|
|
<br><strong>/stress 6 Tigre</strong> : Distribue 6 points des Stress à tout les personnages joueurs, à cause d'un Tigre (Vert)
|
|
<br><strong>/stress 6 Glou Paulo</strong> : Distribue 6 points de Stress au personnage Paulon ou au personnage joueur Paulo, à cause d'un Glou`
|
|
});
|
|
|
|
game.system.rdd.commands = rddCommands;
|
|
}
|
|
}
|
|
constructor() {
|
|
this.commandsTable = {};
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
registerCommand(command) {
|
|
this._addCommand(this.commandsTable, command.path, '', command);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_addCommand(targetTable, path, fullPath, command) {
|
|
if (!this._validateCommand(targetTable, path, command)) {
|
|
return;
|
|
}
|
|
const term = path[0];
|
|
fullPath = fullPath + term + ' '
|
|
if (path.length == 1) {
|
|
command.descr = `<strong>${fullPath}</strong>: ${command.descr}`;
|
|
targetTable[term] = command;
|
|
}
|
|
else {
|
|
if (!targetTable[term]) {
|
|
targetTable[term] = { subTable: {} };
|
|
}
|
|
this._addCommand(targetTable[term].subTable, path.slice(1), fullPath, command)
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_validateCommand(targetTable, path, command) {
|
|
if (path.length > 0 && path[0] && command.descr && (path.length != 1 || targetTable[path[0]] == undefined)) {
|
|
return true;
|
|
}
|
|
console.warn("RdDCommands._validateCommand failed ", targetTable, path, command);
|
|
return false;
|
|
}
|
|
|
|
|
|
/* -------------------------------------------- */
|
|
/* Manage chat commands */
|
|
processChatCommand(commandLine, content = '', msg = {}) {
|
|
// Setup new message's visibility
|
|
let rollMode = game.settings.get("core", "rollMode");
|
|
if (["gmroll", "blindroll"].includes(rollMode)) msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
|
|
if (rollMode === "blindroll") msg["blind"] = true;
|
|
msg["type"] = 0;
|
|
|
|
let command = commandLine[0].toLowerCase();
|
|
let params = commandLine.slice(1);
|
|
|
|
return this.process(command, params, content, msg);
|
|
}
|
|
|
|
process(command, params, content, msg) {
|
|
return this._processCommand(this.commandsTable, command, params, content, msg);
|
|
}
|
|
|
|
_processCommand(commandsTable, name, params, content = '', msg = {}, path = "") {
|
|
let command = commandsTable[name];
|
|
path = path + name + " ";
|
|
if (command && command.subTable) {
|
|
if (params[0]) {
|
|
return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path)
|
|
}
|
|
else {
|
|
this.help(msg, command.subTable);
|
|
return true;
|
|
}
|
|
}
|
|
if (command && command.func) {
|
|
const result = command.func(content, msg, params);
|
|
if (result == false) {
|
|
RdDCommands._chatAnswer(msg, command.descr);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async help(msg) {
|
|
this.help(msg, undefined);
|
|
}
|
|
async help(msg, table) {
|
|
let list = []
|
|
this._buildSubTableHelp(list, table || this.commandsTable);
|
|
|
|
let html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/settings/dialog-aide-commands.html", { commands: list });
|
|
let d = new Dialog(
|
|
{
|
|
title: "Commandes disponibles dans le tchat",
|
|
content: html,
|
|
buttons: {},
|
|
},
|
|
{
|
|
width: 600, height: 500,
|
|
});
|
|
|
|
d.render(true);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _chatAnswer(msg, content) {
|
|
msg.whisper = [game.user.id];
|
|
msg.content = content;
|
|
ChatMessage.create(msg);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_buildSubTableHelp(list, table) {
|
|
for (let [name, command] of Object.entries(table)) {
|
|
if (command) {
|
|
if (command.subTable) {
|
|
this._buildSubTableHelp(list, command.subTable);
|
|
} else {
|
|
list.push(command.descr);
|
|
}
|
|
}
|
|
}
|
|
return list.sort();
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async getRencontreTMR(params) {
|
|
if (params.length == 1 || params.length == 2) {
|
|
return TMRRencontres.rollRencontre(params[0], params[1])
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async rollRdd(msg, params) {
|
|
if (params.length == 0) {
|
|
RdDRollResolutionTable.open();
|
|
}
|
|
else {
|
|
let flatParams = Misc.join(params, ' ');
|
|
const numericParams = flatParams.match(rddRollNumeric);
|
|
if (numericParams) {
|
|
const carac = Misc.toInt(numericParams[1]);
|
|
const diff = Misc.toInt(numericParams[2] || 0);
|
|
const significative = numericParams[3] == 's'
|
|
await this.rollRdDNumeric(msg, carac, diff, significative);
|
|
return;
|
|
}
|
|
|
|
let actors = canvas.tokens.controlled.map(it => it.actor).filter(it => it);
|
|
if (actors && actors.length > 0) {
|
|
let length = params.length;
|
|
let diff = Number(params[length - 1]);
|
|
if (Number.isInteger(Number(diff))) {
|
|
length--;
|
|
}
|
|
else {
|
|
diff = 0;
|
|
}
|
|
const caracName = params[0];
|
|
const compName = length > 1 ? Misc.join(params.slice(1, length), ' ') : undefined;
|
|
for (let actor of actors) {
|
|
await actor.rollCaracCompetence(caracName, compName, diff);
|
|
}
|
|
return;
|
|
}
|
|
else {
|
|
ui.notifications.warn("Sélectionnez au moins un personnage pour lancer les dés")
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async rollRdDNumeric(msg, carac, diff, significative = false) {
|
|
let rollData = {
|
|
caracValue: carac,
|
|
finalLevel: diff,
|
|
diviseurSignificative: significative ? 2 : 1,
|
|
show: { title: "Table de résolution" }
|
|
};
|
|
await RdDResolutionTable.rollData(rollData);
|
|
RdDCommands._chatAnswer(msg, await RdDResolutionTable.buildRollDataHtml(rollData));
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async rollDeDraconique(msg) {
|
|
let ddr = await RdDDice.rollTotal("1dr + 7");
|
|
RdDCommands._chatAnswer(msg, `Lancer d'un Dé draconique: ${ddr}`);
|
|
}
|
|
|
|
async getTMRAleatoire(msg, params) {
|
|
if (params.length < 2) {
|
|
let type = params[0];
|
|
const tmr = await TMRUtility.getTMRAleatoire(type ? (it => it.type == type) : (it => true));
|
|
RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async findTMR(msg, params) {
|
|
const search = Misc.join(params, ' ');
|
|
const found = TMRUtility.findTMR(search);
|
|
if (found?.length > 0) {
|
|
return RdDCommands._chatAnswer(msg, `Les TMRs correspondant à '${search}' sont:` + Misc.join(found.map(it => `<br>${it.coord}: ${it.label}`)));
|
|
}
|
|
return RdDCommands._chatAnswer(msg, 'Aucune TMR correspondant à ' + search);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getCoutXpComp(msg, params) {
|
|
if (params && (params.length == 1 || params.length == 2)) {
|
|
let to = params.length == 1 ? Number(params[0]) : Number(params[1]);
|
|
let from = params.length == 1 ? to - 1 : Number(params[0]);
|
|
RdDCommands._chatAnswer(msg, `Coût pour passer une compétence de ${from} à ${to}: ${RdDItemCompetence.getDeltaXp(from, to)}`);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getCoutXpCarac(msg, params) {
|
|
if (params && params.length == 1) {
|
|
let to = Number(params[0]);
|
|
RdDCommands._chatAnswer(msg, `Coût pour passer une caractéristique de ${to - 1} à ${to}: ${RdDCarac.getCaracXp(to)}`);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async creerSignesDraconiques() {
|
|
DialogCreateSigneDraconique.createSigneForActors();
|
|
return true;
|
|
}
|
|
|
|
async supprimerSignesDraconiquesEphemeres() {
|
|
game.actors.forEach(actor => {
|
|
const ephemeres = actor.filterItems(item => Misc.data(item).type = 'signedraconique' && Misc.data(item).data.ephemere)
|
|
.map(item => item.id);
|
|
if (ephemeres.length > 0) {
|
|
actor.deleteEmbeddedDocuments("Item", ephemeres);
|
|
}
|
|
});
|
|
return true;
|
|
}
|
|
|
|
async distribuerStress(params) {
|
|
if (!game.user.isGM) {
|
|
ui.notifications.warn("Seul le MJ est autorisé à utiliser la commande /stress");
|
|
return false;
|
|
}
|
|
if (params.length < 3) {
|
|
DialogStress.distribuerStress();
|
|
}
|
|
else {
|
|
let stress = params[0]
|
|
if (stress == undefined) {
|
|
ui.notifications.warn("Pas de valeur de stress à distribuer!");
|
|
return;
|
|
}
|
|
|
|
let motif = params.slice(1, params.length - 2);
|
|
let name = params[params.length - 1];
|
|
if (name == undefined) {
|
|
for (let actor of game.actors) {
|
|
actor.distribuerStress('stress', stress, motif);
|
|
}
|
|
} else {
|
|
//console.log(stressValue, nomJoueur);
|
|
let actor = Misc.findActor(name, game.actors.filter(it => it.hasPlayerOwner)) ?? Misc.findPlayer(name)?.character
|
|
if (actor) {
|
|
actor.distribuerStress('stress', stress, motif);
|
|
}
|
|
else {
|
|
ui.notifications.warn(`Pas de personnage ou de joueur correspondant à ${name}!`);
|
|
}
|
|
}
|
|
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|