From fa6769fcd7405c41b94b4af1d45c22d551d622b6 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Sun, 5 Oct 2025 02:24:34 +0200 Subject: [PATCH] =?UTF-8?q?Fen=C3=AAtres=20Roll=20V2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maintenant disponibles pour: - méditation - tâches - soins --- assets/ui/fatigue.svg | 1 + changelog.md | 12 ++-- css/foundryvtt-reve-de-dragon.css | 5 ++ less/colors.less | 5 ++ less/roll-dialog.less | 6 +- module/actor-sheet.js | 6 +- module/actor.js | 78 ++++++++++++++++------ module/actor/base-actor-reve.js | 18 ++++- module/actor/base-actor-sang.js | 2 +- module/actor/base-actor.js | 6 +- module/actor/export-scriptarium/mapping.js | 5 +- module/rdd-bonus.js | 6 +- module/rdd-combat.js | 7 +- module/rdd-utility.js | 8 ++- module/roll/roll-dialog.mjs | 35 +++++++--- module/roll/roll-part-checkbox.mjs | 4 +- module/roll/roll-part-comp.mjs | 8 ++- module/roll/roll-part-meditation.mjs | 54 +++++++++------ module/roll/roll-part-tache.mjs | 7 +- module/roll/roll-part.mjs | 5 +- module/roll/roll-type-meditation.mjs | 17 +++++ module/roll/roll-type-tache.mjs | 24 +++++++ module/roll/roll-type.mjs | 7 +- module/settings/options-avancees.js | 6 +- templates/actor/combat.hbs | 1 - templates/roll/result/chat-meditation.hbs | 44 ++++++++++++ templates/roll/result/chat-tache.hbs | 57 ++++++++++++++++ templates/roll/roll-part-meditation.hbs | 27 ++++++-- 28 files changed, 359 insertions(+), 102 deletions(-) create mode 100644 assets/ui/fatigue.svg diff --git a/assets/ui/fatigue.svg b/assets/ui/fatigue.svg new file mode 100644 index 00000000..8397e044 --- /dev/null +++ b/assets/ui/fatigue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/changelog.md b/changelog.md index cb628e80..33b97cc8 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Ajout d'un statut "saignement" en cas de blessure grave ou critique sans premiers soins - Nouvelle fenêtre de jets de dés + - jets de méditation + - jets de tâches + - jets de caractéristiques + - jets de compétences - Boutons d'initiative et d'attaque V2 - fenêtre d'attaque - choix des armes @@ -12,10 +16,10 @@ - message au défenseur - gestion des demi-surprises (attaquant/défenseur) - gestion des tactiques (attaquant/défenseur) -- en cours nouvelle fenêtre de jets - - jets de compétence avec messages - - jets de cuisine séparés avec messages (pour gérer plus tard les spécificités: fabricatioon de plats) - - gestion des empoignades + - en cours nouvelle fenêtre de jets + - jets de compétence avec messages + - jets de cuisine séparés avec messages (pour gérer plus tard les spécificités: fabricatioon de plats) + - gestion des empoignades - Technique: suppression de warnings foundry sur renderTemplate diff --git a/css/foundryvtt-reve-de-dragon.css b/css/foundryvtt-reve-de-dragon.css index 15085962..41de9c72 100644 --- a/css/foundryvtt-reve-de-dragon.css +++ b/css/foundryvtt-reve-de-dragon.css @@ -96,6 +96,7 @@ select, --gradient-red: 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)); --gradient-violet: 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)); --gradient-purple-black: 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)); + --gradient-warning: linear-gradient(150deg, hsla(32, 100%, 50%, 0.3), hsla(52, 60%, 50%, 0.1), hsla(32, 60%, 50%, 0.1), hsla(32, 100%, 50%, 0.3)); --gradient-silver-light: 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)); --gradient-daylight: conic-gradient(from 0deg, hsla(50, 100%, 80%, 0.7), hsla(30, 30%, 40%, 0.1) 25%, hsla(250, 50%, 40%, 0.1) 25%, hsla(250, 30%, 30%, 0.7) 50%, hsla(250, 50%, 40%, 0.1) 75%, hsla(30, 30%, 40%, 0.1) 75%, hsla(50, 100%, 80%, 0.7)); --background-custom-button: linear-gradient(to bottom, hsla(208, 38%, 21%, 0.988) 5%, hsla(202, 42%, 14%, 0.671) 100%); @@ -528,6 +529,10 @@ select, flex-direction: row; margin: 0.1rem 0; } +.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section subline .warning { + border-radius: 6px; + background: var(--gradient-warning); +} .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-img { display: flex; flex-direction: column; diff --git a/less/colors.less b/less/colors.less index 6320490a..bf889464 100644 --- a/less/colors.less +++ b/less/colors.less @@ -27,6 +27,11 @@ --gradient-red: 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)); --gradient-violet: 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)); --gradient-purple-black: 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)); + --gradient-warning: linear-gradient(150deg, + hsla(32, 100%, 50%, 0.3), + hsla(52, 60%, 50%, 0.1), + hsla(32, 60%, 50%, 0.1), + hsla(32, 100%, 50%, 0.3)); --gradient-silver-light: 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)); --gradient-daylight: conic-gradient( from 0deg, diff --git a/less/roll-dialog.less b/less/roll-dialog.less index c5628f3a..ba05a596 100644 --- a/less/roll-dialog.less +++ b/less/roll-dialog.less @@ -89,8 +89,12 @@ display: flex; flex-direction: row; margin: 0.1rem 0; + .warning { + border-radius: 6px; + background: var(--gradient-warning); + } } - + roll-part-img { display: flex; flex-direction: column; diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 1dacbf44..27f658b8 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -3,7 +3,7 @@ import { HtmlUtility } from "./html-utility.js"; import { RdDBonus } from "./rdd-bonus.js"; import { Misc } from "./misc.js"; import { RdDCombatManager } from "./rdd-combat.js"; -import { RdDCarac } from "./rdd-carac.js"; +import { CARACS, RdDCarac } from "./rdd-carac.js"; import { DialogSplitItem } from "./dialog-split-item.js"; import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"; import { RdDSheetUtility } from "./rdd-sheet-utility.js"; @@ -174,7 +174,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet { await this.getBlessure(event)?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } }) }); - this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac('chance-actuelle')) + this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac(CARACS.CHANCE_ACTUELLE)) this.html.find('.button-appel-chance').click(async event => await this.actor.rollAppelChance()) this.html.find('[name="jet-astrologie"]').click(async event => await this.actor.astrologieNombresAstraux()) @@ -208,7 +208,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet { } // Points de reve actuel - this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollCarac('reve-actuel', { resistance: true })) + this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollCarac(CARACS.REVE_ACTUEL, { resistance: true })) this.html.find('.action-empoignade').click(async event => await RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor))) this.html.find('.roll-arme').click(async event => { diff --git a/module/actor.js b/module/actor.js index 3f381c45..6b0c44e4 100644 --- a/module/actor.js +++ b/module/actor.js @@ -47,6 +47,9 @@ import { RdDRollResult } from "./rdd-roll-result.js"; import { RdDInitiative } from "./initiative.mjs"; import RollDialog from "./roll/roll-dialog.mjs"; import { OptionsAvancees, ROLL_DIALOG_V2, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js"; +import { ROLL_TYPE_MEDITATION } from "./roll/roll-constants.mjs"; +import { PART_TACHE } from "./roll/roll-part-tache.mjs"; +import { PART_COMP } from "./roll/roll-part-comp.mjs"; export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre'] @@ -98,6 +101,8 @@ export class RdDActor extends RdDBaseActorSang { } isPersonnage() { return true } + isFeminin() { return this.system.sexe.length > 0 && this.system.sexe.charAt(0).toLowerCase() == 'f' } + isHautRevant() { return this.system.attributs.hautrevant.value != "" } /* -------------------------------------------- */ @@ -1343,7 +1348,7 @@ export class RdDActor extends RdDBaseActorSang { async _apprecierCuisine(item, seForcer) { const surmonteExotisme = await this._surmonterExotisme(item, seForcer); if (surmonteExotisme) { - await this.apprecier('gout', 'cuisine', item.system.qualite, item.system.boisson ? "apprécie la boisson" : "apprécie le plat"); + await this.apprecier(CARACS.ODORATGOUT, 'cuisine', item.system.qualite, item.system.boisson ? "apprécie la boisson" : "apprécie le plat"); } else if (seForcer) { await this.jetDeMoral('malheureux'); @@ -1361,7 +1366,7 @@ export class RdDActor extends RdDBaseActorSang { if (exotisme < 0 || qualite < 0) { const competence = qualite > 0 ? 'cuisine' : undefined const difficulte = Math.min(exotisme, qualite) - const rolled = await this.doRollCaracCompetence('volonte', competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` }) + const rolled = await this.doRollCaracCompetence(CARACS.VOLONTE, competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` }) return rolled.isSuccess } return true; @@ -1923,6 +1928,20 @@ export class RdDActor extends RdDBaseActorSang { } async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) { + + if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + ids: { actorId: this.id }, + type: { allowed: [PART_COMP], current: PART_COMP }, + selected: { + carac: {key: caracName }, + comp: { key: compName, forced: options.forced } + } + } + RollDialog.create(rollData, options) + return + } + RdDEmpoignade.checkEmpoignadeEnCours(this) const competence = this.getCompetence(compName); await this.openRollDialog({ @@ -1945,23 +1964,34 @@ export class RdDActor extends RdDBaseActorSang { /* -------------------------------------------- */ async rollTache(id, options = {}) { + RdDEmpoignade.checkEmpoignadeEnCours(this) - const tacheData = this.getTache(id) - const compData = this.getCompetence(tacheData.system.competence) - compData.system.defaut_carac = tacheData.system.carac; // Patch ! + const tache = this.getTache(id) + if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + ids: { actorId: this.id }, + selected: { tache: { key: tache.id, forced: options.forced} }, + type: { allowed: [PART_TACHE], current: PART_TACHE } + } + RollDialog.create(rollData, options) + return + } + + const compData = this.getCompetence(tache.system.competence) + compData.system.defaut_carac = tache.system.carac; // Patch ! await this.openRollDialog({ name: 'jet-competence', - label: 'Jet de Tâche ' + tacheData.name, + label: 'Jet de Tâche ' + tache.name, template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.hbs', rollData: { competence: compData, - tache: tacheData, - diffLibre: tacheData.system.difficulte, + tache: tache, + diffLibre: tache.system.difficulte, diffConditions: 0, use: { libre: false, conditions: true }, carac: { - [tacheData.system.carac]: foundry.utils.duplicate(this.system.carac[tacheData.system.carac]) + [tache.system.carac]: foundry.utils.duplicate(this.system.carac[tache.system.carac]) } }, callbacks: [{ action: r => this._tacheResult(r, options) }] @@ -2015,6 +2045,17 @@ export class RdDActor extends RdDBaseActorSang { /* -------------------------------------------- */ async rollMeditation(id) { const meditation = foundry.utils.duplicate(this.getMeditation(id)); + + if (meditation && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + ids: { actorId: this.id }, + selected: { meditation: { key: id } }, + type: { allowed: [ROLL_TYPE_MEDITATION], current: ROLL_TYPE_MEDITATION } + } + await RollDialog.create(rollData) + return + } + const competence = foundry.utils.duplicate(this.getCompetence(meditation.system.competence)); competence.system.defaut_carac = "intellect"; // Meditation = toujours avec intellect let meditationData = { @@ -2415,14 +2456,17 @@ export class RdDActor extends RdDBaseActorSang { if (!blessure.system.premierssoins.done) { const tache = await this.getTacheBlessure(blesse, blessure); return await this.rollTache(tache.id, { - onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r) + onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r), + title: 'Premiers soins', + forced: true }); } - if (!blessure.system.soinscomplets.done) { + else if (!blessure.system.soinscomplets.done) { const diff = blessure.system.difficulte + (blessure.system.premierssoins.bonus ?? 0); - return await this.rollCaracCompetence("dexterite", "Chirurgie", diff, { + return await this.rollCaracCompetence(CARACS.DEXTERITE, "Chirurgie", diff, { title: "Soins complets", - onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r) + onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r), + forced: true }) } } @@ -2437,6 +2481,7 @@ export class RdDActor extends RdDBaseActorSang { }) } const blessure = this.getItem(blessureId, 'blessure') + if (blessure && !blessure.system.premierssoins.done) { const tache = rollData.tache; if (rollData.rolled.isETotal) { @@ -3065,12 +3110,7 @@ export class RdDActor extends RdDBaseActorSang { actorId: this.id } } - await RollDialog.create(rollData, { - onRollDone: (dialog) => { - if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST)) - dialog.close() - } - }) + await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose }) } /* -------------------------------------------- */ diff --git a/module/actor/base-actor-reve.js b/module/actor/base-actor-reve.js index 3d1ec42f..5e548843 100644 --- a/module/actor/base-actor-reve.js +++ b/module/actor/base-actor-reve.js @@ -11,7 +11,7 @@ import { ITEM_TYPES } from "../constants.js"; import { StatusEffects, STATUSES } from "../settings/status-effects.js"; import { Targets } from "../targets.js"; import { RdDConfirm } from "../rdd-confirm.js"; -import { RdDCarac } from "../rdd-carac.js"; +import { CARACS, RdDCarac } from "../rdd-carac.js"; import { RdDRollResult } from "../rdd-roll-result.js"; import { RdDItemArme } from "../item/arme.js"; @@ -28,6 +28,7 @@ import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs"; import RollDialog from "../roll/roll-dialog.mjs"; import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE } from "../roll/roll-constants.mjs"; import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js"; +import { PART_COMP } from "../roll/roll-part-comp.mjs"; /** * Classe de base pour les acteurs disposant de rêve (donc, pas des objets) @@ -380,9 +381,22 @@ export class RdDBaseActorReve extends RdDBaseActor { } /* -------------------------------------------- */ async rollCarac(caracName, options = {}) { - if (Grammar.equalsInsensitive(caracName, 'taille')) { + if (Grammar.equalsInsensitive(caracName, CARACS.TAILLE)) { return } + if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + ids: { actorId: this.id }, + type: { allowed: [PART_COMP], current: PART_COMP }, + selected: { + carac: { key: caracName }, + comp: options.resistance ? { key: undefined, forced: true } : undefined + } + } + RollDialog.create(rollData, options) + return + } + foundry.utils.mergeObject(options, { resistance: false, diff: 0 }, { overwrite: false }) RdDEmpoignade.checkEmpoignadeEnCours(this) let selectedCarac = this.getCaracByName(caracName) diff --git a/module/actor/base-actor-sang.js b/module/actor/base-actor-sang.js index 7eeede7b..a3f00555 100644 --- a/module/actor/base-actor-sang.js +++ b/module/actor/base-actor-sang.js @@ -161,7 +161,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve { if (ReglesOptionnelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) { sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this.getFatigueMin()); } - await this.update({ "system.sante": sante }) + await this.update({ "system.sante": sante }, { render: true }) if (this.isDead()) { await this.setEffect(STATUSES.StatusComma, true); } diff --git a/module/actor/base-actor.js b/module/actor/base-actor.js index 3a71d922..b0d8cab0 100644 --- a/module/actor/base-actor.js +++ b/module/actor/base-actor.js @@ -46,6 +46,7 @@ export class RdDBaseActor extends Actor { } static init() { + Handlebars.registerHelper('actor-isFeminin', actor => actor.isFeminin()) Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id)) Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id)) Hooks.on("updateItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onUpdateItem(item, options, id)) @@ -220,6 +221,7 @@ export class RdDBaseActor extends Actor { isHautRevant() { return false } isVehicule() { return false } isPersonnage() { return false } + isFeminin() { return false } getItem(id, type = undefined) { const item = this.items.get(id); if (type == undefined || (item?.type == type)) { @@ -254,7 +256,7 @@ export class RdDBaseActor extends Actor { } } - async onUpdateItem(item, options, id){ + async onUpdateItem(item, options, id) { switch (item.type) { case ITEM_TYPES.blessure: await this.changeBleedingState() @@ -801,6 +803,6 @@ export class RdDBaseActor extends Actor { listActionsCombat() { const possessions = this.listActionsPossessions() return possessions.length > 0 ? possessions : this.listActions({}) - + } } \ No newline at end of file diff --git a/module/actor/export-scriptarium/mapping.js b/module/actor/export-scriptarium/mapping.js index acd18e4c..3b0fa256 100644 --- a/module/actor/export-scriptarium/mapping.js +++ b/module/actor/export-scriptarium/mapping.js @@ -274,11 +274,10 @@ export class Mapping { } static getDescription(actor) { - const sexe = actor.system.sexe - const sexeFeminin = sexe.length > 0 && sexe.charAt(0).toLowerCase() == 'f' ? 'Née' : 'Né' + const naissance = actor.isFeminin() ? 'née' : 'né' const race = ['', 'humain'].includes(Grammar.toLowerCaseNoAccent(actor.system.race)) ? '' : (actor.system.race + ' ') const heure = actor.system.heure - const hn = `${sexeFeminin} à l'heure ${RdDTimestamp.definition(heure).avecArticle}` + const hn = `${naissance} à l'heure ${RdDTimestamp.definition(heure).avecArticle}` const age = (actor.system.age && actor.system.age >0) ? `${actor.system.age} ans` : undefined const taille = actor.system.taille const poids = actor.system.poids diff --git a/module/rdd-bonus.js b/module/rdd-bonus.js index 8b8fd600..f808768e 100644 --- a/module/rdd-bonus.js +++ b/module/rdd-bonus.js @@ -55,7 +55,7 @@ export class RdDBonus { dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante return dmg; } - + static dmgRollV2(rollData, attaque) { const actor = rollData.active.actor const arme = attaque.arme @@ -63,12 +63,12 @@ export class RdDBonus { const dmg = { total: 0, dmgArme: dmgArme, - penetration: arme.penetration(), + penetration: arme?.penetration() ?? 0, diff: attaque.diff, dmgTactique: attaque.tactique?.dmg ?? 0, dmgParticuliere: RdDBonus._dmgParticuliere(rollData), dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0, - mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme.system.mortalite, rollData.opponent?.actor?.isEntite()), + mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme?.system.mortalite, rollData.opponent?.actor?.isEntite()), dmgActor: RdDBonus.bonusDmg(actor, attaque.carac.key, dmgArme, attaque.forceRequise), dmgForceInsuffisante: Math.min(0, actor.getForce() - attaque.forceRequise), dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0 diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 97d17ba8..821adf98 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -1066,7 +1066,7 @@ export class RdDCombat { opponentTokenId: this.attackerTokenId, opponentId: this.attackerId, }, - type: { allowed: ['defense'], current: 'defense' }, + type: { allowed: [ROLL_TYPE_DEFENSE], current: ROLL_TYPE_DEFENSE }, attackerRoll: RollDialogAdapter.mapActionAttaque(attackerRoll), passeArme: attackerRoll.passeArme, }) @@ -1074,10 +1074,7 @@ export class RdDCombat { async doRollDefense(rollData, callbacks = []) { await RollDialog.create(rollData, { - onRollDone: (dialog) => { - if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST)) - dialog.close() - }, + onRollDone: RollDialog.onRollDoneClose, customChatMessage: true, callbacks: [ async (roll) => { diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 7a49e6c1..b37b79c5 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -278,7 +278,7 @@ export class RdDUtility { Handlebars.registerHelper('plusMoins', diff => parseInt(diff) ? (diff > 0 ? '+' : '') + Math.round(diff) : diff) Handlebars.registerHelper('fractionOneN', n => new Handlebars.SafeString(Misc.getFractionOneN(n))) - + // Handle v12 removal of this helper Handlebars.registerHelper('select', function (selected, options) { const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected)); @@ -286,7 +286,7 @@ export class RdDUtility { const html = options.fn(this); return html.replace(rgx, "$& selected"); }) - + // logic Handlebars.registerHelper('either', (a, b) => a ?? b); // string manipulation @@ -299,8 +299,10 @@ export class RdDUtility { Handlebars.registerHelper('grammar-un', str => Grammar.articleIndetermine(str)); Handlebars.registerHelper('grammar-accord', (genre, ...args) => Grammar.accord(genre, args)); Handlebars.registerHelper('json-stringify', object => JSON.stringify(object)) - + // math + Handlebars.registerHelper('math-sum', (...values) => values.slice(0, -1).reduce(Misc.sum(), 0)) + Handlebars.registerHelper('math-abs', diff => Math.abs(parseInt(diff))) Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1))); Handlebars.registerHelper('repeat', function (n, block) { let accum = ''; diff --git a/module/roll/roll-dialog.mjs b/module/roll/roll-dialog.mjs index 33cc625e..a49c510c 100644 --- a/module/roll/roll-dialog.mjs +++ b/module/roll/roll-dialog.mjs @@ -42,10 +42,10 @@ import ChatRollResult from "./chat-roll-result.mjs"; import { renderTemplate } from "../constants.js"; import { RollTypeCuisine } from "./roll-type-cuisine.mjs"; import { RollPartCuisine } from "./roll-part-cuisine.mjs"; +import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "../settings/options-avancees.js"; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api -const doNothing = (dialog) => { } const ALL_ROLL_TYPES = [ new RollTypeComp(), @@ -172,6 +172,14 @@ const ROLL_PARTS = [ /* -------------------------------------------- */ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2) { + static onRollDoneDoNothing(dialog) { + dialog.render() + } + static onRollDoneClose(dialog) { + if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST)) + dialog.close() + } + static init() { } @@ -275,9 +283,9 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 rollData.options = rollData.options ?? { rollMode: game.settings.get("core", "rollMode") } ROLL_PARTS.forEach(p => p.initialize(rollData)) - ROLL_PARTS.forEach(p => p.restore(rollData)) ROLL_PARTS.filter(p => p.isValid(rollData)) .forEach(p => { + p.restore(rollData) p.loadRefs(rollData) p.prepareContext(rollData) }) @@ -307,7 +315,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 ...(rollOptions.callbacks ?? []) ], customChatMessage: rollOptions.customChatMessage, - onRollDone: rollOptions.onRollDone ?? doNothing + onRollDone: rollOptions.onRollDone ?? RollDialog.onRollDoneDoNothing } this.chatRollResult = new ChatRollResult(); this.selectType() @@ -328,16 +336,16 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 return ROLL_PARTS.filter(p => p.isActive(rollData)) } - // get title() { - // return this.rollData.title ?? `Jet de dés de ${this.rollData.active.actor.name}` - // } - rollTitle(rollData) { - return rollData.label ?? ROLL_PARTS + const title = rollData.label ?? ROLL_PARTS .filter(it => it.section == ROLLDIALOG_SECTION.ACTION) .filter(it => it.isActive(rollData)) .map(it => it.title(rollData)) - .reduce(Misc.joining(' ')) + .reduce(Misc.joining(' ')); + if (this.rollOptions.title) { + return `${this.rollOptions.title} ${title}` + } + return title } async _onRender(context, options) { @@ -428,15 +436,20 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 async roll() { const roll = RollDialog.saveParts(this.rollData) + const selectedRollType = this.getSelectedType(roll); RollDialog.loadRollData(roll) roll.current.resultat = this.rollData.current[PART_TRICHER]?.resultat ?? -1 roll.choix = {} roll.rolled = await RollDialogAdapter.rollDice(roll, this.rollTitle(roll)) - roll.result = this.getSelectedType(roll).getResult(roll) + roll.result = selectedRollType.getResult(roll) console.info('RollDialog.roll:', roll) + const callbacks = [ + ...this.rollOptions.callbacks, + ...selectedRollType.callbacks(this.rollOptions), + ] + await Promise.all(callbacks.map(async callback => await callback(roll))) await this.chatRollResult.display(roll) - await Promise.all(this.rollOptions.callbacks.map(async callback => await callback(roll))) this.rollOptions.onRollDone(this) } diff --git a/module/roll/roll-part-checkbox.mjs b/module/roll/roll-part-checkbox.mjs index 1d128941..c5476ab0 100644 --- a/module/roll/roll-part-checkbox.mjs +++ b/module/roll/roll-part-checkbox.mjs @@ -28,9 +28,7 @@ export class RollPartCheckbox extends RollPart { /* TODO: user setting? */ current.checked = true } - if (current.value == undefined) { - current.value = this.getCheckboxValue(rollData) - } + current.value = this.getCheckboxValue(rollData) current.icon = this.getCheckboxIcon(rollData) } diff --git a/module/roll/roll-part-comp.mjs b/module/roll/roll-part-comp.mjs index 0463307f..2057c889 100644 --- a/module/roll/roll-part-comp.mjs +++ b/module/roll/roll-part-comp.mjs @@ -17,7 +17,14 @@ export class RollPartComp extends RollPartSelect { loadRefs(rollData) { const refs = this.getRefs(rollData) + const selected = this.getSelected(rollData) refs.all = this.$getActorComps(rollData) + .filter(comp => !selected.forced || + (selected.key ? + Grammar.includesLowerCaseNoAccent(comp.name, selected.key) + : comp.key == '') + ) + refs.comps = refs.all this.$selectComp(rollData) } @@ -49,7 +56,6 @@ export class RollPartComp extends RollPartSelect { allowed = allowed.filter(it => it != undefined) const refs = this.getRefs(rollData) refs.comps = allowed.length > 0 - // ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.label))) ? refs.all.filter(it => allowed.includes(it.label)) : refs.all this.$selectComp(rollData) diff --git a/module/roll/roll-part-meditation.mjs b/module/roll/roll-part-meditation.mjs index 1d929dbc..0e55646e 100644 --- a/module/roll/roll-part-meditation.mjs +++ b/module/roll/roll-part-meditation.mjs @@ -11,26 +11,41 @@ import { ROLLDIALOG_SECTION } from "./roll-part.mjs" export const PART_MEDITATION = "meditation" +const COMPORTEMENTS = ['isComportement', 'isHeure', 'isPurification', 'isVeture'] + export class RollPartMeditation extends RollPartSelect { get code() { return PART_MEDITATION } get section() { return ROLLDIALOG_SECTION.CHOIX } + store(rollData, targetData) { + const current = this.getCurrent(rollData) + this.setSaved(targetData, { + key: current.key, + isComportement: current.isComportement, + isHeure: current.isHeure, + isPurification: current.isPurification, + isVeture: current.isVeture + }) + } + isValid(rollData) { return rollData.active.actor.isPersonnage() && rollData.active.actor.isHautRevant() } visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_MEDITATION) } loadRefs(rollData) { const refs = this.getRefs(rollData) - foundry.utils.mergeObject(refs, - { - meditations: rollData.active.actor.itemTypes[ITEM_TYPES.meditation] - .map(it => RollPartMeditation.$extractMeditation(it, rollData.active.actor)) - } - ) + refs.meditations = rollData.active.actor.itemTypes[ITEM_TYPES.meditation] + .map(it => RollPartMeditation.$extractMeditation(it, rollData.active.actor)) + if (refs.meditations.length > 0) { this.$selectMeditation(rollData) + this.$selectConditionMeditation(rollData) + const selected = this.getSelected(rollData) + const current = this.getCurrent(rollData) + COMPORTEMENTS.filter(it => selected[it]).forEach(it => current[it] = selected[it]) } } + choices(refs) { return refs.meditations } static $extractMeditation(meditation, actor) { @@ -48,12 +63,7 @@ export class RollPartMeditation extends RollPartSelect { getMalusConditions(rollData) { const current = this.getCurrent(rollData) - const conditionsManquantes = [ - current.isComportement, - current.isHeure, - current.isPurification, - current.isVeture - ].filter(it => !it).length + const conditionsManquantes = COMPORTEMENTS.filter(it => !current[it]).length return -2 * conditionsManquantes } @@ -71,13 +81,18 @@ export class RollPartMeditation extends RollPartSelect { const previous = this.getCurrent(rollData) const current = this.selectByKey(rollData, key, 0) if (current.key != previous.key) { - const heureMonde = RdDTimestamp.getWorldTime().heure - const heureMeditation = RdDTimestamp.findHeure(current.meditation.system.heure)?.heure - current.isHeure = heureMeditation == heureMonde - current.isTMR = Grammar.equalsInsensitive(current.meditation.system.tmr, TMRUtility.getTMRType(rollData.active.actor.system.reve.tmrpos.coord)) + this.$selectConditionMeditation(rollData) } } + $selectConditionMeditation(rollData) { + const current = this.getCurrent(rollData) + current.heureMonde = RdDTimestamp.getWorldTime().heure + current.heureMeditation = RdDTimestamp.findHeure(current.meditation.system.heure)?.heure + current.isHeure = current.heureMeditation == current.heureMonde + current.isTMR = Grammar.equalsInsensitive(current.meditation.system.tmr, TMRUtility.getTMRType(rollData.active.actor.system.reve.tmrpos.coord)) + } + async _onRender(rollDialog, context, options) { const selectMeditation = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-meditation"]`) @@ -88,10 +103,7 @@ export class RollPartMeditation extends RollPartSelect { rollDialog.render() }) - this.setupListenerCondition(rollDialog, 'isComportement') - this.setupListenerCondition(rollDialog, 'isHeure') - this.setupListenerCondition(rollDialog, 'isPurification') - this.setupListenerCondition(rollDialog, 'isVeture') + COMPORTEMENTS.forEach(it => this.setupListenerCondition(rollDialog, it)) } setupListenerCondition(rollDialog, inputName) { @@ -108,7 +120,7 @@ export class RollPartMeditation extends RollPartSelect { const current = this.getCurrent(rollData) switch (part.code) { case PART_CARAC: return part.filterCaracs(rollData, [CARACS.INTELLECT]) - case PART_COMP: return part.filterComps(rollData,[current.comp?.name]) + case PART_COMP: return part.filterComps(rollData, [current.comp?.name]) } } return undefined diff --git a/module/roll/roll-part-tache.mjs b/module/roll/roll-part-tache.mjs index 9a914783..1abb670e 100644 --- a/module/roll/roll-part-tache.mjs +++ b/module/roll/roll-part-tache.mjs @@ -1,5 +1,4 @@ import { ITEM_TYPES } from "../constants.js" -import { Grammar } from "../grammar.js" import { ROLL_TYPE_TACHE } from "./roll-constants.mjs" import { PART_CARAC } from "./roll-part-carac.mjs" import { PART_COMP } from "./roll-part-comp.mjs" @@ -18,13 +17,17 @@ export class RollPartTache extends RollPartSelect { loadRefs(rollData) { const refs = this.getRefs(rollData) - refs.taches = rollData.active.actor.itemTypes[ITEM_TYPES.tache] + const selected = this.getSelected(rollData) + refs.all = rollData.active.actor.itemTypes[ITEM_TYPES.tache] + .filter(tache => !selected.forced || tache.id == selected.key) .filter(tache => tache.system.points_de_tache_courant < tache.system.points_de_tache) .map(tache => RollPartTache.$extractTache(tache, rollData.active.actor)) + refs.taches = refs.all if (refs.taches.length > 0) { this.$selectTache(rollData) } } + choices(refs) { return refs.taches } static $extractTache(tache, actor) { diff --git a/module/roll/roll-part.mjs b/module/roll/roll-part.mjs index 3543dd72..dc7dfb9d 100644 --- a/module/roll/roll-part.mjs +++ b/module/roll/roll-part.mjs @@ -43,6 +43,7 @@ export class RollPart { } /** les informations minimales représentant la sélection dans le rollData permettant de restaurer la fenêtre */ + getSelected(rollData) { return this.getSaved(rollData) } getSaved(rollData) { return rollData.selected[this.code] ?? {} } @@ -84,9 +85,9 @@ export class RollPart { loadRefs(rollData) { } prepareContext(rollData) { } - + /** permet de sauvegarder dans rollData les informations (cas des champs edit) */ - validate(rollData) {} + validate(rollData) { } /** ---- cross roll-part filtering ---- */ applyImpact(rollData, filter) { } diff --git a/module/roll/roll-type-meditation.mjs b/module/roll/roll-type-meditation.mjs index 9fd9c4c9..3248f428 100644 --- a/module/roll/roll-type-meditation.mjs +++ b/module/roll/roll-type-meditation.mjs @@ -1,3 +1,4 @@ +import { RdDItemSigneDraconique } from "../item/signedraconique.js" import { DIFF, ROLL_TYPE_MEDITATION } from "./roll-constants.mjs" import { PART_MEDITATION } from "./roll-part-meditation.mjs" import { RollType } from "./roll-type.mjs" @@ -16,4 +17,20 @@ export class RollTypeMeditation extends RollType { onSelect(rollData) { this.setDiffType(rollData, DIFF.AUCUN) } + callbacks(rollOptions) { return [RollTypeMeditation.$onRollMeditation] } + + static async $onRollMeditation(rollData) { + const actor = rollData.active.actor + const meditation = rollData.current.meditation.meditation + const rolled = rollData.rolled + if (meditation && rolled) { + if (rolled.isSuccess) { + await actor.createEmbeddedDocuments("Item", [RdDItemSigneDraconique.prepareSigneDraconiqueMeditation(meditation, rolled)]) + } + if (rolled.isEPart) { + await actor.updateEmbeddedDocuments('Item', [{ _id: meditation._id, 'system.malus': meditation.system.malus - 1 }]) + } + await actor.santeIncDec("fatigue", 2) + } + } } \ No newline at end of file diff --git a/module/roll/roll-type-tache.mjs b/module/roll/roll-type-tache.mjs index 267763d3..3e2e19a0 100644 --- a/module/roll/roll-type-tache.mjs +++ b/module/roll/roll-type-tache.mjs @@ -1,3 +1,5 @@ +import { ITEM_TYPES } from "../constants.js" +import { ReglesOptionnelles } from "../settings/regles-optionnelles.js" import { DIFF, ROLL_TYPE_TACHE } from "./roll-constants.mjs" import { PART_TACHE } from "./roll-part-tache.mjs" import { RollType } from "./roll-type.mjs" @@ -16,4 +18,26 @@ export class RollTypeTache extends RollType { onSelect(rollData) { this.setDiffType(rollData, DIFF.AUCUN) } + + callbacks(rollOptions) { return [ async r => await RollTypeTache.$onRollTache(r, rollOptions)] } + + static async $onRollTache(rollData, rollOptions) { + const actor = rollData.active.actor + const tache = rollData.current[PART_TACHE].tache + if (ReglesOptionnelles.isUsing("appliquer-fatigue")) { + await actor.santeIncDec("fatigue", tache.system.fatigue) + } + + rollData.current[PART_TACHE].tache = await tache.update({ + 'system.points_de_tache_courant': tache.system.points_de_tache_courant + rollData.rolled.ptTache, + 'system.nb_jet_succes': tache.system.nb_jet_succes + (rollData.rolled.isSuccess ? 1 : 0), + 'system.nb_jet_echec': tache.system.nb_jet_echec + (rollData.rolled.isSuccess ? 0 : 1), + 'system.difficulte': tache.system.difficulte - (rollData.rolled.isETotal ? 1 : 0), + }, {render:true}) + + + if (rollOptions?.onRollAutomate) { + await rollOptions.onRollAutomate(rollData) + } + } } \ No newline at end of file diff --git a/module/roll/roll-type.mjs b/module/roll/roll-type.mjs index 4b97f57a..124f503d 100644 --- a/module/roll/roll-type.mjs +++ b/module/roll/roll-type.mjs @@ -18,7 +18,6 @@ export class RollType { isAllowed(rollData) { return rollData.type.allowed == undefined || rollData.type.allowed.includes(this.code) } visible(rollData) { return true } - title(rollData) { return this.code } isSelected(rollData) { return rollData.type.current == this.code } @@ -52,7 +51,7 @@ export class RollType { this.setRollDataType(rollData) } - getResult(rollData) { - return undefined - } + callbacks(rollOptions) { return [] } + + getResult(rollData) { return undefined } } diff --git a/module/settings/options-avancees.js b/module/settings/options-avancees.js index 720a3923..f5828268 100644 --- a/module/settings/options-avancees.js +++ b/module/settings/options-avancees.js @@ -50,7 +50,7 @@ export class OptionsAvancees extends FormApplication { .map(it => { it = foundry.utils.duplicate(it) it.id = OptionsAvancees._getId(it.name) - it.active = OptionsAvancees.isSet(it.name) + it.active = OptionsAvancees.isUsing(it.name) return it }) formData.regles = regles @@ -63,10 +63,6 @@ export class OptionsAvancees extends FormApplication { } static isUsing(name) { - return OptionsAvancees.isSet(name) - } - - static isSet(name) { return game.settings.get(SYSTEM_RDD, OptionsAvancees._getId(name)) } diff --git a/templates/actor/combat.hbs b/templates/actor/combat.hbs index 563fc29d..68f686e4 100644 --- a/templates/actor/combat.hbs +++ b/templates/actor/combat.hbs @@ -8,7 +8,6 @@ {{#each combat as |action key|}}
  • diff --git a/templates/roll/result/chat-meditation.hbs b/templates/roll/result/chat-meditation.hbs index e69de29b..362bc806 100644 --- a/templates/roll/result/chat-meditation.hbs +++ b/templates/roll/result/chat-meditation.hbs @@ -0,0 +1,44 @@ +
    +
    + + +
    +
    + {{active.name}} a médité : {{current.meditation.label}} +
    + +
    + {{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}} +
    {{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}} +
    + +
    +
    + + {{#if rolled.isSuccess}} + + {{active.name}} aperçoit un signe draconique éphémère, qu'il faut aller lire en {{typeTmr-name current.meditation.meditation.system.tmr}}. + + {{else}} + {{active.name}} ne distingue aucun signe Draconique, la meditation n'a pas porté de fruits. + {{/if}} + +
    + {{#if rolled.isEPart}} + + L'échec {{#if rolled.isETotal}}total{{else}}particulier{{/if}} augmente la difficulté de la méditation + {{current.meditation.meditation.name}} de 1 ! + + {{/if}} + + {{#if (regle-optionnelle 'appliquer-fatigue')}} + + + {{active.name}} s'est fatigué{{~#if (actor-isFeminin active.actor)}}e{{/if}} de 2 cases. + + {{/if}} +
    + +
    +
    +
    diff --git a/templates/roll/result/chat-tache.hbs b/templates/roll/result/chat-tache.hbs index e69de29b..cc26cd2e 100644 --- a/templates/roll/result/chat-tache.hbs +++ b/templates/roll/result/chat-tache.hbs @@ -0,0 +1,57 @@ +
    +
    + + +
    +
    + {{active.name}} travaille à sa tâche : {{current.tache.label}} +
    + +
    + {{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}} +
    {{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}} +
    + +
    +
    +

    + {{log 'tache' this }} + {{#if rolled.isSuccess}} + {{active.name}} a obtenu {{rolled.ptTache}} point{{~#unless (eq rolled.ptTache 1)}}s{{/unless}} de tâche, + {{else if (or rolled.isETotal rolled.isEPart)}} + {{active.name}} a perdu {{math-abs rolled.ptTache}} points de tâche, + {{else}} + {{active.name}} n'a pas progressé dans la tâche. + {{/if}} + {{#with current.tache.tache.system as |system|}} + Son avancement est de + + {{system.points_de_tache_courant}} + {{#unless system.cacher_points_de_tache}} sur {{system.points_de_tache}}{{/unless}} + + point{{~#unless (eq system.points_de_tache_courant 1)}}s{{/unless}} de tâche + {{log 'tentatives' (math-sum system.nb_jet_succes system.nb_jet_echec) }} + ({{~#with (math-sum system.nb_jet_succes system.nb_jet_echec) as |tentatives|}} + {{log 'tentatives' tentatives}} + {{~#if (ne tentatives 1)}}{{tentatives}} tentatives{{else}}première tentative{{/if~}} + {{/with~}}). + {{/with}} +

    + + {{#if rolled.isETotal}} + + Son échec total augmente de 1 la difficulté de la tâche! + + {{/if}} + + {{#if (and current.tache.tache.system.fatigue (regle-optionnelle 'appliquer-fatigue'))}} + + + {{active.name}} s'est fatigué{{~#if (actor-isFeminin active.actor)}}e{{/if}} de {{current.tache.tache.system.fatigue}} case{{~#if (gt current.tache.tache.system.fatigue 1)}}s{{/if}}. + + {{/if}} +
    + +
    +
    +
    diff --git a/templates/roll/roll-part-meditation.hbs b/templates/roll/roll-part-meditation.hbs index c673862e..f9be4cbf 100644 --- a/templates/roll/roll-part-meditation.hbs +++ b/templates/roll/roll-part-meditation.hbs @@ -19,14 +19,22 @@ {{current.meditation.system.theme}} Support: {{current.meditation.system.support}} + TMR: {{typeTmr-name current.meditation.system.tmr}} {{#unless current.isTMR}} -   actuelle: {{caseTmr-label rollData.active.actor.system.reve.tmrpos.coord}} {{rollData.active.actor.system.reve.tmrpos.coord}} +  
    + + {{caseTmr-label rollData.active.actor.system.reve.tmrpos.coord}} + {{rollData.active.actor.system.reve.tmrpos.coord}} +
    {{/unless}} +
    Durée: 60 minutes -
    + +
    +