Merge branch 'chat-improve' into 'v1.2'

Chat improve

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!95
This commit is contained in:
Leratier Bretonnien 2020-12-31 08:49:30 +00:00
commit 2355406546
13 changed files with 488 additions and 184 deletions

View File

@ -517,7 +517,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async sortMisEnReserve(rollData, sort) {
let reserve = duplicate(this.data.data.reve.reserve);
reserve.list.push({ coord: rollData.coord, sort: sort, draconic: duplicate(rollData.selectedDraconic) });
reserve.list.push({ coord: rollData.coord, sort: sort, draconic: duplicate(rollData.competence) });
await this.update({ "data.reve.reserve": reserve });
this.currentTMR.updateSortReserve();
}
@ -1290,14 +1290,14 @@ export class RdDActor extends Actor {
createCallbackExperience() {
return {
condition: r => r.rolled.isPart && r.finalLevel < 0 && game.settings.get("core", "rollMode") != 'selfroll',
action: r => this._appliquerAjoutExperience(r)
action: r => this._appliquerAjoutExperience(r, game.settings.get("core", "rollMode") != 'blindroll')
};
}
/* -------------------------------------------- */
async _appliquerAjoutExperience(rollData) {
let xpResult = this.appliquerExperience( rollData.rolled, rollData.selectedCarac.label, (rollData.competence) ? rollData.competence.data.name: undefined );
if (xpResult.result ) {
async _appliquerAjoutExperience(rollData, display=true) {
let xpResult = this.appliquerExperience( rollData.rolled, rollData.selectedCarac.label, rollData.competence);
if (display && xpResult.result ) {
let xpmsg = "<br>Points d'expérience gagnés ! Carac: " + xpResult.xpCarac + ", Comp: " + xpResult.xpCompetence;
let message = ChatUtility.prepareChatMessage('gmroll', this.name);
message.content = "<strong>" + rollData.selectedCarac.label + "</strong>"
@ -1319,7 +1319,7 @@ export class RdDActor extends Actor {
selectedCarac: this.data.data.carac.reve,
draconicList: this.getDraconicList(),
sortList: sortList,
selectedDraconic: this.getBestDraconic(),
competence: this.getBestDraconic(),
selectedSort: sortList[0],
coord: coord,
coordLabel: TMRUtility.getTMRDescription( coord).label,
@ -1447,82 +1447,67 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async _rollUnSortResult(rollData, isSortReserve = false) {
rollData.isSortReserve = isSortReserve;
let rolled = rollData.rolled;
let sort = rollData.selectedSort;
let closeTMR = !rollData.isSortReserve;
if (sort.data.isrituel && isSortReserve) {
ui.notifications.error("Impossible de mettre le rituel '" + sort.name + "' en réserve");
let selectedSort = rollData.selectedSort;
let closeTMR = !isSortReserve;
if (selectedSort.data.isrituel && isSortReserve) {
ui.notifications.error("Impossible de mettre le rituel '" + selectedSort.name + "' en réserve");
this.currentTMR.close(); // Close TMR !
return;
}
let explications = rollData.isSortReserve
? ("<br>Mise en réserve du sort en " + rollData.coordLabel + "(" + rollData.coord + ")<strong>")
: "<br>Lancement du sort <strong>";
explications += sort.name + "</strong> : " + Misc.upperFirst(sort.data.draconic)
+ " pour " + sort.data.ptreve_reel + " points de Rêve"
+ "<br>Depuis la case " + rollData.coord + " (" + TMRUtility.getTMRDescription(rollData.coord).label + ")";
rollData.isSortReserve = isSortReserve;
rollData.show = {}
rollData.depenseReve = Number(selectedSort.data.ptreve_reel);
let depenseReve = Number(sort.data.ptreve_reel);
let myReve = duplicate(this.data.data.reve.reve);
if (rolled.isSuccess) { // Réussite du sort !
//sort.ptreve_reel = coutReve;
if (rolled.isPart) {
depenseReve = Math.max(Math.floor(depenseReve / 2), 1);
rollData.depenseReve = Math.max(Math.floor(rollData.depenseReve / 2), 1);
}
if (rollData.isSortReserve) {
depenseReve++;
rollData.depenseReve++;
}
if (myReve.value > depenseReve) {
explications += "<br>Réussite du sort: " + depenseReve + " points de Rêve sont dépensés (Bonus de case en " + rollData.coord + ": +" + rolled.bonus + "%)";
if (myReve.value > rollData.depenseReve) {
// Incrémenter/gére le bonus de case
RdDItemSort.incrementBonusCase(this, sort, rollData.coord);
RdDItemSort.incrementBonusCase(this, selectedSort, rollData.coord);
if (rollData.isSortReserve) {
await this.sortMisEnReserve(rollData, sort);
await this.sortMisEnReserve(rollData, selectedSort);
closeTMR = false;
}
}
else {
// Todo 0 pts de reve !!!!
depenseReve = 0;
explications += "<br>Pas assez de rêve";
mergeObject(rollData, RdDResolutionTable.getResultat("echec"));
rollData.depenseReve = 0;
rollData.show.reveInsuffisant = true;
mergeObject(rollData.rolled, RdDResolutionTable.getResultat("echec"), {overwrite:true});
}
} else {
if (rolled.isETotal) { // Echec total !
depenseReve = Math.min(myReve.value, Math.floor(depenseReve * 1.5));
explications += "<br><strong>Echec TOTAL</strong> du sort : " + depenseReve + " Points de Rêve";
rollData.depenseReve = Math.min(myReve.value, Math.floor(rollData.depenseReve * 1.5))
// TODO: mise en réserve d'un échec total...
} else {
depenseReve = 0
explications += "<br>Echec du sort !";
rollData.depenseReve = 0
}
}
myReve.value = Math.max(myReve.value - depenseReve, 0);
myReve.value = Math.max(myReve.value - rollData.depenseReve, 0);
await this.update({ "data.reve.reve": myReve });
if (myReve.value == 0) { // 0 points de reve
ChatMessage.create({ content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" });
closeTMR = true;
}
if (closeTMR) {
this.currentTMR.close(); // Close TMR !
} else {
this.currentTMR.maximize(); // Re-display TMR
}
// Final chat message
let chatOptions = {
content: "<strong>Test : " + rollData.selectedCarac.label + " / " + rollData.selectedDraconic.name + " / " + rollData.selectedSort.name + "</strong>"
+ RdDResolutionTable.explainRollData(rollData)
+ explications
}
let chatOptions = { content: await RdDResolutionTable.explainRollDataV2(rollData, 'chat-resultat-sort.html') }
ChatUtility.chatWithRollMode(chatOptions, this.name)
if (myReve.value == 0) { // 0 points de reve
ChatMessage.create({ content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" });
closeTMR = true;
}
}
/* -------------------------------------------- */
@ -1548,17 +1533,12 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async _rollCaracResult(rollData) {
let rolled = rollData.rolled;
let resumeCompetence = (rollData.diffLibre + rollData.diffConditions);
let explications = "<br>Points de taches : " + rolled.ptTache;
rollData.show = {
title: rollData.selectedCarac.label,
points: true
};
// Final chat message
let chatOptions = {
content: "<strong>Test : " + rollData.selectedCarac.label + " / " + resumeCompetence + "</strong>"
+ RdDResolutionTable.explainRollData(rollData)
+ explications
}
let chatOptions = { content: await RdDResolutionTable.explainRollDataV2(rollData) }
ChatUtility.chatWithRollMode(chatOptions, this.name)
}
@ -1740,7 +1720,7 @@ export class RdDActor extends Actor {
}
/* -------------------------------------------- */
async appliquerExperience( rolled, caracName, competenceName = undefined ) {
async appliquerExperience( rolled, caracName, competence = undefined ) {
if ( rolled.isPart && rolled.finalLevel < 0) {
// Cas de désir lancinant, pas d'expérience sur particulière
@ -1751,37 +1731,38 @@ export class RdDActor extends Actor {
}
if (caracName == 'derobee') caracName = 'agilite';
let carac = duplicate(this.actor.data.data.carac);
let xp = Math.abs(rolled.finalLevel);
let xpCarac = Math.floor(xp / 2); // impair: arrondi inférieur en carac
let xpComp = 0;
if ( competenceName ) {
if ( competence ) {
xpComp = xp - xpCarac;
let competence = duplicate( RdDUtility.findCompetence( this.data.items, competenceName ) );
competence.data.xp += xpComp;
competence = duplicate(competence );
competence.data.xp = Misc.toInt(competence.data.xp) + xpComp;
await this.updateEmbeddedEntity( "OwnedItem", competence);
} else {
xpCarac = Math.max(xpCarac, 1);
}
if ( !carac[caracName].isderivee) {
carac[caracName].xp += xpCarac;
if (xpCarac > 0) {
let carac = duplicate(this.data.data.carac);
let selectedCarac = RdDActor._findCaracByName(carac, caracName);
if ( !selectedCarac.derivee) {
selectedCarac.xp = Misc.toInt(selectedCarac.xp) + xpCarac;
await this.update( {"data.carac": carac } );
} else {
ChatMessage.create( { content: `Vous avez ${xpCarac} à répartir pour la caractérisque dérivée ${caracName}. Vous devez le faire manuellement.`,
whisper: ChatMessage.getWhisperRecipients(game.user.name) } );
}
}
return { result:true, xpcarac:xpCarac, xpCompetence: xpComp }; //XP
}
return { result:false, xpcarac:0, xpCompetence: 0 }; // Pas d'XP
}
/* -------------------------------------------- */
async ajouteNombreAstral( data ) {
// Gestion expérience (si existante)
this.appliquerExperience( data.rolled, "vue", "astrologie");
let astrologie = RdDUtility.findCompetence( this.data.items, "astrologie");
this.appliquerExperience( data.rolled, "vue", astrologie);
// Ajout du nombre astral
const item = {name: "Nombre Astral", type: "nombreastral", data:
@ -1808,23 +1789,39 @@ export class RdDActor extends Actor {
getCaracByName(caracName) {
switch (caracName)
{
case 'reve-actuel':
case 'reve-actuel': case 'Rêve actuel':
return {
label: 'Rêve Actuel',
label: 'Rêve actuel',
value: this.getReveActuel(),
type: "number",
ignoreEtatGeneral: true
};
case 'chance-actuelle':
case 'chance-actuelle': case 'Chance actuelle':
return {
type: "number",
value: this.getChanceActuel(),
label: 'Chance actuelle',
value: this.getChanceActuel(),
type: "number",
ignoreEtatGeneral: true
};
default:
return this.data.data.carac[caracName]; // Per default
}
return RdDActor._findCaracByName(this.data.data.carac, caracName);
}
static _findCaracByName(carac, name) {
name = name.toLowerCase();
switch (name)
{
case 'reve-actuel': case 'rêve actuel':
return carac.reve;
case 'chance-actuelle': case 'chance actuelle':
return carac.chance;
}
for (const [key, value] of Object.entries(carac)) {
if (name == key || name == value.label.toLowerCase()) {
return carac[key];
}
}
return carac[name]; // Per default
}
/* -------------------------------------------- */

185
module/rdd-commands.js Normal file
View File

@ -0,0 +1,185 @@
/* -------------------------------------------- */
import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRollResolution } from "./rdd-roll-resolution.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { RdDUtility } from "./rdd-utility.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"], descr: "Affiche l'aide pour toutes les commandes", func: (content, msg, params) => rddCommands.help(msg) });
rddCommands.registerCommand({ path: ["/help"], descr: "Affiche l'aide pour toutes les commandes", func: (content, msg, params) => rddCommands.help(msg) });
rddCommands.registerCommand({ path: ["/table", "queues"], descr: "Tire une Queue de Dragon", func: (content, msg, params) => RdDRollTables.getQueue() });
rddCommands.registerCommand({ path: ["/table", "ombre"], descr: "Tire une Ombre de Dragon", func: (content, msg, params) => RdDRollTables.getOmbre() });
rddCommands.registerCommand({ path: ["/table", "tetehr"], descr: "Tire une Tête de Dragon pour Hauts Revants", func: (content, msg, params) => RdDRollTables.getTeteHR() });
rddCommands.registerCommand({ path: ["/table", "tete"], descr: "Tire une Tête de Dragon", func: (content, msg, params) => RdDRollTables.getTete() });
rddCommands.registerCommand({ path: ["/table", "souffle"], descr: " Tire un Souffle de Dragon", func: (content, msg, params) => RdDRollTables.getSouffle() });
rddCommands.registerCommand({ path: ["/table", "tarot"], descr: "Tire une carte du Tarot Draconique", func: (content, msg, params) => RdDRollTables.getTarot() });
rddCommands.registerCommand({ path: ["/table", "tmr"], descr: "Tire une case aléatoire des Terre médiane", func: (content, msg, params) => TMRUtility.getTMRAleatoire() });
rddCommands.registerCommand({ path: ["/tmra"], descr: "Tire une case aléatoire des Terre médiane", func: (content, msg, params) => TMRUtility.getTMRAleatoire() });
rddCommands.registerCommand({ path: ["/tmrr"], descr: "Syntaxe: <strong>/tmrr case jet</strong><br>Détermine quelle est la rencontre dans la case pour le jet<br>Example: <strong>/tmrr forêt 50</strong>", func: (content, msg, params) => rddCommands.getRencontreTMR(params) });
rddCommands.registerCommand({
path: ["/rdd"], descr: `Effectue un jet de dés dans la table de résolution. Examples:
<br><strong>/rdd</strong> ouvre la table de résolution
<br><strong>/rdd 10 3</strong> effectue un jet 10 à +3
<br><strong>/rdd 10 +2</strong> effectue un jet 10 à +2
<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
`, func: (content, msg, params) => rddCommands.rollRdd(msg, params)
});
rddCommands.registerCommand({ path: ["/payer"], 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
`, func: (content, msg, params) => RdDUtility.afficherDemandePayer(params[0], params[1])});
game.system.rdd.commands = rddCommands;
}
}
constructor() {
this.commandsTable = {};
}
registerCommand(command) {
this._addCommand(this.commandsTable, command.path, command);
}
_addCommand(targetTable, path, command) {
if (!this._validateCommand(targetTable, path, command)) {
return;
}
const term = path[0];
if (path.length == 1) {
targetTable[term] = command;
}
else {
if (!targetTable[term]) {
targetTable[term] = { subTable: {} };
}
this._addCommand(targetTable[term].subTable, path.slice(1), 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];
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) {
if (command.func(content, msg, params) === false) {
this._displayHelp(msg, `${path}: ${command.descr}`);
}
return true;
}
return false;
}
/* -------------------------------------------- */
help(msg, table = undefined) {
let list = []
this._buildSubTableHelp(list, '', table || this.commandsTable);
msg.whisper = [game.user._id];
msg.content = 'Commandes disponibles<ul class="alterne-list"><li class="list-item">' + list.reduce((a, b) => a + '</li><li class="list-item">' + b) + '</li></ul>';
ChatMessage.create(msg);
}
/* -------------------------------------------- */
_buildSubTableHelp(list, path, table) {
for (let [name, command] of Object.entries(table)) {
if (command) {
if (command.subTable) {
this._buildSubTableHelp(list, path + name + " ", command.subTable);
} else {
list.push(`<strong>${path}${name}</strong>: ${command.descr}`);
}
}
}
return list;
}
getRencontreTMR(params) {
if (params.length == 2) {
return TMRUtility.getRencontre(params[0], params[1])
}
else {
return false;
}
}
async rollRdd(msg, params) {
if (params.length == 0) {
RdDRollResolution.open();
}
else {
let flatParams = params.reduce((a, b) => `${a} ${b}`);
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;
}
}
}
async rollRdDNumeric(msg, carac, diff, significative = false) {
let rollData = {
caracValue: carac,
finalLevel: diff,
showDice: true,
needSignificative: significative,
show: { title: "Table de résolution", points: true }
};
await RdDResolutionTable.rollData(rollData);
msg.content = await RdDResolutionTable.explainRollDataV2(rollData);
ChatUtility.chatWithRollMode(msg, game.user.name);
}
}

View File

@ -19,6 +19,7 @@ import { TMRUtility } from "./tmr-utility.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTokenHud } from "./rdd-token-hud.js";
import { RdDCommands } from "./rdd-commands.js";
/* -------------------------------------------- */
/* Foundry VTT Initialization */
@ -212,14 +213,15 @@ Hooks.once("init", async function() {
/* -------------------------------------------- */
function messageDeBienvenue(){
game.messages
.filter(it => it.user._id == game.user._id && it.data.content.match(/^Bienvenu(e)? dans le Rêve des Dragons/))
.filter(it => it.user._id == game.user._id && it.data.content.match(/^<div id="message-bienvenue-rdd/))
.forEach(it => it.delete());
ChatMessage.create( {
user: game.user._id,
whisper: [game.user._id],
content : "Bienvenue dans le Rêve des Dragons !<br> " +
"Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}" }
);
content : `<div id="message-bienvenue-rdd"><span class="rdd-roll-part">Bienvenue dans le Rêve des Dragons !</span>
<br>Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}
<br>La commande <code>/aide</code> dans le chat permet de voir les commandes spécifiques à Rêve de Dragon.</div>
` });
}
/* -------------------------------------------- */
@ -232,6 +234,8 @@ Hooks.once("renderApplication", () => {
/* -------------------------------------------- */
Hooks.once("ready", function() {
// préparation des lignes de commandes
RdDCommands.init();
/* -------------------------------------------- */
/* Affiche/Init le calendrier */
let calendrier = new RdDCalendrier();
@ -271,11 +275,15 @@ Hooks.on("preCreateToken", (scene, tokenData, options) => {
/* Foundry VTT Initialization */
/* -------------------------------------------- */
Hooks.on("chatMessage", (html, content, msg) => {
if (content[0] == '/') {
let regExp = /(\S+)/g;
let commands = content.match(regExp);
return RdDUtility.processChatCommand( commands, content, msg );
} );
let commands = content.toLowerCase().match(regExp);
if (game.system.rdd.commands.processChatCommand(commands, content, msg)){
return false;
}
}
return true;
});
/* -------------------------------------------- */
Hooks.on("getCombatTrackerEntryContext", (html, options) => {

View File

@ -70,7 +70,7 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static getResultat(code) {
let resultat = reussites.filter(r => code == r.code);
let resultat = reussites.find(r => code == r.code);
if (resultat == undefined) {
resultat = reussites.find(r => r.code == "error");
}
@ -88,14 +88,64 @@ export class RdDResolutionTable {
return message;
}
static explainRollData(rollData) {
let message = "<br>Difficultés <strong>libre: " + rollData.diffLibre + "</strong> / conditions: " + Misc.toSignedString(rollData.diffConditions)
+ " / état: " + rollData.etat;
message += RdDResolutionTable.explain(rollData.rolled)
if (rollData.selectedCarac == rollData.carac.volonte) {
message += " / moral: " + rollData.moral;
static async explainRollDataV2(rollData, template = 'chat-resultat-rdd.html') {
rollData.ajustements = RdDResolutionTable._buildAjustements(rollData);
rollData.show = rollData.show || {};
let html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
return html;
}
return message;
static _buildAjustements(rollData) {
let list = [];
if (rollData.competence) {
list.push({ label: rollData.competence.name, value: rollData.competence.data.niveau});
}
if (rollData.diffLibre != undefined) {
const label = rollData.selectedSort ? rollData.selectedSort.name : 'libre';
list.push({ label: label, value: rollData.diffLibre });
}
if (rollData.diffConditions != undefined) {
list.push({ label: 'conditions', value: rollData.diffConditions });
}
if (rollData.etat != undefined) {
list.push({ label: 'état', value: rollData.etat });
}
if (rollData.selectedCarac != undefined && rollData.moral != undefined && rollData.selectedCarac.label == 'Volonté') {
list.push({ label: 'moral', value: rollData.selectedCarac != undefined && rollData.moral != undefined && rollData.selectedCarac.label == 'Volonté' ? rollData.moral : undefined });
}
if (RdDResolutionTable.isAjustementAstrologique(rollData)) {
list.push({ label: 'astrologique', value: rollData.ajustementAstrologique||0 });
}
if (rollData.rolled.bonus && rollData.selectedSort) {
list.push({ label: 'Bonus de case', value: rollData.rolled.bonus, unit: '%' });
}
return list;
}
static explainRollData(rollData) {
let parts = RdDResolutionTable._buildAjustementsList(rollData);
let message = parts.length > 0 ? "<br>Difficulté " + parts.reduce((a, b) => a + ' / ' + b) : "";
return message+ RdDResolutionTable.explain(rollData.rolled)
}
static _buildAjustementsList(rollData) {
let parts = [];
if (rollData.diffLibre != undefined) {
parts.push(`<strong>libre: ${rollData.diffLibre}</strong>`);
}
if (rollData.diffConditions != undefined) {
parts.push(`conditions: ${Misc.toSignedString(rollData.diffConditions)}`);
}
if (rollData.etat != undefined) {
parts.push(`état: ${rollData.etat}`);
}
if (rollData.selectedCarac != undefined && rollData.moral != undefined && rollData.selectedCarac.label == 'Volonté') {
parts.push(`moral: ${rollData.moral}`);
}
return parts;
}
/* -------------------------------------------- */
@ -156,6 +206,16 @@ export class RdDResolutionTable {
return duplicate(this.resolutionTable[caracValue][difficulte + 10]);
}
static isAjustementAstrologique(rollData) {
if (rollData.selectedCarac && rollData.selectedCarac.label.toLowerCase().includes('chance')) {
return true;
}
if (rollData.selectedSort && rollData.selectedSort.data.isrituel) {
return true;
}
return false;
}
/* -------------------------------------------- */
static isEchec(rollData) {
switch (rollData.surprise) {

View File

@ -2,6 +2,7 @@ import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
const titleTableDeResolution = 'Table de résolution';
/**
* Extend the base Dialog entity to select roll parameters
* @extends {Dialog}
@ -10,16 +11,18 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js";
export class RdDRollResolution extends Dialog {
/* -------------------------------------------- */
static async open() {
let rollData = RdDRollResolution._prepareDefaultOptions();
static async open(rollData = {selectedCarac:10}) {
RdDRollResolution._setDefaultOptions(rollData);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-resolution.html', rollData);
const dialog = new RdDRollResolution(rollData, html);
dialog.render(true);
}
/* -------------------------------------------- */
static _prepareDefaultOptions() {
let rollData = {
static _setDefaultOptions(rollData) {
let defRollData = {
show: {title: titleTableDeResolution, points:true},
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
difficultesLibres: CONFIG.RDD.difficultesLibres,
etat: 0,
@ -29,18 +32,19 @@ export class RdDRollResolution extends Dialog {
diffConditions: 0,
diffLibre: 0
}
mergeObject(rollData, defRollData, {overwrite: false});
for (let i = 1; i < 21; i++) {
rollData.carac[i] = { type: "number", value: i, label: i }
if (rollData.selectedCarac == i) {
rollData.selectedCarac = rollData.carac[i];
}
}
rollData.selectedCarac = rollData.carac[10];
return rollData;
}
/* -------------------------------------------- */
constructor(rollData, html) {
let conf = {
title: 'Lancer les dés',
title: titleTableDeResolution,
content: html,
buttons: {
'lancer': { label: 'Lancer les dés', callback: html => this.onAction(html) }
@ -55,9 +59,7 @@ export class RdDRollResolution extends Dialog {
async onAction(html) {
await RdDResolutionTable.rollData(this.rollData);
console.log("RdDRollResolution -=>", this.rollData, this.rollData.rolled);
const message = {
content: "Table de résolution: " + RdDResolutionTable.explainRollData(this.rollData)
};
const message = { content: await RdDResolutionTable.explainRollDataV2(this.rollData)};
ChatUtility.chatWithRollMode(message, game.user.name)
}
@ -117,9 +119,4 @@ export class RdDRollResolution extends Dialog {
_computeDiffLibre(rollData) {
return Misc.toInt(rollData.diffLibre);
}
/* -------------------------------------------- */
_getTitle(rollData) {
return 'Table de résolution';
}
}

View File

@ -121,7 +121,7 @@ export class RdDRoll extends Dialog {
HtmlUtility._showControlWhen($(".diffMoral"), rollData.selectedCarac == actor.data.data.carac.volonte);
HtmlUtility._showControlWhen($("#etat-general"), !dialog._isIgnoreEtatGeneral(rollData));
HtmlUtility._showControlWhen($("#ajust-astrologique"), dialog._isAjustementAstrologique(rollData));
HtmlUtility._showControlWhen($("#ajust-astrologique"), RdDResolutionTable.isAjustementAstrologique(rollData));
// Sort management
if (rollData.selectedSort) {
@ -176,7 +176,7 @@ export class RdDRoll extends Dialog {
});
html.find('#draconic').change((event) => {
let draconicKey = Misc.toInt(event.currentTarget.value);
this.rollData.selectedDraconic = rollData.draconicList[draconicKey]; // Update the selectedCarac
this.rollData.competence = rollData.draconicList[draconicKey]; // Update the selectedCarac
//console.log("RdDRollSelectDialog","CARAC CLICKED !!!", rollData);
updateRollResult(rollData);
});
@ -229,7 +229,7 @@ export class RdDRoll extends Dialog {
const malusEnc = (rollData.surencMalusApply) ? rollData.surencMalusValue : 0;
const bonusTactique = RdDBonus.bonusAttaque(rollData.tactique);
const malusEncTotal = (rollData.useMalusEncTotal) ? -rollData.encTotal : 0;
const ajustementChance = this._isAjustementAstrologique(rollData) ? rollData.ajustementAstrologique : 0;
const ajustementChance = RdDResolutionTable.isAjustementAstrologique(rollData) ? rollData.ajustementAstrologique : 0;
// Gestion malus armure
const malusArmureValue = this._computeMalusArmure(rollData);
@ -240,22 +240,12 @@ export class RdDRoll extends Dialog {
return etat + diffCompetence + diffLibre + diffMoral + diffConditions + malusEnc + malusEncTotal + malusArmureValue + ajustementChance + bonusTactique;
}
_isAjustementAstrologique(rollData) {
if (rollData.selectedCarac.label.toLowerCase().includes('chance')) {
return true;
}
if (rollData.selectedSort && rollData.selectedSort.data.isrituel) {
return true;
}
return false;
}
_computeDiffCompetence(rollData) {
if (rollData.competence) {
return Misc.toInt(rollData.competence.data.niveau);
}
if (rollData.draconicList) {
return Misc.toInt(rollData.selectedDraconic.data.niveau);
return Misc.toInt(rollData.competence.data.niveau);
}
return 0;
}
@ -288,7 +278,7 @@ export class RdDRoll extends Dialog {
return rollData.selectedCarac.label + "/" + rollData.competence.name + armeTitle + " " + niveau
}
if (rollData.draconicList) {
return rollData.selectedDraconic.name + " - " + rollData.selectedSort.name;
return rollData.competence.name + " - " + rollData.selectedSort.name;
}
return rollData.selectedCarac.label;
}

View File

@ -145,7 +145,7 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async gererTourbillon( value ) {
this.nbFatigue += value;
this.nbFatigue += 1;
await this.actor.reveActuelIncDec( -value );
if ( !this.currentRencontre.tourbillonDirection ) {
this.currentRencontre.tourbillonDirection = TMRUtility.getDirectionPattern();
@ -158,7 +158,7 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async gererTourbillonRouge( ) {
this.nbFatigue += 4; // 4 cases -> 4 pts de fatigue
this.nbFatigue += 1;
await this.actor.reveActuelIncDec( -2 ); // -2 pts de Reve a chaque itération
if ( !this.currentRencontre.tourbillonDirection ) {
this.currentRencontre.tourbillonDirection = TMRUtility.getDirectionPattern();

View File

@ -891,17 +891,6 @@ export class RdDUtility {
});
}
/* -------------------------------------------- */
/* Display help for /table */
static displayHelpTable( msg )
{
msg.content = "";
for (let [name, tableData] of Object.entries(table2func)) {
msg.content += "<br>" + tableData.descr;
}
ChatMessage.create( msg );
}
/* -------------------------------------------- */
static afficherDemandePayer(som1, som2) {
som1 = (som1) ? som1.toLowerCase() : "0d";
@ -922,38 +911,4 @@ export class RdDUtility {
msgPayer += "<a id='payer-button' data-somme-denier='"+sumtotald+"'>Payer</a>"
ChatMessage.create( { content: msgPayer } );
}
/* -------------------------------------------- */
/* Manage chat commands */
static processChatCommand( commands, 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 = commands[0];
// Roll on a table
if (command === "/table") {
if ( commands[1] ) {
let tableName = commands[1].toLowerCase();
table2func[tableName].func();
} else {
this.displayHelpTable( msg );
}
return false
} else if (command === "/tmrr") {
TMRUtility.getRencontre(commands[1], commands[2] )
return false
} else if (command === "/tmra") {
TMRUtility.getTMRAleatoire( )
return false
} else if (command === "/payer") {
RdDUtility.afficherDemandePayer( commands[1], commands[2] )
return false
}
return true;
}
}

View File

@ -86,6 +86,9 @@
margin: 0;
padding: 0;
}
.strong-text{
font-weight: bold;
}
.tabs .item.active, .blessures-list li ul li:first-child:hover, a:hover {
text-shadow: 1px 0px 0px #ff6600;
@ -364,6 +367,35 @@ table {border: 1px solid #7a7971;}
z-index: 9999;
display: block;
}
.rdd-roll-part{
align-items: center;
border-radius: 6px; padding: 3px;
background:linear-gradient(30deg, rgba(191, 149, 63, 0.3), rgba(252, 246, 186, 0.3), rgba(179, 135, 40, 0.3), rgba(251, 245, 183, 0.3), rgba(170, 119, 28, 0.3));
}
.rdd-roll-sign{
border-radius: 6px; padding: 3px;
background:linear-gradient(30deg, rgba(61, 55, 93, 0.3), rgba(178, 179, 196, 0.3), rgba(59, 62, 63, 0.6), rgba(206, 204, 199, 0.3), rgba(61, 46, 49, 0.3));
}
.rdd-roll-norm{
border-radius: 6px; padding: 3px;
background:linear-gradient(30deg, rgba(7, 76, 0, 0.3), rgba(66, 163, 65, 0.2), rgba(184, 226, 163, 0.1), rgba(66, 163, 65, 0.2), rgba(184, 226, 163, 0.3));
}
.rdd-roll-echec{
border-radius: 6px; padding: 3px;
background-image: linear-gradient(150deg, rgba(255, 0, 0, 0.3), rgba(255, 200, 128, 0.05),rgba(255, 200, 128, 0.1), rgba(255,10,0,0.3));
}
.rdd-roll-epart{
border-radius: 6px; padding: 3px;
background:linear-gradient(150deg, rgba(100, 45, 124, 0.6), rgba(216, 157, 192, 0.3), rgba(177, 157, 216, 0.5), rgba(107, 62, 121, 0.3), rgba(100, 45, 124, 0.6));
}
.rdd-roll-etotal{
border-radius: 6px; padding: 3px;
background:linear-gradient(150deg, rgba(0, 0, 0, 0.7), rgba(100, 45, 124, 0.4), rgba(82, 17, 131, 0.3),rgba(100, 45, 124, 0.4), rgba(0, 0, 0, 0.7));
}
.rdd-need-significative{
border-radius: 6px; padding: 3px;
background:linear-gradient(30deg, rgba(61, 55, 93, 0.2), rgba(178, 179, 196, 0.1), rgba(59, 62, 63, 0.2), rgba(206, 204, 199, 0.1), rgba(61, 46, 49, 0.2));
}
.table-resolution-carac {
background-color: yellow;
@ -1180,8 +1212,6 @@ display: inline-flex;
/* Tooltip text */
.tooltip .tooltiptext {
visibility: hidden;
width: 360px;
background-color: rgba(30, 25, 20, 0.9);
text-align: center;
padding: 5px 0;
border-radius: 6px;
@ -1189,13 +1219,18 @@ display: inline-flex;
/* Position the tooltip text */
position: absolute;
z-index: 1;
top: 30px;
left: -30%;
/* Fade in tooltip */
opacity: 0;
transition: opacity 0.3s;
}
.tooltiptext-fatigue{
width: 360px;
top: 30px;
left: -30%;
background-color: rgba(30, 25, 20, 0.9);
border-image: url(img/ui/bg_control.jpg) 21 repeat;
border-image-slice: 6 6 6 6 fill;
border-image-width: 6px 6px 6px 6px;
@ -1203,6 +1238,13 @@ display: inline-flex;
border-radius: 0px;
}
.tooltiptext-ajustements {
width: 150px;
top: 30px;
background: rgba(220,220,210,0.9);
font-size: 0.8rem;
}
/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover .tooltiptext {
visibility: visible;

View File

@ -31,7 +31,7 @@
<label class="ctn-fatigue tooltip">
Fatigue
<a id="fatigue-moins">-</a>
<span class="tooltiptext">{{{data.fatigue.html}}}</span>
<span class="tooltiptext tooltiptext-fatigue">{{{data.fatigue.html}}}</span>
<input class="resource-content" id="fatigue-value" type="text" name="data.sante.fatigue.value" value="{{data.sante.fatigue.value}}" data-dtype="Number" />
<span>/ {{data.sante.fatigue.max}}</span>
<a id="fatigue-plus">+</a>
@ -65,7 +65,7 @@
</div>
<div class="flexrow">
<span class="tooltip">Malus de fatigue : {{data.fatigue.malus}}
<span class="tooltiptext">{{{data.fatigue.html}}}</span>
<span class="tooltiptext tooltiptext-fatigue">{{{data.fatigue.html}}}</span>
</span>
<span>{{data.blessures.resume}}</span>
</div>

View File

@ -0,0 +1,30 @@
<h4>
{{#if show.title}}{{show.title}}: {{/if}}
<span class="tooltip">
{{rolled.caracValue}} à {{numberFormat rolled.finalLevel decimals=0 sign=true}}
{{#if ajustements}}
<div class="tooltiptext tooltiptext-ajustements">
<div>Ajustements</div>
{{#each ajustements as |item key|}}
<div class="{{#if item.strong}}strong-text{{/if}}" >{{item.label}}: {{numberFormat item.value decimals=0 sign=true}}{{#if item.unit}}{{item.unit}}{{/if}}</div>
{{/each}}
</div>
{{/if}}
</span>
{{#if rolled.needSignificative}}<label class="rdd-need-significative">(&times;&frac12;)</label>{{/if}} =
{{rolled.score}}%
</h4>
<div>
<span>{{rolled.roll}} : </span>
<span class="rdd-roll-{{rolled.code}} strong-text">{{rolled.quality}}</span>
</div>
{{#if show.points}}
<div>
<span>{{rolled.ptTache}} points de tâche{{#if rolled.ptQualite}}, ajustement Qualité {{numberFormat rolled.ptQualite decimals=0 sign=true}}{{/if}}</span>
</div>
{{/if}}
{{~#if show.explications}}
<div>
{{show.explications}}
</div>
{{/if}}

View File

@ -0,0 +1,40 @@
<h4>
{{#if isSortReserve}}Mise en réserve{{else}}Lancement{{/if}} du sort {{selectedSort.name}} r{{selectedSort.data.ptreve_reel}}
<br>
<span class="tooltip">
{{competence.name}}: {{rolled.caracValue}} à {{numberFormat rolled.finalLevel decimals=0 sign=true}}
<div class="tooltiptext tooltiptext-ajustements">
<div>Ajustements</div>
{{#each ajustements as |item key|}}
<div class="{{#if item.strong}}strong-text{{/if}}" >{{item.label}}: {{numberFormat item.value decimals=0 sign=true}}{{#if item.unit}}{{item.unit}}{{/if}}</div>
{{/each}}
</div>
</span>
{{#if rolled.needSignificative}}<label class="rdd-need-significative">(&times;&frac12;)</label>{{/if}} =
{{rolled.score}}%
</h4>
<div>Pour {{selectedSort.data.ptreve_reel}} points de rêve en {{coordLabel}} ({{coord}})</div>
<hr>
<div>
<span>{{rolled.roll}} :</span>
<span class="rdd-roll-{{rolled.code}} strong-text">{{rolled.quality}}</span>
{{#if show.reveInsuffisant}}<span>Pas assez de rêve!</span>{{/if}}
<span>
{{#if rolled.isETotal}}
Echec TOTAL du sort!
{{else if rolled.isEchec}}
Echec du sort,
{{else}}
Réussite du sort,
{{/if~}}
</span>
<span>
{{#if (eq depenseReve 0)}}
pas de dépense de rêve
{{else if (eq depenseReve 1)}}
1 point de rêve a été dépensé
{{else}}
{{depenseReve}} points de rêve ont été dépensés
{{/if}}
</span>
</div>

View File

@ -12,7 +12,7 @@
<label>Carac. Rêve : </label><label>{{caracReve}}</label>
</div>
<div class="flex-group-center">
<label>Rêve Actuel : </label><span id="tmr-pointsreve-value">0</span>
<label>Rêve actuel : </label><span id="tmr-pointsreve-value">0</span>
</div>
<div class="flex-group-center">
<label>Coordonnées : </label><span id="tmr-pos">0</span>