diff --git a/assets/actions/cuisine.svg b/assets/actions/cuisine.svg new file mode 100644 index 00000000..2bb4ca88 --- /dev/null +++ b/assets/actions/cuisine.svg @@ -0,0 +1 @@ + \ No newline at end of file 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/assets/ui/part-finesse.svg b/assets/ui/part-finesse.svg new file mode 100644 index 00000000..b9fc0de7 --- /dev/null +++ b/assets/ui/part-finesse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/ui/part-force.svg b/assets/ui/part-force.svg new file mode 100644 index 00000000..15f4c4eb --- /dev/null +++ b/assets/ui/part-force.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/ui/part-rapidite.svg b/assets/ui/part-rapidite.svg new file mode 100644 index 00000000..dbb6d451 --- /dev/null +++ b/assets/ui/part-rapidite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/changelog.md b/changelog.md index cefc6aec..33b97cc8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,28 @@ # 13.0 +## 13.0.10 - Les papilles d'Illysis + +- 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 + - gestion des particulières + - 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 + +- Technique: suppression de warnings foundry sur renderTemplate + ## 13.0.9 - Le combat d'Illysis - Fix - La montée en TMR fonctionne diff --git a/css/foundryvtt-reve-de-dragon.css b/css/foundryvtt-reve-de-dragon.css index 36ac4ced..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; @@ -655,7 +660,8 @@ select, width: 1.5rem; text-align: center; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat { font-family: CaslonAntique; display: grid; grid-template-areas: "img header buttons" "img resume buttons" "details details details" "actions actions actions"; @@ -663,51 +669,61 @@ select, grid-template-rows: max-content max-content max-content max-content; gap: 0 0.5rem; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-img { grid-area: img; display: flex; flex-direction: column; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img img { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img img, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-img img { border: 0; max-height: 3rem; max-width: 3rem; object-fit: contain; height: 100%; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-header { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-header, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-header { grid-area: header; font-weight: bold; font-size: 0.9rem; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-resume { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-resume, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-resume { grid-area: resume; text-align: justify; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-details { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-details, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-details { grid-area: details; text-align: justify; display: flex; flex-direction: column; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-actions { grid-area: actions; display: flex; flex-direction: column; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-actions a { display: flex; flex-direction: row; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a img { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a img, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-actions a img { margin-right: 0.5rem; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons { grid-area: buttons; display: flex; flex-direction: column; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a { border-radius: 0.2rem; cursor: pointer; padding: 0.2rem; @@ -718,14 +734,17 @@ select, display: inline-block; align-items: center; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a img { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a img, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a img { max-width: 1rem; max-height: 1rem; } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:hover { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:hover, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a:hover { background: var(--background-custom-button-hover); } -.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:active { +.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:active, +.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a:active { position: relative; top: 1px; } @@ -1954,13 +1973,13 @@ select, justify-content: flex-start; flex-direction: column; position: absolute; - top: 4.6rem; - left: -19rem; + top: 10rem; + left: -9rem; } .system-foundryvtt-reve-de-dragon .token-hud-ext.soins { flex-direction: column; position: absolute; - top: 14.7rem; + top: 15.5rem; left: -6rem; max-width: 8rem; line-height: 1rem; @@ -1975,14 +1994,18 @@ select, width: 9rem; height: fit-content; border-radius: 0.3rem; - min-width: 6rem; + min-width: 8rem; flex-basis: auto; padding: 0; - line-height: 0.95rem; + line-height: 1.6rem; margin: 0.2rem; + display: flex; + flex-direction: row; + align-items: center; } -.system-foundryvtt-reve-de-dragon .rdd-hud-menu label { - font-size: 0.8rem; +.system-foundryvtt-reve-de-dragon div.control-icon.token-hud-icon select, +.system-foundryvtt-reve-de-dragon div.control-icon.token-hud-icon label { + font-size: 1rem; } .system-foundryvtt-reve-de-dragon .item-checkbox { height: 25px; 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/foundryvtt-reve-de-dragon.less b/less/foundryvtt-reve-de-dragon.less index f204ceab..975e0224 100644 --- a/less/foundryvtt-reve-de-dragon.less +++ b/less/foundryvtt-reve-de-dragon.less @@ -1319,13 +1319,13 @@ justify-content: flex-start; flex-direction: column; position: absolute; - top: 4.6rem; - left: -19rem; + top: 10rem; + left: -9rem; } .token-hud-ext.soins { flex-direction: column; position: absolute; - top: 14.7rem; + top: 15.5rem; left: -6rem; max-width: 8rem; line-height: 1rem; @@ -1341,14 +1341,18 @@ width: 9rem; height: fit-content; border-radius: 0.3rem; - min-width: 6rem; + min-width: 8rem; flex-basis: auto; padding: 0; - line-height: 0.95rem; + line-height: 1.6rem; margin: 0.2rem; - } - .rdd-hud-menu label { - font-size: 0.8rem; + + display: flex; + flex-direction: row; + align-items: center; + select, label { + font-size: 1rem; + } } /* ======================================== */ .item-checkbox { diff --git a/less/roll-chat.less b/less/roll-chat.less index c5da339c..69eb436e 100644 --- a/less/roll-chat.less +++ b/less/roll-chat.less @@ -1,87 +1,86 @@ -.chat-message { - div.roll-chat { - font-family: CaslonAntique; - display: grid; - grid-template-areas: - "img header buttons" - "img resume buttons" - "details details details" - "actions actions actions"; - grid-template-columns: 3rem 1fr 1.4rem; - grid-template-rows: max-content max-content max-content max-content; - gap: 0 0.5rem; +.chat-message div.roll-chat, +.dialog-content div.roll-chat { + font-family: CaslonAntique; + display: grid; + grid-template-areas: + "img header buttons" + "img resume buttons" + "details details details" + "actions actions actions"; + grid-template-columns: 3rem 1fr 1.4rem; + grid-template-rows: max-content max-content max-content max-content; + gap: 0 0.5rem; - div.chat-img { - grid-area: img; + div.chat-img { + grid-area: img; + display: flex; + flex-direction: column; + img { + border: 0; + max-height: 3rem; + max-width: 3rem; + object-fit: contain; + height: 100%; + } + } + div.chat-header { + grid-area: header; + font-weight: bold; + font-size: 0.9rem; + } + div.chat-resume { + grid-area: resume; + text-align: justify; + } + div.chat-details { + grid-area: details; + text-align: justify; + display: flex; + flex-direction: column; + } + div.chat-actions { + grid-area: actions; + display: flex; + flex-direction: column; + a { display: flex; - flex-direction: column; + flex-direction: row; img { - border: 0; - max-height: 3rem; - max-width: 3rem; - object-fit: contain; - height: 100%; + margin-right: 0.5rem; } } - div.chat-header { - grid-area: header; - font-weight: bold; - font-size: 0.9rem; - } - div.chat-resume { - grid-area: resume; - text-align: justify; - } - div.chat-details { - grid-area: details; - text-align: justify; - display: flex; - flex-direction: column; - } - div.chat-actions { - grid-area: actions; - display: flex; - flex-direction: column; - a { - display: flex; - flex-direction: row; - img { - margin-right: 0.5rem; - } + } + + div.chat-buttons { + grid-area: buttons; + display: flex; + flex-direction: column; + + a { + border-radius: 0.2rem; + cursor: pointer; + padding: 0.2rem; + position: relative; + box-shadow: inset 1x 1px #a6827e; + color: var(--color-controls); + border: 1px ridge #846109; + display: inline-block; + align-items: center; + + img { + max-width: 1rem; + max-height: 1rem; } } - div.chat-buttons { - grid-area: buttons; - display: flex; - flex-direction: column; - - a { - border-radius: 0.2rem; - cursor: pointer; - padding: 0.2rem; - position: relative; - box-shadow: inset 1x 1px #a6827e; - color: var(--color-controls); - border: 1px ridge #846109; - display: inline-block; - align-items: center; - - img { - max-width: 1rem; - max-height: 1rem; - } - } - - a:hover { - background: var(--background-custom-button-hover); - } - - a:active{ - position:relative; - top:1px; - } - + a:hover { + background: var(--background-custom-button-hover); } + + a:active{ + position:relative; + top:1px; + } + } -} \ No newline at end of file +} 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/achat-vente/chat-vente.js b/module/achat-vente/chat-vente.js index a921a356..66a0f2a9 100644 --- a/module/achat-vente/chat-vente.js +++ b/module/achat-vente/chat-vente.js @@ -1,4 +1,4 @@ -import { SYSTEM_RDD } from "../constants.js"; +import { renderTemplate, SYSTEM_RDD } from "../constants.js"; import { RdDUtility } from "../rdd-utility.js"; const DETAIL_VENTE = 'detailVente'; diff --git a/module/achat-vente/dialog-item-achat.js b/module/achat-vente/dialog-item-achat.js index 58992d7e..09b686ff 100644 --- a/module/achat-vente/dialog-item-achat.js +++ b/module/achat-vente/dialog-item-achat.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "../constants.js"; import { Misc } from "../misc.js"; import { RdDUtility } from "../rdd-utility.js"; import { ChatVente } from "./chat-vente.js"; diff --git a/module/achat-vente/dialog-item-vente.js b/module/achat-vente/dialog-item-vente.js index 64a2166e..13c3f448 100644 --- a/module/achat-vente/dialog-item-vente.js +++ b/module/achat-vente/dialog-item-vente.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "../constants.js"; import { HtmlUtility } from "../html-utility.js"; import { RdDUtility } from "../rdd-utility.js"; import { ChatVente } from "./chat-vente.js"; 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-token.mjs b/module/actor-token.mjs index 465e98e8..8a05ee30 100644 --- a/module/actor-token.mjs +++ b/module/actor-token.mjs @@ -2,6 +2,9 @@ * class providing the actor and token, and choosing the name and image from the token if available. */ export class ActorToken { + static fromTokenActor(token, actor){ + return token ? ActorToken.fromToken(token) : ActorToken.fromActor(actor) + } static fromActorId(actorId, onError = () => undefined) { actorId = actorId ?? (canvas.tokens.controlled.length > 0 diff --git a/module/actor.js b/module/actor.js index e683cdf5..6b0c44e4 100644 --- a/module/actor.js +++ b/module/actor.js @@ -17,9 +17,9 @@ import { Draconique } from "./tmr/draconique.js"; import { CARACS, LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js"; import { DialogConsommer } from "./dialog-item-consommer.js"; import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js"; -import { RollDataAjustements } from "./rolldata-ajustements.js"; +import { RollDataAjustements } from "./rolldata-ajustements-v1.js"; import { RdDPossession } from "./rdd-possession.js"; -import { ACTOR_TYPES, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js"; +import { ACTOR_TYPES, renderTemplate, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js"; import { RdDConfirm } from "./rdd-confirm.js"; import { DialogRepos } from "./sommeil/dialog-repos.js"; import { RdDBaseActor } from "./actor/base-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 != "" } /* -------------------------------------------- */ @@ -136,12 +141,12 @@ export class RdDActor extends RdDBaseActorSang { } listActions({ isAttaque = false, isEquipe = false }) { - // Recupération des armes + // Recupération des attaques const actions = this.listActionsAttaque() .filter(it => !isEquipe || it.arme.system.equipe) if (!isAttaque && this.system.attributs.hautrevant.value) { - actions.push({ name: "Draconic", action: 'haut-reve', initOnly: true }) + actions.push({ label: "Draconic", action: 'haut-reve', initOnly: true }) } return actions } @@ -177,7 +182,7 @@ export class RdDActor extends RdDBaseActorSang { const actions = [] const uniques = [] - const addAttaque = (arme, main) => { + const addAttaque = (arme, main = undefined, action = 'attaque') => { const dommagesArme = RdDItemArme.valeurMain(arme.system.dommages, main) const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main) const ecaillesEfficacite = arme.system.magique ? arme.system.ecaille_efficacite : 0; @@ -196,7 +201,7 @@ export class RdDActor extends RdDBaseActorSang { const ajustement = (arme.parent?.getEtatGeneral() ?? 0) + ecaillesEfficacite actions.push({ - name: arme.name + (main ? ' ' + main : ''), + label: arme.name + (main ? ' ' + main : ''), action: 'attaque', initOnly: false, arme: arme, @@ -206,22 +211,23 @@ export class RdDActor extends RdDBaseActorSang { equipe: arme.system.equipe, dommagesArme: dommagesArme, forceRequise: forceRequise, - initiative: RdDInitiative.calculInitiative(niveau, caracValue, ajustement) + initiative: RdDInitiative.getRollInitiative(caracValue, niveau, ajustement) }) } - addAttaque(RdDItemArme.empoignade(this)) - addAttaque(RdDItemArme.corpsACorps(this)) - this.itemTypes[ITEM_TYPES.arme] .filter(it => it.isAttaque()) .sort(Misc.ascending(it => it.name)) .forEach(arme => { - if (arme.system.unemain && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.UNE_MAIN) } - if (arme.system.deuxmains && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.DEUX_MAINS) } - if (arme.system.lancer) { addAttaque(arme, ATTAQUE_TYPE.LANCER) } + if (arme.system.unemain && arme.system.competence && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.UNE_MAIN) } + if (arme.system.deuxmains && arme.system.competence && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.DEUX_MAINS) } + if (arme.system.lancer && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.LANCER) } if (arme.system.tir) { addAttaque(arme, ATTAQUE_TYPE.TIR) } }) + + addAttaque(RdDItemArme.pugilat(this), ATTAQUE_TYPE.CORPS_A_CORPS) + addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS, 'empoignade') + return actions } @@ -1342,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'); @@ -1360,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; @@ -1922,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({ @@ -1944,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) }] @@ -2014,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 = { @@ -2414,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 }) } } @@ -2436,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) { @@ -2916,7 +2962,9 @@ export class RdDActor extends RdDBaseActorSang { break case ITEM_TYPES.race: await this.onCreateOwnedRace(item, options, id) + break } + await super.onCreateItem(item, options, id) await item.onCreateItemTemporel(this); await item.onCreateDecoupeComestible(this); } @@ -3062,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 9207ade4..5e548843 100644 --- a/module/actor/base-actor-reve.js +++ b/module/actor/base-actor-reve.js @@ -1,4 +1,4 @@ -import { ENTITE_INCARNE, SHOW_DICE, SYSTEM_RDD } from "../constants.js"; +import { ENTITE_INCARNE, renderTemplate, SHOW_DICE, SYSTEM_RDD } from "../constants.js"; import { Grammar } from "../grammar.js"; import { Misc } from "../misc.js"; import { RdDResolutionTable } from "../rdd-resolution-table.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"; @@ -23,8 +23,12 @@ import { RdDCombat } from "../rdd-combat.js"; import { RdDEmpoignade } from "../rdd-empoignade.js"; import { RdDPossession } from "../rdd-possession.js"; import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, POSSESSION_SANS_DRACONIC } from "../item/base-items.js"; -import { RollDataAjustements } from "../rolldata-ajustements.js"; +import { RollDataAjustements } from "../rolldata-ajustements-v1.js"; 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) @@ -377,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) @@ -407,9 +424,33 @@ export class RdDBaseActorReve extends RdDBaseActor { } /* -------------------------------------------- */ + async rollCompetenceV2(rollData) { + rollData.ids = rollData?.ids ?? {} + rollData.type = rollData.type ?? { allowed: DEFAULT_ROLL_TYPES } + rollData.ids.actorId = this.id + await RollDialog.create(rollData) + } + async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) { RdDEmpoignade.checkEmpoignadeEnCours(this) + const competence = this.getCompetence(idOrName); + if (competence.type != ITEM_TYPES.competencecreature && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + selected: { + comp: { key: competence.name }, + diff: { type: DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 } + } + } + if (options.arme) { + rollData.selected.attaque = { arme: { id: options.arme.id }, comp: { id: competence.id } } + rollData.type = { allowed: ATTAQUE_ROLL_TYPES } + } + + await this.rollCompetenceV2(rollData) + return + } + let rollData = { carac: this.system.carac, competence: competence, @@ -427,7 +468,7 @@ export class RdDBaseActorReve extends RdDBaseActor { RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme) } }); - return; + return } // Transformer la competence de créature MappingCreatureArme.setRollDataCreature(rollData) @@ -449,6 +490,46 @@ export class RdDBaseActorReve extends RdDBaseActor { } } + rollAttaque(token) { + token = token ?? RdDUtility.getSelectedToken(this) + + if (Targets.hasTargets()) { + Targets.selectOneTargetToken(target => { + if (Targets.isTargetEntite(target)) { + ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée!!!!`) + return + } + + RdDCombat.rddCombatTarget(target, this, token).attaqueV2(); + }) + } + else { + return RdDConfirm.confirmer({ + settingConfirmer: "confirmer-combat-sans-cible", + content: `

Voulez vous faire une attaque sans choisir de cible valide? +
Tous les jets de combats devront être gérés à la main +

`, + title: 'Ne pas utiliser les automatisation de combat', + buttonLabel: "Pas d'automatisation", + onAction: async () => { + this.rollCompetenceV2({ + ids: { + actorId: this.id, + actorTokenId: token?.id, + }, + selected: { + conditions: { value: 0 } + }, + type: { + allowed: [ROLL_TYPE_COMP, ROLL_TYPE_ATTAQUE, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE, ROLL_TYPE_JEU], + current: ROLL_TYPE_COMP + } + }) + } + }) + } + } + /** -------------------------------------------- * @param {*} arme item d'arme/compétence de créature * @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession diff --git a/module/actor/base-actor-sang.js b/module/actor/base-actor-sang.js index c80ba39d..a3f00555 100644 --- a/module/actor/base-actor-sang.js +++ b/module/actor/base-actor-sang.js @@ -34,17 +34,17 @@ export class RdDBaseActorSang extends RdDBaseActorReve { getFatigueActuelle() { if (ReglesOptionnelles.isUsing("appliquer-fatigue")) { - return Math.max(0, Math.min(this.getFatigueMax(), Misc.toInt(this.system.sante.fatigue?.value))) + return Math.max(0, Math.min(this.getFatigueMax(), Misc.toInt(this.system.sante.fatigue?.value))) } return 0; } - isCumulFatigueCauseSommeil(cumulFatigue){ + isCumulFatigueCauseSommeil(cumulFatigue) { return ReglesOptionnelles.isUsing("appliquer-fatigue") - ? (this.getFatigueRestante() <= cumulFatigue) - : (this.getEnduranceActuelle() <= cumulFatigue) + ? (this.getFatigueRestante() <= cumulFatigue) + : (this.getEnduranceActuelle() <= cumulFatigue) } - getFatigueRestante() {return this.getFatigueMax() - this.getFatigueActuelle() } + getFatigueRestante() { return this.getFatigueMax() - this.getFatigueActuelle() } getFatigueMin() { return this.system.sante.endurance.max - this.system.sante.endurance.value } malusFatigue() { @@ -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); } @@ -195,7 +195,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve { const endActuelle = this.getEnduranceActuelle(); const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken); if (blessure.isCritique()) { - encaissement.endurance = endActuelle; + encaissement.endurance = endActuelle } if (blessure.isMort()) { diff --git a/module/actor/base-actor-sheet.js b/module/actor/base-actor-sheet.js index 779b5f88..bdbca38f 100644 --- a/module/actor/base-actor-sheet.js +++ b/module/actor/base-actor-sheet.js @@ -56,10 +56,8 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet { this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires); formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs); formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature) - .forEach(it => { - const competenceCreature = new RdDItemCompetenceCreature(it.toObject(), { parent: it.parent }); - it.isdommages = competenceCreature.isDommages(); - }) + .forEach(it => it.isdommages = it.isDommages() + ) return formData; } diff --git a/module/actor/base-actor.js b/module/actor/base-actor.js index b12edb50..b0d8cab0 100644 --- a/module/actor/base-actor.js +++ b/module/actor/base-actor.js @@ -1,6 +1,6 @@ import { ChatVente } from "../achat-vente/chat-vente.js"; import { ChatUtility } from "../chat-utility.js"; -import { SYSTEM_SOCKET_ID } from "../constants.js"; +import { renderTemplate, SYSTEM_SOCKET_ID } from "../constants.js"; import { Grammar } from "../grammar.js"; import { Monnaie } from "../item-monnaie.js"; import { ITEM_TYPES } from "../constants.js"; @@ -10,6 +10,7 @@ import { RdDConfirm } from "../rdd-confirm.js"; import { RdDUtility } from "../rdd-utility.js"; import { SystemCompendiums } from "../settings/system-compendiums.js"; import { RdDItem } from "../item.js"; +import { STATUSES } from "../settings/status-effects.js"; export class RdDBaseActor extends Actor { @@ -45,8 +46,10 @@ 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)) Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id)) Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId)) } @@ -218,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)) { @@ -243,7 +247,28 @@ export class RdDBaseActor extends Actor { } /* -------------------------------------------- */ async onPreUpdateItem(item, change, options, id) { } - async onCreateItem(item, options, id) { } + + async onCreateItem(item, options, id) { + switch (item.type) { + case ITEM_TYPES.blessure: + await this.changeBleedingState() + break + } + } + + async onUpdateItem(item, options, id) { + switch (item.type) { + case ITEM_TYPES.blessure: + await this.changeBleedingState() + break + } + } + + async changeBleedingState() { + const bleeding = this.itemTypes[ITEM_TYPES.blessure].find(it => it.isBleeding()) + await this.setEffect(STATUSES.StatusBleeding, bleeding ? true : false) + } + async onUpdateActor(update, options, actorId) { } async onDeleteItem(item, options, id) { if (item.isInventaire()) { @@ -738,7 +763,7 @@ export class RdDBaseActor extends Actor { name: this.getAlias(), system: { description: this.system.description } } - foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.hbs', chatData) + renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.hbs', chatData) .then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride))); } @@ -769,11 +794,15 @@ export class RdDBaseActor extends Actor { return this.itemTypes[ITEM_TYPES.possession] .map(p => { return { - name: p.name, + label: p.name, action: 'possession', possessionid: p.system.possessionid, } }) } + 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 df373d3b..3b0fa256 100644 --- a/module/actor/export-scriptarium/mapping.js +++ b/module/actor/export-scriptarium/mapping.js @@ -1,5 +1,5 @@ import { Grammar } from "../../grammar.js" -import { RdDItemArme } from "../../item/arme.js" +import { EMPOIGNADE, RdDItemArme } from "../../item/arme.js" import { RdDItemCompetence } from "../../item-competence.js" import { RdDItemSort } from "../../item-sort.js" import { ITEM_TYPES } from "../../constants.js" @@ -141,7 +141,7 @@ export class Mapping { static prepareArmes(actor) { const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme) - armes.push(RdDItemArme.corpsACorps(actor)); + armes.push(RdDItemArme.pugilat(actor)); armes.push(RdDItemArme.empoignade(actor)); return armes.map(arme => [ arme.system.unemain ? Mapping.prepareArme(actor, arme, '(1 main)') : undefined, @@ -175,7 +175,7 @@ export class Mapping { const dommages = Misc.toSignedString(dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme)) switch (arme.system.mortalite) { case 'non-mortel': return `(${dommages})` - case 'empoignade': return '-' + case EMPOIGNADE: return '-' } return dommages } @@ -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/apps/textroll/text-roll-formatter.js b/module/apps/textroll/text-roll-formatter.js index 7e0dbc05..73015c97 100644 --- a/module/apps/textroll/text-roll-formatter.js +++ b/module/apps/textroll/text-roll-formatter.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "../../constants.js"; export class TextRollManager { diff --git a/module/chat-utility.js b/module/chat-utility.js index c1649e3c..a1c9e1d9 100644 --- a/module/chat-utility.js +++ b/module/chat-utility.js @@ -1,5 +1,5 @@ import { Misc } from "./misc.js"; -import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js"; +import { renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js"; import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js"; @@ -190,7 +190,7 @@ export class ChatUtility { if (rddTimestamp) { const timestamp = new RdDTimestamp(rddTimestamp); const timestampData = timestamp.toCalendrier(); - const dateHeure = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData); + const dateHeure = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData); $(html).find('header.message-header .message-sender').after(dateHeure) } } diff --git a/module/coeur/rdd-coeur.js b/module/coeur/rdd-coeur.js index d96b2e7f..4954f725 100644 --- a/module/coeur/rdd-coeur.js +++ b/module/coeur/rdd-coeur.js @@ -1,5 +1,6 @@ import { RdDBaseActor } from "../actor/base-actor.js"; import { ChatUtility } from "../chat-utility.js"; +import { renderTemplate } from "../constants.js"; const INFO_COEUR = 'info-coeur'; diff --git a/module/constants.js b/module/constants.js index 6b013361..7bfb3481 100644 --- a/module/constants.js +++ b/module/constants.js @@ -9,17 +9,19 @@ export const ENTITE_INCARNE = 'incarne' export const ENTITE_NONINCARNE = 'nonincarne' export const ENTITE_BLURETTE = 'blurette' +export const renderTemplate = foundry.applications.handlebars.renderTemplate + export const RDD_CONFIG = { - niveauEthylisme : [ - {value: "1", label: "Aucun"}, - {value: "0", label: "Eméché (0)"}, - {value: "-1", label: "Gris (-1)"}, - {value: "-2", label: "Pinté (-2)"}, - {value: "-3", label: "Pas Frais (-3)"}, - {value: "-4", label: "Ivre (-4)"}, - {value: "-5", label: "Bu (-5)"}, - {value: "-6", label: "Complètement fait (-6)"}, - {value: "-7", label: "Ivre mort (-7)"} + niveauEthylisme: [ + { value: "1", label: "Aucun" }, + { value: "0", label: "Eméché (0)" }, + { value: "-1", label: "Gris (-1)" }, + { value: "-2", label: "Pinté (-2)" }, + { value: "-3", label: "Pas Frais (-3)" }, + { value: "-4", label: "Ivre (-4)" }, + { value: "-5", label: "Bu (-5)" }, + { value: "-6", label: "Complètement fait (-6)" }, + { value: "-7", label: "Ivre mort (-7)" } ], categorieEntite: { "cauchemar": "Cauchemar", @@ -30,28 +32,34 @@ export const RDD_CONFIG = { "nonincarne": "Non Incarnée", "blurette": "Blurette" }, - heuresRdD : [ - {value : "vaisseau", label: "Vaisseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd01.webp"}, - {value : "sirene", label: "Sirène", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd02.webp"}, - {value : "faucon", label: "Faucon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd03.webp"}, - {value : "couronne", label: "Couronne", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd04.webp"}, - {value : "dragon", label: "Dragon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd05.webp"}, - {value : "epees", label: "Epées", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd06.webp"}, - {value : "lyre", label: "Lyre", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd07.webp"}, - {value : "serpent", label: "Serpent", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd08.webp"}, - {value : "poissonacrobate", label: "Poisson Acrobate", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd09.webp"}, - {value : "araignee", label: "Araignée", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd10.webp"}, - {value : "roseau", label: "Roseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd11.webp"}, - {value : "chateaudormant", label: "Chateau Dormant", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd12.webp"} + heuresRdD: [ + { value: "vaisseau", label: "Vaisseau", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd01.webp" }, + { value: "sirene", label: "Sirène", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd02.webp" }, + { value: "faucon", label: "Faucon", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd03.webp" }, + { value: "couronne", label: "Couronne", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd04.webp" }, + { value: "dragon", label: "Dragon", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd05.webp" }, + { value: "epees", label: "Epées", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd06.webp" }, + { value: "lyre", label: "Lyre", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd07.webp" }, + { value: "serpent", label: "Serpent", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd08.webp" }, + { value: "poissonacrobate", label: "Poisson Acrobate", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd09.webp" }, + { value: "araignee", label: "Araignée", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd10.webp" }, + { value: "roseau", label: "Roseau", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd11.webp" }, + { value: "chateaudormant", label: "Chateau Dormant", img: "systems/foundryvtt-reve-de-dragon/icons/heures/hd12.webp" } ], raretes: [ - {value: "Commune", label: "Commune"}, - {value: "Frequente", label: "Fréquente"}, - {value: "Rare", label: "Rare"}, - {value: "Rarissime", label: "Rarissime"} - ] + { value: "Commune", label: "Commune" }, + { value: "Frequente", label: "Fréquente" }, + { value: "Rare", label: "Rare" }, + { value: "Rarissime", label: "Rarissime" } + ], + particuliere: { + force: { key: 'force', descr: 'en force', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-force.svg'}, + finesse: { key: 'finesse', descr: 'en finesse', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-finesse.svg'}, + rapidite: { key: 'finesse', descr: 'en rapidité', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-rapidite.svg'}, + } } + export const ACTOR_TYPES = { personnage: 'personnage', creature: 'creature', @@ -107,13 +115,3 @@ export const ITEM_TYPES = { extraitpoetique: 'extraitpoetique', } -export const CATEGORIES_COMPETENCES = { - "generale": { base: -4, label: "Générales" }, - "particuliere": { base: -8, label: "Particulières" }, - "specialisee": { base: -11, label: "Spécialisées" }, - "connaissance": { base: -11, label: "Connaissances" }, - "draconic": { base: -11, label: "Draconic" }, - "melee": { base: -6, label: "Mêlée" }, - "tir": { base: -8, label: "Tir" }, - "lancer": { base: -8, label: "Lancer" } -} diff --git a/module/dialog-choix-xp-carac.js b/module/dialog-choix-xp-carac.js index 9afdda88..9ed6d428 100644 --- a/module/dialog-choix-xp-carac.js +++ b/module/dialog-choix-xp-carac.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "./constants.js" export class DialogChoixXpCarac extends Dialog { diff --git a/module/dialog-chronologie.js b/module/dialog-chronologie.js index f67e7793..e53eb6c1 100644 --- a/module/dialog-chronologie.js +++ b/module/dialog-chronologie.js @@ -1,4 +1,4 @@ -import { SYSTEM_RDD } from "./constants.js"; +import { renderTemplate, SYSTEM_RDD } from "./constants.js"; import { Grammar } from "./grammar.js"; import { HtmlUtility } from "./html-utility.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js"; diff --git a/module/dialog-create-signedraconique.js b/module/dialog-create-signedraconique.js index 60b67b01..92a5e873 100644 --- a/module/dialog-create-signedraconique.js +++ b/module/dialog-create-signedraconique.js @@ -1,4 +1,5 @@ import { ChatUtility } from "./chat-utility.js"; +import { renderTemplate } from "./constants.js"; import { HtmlUtility } from "./html-utility.js"; import { RdDItemSigneDraconique } from "./item/signedraconique.js"; import { TMRUtility } from "./tmr-utility.js"; diff --git a/module/dialog-fabriquer-potion.js b/module/dialog-fabriquer-potion.js index 24a157d9..6e88092d 100644 --- a/module/dialog-fabriquer-potion.js +++ b/module/dialog-fabriquer-potion.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "./constants.js"; import { Misc } from "./misc.js"; import { RdDUtility } from "./rdd-utility.js"; diff --git a/module/dialog-item-consommer.js b/module/dialog-item-consommer.js index a71ffaeb..ac15e261 100644 --- a/module/dialog-item-consommer.js +++ b/module/dialog-item-consommer.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "./constants.js"; import { Misc } from "./misc.js"; export class DialogConsommer extends Dialog { diff --git a/module/dialog-select.js b/module/dialog-select.js index 4107d5c0..33253e2a 100644 --- a/module/dialog-select.js +++ b/module/dialog-select.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "./constants.js" export class DialogSelect extends Dialog { static extractIdNameImg(it) { return { id: it.id, name: it.name, img: it.img } } diff --git a/module/dialog-split-item.js b/module/dialog-split-item.js index 79c5404c..2f4419aa 100644 --- a/module/dialog-split-item.js +++ b/module/dialog-split-item.js @@ -1,4 +1,4 @@ -import { Misc } from "./misc.js"; +import { renderTemplate } from "./constants.js"; export class DialogSplitItem extends Dialog { diff --git a/module/dialog-validation-encaissement.js b/module/dialog-validation-encaissement.js index 825b4c06..90bbf91d 100644 --- a/module/dialog-validation-encaissement.js +++ b/module/dialog-validation-encaissement.js @@ -1,4 +1,4 @@ -import { HIDE_DICE, SHOW_DICE } from "./constants.js"; +import { HIDE_DICE, renderTemplate, SHOW_DICE } from "./constants.js"; import { RdDUtility } from "./rdd-utility.js"; /** diff --git a/module/enchantement/dialog-enchanter.js b/module/enchantement/dialog-enchanter.js index 15e1c431..a64533bf 100644 --- a/module/enchantement/dialog-enchanter.js +++ b/module/enchantement/dialog-enchanter.js @@ -1,4 +1,4 @@ -import { ITEM_TYPES } from "../constants.js" +import { ITEM_TYPES, renderTemplate } from "../constants.js" import { RdDItemSort } from "../item-sort.js" import { Misc } from "../misc.js" diff --git a/module/initiative.mjs b/module/initiative.mjs index eedf6978..4ddf5c10 100644 --- a/module/initiative.mjs +++ b/module/initiative.mjs @@ -1,7 +1,65 @@ +export const MAP_PHASE = { + possession: { label: "possession", rang: 10 }, + draconic: { label: "draconic", rang: 9 }, + tir: { label: "tir", rang: 8 }, + lancer: { label: "lancer", rang: 7 }, + arme: { label: "mêlée", rang: 5 }, + pugilat: { label: "pugilat", rang: 4 }, + naturelle: { label: "créature", rang: 4 }, + empoignade: { label: "empoignade", rang: 3 }, + autre: { label: "autre action", rang: 2 }, + demi: { label: "demi-surprise", rang: 0 }, + totale: { label: "surprise totale", rang: -1 }, +} +export const PHASE = Object.values(MAP_PHASE) + export class RdDInitiative { - static calculInitiative(niveau, caracValue, bonus = 0) { - let base = niveau + Math.floor(caracValue / 2) + bonus; - return "1d6" + (base >= 0 ? "+" : "") + base; + static getRollInitiative(caracValue, niveau, bonus = 0) { + const base = RdDInitiative.ajustementInitiative(caracValue, niveau, bonus) + return "1d6" + (base >= 0 ? "+" : "") + base + } + + static ajustementInitiative(caracValue, niveau, bonus) { + return niveau + Math.floor(caracValue / 2) + bonus + } + + static formule(phase, carac, niveau, bonusMalus) { + const ajustement = RdDInitiative.ajustementInitiative(carac, niveau, bonusMalus) + return { phase, ajustement } + } + + static phaseArme(categorie, arme) { + switch (categorie) { + case "tir": + case "lancer": + return MAP_PHASE[categorie] + default: + switch (arme.system.cac) { + case "empoignade": + case "pugilat": + case "naturelle": + return MAP_PHASE[arme.system.cac] + default: + return MAP_PHASE['arme'] + } + } + } + + static phase(keyOrRang) { + return MAP_PHASE[keyOrRang] ?? PHASE.find(it => it.rang == keyOrRang) ?? MAP_PHASE.autre + } + + static async roll(formule) { + const sign = formule.ajustement >= 0 ? "+" : "" + const roll = new Roll(`1d6 + ${sign} + ${formule.ajustement}`) + await roll.evaluate() + const value = Math.max(roll.total, 0) + return { + roll: roll, + value: value, + init: formule.phase.rang + value / 100, + label: formule.phase.label + } } } diff --git a/module/item-competencecreature.js b/module/item-competencecreature.js index d39c29fb..35519b3f 100644 --- a/module/item-competencecreature.js +++ b/module/item-competencecreature.js @@ -10,15 +10,15 @@ export class RdDItemCompetenceCreature extends RdDItem { static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" } + isAttaque() { return this.getCategorieAttaque() != undefined } isParade() { return this.system.iscombat && (this.system.categorie_parade ?? '') != '' } isBouclier() { return this.system.categorie_parade.includes('bouclier') } - attaqueCreature() { const categorieAttaque = this.getCategorieAttaque() if (categorieAttaque != undefined) { - const initative = RdDInitiative.calculInitiative(this.system.niveau, this.system.carac_value); + const initative = RdDInitiative.getRollInitiative(this.system.carac_value, this.system.niveau); const attaque = { - name: this.name, + label: this.name, action: this.isCompetencePossession() ? 'possession' : 'attaque', initOnly: false, arme: new RdDItemArme({ @@ -51,10 +51,6 @@ export class RdDItemCompetenceCreature extends RdDItem { return undefined; } - isAttaque() { - return this.getCategorieAttaque() != undefined - } - getCategorieAttaque() { switch (this.system.categorie) { case "melee": diff --git a/module/item.js b/module/item.js index 5df8e478..44829cd2 100644 --- a/module/item.js +++ b/module/item.js @@ -1,4 +1,4 @@ -import { ITEM_TYPES } from "./constants.js"; +import { ITEM_TYPES, renderTemplate } from "./constants.js"; import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES, CATEGORIES_COMPETENCES_CREATURES } from "./item/base-items.js"; import { ITEM_ACTIONS, DEFAULT_ACTIONS, COMMON_ACTIONS } from "./item/item-actions.js"; diff --git a/module/item/arme.js b/module/item/arme.js index 293ec226..b775a1b2 100644 --- a/module/item/arme.js +++ b/module/item/arme.js @@ -4,6 +4,7 @@ import { BASE_CORPS_A_CORPS } from "./base-items.js"; import { Grammar } from "../grammar.js"; import { RdDInitiative } from "../initiative.mjs"; import { MappingCreatureArme } from "./mapping-creature-arme.mjs"; +import { Misc } from "../misc.js"; const nomCategorieParade = { "sans-armes": "Sans arme", @@ -21,11 +22,16 @@ const nomCategorieParade = { export const ATTAQUE_TYPE = { UNE_MAIN: '(1 main)', DEUX_MAINS: '(2 mains)', + CORPS_A_CORPS: '(corps à corps)', COMPETENCE: 'competence', TIR: '(tir)', LANCER: '(lancer)' } +export const CORPS_A_CORPS = 'Corps à corps' +export const PUGILAT = 'pugilat' +export const EMPOIGNADE = 'empoignade' + /* -------------------------------------------- */ export class RdDItemArme extends RdDItem { @@ -62,7 +68,7 @@ export class RdDItemArme extends RdDItem { case ITEM_TYPES.competencecreature: return MappingCreatureArme.armeCreature(arme); } - return RdDItemArme.corpsACorps(); + return RdDItemArme.pugilat(); } static getCompetenceArme(arme, maniement) { @@ -76,6 +82,7 @@ export class RdDItemArme extends RdDItem { case ATTAQUE_TYPE.DEUX_MAINS: return arme.competence2Mains() case ATTAQUE_TYPE.TIR: case 'tir': return arme.system.tir case ATTAQUE_TYPE.LANCER: case 'lancer': return arme.system.lancer; + case ATTAQUE_TYPE.CORPS_A_CORPS: return CORPS_A_CORPS } } return undefined @@ -244,47 +251,41 @@ export class RdDItemArme extends RdDItem { } isAttaque() { - return this.system.resistance > 0 || this.system.portee_courte > 0 + return this.system.resistance > 0 || (this.system.tir != '' && this.system.portee_courte > 0) } - static corpsACorps(actor) { - let competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS - let melee = actor ? actor.system.carac['melee'].value : 0 + static pugilat(actor) { + return RdDItemArme.$corpsACorps(actor, 'Pugilat', PUGILAT) + } + + static empoignade(actor) { + return RdDItemArme.$corpsACorps(actor, 'Empoignade', EMPOIGNADE) + } + + static $corpsACorps(actor, name, cac, system) { + const competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS + const melee = actor ? actor.system.carac['melee'].value : 0 return new RdDItemArme({ - _id: competence.id, - name: 'Corps à corps', + _id: Misc.fakeId(cac), + name: name, type: ITEM_TYPES.arme, img: competence.img, system: { - initiative: RdDInitiative.calculInitiative(competence.system.niveau, melee), + initiative: RdDInitiative.getRollInitiative(melee, competence.system.niveau), equipe: true, rapide: true, force: 0, dommages: "0", dommagesReels: 0, - mortalite: 'non-mortel', - competence: 'Corps à corps', + mortalite: cac == EMPOIGNADE ? EMPOIGNADE : 'non-mortel', + competence: CORPS_A_CORPS, resistance: 1, - baseInit: 4, - cac: 'pugilat', + baseInit: cac == EMPOIGNADE ? 3 : 4, + cac: cac, deuxmains: true, categorie_parade: 'sans-armes' } }) } - static mainsNues(actor) { - const mainsNues = RdDItemArme.corpsACorps(actor) - mainsNues.name = 'Mains nues' - return mainsNues; - } - - static empoignade(actor) { - const empoignade = RdDItemArme.corpsACorps(actor) - empoignade.name = 'Empoignade' - empoignade.system.cac = 'empoignade' - empoignade.system.baseInit = 3 - empoignade.system.mortalite = 'empoignade' - return empoignade - } } diff --git a/module/item/blessure.js b/module/item/blessure.js index 74365fb0..ac282db3 100644 --- a/module/item/blessure.js +++ b/module/item/blessure.js @@ -209,6 +209,9 @@ export class RdDItemBlessure extends RdDItem { isCritique() { return this.system.gravite > 4 && this.system.gravite <= 6 } + isBleeding() { + return this.system.gravite > 2 && !this.system.premierssoins.done + } isMort() { return this.system.gravite > 6 } diff --git a/module/item/mapping-creature-arme.mjs b/module/item/mapping-creature-arme.mjs index a643b6b9..92a73cc1 100644 --- a/module/item/mapping-creature-arme.mjs +++ b/module/item/mapping-creature-arme.mjs @@ -3,7 +3,6 @@ import { RdDInitiative } from "../initiative.mjs"; export class MappingCreatureArme { - /* -------------------------------------------- */ static setRollDataCreature(rollData) { const code = Grammar.toLowerCaseNoAccentNoSpace(rollData.competence.name); @@ -26,7 +25,7 @@ export class MappingCreatureArme { competence: item.name, cac: categorieAttaque == "naturelle" ? "naturelle" : "", niveau: item.system.niveau, - initiative: RdDInitiative.calculInitiative(item.system.niveau, item.system.carac_value), + initiative: RdDInitiative.getRollInitiative(item.system.carac_value, item.system.niveau), equipe: true, resistance: 100, dommagesReels: item.system.dommages, diff --git a/module/misc.js b/module/misc.js index ac0e933a..cca47cfb 100644 --- a/module/misc.js +++ b/module/misc.js @@ -34,9 +34,13 @@ export class Misc { return ((n % m) + m) % m; } - static inRange(value, min,max){ + static inRange(value, min, max) { + if (min > max) { + return Misc.inRange(value, max, min) + } return Math.max(min, Math.min(value, max)) } + static sum() { return (a, b) => Number(a) + Number(b); } @@ -61,6 +65,10 @@ export class Misc { return 0; } + static fakeId(base) { + return (base + foundry.utils.randomID(16)).substring(0, 16) + } + static typeName(type, subType) { return subType ? game.i18n.localize(`TYPES.${type}.${subType}`) : ''; @@ -111,6 +119,10 @@ export class Misc { list.forEach(it => addToObj(obj, it)) return obj; } + static indexed(list, index = 'index') { + let i = 0; + return list.map(it => { it[index] = i++; return it }) + } static concat(lists) { return lists.reduce((a, b) => a.concat(b), []); diff --git a/module/rdd-bonus.js b/module/rdd-bonus.js index f8a9d969..f808768e 100644 --- a/module/rdd-bonus.js +++ b/module/rdd-bonus.js @@ -55,21 +55,20 @@ export class RdDBonus { dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante return dmg; } - - static dmgRollV2(rollData, current) { + + static dmgRollV2(rollData, attaque) { const actor = rollData.active.actor - const attaque = current.attaque const arme = attaque.arme const dmgArme = RdDBonus.dmgArme(arme, attaque.dommagesArme) const dmg = { total: 0, dmgArme: dmgArme, - penetration: arme.penetration(), + penetration: arme?.penetration() ?? 0, diff: attaque.diff, - dmgTactique: current.tactique?.dmg ?? 0, - dmgParticuliere: 0, // TODO RdDBonus._dmgParticuliere(rollData), + dmgTactique: attaque.tactique?.dmg ?? 0, + dmgParticuliere: RdDBonus._dmgParticuliere(rollData), dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0, - mortalite: RdDBonus.mortalite(current.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 76a27948..821adf98 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -1,5 +1,5 @@ import { ChatUtility } from "./chat-utility.js"; -import { ENTITE_BLURETTE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js"; +import { ENTITE_BLURETTE, HIDE_DICE, renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js"; import { Grammar } from "./grammar.js"; import { Misc } from "./misc.js"; import { RdDBonus } from "./rdd-bonus.js"; @@ -10,15 +10,16 @@ import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"; import { Targets } from "./targets.js"; import { RdDEmpoignade } from "./rdd-empoignade.js"; import { RdDRollResult } from "./rdd-roll-result.js"; -import { RdDItemArme } from "./item/arme.js"; +import { EMPOIGNADE, RdDItemArme } from "./item/arme.js"; import { RdDItemCompetence } from "./item-competence.js"; -import { RdDInitiative } from "./initiative.mjs"; +import { MAP_PHASE, RdDInitiative } from "./initiative.mjs"; import RollDialog from "./roll/roll-dialog.mjs"; import { PART_DEFENSE } from "./roll/roll-part-defense.mjs"; import { RollDialogAdapter } from "./roll/roll-dialog-adapter.mjs"; import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll/roll-constants.mjs"; -import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js"; +import { OptionsAvancees, ROLL_DIALOG_V2, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js"; import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs"; +import { RollBasicParts } from "./roll/roll-basic-parts.mjs"; /* -------------------------------------------- */ const premierRoundInit = [ @@ -67,7 +68,7 @@ export class RdDCombatManager extends Combat { if (Misc.isFirstConnectedGM()) { await this.finDeRound({ terminer: true }) ChatUtility.removeChatMessageContaining(`
`) - game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined) + game.messages.filter(m => ChatUtility.getMessageData(m, 'rollData') != undefined && ChatUtility.getMessageData(m, 'rollData') != undefined) .forEach(it => it.delete()) RdDEmpoignade.deleteAllEmpoignades() } @@ -106,14 +107,18 @@ export class RdDCombatManager extends Combat { return combatant.actor } - static calculAjustementInit(actor, arme) { - const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0 - const etatGeneral = actor.getEtatGeneral() ?? 0 - return efficacite + etatGeneral - + static bonusArme(arme) { + return (arme?.system.magique) ? arme.system.ecaille_efficacite : 0 } - /************************************************************************************/ + static etatGeneral(actor) { + return actor.getEtatGeneral() ?? 0; + } + + + /** *********************************************************************************** + * @override lance l'initiative de plusieurs combattants (tous/ous les PNJs) en une fois + */ async rollInitiative(ids, messageOptions = {}) { console.log(`${game.system.title} | Combat.rollInitiative()`, ids, messageOptions) ids = typeof ids === "string" ? [ids] : ids @@ -121,18 +126,17 @@ export class RdDCombatManager extends Combat { return this } - async rollInitRdD(id, formula, messageOptions = {}) { + async rollInitRdD(id, formule, messageOptions = {}) { const combatant = this.combatants.get(id); const actor = RdDCombatManager.getActorCombatant(combatant) if (actor) { - const rollFormula = formula ?? RdDCombatManager.getFirstInitRollFormula(actor) - const roll = combatant.getInitiativeRoll(rollFormula); - if (!roll.total) { - await roll.evaluate(); - } - const total = Math.max(roll.total, 0.00); - console.log("Compute init for", rollFormula, roll, total, combatant); - await this.updateEmbeddedDocuments("Combatant", [{ _id: combatant._id || combatant.id, initiative: total }]); + formule = formule ?? RdDCombatManager.getFirstInitRollFormula(actor) + const init = await RdDInitiative.roll(formule) + + await this.updateEmbeddedDocuments("Combatant", [{ + _id: combatant._id || combatant.id, + initiative: init.init, 'system.init': init + }]) // Send a chat message let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode"); @@ -144,10 +148,10 @@ export class RdDCombatManager extends Combat { alias: combatant.token?.name, sound: CONFIG.sounds.dice, }, - flavor: `${combatant.token?.name} a fait son jet d'Initiative (${messageOptions.info})
` + flavor: `${combatant.token?.name} a une initiatyive de ${init.value} : ${messageOptions.info}
` }, messageOptions); - roll.toMessage(messageData, { rollMode, create: true }); + init.roll.toMessage(messageData, { rollMode, create: true }); RdDCombatManager.processPremierRoundInit(); } @@ -159,16 +163,11 @@ export class RdDCombatManager extends Combat { if (actions.length > 0) { const action = actions[0] const init = RdDCombatManager.getInitData(actor, action) - const ajustement = RdDCombatManager.calculAjustementInit(actor, action) - return RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement); + const ajustement = RdDCombatManager.bonusArme(action.arme) + RdDCombatManager.etatGeneral(actor) + return RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement); } - let ajustement = RdDCombatManager.calculAjustementInit(actor, undefined); - return RdDCombatManager.formuleInitiative(2, 10, 0, ajustement); - } - - static formuleInitiative(rang, carac, niveau, bonusMalus) { - return `${rang} +( (${RdDInitiative.calculInitiative(niveau, carac, bonusMalus)} )/100)`; + return RdDInitiative.formule(MAP_PHASE['autre'], 10, 0, actor.getEtatGeneral() ?? 0); } /* -------------------------------------------- */ @@ -180,7 +179,6 @@ export class RdDCombatManager extends Combat { for (let combatant of game.combat.combatants) { if (combatant.initiativeData?.arme?.type == "arme") { // TODO: get init data premier round - const initiativeData = combatant.initiativeData; const action = combatant.initiativeData.arme; const fromArme = Grammar.toLowerCaseNoAccentNoSpace(action.system.initpremierround) const initData = premierRoundInit.find(it => fromArme.includes(initData.pattern)) @@ -200,10 +198,27 @@ export class RdDCombatManager extends Combat { } /* -------------------------------------------- */ - static incDecInit(combatantId, incDecValue) { - const combatant = game.combat.combatants.get(combatantId); - let initValue = combatant.initiative + incDecValue; - game.combat.setInitiative(combatantId, initValue); + + static applyInitiativeCommand(combatantId, command, commandValue) { + switch (command) { + case 'delta': return RdDCombatManager.incDecInit(combatantId, commandValue); + case 'autre': return RdDCombatManager.rollInitiativeAction(combatantId, + { name: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } }); + } + } + + static async incDecInit(combatantId, incDecValue) { + const combatant = game.combat.combatants.get(combatantId) + if (combatant?.initiative && incDecValue != 0) { + const value = combatant.system.init.value + incDecValue + const newInit = combatant.initiative + incDecValue / 100; + await game.combat.updateEmbeddedDocuments("Combatant", [{ + _id: combatantId, + initiative: newInit, + 'system.init.value': value, + 'system.init.init': newInit, + }]) + } } /* -------------------------------------------- */ @@ -220,56 +235,42 @@ export class RdDCombatManager extends Combat { } } options = [ - { name: "Incrémenter initiative", condition: true, icon: '', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +0.01); } }, - { name: "Décrémenter initiative", condition: true, icon: '', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), -0.01); } } + { name: "Incrémenter initiative", condition: true, icon: '', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +1); } }, + { name: "Décrémenter initiative", condition: true, icon: '', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), -1); } } ].concat(options); } + /* -------------------------------------------- */ static async rollInitiativeAction(combatantId, action) { const combatant = game.combat.combatants.get(combatantId) const actor = RdDCombatManager.getActorCombatant(combatant) - if (actor == undefined) { return [] } - combatant.initiativeData = { arme: action } // pour reclasser l'init au round 0 + if (actor == undefined) { return } const init = RdDCombatManager.getInitData(actor, action) - const ajustement = RdDCombatManager.calculAjustementInit(actor, action.arme) - const rollFormula = RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement); + const ajustement = RdDCombatManager.bonusArme(actor, action.arme) + RdDCombatManager.etatGeneral(actor) + const formule = RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement); - await game.combat.rollInitRdD(combatantId, rollFormula, init); - combatant.initiativeData + await game.combat.rollInitRdD(combatantId, formule, init); + combatant.initiativeData = { action, formule } // pour reclasser l'init au round 0 } static getInitData(actor, action) { - if (actor.getSurprise() == "totale") { return { offset: -1, info: "Surprise Totale", carac: 0, niveau: 0 } } - if (actor.getSurprise() == "demi") { return { offset: 0, info: "Demi Surprise", carac: 0, niveau: 0 } } - if (action.action == 'autre') { return { offset: 2, info: "Autre Action", carac: 0, niveau: 0 } } - if (action.action == 'possession') { return { offset: 10, info: "Possession", carac: actor.getReveActuel(), niveau: 0 } } - if (action.action == 'haut-reve') { return { offset: 9, info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } } + if (actor.getSurprise() == "totale") { return { phase: MAP_PHASE['totale'], info: "Surprise Totale", carac: 0, niveau: 0 } } + if (actor.getSurprise() == "demi") { return { phase: MAP_PHASE['demi'], info: "Demi Surprise", carac: 0, niveau: 0 } } + if (action.action == 'autre') { return { phase: MAP_PHASE['autre'], info: "Autre Action", carac: 0, niveau: 0 } } + if (action.action == 'possession') { return { phase: MAP_PHASE['possession'], info: "Possession", carac: actor.getReveActuel(), niveau: 0 } } + if (action.action == 'haut-reve') { return { phase: MAP_PHASE['draconic'], info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } } const comp = action.comp return { - offset: RdDCombatManager.initOffset(comp?.system.categorie, action.arme), - info: action.name + " / " + comp.name, + phase: RdDInitiative.phaseArme(comp?.system.categorie, action.arme), + info: action.label, carac: actor.getCaracInit(comp), niveau: comp?.system.niveau ?? (['(lancer)', '(tir)'].includes(action.main) ? -8 : -6) } } - static initOffset(categorie, arme) { - switch (categorie) { - case "tir": return 8 - case "lancer": return 7 - default: - switch (arme.system.cac) { - case "empoignade": return 3 - case "pugilat": return 4 - case "naturelle": return 4 - default: return 5 - } - } - } - /* -------------------------------------------- */ static displayInitiativeMenu(html, combatantId) { const combatant = game.combat.combatants.get(combatantId) @@ -297,13 +298,8 @@ export class RdDCombatManager extends Combat { ? possessions : actor.listActions({ isEquipe: true }) - for (let index = 0; index < actions.length; index++) { - actions[index].index = index - } - return actions + return Misc.indexed(actions) } - - } /* -------------------------------------------- */ @@ -383,8 +379,13 @@ export class RdDCombat { let defenderToken = canvas.tokens.get(msg.defenderToken.id) if (defenderToken && Misc.isFirstConnectedGM()) { const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id) - rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme) - rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll) + rddCombat?.removeChatMessageActionsPasseArme(msg.paramChatDefense.attackerRoll.passeArme) + if (msg.defenderRoll.ids) {/* TODO: delete roll V1 */ + RollDialog.loadRollData(msg.paramChatDefense) + rddCombat?._chatMessageDefenseV2(msg.paramChatDefense) + } else { + rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll) + } } } @@ -464,8 +465,8 @@ export class RdDCombat { /* -------------------------------------------- */ async onEvent(button, event) { const chatMessage = ChatUtility.getChatMessage(event); - const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll'); - const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll'); + const defenderRoll = ChatUtility.getMessageData(chatMessage, 'rollData'); + const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'rollData'); console.log('RdDCombat', attackerRoll, defenderRoll); const armeParadeId = event.currentTarget.attributes['data-armeid']?.value; @@ -665,12 +666,101 @@ export class RdDCombat { return { msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3 }; } + + async attaqueV2() { + if (!await this.attacker.accorder(this.defender, 'avant-attaque')) { + return + } + await this.doRollAttaque({ + ids: { + actorId: this.attackerId, + actorTokenId: this.attackerTokenId, + opponentId: this.defender.id, + opponentTokenId: this.defenderTokenId, + }, + type: { allowed: ['attaque'], current: 'attaque' }, + passeArme: foundry.utils.randomID(16), + }) + } + + async doRollAttaque(rollData, callbacks = []) { + // TODO V2 await this.proposerAjustementTirLancer(rollData) + await RollDialog.create(rollData, { + onRollDone: (dialog) => { + if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST)) + dialog.close() + }, + customChatMessage: true, + callbacks: [ + async (roll) => await this.onAttaqueV2(roll), + ...callbacks + ] + }) + } + + async onAttaqueV2(attackerRoll) { + if (!this.defender || !attackerRoll.rolled.isSuccess || attackerRoll.particulieres?.length > 1) { + return + } + if (!await this.attacker.accorder(this.defender, 'avant-defense')) { + return; + } + + RollDialog.loadRollData(attackerRoll) + + const surpriseDefender = this.defender.getSurprise(true); + const paramChatDefense = { + attackerRoll: attackerRoll, + isPossession: this.isPossession(attackerRoll), + defender: this.defender, + attacker: this.attacker, + attackerId: this.attackerId, + attackerToken: this.attackerToken, + defenderToken: this.defenderToken, + surprise: surpriseDefender, + } + + if (Misc.isFirstConnectedGM()) { + await this._chatMessageDefenseV2(paramChatDefense); + } + else { + this._socketSendMessageDefense(paramChatDefense, {}); + } + } + async _chatMessageDefenseV2(paramDemandeDefense) { + const attackerRoll = paramDemandeDefense.attackerRoll; + RollBasicParts.loadSurprises(attackerRoll) + attackerRoll.passeArme = attackerRoll.passeArme ?? foundry.utils.randomID(16) + attackerRoll.dmg = RdDBonus.dmgRollV2(attackerRoll, attackerRoll.current.attaque) + // attackerRoll.current.attaque.dmg = attackerRoll.dmg + // attaque.dmg = attackerRoll.current.attaque.dmg + const attaque = RollDialog.saveParts(attackerRoll) + const defense = { + attackerRoll: attaque, + ids: RollBasicParts.reverseIds(attaque), + passeArme: attaque.passeArme ?? foundry.utils.randomID(16) + } + + const choixDefense = await ChatMessage.create({ + // message privé: du défenseur à lui même (et aux GMs) + speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)), + alias: this.attacker?.getAlias(), + whisper: ChatUtility.getOwners(this.defender), + content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', attackerRoll) + }); + // flag pour garder les jets d'attaque/defense + ChatUtility.setMessageData(choixDefense, 'rollData', defense) + } + /* -------------------------------------------- */ async attaque(competence, arme) { if (!await this.attacker.accorder(this.defender, 'avant-attaque')) { return } - if (arme.system.cac == 'empoignade') { + if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + return this.attacker.rollCompetence(competence.name, { arme: arme }) + } + if (arme.system.cac == EMPOIGNADE) { RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender) return } @@ -723,9 +813,9 @@ export class RdDCombat { } else { // sans armes: à mains nues - rollData.arme = RdDItemArme.corpsACorps(this.attacker) + rollData.arme = RdDItemArme.pugilat(this.attacker) rollData.arme.system.niveau = competence.system.niveau - rollData.arme.system.initiative = RdDInitiative.calculInitiative(competence.system.niveau, this.attacker.system.carac['melee'].value); + rollData.arme.system.initiative = RdDInitiative.getRollInitiative(this.attacker.system.carac['melee'].value, competence.system.niveau); } return rollData; } @@ -776,7 +866,7 @@ export class RdDCombat { passeArme: rollData.passeArme }) }); - ChatUtility.setMessageData(choixParticuliere, 'attacker-roll', rollData); + ChatUtility.setMessageData(choixParticuliere, 'rollData', rollData); } /* -------------------------------------------- */ @@ -852,10 +942,10 @@ export class RdDCombat { speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)), alias: this.attacker?.getAlias(), whisper: ChatUtility.getOwners(this.defender), - content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', paramDemandeDefense), + content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense-v1.hbs', paramDemandeDefense), }); // flag pour garder les jets d'attaque/defense - ChatUtility.setMessageData(choixDefense, 'defender-roll', defenderRoll); + ChatUtility.setMessageData(choixDefense, 'rollData', defenderRoll); } /* -------------------------------------------- */ @@ -869,7 +959,7 @@ export class RdDCombat { defenderToken: this.defenderToken, defenderRoll: defenderRoll, paramChatDefense: paramChatDefense, - rollMode: true + rollMode: true, } }); } @@ -906,7 +996,7 @@ export class RdDCombat { essais: attackerRoll.essais }) }); - ChatUtility.setMessageData(choixEchecTotal, 'attacker-roll', attackerRoll); + ChatUtility.setMessageData(choixEchecTotal, 'rollData', attackerRoll); } /* -------------------------------------------- */ @@ -968,6 +1058,7 @@ export class RdDCombat { async defenseV2(attackerRoll) { // this._prepareParade(attackerRoll, arme, competence); + RollDialog.loadRollData(attackerRoll) await this.doRollDefense({ ids: { actorId: this.defender.id, @@ -975,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, }) @@ -983,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) => { @@ -1034,22 +1122,17 @@ export class RdDCombat { if (RdDCombat.isReussite(rollData)) { if (isParade) { await this.computeDeteriorationArme(rollData) + if (RdDCombat.isParticuliere(rollData)) { + await this.infoAttaquantDesarme(rollData) + } } - if (RdDCombat.isParticuliere(rollData)) { - await this._onDefenseParticuliere(rollData, isEsquive) - } } this.removeChatMessageActionsPasseArme(rollData.passeArme) } - async _onDefenseParticuliere(rollData, isEsquive) { - if (isEsquive) { - ChatUtility.createChatWithRollMode( - { content: "Vous pouvez esquiver une deuxième fois!" }, - this.defender) - } - else if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) { + async infoAttaquantDesarme(rollData) { + if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) { // TODO: attaquant doit jouer résistance et peut être désarmé p132 ChatUtility.createChatWithRollMode( { content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` }, diff --git a/module/rdd-commands.js b/module/rdd-commands.js index 158bc946..762f8d23 100644 --- a/module/rdd-commands.js +++ b/module/rdd-commands.js @@ -19,6 +19,7 @@ import { TMRUtility } from "./tmr-utility.js"; import { DialogFatigueVoyage } from "./voyage/dialog-fatigue-voyage.js"; import { ChatUtility } from "./chat-utility.js"; import { RdDRollResult } from "./rdd-roll-result.js"; +import { renderTemplate } from "./constants.js"; const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/; diff --git a/module/rdd-empoignade.js b/module/rdd-empoignade.js index f7fce4a0..475c6147 100644 --- a/module/rdd-empoignade.js +++ b/module/rdd-empoignade.js @@ -1,5 +1,5 @@ import { STATUSES } from "./settings/status-effects.js"; -import { ITEM_TYPES } from "./constants.js"; +import { ITEM_TYPES, renderTemplate } from "./constants.js"; import { ChatUtility } from "./chat-utility.js"; import { RdDRollResult } from "./rdd-roll-result.js"; import { RdDRoll } from "./rdd-roll.js"; diff --git a/module/rdd-hotbar-drop.js b/module/rdd-hotbar-drop.js index 84e004c6..5ee0087f 100644 --- a/module/rdd-hotbar-drop.js +++ b/module/rdd-hotbar-drop.js @@ -1,4 +1,4 @@ -import { ATTAQUE_TYPE, RdDItemArme } from "./item/arme.js"; +import { ATTAQUE_TYPE, EMPOIGNADE, PUGILAT, RdDItemArme } from "./item/arme.js"; import { ITEM_TYPES } from "./constants.js"; export class RdDHotbar { @@ -28,8 +28,8 @@ export class RdDHotbar { return ' ' + maniement case 'tir': return ' (tir)'; case 'lancer': return ' (lancer)'; - case 'pugilat': return ' (pugilat)'; - case 'empoignade': return ' (empoignade)'; + case PUGILAT: return ' (pugilat)'; + case EMPOIGNADE: return ' (empoignade)'; } return '' } @@ -63,8 +63,8 @@ export class RdDHotbar { case ITEM_TYPES.competence: await this.createItemMacro(item, slot++, 'competence') if (item.isCorpsACorps()) { - await this.createItemMacro(item, slot++, 'pugilat') - await this.createItemMacro(item, slot++, 'empoignade') + await this.createItemMacro(item, slot++, PUGILAT) + await this.createItemMacro(item, slot++, EMPOIGNADE) } else if (item.isCompetenceArme()) { ui.notifications.info(`${item.name} est une compétence d'arme, la macro n'est pas liée à un arme.
@@ -121,9 +121,9 @@ export class RdDHotbar { case ITEM_TYPES.competence: if (item.isCorpsACorps()) { switch (categorieArme) { - case 'pugilat': - return actor.rollArme(RdDItemArme.corpsACorps(actor)); - case 'empoignade': + case PUGILAT: + return actor.rollArme(RdDItemArme.pugilat(actor)); + case EMPOIGNADE: return actor.rollArme(RdDItemArme.empoignade(actor)); } } diff --git a/module/rdd-meteo.js b/module/rdd-meteo.js index e3512f22..d1cfb738 100644 --- a/module/rdd-meteo.js +++ b/module/rdd-meteo.js @@ -1,4 +1,5 @@ import { ChatUtility } from "./chat-utility.js" +import { renderTemplate } from "./constants.js" const vents = [ { min: 0, max: 0, valeur: 'Calme' }, diff --git a/module/rdd-namegen.js b/module/rdd-namegen.js index f0768cf7..9429e6fe 100644 --- a/module/rdd-namegen.js +++ b/module/rdd-namegen.js @@ -1,5 +1,6 @@ import { RdDBaseActor } from "./actor/base-actor.js"; import { ChatUtility } from "./chat-utility.js"; +import { renderTemplate } from "./constants.js"; import { Misc } from "./misc.js"; import { RdDDice } from "./rdd-dice.js"; diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js index 99d51688..18c7545f 100644 --- a/module/rdd-resolution-table.js +++ b/module/rdd-resolution-table.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "./constants.js"; import { Misc } from "./misc.js"; import { RdDDice } from "./rdd-dice.js"; import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"; @@ -66,9 +67,9 @@ export class RdDResolutionTable { } /* -------------------------------------------- */ - static _computeCell(niveau, percentage) { + static _computeCell(level, percentage) { return { - niveau: niveau, + level: level, score: percentage, norm: Math.min(99, percentage), sign: this._reussiteSignificative(percentage), @@ -188,7 +189,7 @@ export class RdDResolutionTable { static computeReussite(chances, roll, diviseur) { const reussite = reussites.find(x => x.condition(chances, roll)) if (diviseur > 1 && reussite.isSuccess) { - if (chances > roll * diviseur) { + if (chances.norm < roll * diviseur) { return reussiteInsuffisante } } @@ -236,7 +237,7 @@ export class RdDResolutionTable { maxCarac = Math.min(maxCarac, minCarac + 20); minLevel = Math.max(minLevel, -10); maxLevel = Math.max(Math.min(maxLevel, 30), minLevel + colonnes); - return await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.hbs', { + return await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.hbs', { carac: carac, difficulte: level, min: minLevel, diff --git a/module/rdd-roll-encaisser.js b/module/rdd-roll-encaisser.js index e11b3a23..058c93ff 100644 --- a/module/rdd-roll-encaisser.js +++ b/module/rdd-roll-encaisser.js @@ -1,4 +1,4 @@ -import { ENTITE_BLURETTE, ENTITE_INCARNE } from "./constants.js"; +import { ENTITE_BLURETTE, ENTITE_INCARNE, renderTemplate } from "./constants.js"; import { RdDUtility } from "./rdd-utility.js"; /** diff --git a/module/rdd-roll-resolution-table.js b/module/rdd-roll-resolution-table.js index 6901c523..337a9568 100644 --- a/module/rdd-roll-resolution-table.js +++ b/module/rdd-roll-resolution-table.js @@ -1,5 +1,7 @@ +import { renderTemplate } from "./constants.js"; import { Misc } from "./misc.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js"; +import { RdDRollResult } from "./rdd-roll-result.js"; const titleTableDeResolution = 'Table de résolution'; /** @@ -98,14 +100,14 @@ export class RdDRollResolutionTable extends Dialog { async onLancer() { await RdDResolutionTable.rollData(this.rollData); console.log("RdDRollResolutionTable -=>", this.rollData, this.rollData.rolled); - await RdDResolutionTable.displayRollData(this.rollData); + await RdDRollResult.displayRollData(this.rollData); } /* -------------------------------------------- */ async onLancerFermer() { await RdDResolutionTable.rollData(this.rollData); console.log("RdDRollResolutionTable -=>", this.rollData, this.rollData.rolled); - await RdDResolutionTable.displayRollData(this.rollData); + await RdDRollResult.displayRollData(this.rollData); } /* -------------------------------------------- */ diff --git a/module/rdd-roll-result.js b/module/rdd-roll-result.js index 3087ff2f..2495e366 100644 --- a/module/rdd-roll-result.js +++ b/module/rdd-roll-result.js @@ -1,4 +1,5 @@ import { ChatUtility } from "./chat-utility.js"; +import { renderTemplate } from "./constants.js"; export class RdDRollResult { @@ -13,6 +14,6 @@ export class RdDRollResult { static async buildRollDataHtml(rollData, template = 'chat-resultat-general.hbs') { rollData.show = rollData.show || {}; - return await foundry.applications.handlebars.renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData); + return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData); } } diff --git a/module/rdd-roll.js b/module/rdd-roll.js index 7ae8900d..65c08de6 100644 --- a/module/rdd-roll.js +++ b/module/rdd-roll.js @@ -1,4 +1,4 @@ -import { RollDataAjustements } from "./rolldata-ajustements.js"; +import { RollDataAjustements } from "./rolldata-ajustements-v1.js"; import { HtmlUtility } from "./html-utility.js"; import { RdDItemCompetence } from "./item-competence.js"; import { RdDItemSort } from "./item-sort.js"; @@ -8,8 +8,8 @@ import { RdDCarac } from "./rdd-carac.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js"; import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"; import { Grammar } from "./grammar.js"; -import { ACTOR_TYPES } from "./constants.js"; -import { RdDUtility } from "./rdd-utility.js"; +import { ACTOR_TYPES, renderTemplate } from "./constants.js"; +import { EMPOIGNADE } from "./item/arme.js"; /** * Extend the base Dialog entity to select roll parameters @@ -23,7 +23,7 @@ export class RdDRoll extends Dialog { RdDRoll._ensureCorrectAction(action); RdDRoll._setDefaultOptions(actor, rollData); - const html = await foundry.applications.handlebars.renderTemplate(dialogConfig.html, rollData); + const html = await renderTemplate(dialogConfig.html, rollData); let options = { classes: ["rdd-roll-dialog"], width: 650, height: 'fit-content', 'z-index': 99999, close: html => { } }; if (dialogConfig.close) { @@ -334,10 +334,8 @@ export class RdDRoll extends Dialog { // Mise à jour valeurs this.html.find(".dialog-roll-title").text(this._getTitle(rollData)); this.html.find("input.check-mortalite").prop('checked', rollData.dmg.mortalite == 'non-mortel'); - this.html.find("label.dmg-arme-actor").text(rollData.dmg.mortalite == 'empoignade' ? 'empoignade' : Misc.toSignedString(rollData.dmg.total)); + this.html.find("label.dmg-arme-actor").text(rollData.dmg.mortalite == EMPOIGNADE ? EMPOIGNADE : Misc.toSignedString(rollData.dmg.total)); this.html.find("label.arme-mortalite").text(rollData.dmg.mortalite); - // this.html.find("[name='dmg-arme-actor']").text(rollData.dmg.mortalite == 'empoignade'? 'empoignade': Misc.toSignedString(rollData.dmg.total) ); - // this.html.find("[name='arme-mortalite']").text(rollData.dmg.mortalite); this.html.find("div.placeholder-ajustements").empty().append(adjustements); this.html.find("div.placeholder-resolution").empty().append(resolutionTable) } @@ -345,7 +343,7 @@ export class RdDRoll extends Dialog { /* -------------------------------------------- */ async buildAjustements(rollData) { - return await foundry.applications.handlebars.renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.hbs`, rollData); + return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.hbs`, rollData); } /* -------------------------------------------- */ diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index dbf635f5..fbc88b7f 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -1,5 +1,5 @@ -import { SHOW_DICE, SYSTEM_RDD } from "./constants.js"; -import { RollDataAjustements } from "./rolldata-ajustements.js"; +import { renderTemplate, SHOW_DICE, SYSTEM_RDD } from "./constants.js"; +import { RollDataAjustements } from "./rolldata-ajustements-v1.js"; import { RdDUtility } from "./rdd-utility.js"; import { COORD_TMR_INCONNU, TMRUtility } from "./tmr-utility.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js"; @@ -47,7 +47,7 @@ export class RdDTMRDialog extends Dialog { static async create(actor, tmrData) { await PixiTMR.init() - let html = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.hbs', tmrData); + let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.hbs', tmrData); if (tmrData.mode != 'visu' && !game.user.isGM) { ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatUtility.getGMs() }); } @@ -508,7 +508,7 @@ export class RdDTMRDialog extends Dialog { ChatMessage.create({ whisper: ChatUtility.getOwners(this.actor), - content: await foundry.applications.handlebars.renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.hbs`, rencData) + content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.hbs`, rencData) }); this.$updateValuesDisplay(); diff --git a/module/rdd-token-hud.js b/module/rdd-token-hud.js index 83553c8f..f9612726 100644 --- a/module/rdd-token-hud.js +++ b/module/rdd-token-hud.js @@ -1,6 +1,9 @@ /* -------------------------------------------- */ +import { renderTemplate } from "./constants.js"; import { HtmlUtility } from "./html-utility.js"; +import { Misc } from "./misc.js"; import { RdDCombatManager } from "./rdd-combat.js"; +import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js"; import { Targets } from "./targets.js"; /* -------------------------------------------- */ @@ -30,47 +33,92 @@ export class RdDTokenHud { const combatant = game.combat.combatants.find(c => c.tokenId == tokenId) const actor = RdDCombatManager.getActorCombatant(combatant, { warning: false }) if (actor) { - const actions = RdDCombatManager.listActionsActorCombatant(actor) - // initiative - await RdDTokenHud.addExtensionHudInit(html, combatant, actions) - // combat - await RdDTokenHud.addExtensionHudCombat(html, combatant, token, actions.filter(it => !it.initOnly)) + if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + await RdDTokenHud.addExtensionHudCombat(html, combatant, actor, token) + } + else { + const actions = RdDCombatManager.listActionsActorCombatant(actor) + // initiative + await RdDTokenHud.addExtensionHudInit(html, combatant, actions) + // combat + await RdDTokenHud.addExtensionHudAttaques(html, combatant, token, actions.filter(it => !it.initOnly)) + } } } + } + static async addExtensionHudCombat(html, combatant, actor, token) { + const actionsActor = actor.listActionsCombat(); + const ajustements = combatant?.initiative ? + [ + { label: 'Initiative +1', action: 'delta', value: 1 }, + { label: 'Initiative -1', action: 'delta', value: -1 } + ] : [] + const autres = [{ label: "Autre action", action: 'autre' }] + const actions = Misc.indexed(actionsActor.concat(ajustements).concat(autres)) + const hudData = { combatant, token, actions }; + const hud = $(await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/hud-actor-combat.hbs', hudData)) + $(html).find('div.col.left').append(hud) + + const list = hud.find('div.rdd-hud-list') + RdDTokenHud.setupHudToggle(hud, list) + + const selectInitiative = list.find('select[name="initiative"]'); + selectInitiative.change(event => { + const action = actions.find(it => it.index == event.currentTarget.value) + console.log('select initiative', combatant.id, action) + if (action) { + switch (action.action) { + case 'delta': + RdDCombatManager.incDecInit(combatant.id, action.value); + break + case 'autre': + RdDCombatManager.rollInitiativeAction(combatant.id, + { label: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } }); + break + default: + RdDCombatManager.rollInitiativeAction(combatant.id, action) + } + selectInitiative.select("") + } + }) + list.find('.rdd-attaque-v2').click(event => combatant.actor.rollAttaque(token)) } static async addExtensionHudInit(html, combatant, actions) { const hudData = { combatant, actions, commandes: [ - { name: "Autre action", command: 'autre' }, - { name: 'Initiative +1', command: 'inc', value: 0.01 }, - { name: 'Initiative -1', command: 'dec', value: -0.01 }] + { label: "Autre action", command: 'autre' }, + { label: 'Initiative +1', command: 'delta', value: 1 }, + { label: 'Initiative -1', command: 'deltac', value: -1 }] }; const controlIconCombat = $(html).find('.control-icon[data-action=combat]'); - await RdDTokenHud._configureSubMenu(controlIconCombat, + await RdDTokenHud._configureSubMenu(it => controlIconCombat.after(it), 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-init.hbs', hudData, (event) => { - let initCommand = event.currentTarget.attributes['data-command']?.value; - let combatantId = event.currentTarget.attributes['data-combatant-id']?.value; + let initCommand = event.currentTarget.attributes['data-command']?.value + let initCommandValue = Number(event.currentTarget.attributes['data-command-value']?.value ?? 0) + let combatantId = event.currentTarget.attributes['data-combatant-id']?.value if (initCommand) { - RdDTokenHud._initiativeCommand(initCommand, combatantId); + RdDCombatManager.applyInitiativeCommand(combatantId, initCommand, initCommandValue) } else { - let index = event.currentTarget.attributes['data-action-index'].value; - let action = hudData.actions[index]; - RdDCombatManager.rollInitiativeAction(combatantId, action); + let index = event.currentTarget.attributes['data-action-index'].value + let action = hudData.actions[index] + RdDCombatManager.rollInitiativeAction(combatantId, action) } - }); + }) } - static async addExtensionHudCombat(html, combatant, token, actions) { + static async addExtensionHudAttaques(html, combatant, token, actions) { const hudData = { combatant, token, actions, commandes: [] }; - const controlIconTarget = $(html).find('.control-icon[data-action=target]'); - await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.hbs', hudData, + const divColLeft = $(html).find('div.col.left'); + await RdDTokenHud._configureSubMenu(it => divColLeft.append(it), + 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.hbs', + hudData, (event) => { const actionIndex = event.currentTarget.attributes['data-action-index']?.value; const action = hudData.actions[actionIndex]; @@ -90,7 +138,7 @@ export class RdDTokenHud { const hudSoins = { blessures: target.actor.blessuresASoigner() ?? [] }; if (hudSoins.blessures.length > 0) { const controlIconTarget = $(html).find('.control-icon[data-action=combat]'); - await RdDTokenHud._configureSubMenu(controlIconTarget, + await RdDTokenHud._configureSubMenu(it => controlIconTarget.after(it), 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-soins.hbs', hudSoins, (event) => { @@ -101,15 +149,6 @@ export class RdDTokenHud { } } - static _initiativeCommand(initCommand, combatantId) { - switch (initCommand) { - case 'inc': return RdDCombatManager.incDecInit(combatantId, 0.01); - case 'dec': return RdDCombatManager.incDecInit(combatantId, -0.01); - case 'autre': return RdDCombatManager.rollInitiativeAction(combatantId, - { name: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } }); - } - } - /* -------------------------------------------- */ static async addTokenHudExtensions(app, html, tokenId) { console.log(`Adding token HUD extensions for token ${tokenId}`); @@ -129,20 +168,24 @@ export class RdDTokenHud { } /* -------------------------------------------- */ - static async _configureSubMenu(insertionPoint, template, hudData, onMenuItem) { - const hud = $(await foundry.applications.handlebars.renderTemplate(template, hudData)); + static async _configureSubMenu(callInsertion, template, hudData, onMenuItem) { + const hud = $(await renderTemplate(template, hudData)); const list = hud.find('div.rdd-hud-list'); - RdDTokenHud._toggleHudListActive(hud, list); + RdDTokenHud.setupHudToggle(hud, list) - hud.find('img.rdd-hud-togglebutton').click(event => RdDTokenHud._toggleHudListActive(hud, list)); list.find('.rdd-hud-menu').click(onMenuItem); - insertionPoint.after(hud); + callInsertion(hud); } - static _toggleHudListActive(hud, list) { - hud.toggleClass('active'); - HtmlUtility.showControlWhen(list, hud.hasClass('active')); + static setupHudToggle(hud, list) { + function toggleHudList(hud, list) { + hud.toggleClass('active') + HtmlUtility.showControlWhen(list, hud.hasClass('active')) + } + toggleHudList(hud, list) + $(hud).find('img.rdd-hud-togglebutton').click(event => toggleHudList(hud, list)) } + } \ No newline at end of file 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/chat-roll-result.mjs b/module/roll/chat-roll-result.mjs index 7c22ad21..69bfbbbf 100644 --- a/module/roll/chat-roll-result.mjs +++ b/module/roll/chat-roll-result.mjs @@ -4,6 +4,8 @@ import { RdDCarac } from "../rdd-carac.js" import { RdDCombat } from "../rdd-combat.js" import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs" import { RdDResolutionTable } from "../rdd-resolution-table.js" +import { RDD_CONFIG, renderTemplate } from "../constants.js" +import { EMPOIGNADE } from "../item/arme.js" export default class ChatRollResult { static init() { @@ -15,6 +17,7 @@ export default class ChatRollResult { static onReady() { foundry.applications.handlebars.loadTemplates({ 'partial-appel-chance': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-appel-chance.hbs', + 'partial-attaque-particuliere': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-attaque-particuliere.hbs', 'partial-encaissement': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-encaissement.hbs', 'partial-recul-choc': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-recul-choc.hbs', 'partial-info-appel-moral': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-info-appel-moral.hbs', @@ -37,12 +40,12 @@ export default class ChatRollResult { } prepareDisplay(roll) { - roll.done = roll.done || {} - roll.show = roll.show || {} + roll.done = roll.done ?? {} + roll.show = roll.show ?? {} roll.show.chance = this.isAppelChancePossible(roll) roll.show.encaissement = this.isShowEncaissement(roll) - roll.show.recul = this.getReculChoc(roll) - + roll.show.recul = this.getRecul(roll) + //roll.show.particuliere = roll.show.particuliere ?? [] } isAppelChancePossible(roll) { @@ -52,31 +55,50 @@ export default class ChatRollResult { } isShowEncaissement(roll) { - return roll.rolled.isEchec && - roll.attackerRoll?.dmg.mortalite != 'empoignade' + switch (roll.type.current) { + case ROLL_TYPE_DEFENSE: + return roll.rolled.isEchec && roll.attackerRoll?.dmg.mortalite != EMPOIGNADE + } + return false } - getReculChoc(roll, defender = roll.active.actor, attacker = roll.opponent.actor) { - const attaque = roll.attackerRoll - if (attaque && - (roll.rolled.isEchec || !roll.current.defense.isEsquive) && - (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) { - const taille = defender.system.carac.taille.value - const impact = attacker.system.carac.force.value + roll.attackerRoll?.dmg.dmgArme - return { - raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force', - taille: taille, - impact: impact, - chances: RdDResolutionTable.computeChances(10, taille-impact).norm, - diff: taille - impact - } + + getRecul(roll, defender = roll.active.actor, attacker = roll.opponent?.actor) { + switch (roll.type.current) { + case ROLL_TYPE_DEFENSE: + { + const attaque = roll.attackerRoll + if (attaque && + (roll.rolled.isEchec || !roll.current.defense.isEsquive) && + (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) { + const taille = defender.system.carac.taille.value + const impact = attacker.system.carac.force.value + roll.attackerRoll?.dmg.dmgArme + return { + raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force', + taille: taille, + impact: impact, + chances: RdDResolutionTable.computeChances(10, taille - impact).norm, + diff: taille - impact + } + } + break + } + case ROLL_TYPE_ATTAQUE: + { + const attaque = roll + if (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key) { + return { + raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force', + } + } + } } return undefined } async buildRollHtml(roll) { const template = `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-${roll.type.current}.hbs` - return await foundry.applications.handlebars.renderTemplate(template, roll) + return await renderTemplate(template, roll) } async chatListeners(html) { @@ -84,6 +106,7 @@ export default class ChatRollResult { $(html).on("click", '.appel-destinee', event => this.onClickAppelDestinee(event)) $(html).on("click", '.encaissement', event => this.onClickEncaissement(event)) $(html).on("click", '.resister-recul', event => this.onClickRecul(event)) + $(html).on("click", '.choix-particuliere', event => this.onClickChoixParticuliere(event)) } @@ -177,4 +200,13 @@ export default class ChatRollResult { await this.updateChatMessage(chatMessage, savedRoll) } + async onClickChoixParticuliere(event) { + const choix = event.currentTarget.attributes['data-particuliere'].value + const chatMessage = ChatUtility.getChatMessage(event) + const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') + savedRoll.particuliere = choix + savedRoll.particulieres = [RDD_CONFIG.particuliere[choix]] + await this.updateChatMessage(chatMessage, savedRoll) + await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll, callbacks) + } } \ No newline at end of file diff --git a/module/roll/roll-basic-parts.mjs b/module/roll/roll-basic-parts.mjs index 3a1c5746..8a6fc14d 100644 --- a/module/roll/roll-basic-parts.mjs +++ b/module/roll/roll-basic-parts.mjs @@ -4,9 +4,10 @@ import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs" import { PART_ATTAQUE } from "./roll-part-attaque.mjs" import { PART_DEFENSE } from "./roll-part-defense.mjs" + export class RollBasicParts { - restore(rollData) { + static restore(rollData) { rollData.ids.sceneId = rollData.ids.sceneId ?? canvas.scene.id rollData.active = RollBasicParts.$getActor(rollData) rollData.opponent = RollBasicParts.$getOpponent(rollData) @@ -15,14 +16,14 @@ export class RollBasicParts { } } - loadSurprises(rollData, type) { + static loadSurprises(rollData, type = rollData.type.current) { if (!rollData.type.passif) { - this.loadSurprise(rollData.active, this.getForceRequiseActiveActor(rollData, type)) - this.loadSurprise(rollData.opponent, 0) + RollBasicParts.loadSurprise(rollData.active, RollBasicParts.getForceRequiseActiveActor(rollData, type)) + RollBasicParts.loadSurprise(rollData.opponent, 0) } } - loadSurprise(who, forceRequise) { + static loadSurprise(who, forceRequise) { if (who?.actor) { foundry.utils.mergeObject(who, StatusEffects.getActorEffetSurprise(who.actor, forceRequise), @@ -30,15 +31,15 @@ export class RollBasicParts { } } - getForceRequiseActiveActor(rollData, type) { + static getForceRequiseActiveActor(rollData, type) { switch (type) { - case ROLL_TYPE_ATTAQUE: return rollData.current[PART_ATTAQUE].attaque.forceRequise + case ROLL_TYPE_ATTAQUE: return rollData.current[PART_ATTAQUE].forceRequise case ROLL_TYPE_DEFENSE: return rollData.current[PART_DEFENSE].forceRequise default: return 0 } } - initFrom(rollData) { + static initFrom(rollData) { return { selected: {}, type: rollData.type, @@ -52,6 +53,16 @@ export class RollBasicParts { } } + static reverseIds(rollData) { + return { + sceneId: rollData.ids.sceneId, + actorId: rollData.ids.opponentId, + actorTokenId: rollData.ids.opponentTokenId, + opponentId: rollData.ids.actorId, + opponentTokenId: rollData.actorTokenId + } + } + static $getActor(rollData) { if (rollData.ids.actorTokenId) { return ActorToken.fromTokenId(rollData.ids.actorTokenId, rollData.ids.sceneId) diff --git a/module/roll/roll-constants.mjs b/module/roll/roll-constants.mjs index f44f9bd5..7b5f54f9 100644 --- a/module/roll/roll-constants.mjs +++ b/module/roll/roll-constants.mjs @@ -4,10 +4,19 @@ export const ROLL_TYPE_COMP = 'comp' export const ROLL_TYPE_DEFENSE = 'defense' export const ROLL_TYPE_JEU = 'jeu' export const ROLL_TYPE_MEDITATION = 'meditation' +export const ROLL_TYPE_CUISINE = 'cuisine' export const ROLL_TYPE_OEUVRE = 'oeuvre' export const ROLL_TYPE_SORT = 'sort' export const ROLL_TYPE_TACHE = 'tache' +export const ATTAQUE_ROLL_TYPES = [ROLL_TYPE_ATTAQUE] +export const COMBAT_ROLL_TYPES = [ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE] +export const DEMIREVE_ROLL_TYPES = [ROLL_TYPE_SORT] +export const DEFAULT_ROLL_TYPES = [ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_CUISINE, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE] +export const ALL_ROLL_TYPES = [...DEFAULT_ROLL_TYPES, ...COMBAT_ROLL_TYPES, ...DEMIREVE_ROLL_TYPES] + + + export const DIFF = { LIBRE: 'libre', ATTAQUE: 'attaque', diff --git a/module/roll/roll-dialog-adapter.mjs b/module/roll/roll-dialog-adapter.mjs index 55beea84..ba3e432f 100644 --- a/module/roll/roll-dialog-adapter.mjs +++ b/module/roll/roll-dialog-adapter.mjs @@ -6,6 +6,10 @@ import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"; import { PART_OEUVRE } from "./roll-part-oeuvre.mjs"; import { RdDItemArme } from "../item/arme.js"; import { RdDBonus } from "../rdd-bonus.js"; +import { ITEM_TYPES, RDD_CONFIG } from "../constants.js"; +import { CARACS } from "../rdd-carac.js"; +import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs"; +import { PART_ATTAQUE } from "./roll-part-attaque.mjs"; /* -------------------------------------------- */ export class RollDialogAdapter { @@ -15,21 +19,21 @@ export class RollDialogAdapter { carac: rollData.current.carac.value, diff: rollData.current.totaldiff, bonus: rollData.current.bonus, - sign: rollData.current.sign, showDice: rollData.options.showDice, rollMode: rollData.current.rollmode.key }) const rolled = await RollDialogAdapter.rollChances(rollData, chances) - RollDialogAdapter.adjustRollDataForV1(rollData, rolled, rollTitle) + RollDialogAdapter.setRollDataRolled(rollData, rolled, rollTitle) + RollDialogAdapter.adjustRollDataForV1(rollData) + RollDialogAdapter.adjustAttaqueParticuliere(rollData) return rolled } - static computeChances({ carac, diff, bonus, sign, showDice, rollMode }) { + static computeChances({ carac, diff, bonus, showDice, rollMode }) { const chances = foundry.utils.duplicate(RdDResolutionTable.computeChances(carac, diff)) RdDResolutionTable._updateChancesWithBonus(chances, bonus, diff) - RdDResolutionTable._updateChancesFactor(chances, sign) chances.showDice = showDice chances.rollMode = rollMode return chances @@ -37,7 +41,7 @@ export class RollDialogAdapter { static async rollChances(rollData, chances) { const rolled = await RdDResolutionTable.rollChances(chances, - rollData.current.sign, + rollData.current.sign.diviseur, rollData.current.resultat) rolled.caracValue = rollData.current.carac.value rolled.finalLevel = rollData.current.totaldiff @@ -46,13 +50,20 @@ export class RollDialogAdapter { return rolled } - static adjustRollDataForV1(rollData, rolled, rollTitle) { + static setRollDataRolled(rollData, rolled, rollTitle) { + rollData.rolled = rolled + rollData.choix = rollData.choix ?? {} + rollData.show = rollData.show ?? {} + rollData.show.title = rollTitle + } + + static adjustRollDataForV1(rollData) { + const rolled = rollData.rolled // temporaire pour être homogène roll v1 rollData.alias = rollData.active.actor.getAlias() // pour experience rollData.finalLevel = rollData.current.totaldiff if (rollData.use == undefined) { rollData.use = {} } - if (rollData.show == undefined) { rollData.show = {} } if (rollData.ajustements == undefined) { rollData.ajustements = {} } @@ -75,7 +86,6 @@ export class RollDialogAdapter { if (rollData.current[PART_APPELMORAL]?.checked) { rollData.use.moral = true } - rollData.rolled = rolled if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) { rolled.niveauNecessaire = this.findNiveauNecessaire(carac, rolled.roll) rolled.ajustementNecessaire = rolled.niveauNecessaire - diff @@ -88,7 +98,39 @@ export class RollDialogAdapter { descr: aj.diff == undefined ? aj.label : undefined } }) - rollData.show.title = rollTitle + } + + static adjustAttaqueParticuliere(rollData) { + if (rollData.type.current != ROLL_TYPE_ATTAQUE || !rollData.rolled.isPart) { + return + } + + const attaque = rollData.current.attaque; + const choix = [] + const isEmpoignade = attaque.dmg.mortalite == 'empoignade'; + const isCharge = attaque.tactique == 'charge' + /* TODO: cas de créatures faisant des lancers, Glou, Glipzouk */ + const isMeleeDiffNegative = (attaque.comp.type == ITEM_TYPES.competencecreature || rollData.current.carac.key == CARACS.MELEE) + && rollData.current.diff.value < 0 + + // force toujours, sauf empoignade + if (!isEmpoignade) { + choix.push(RDD_CONFIG.particuliere.force) + } + + // finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum + if (!isCharge && (isEmpoignade || isMeleeDiffNegative)) { + choix.push(RDD_CONFIG.particuliere.finesse) + } + + // rapidité seulement en mêlée, si l'arme le permet, et si la difficulté libre est de -1 minimum + if (!isCharge && !isEmpoignade && isMeleeDiffNegative && attaque.arme.system.rapide) { + choix.push(RDD_CONFIG.particuliere.rapidite) + } + if (choix.length == 1) { + rollData.particuliere = choix[0].key + } + rollData.particulieres = choix } static mapActionAttaque(attackerRoll) { @@ -99,17 +141,15 @@ export class RollDialogAdapter { return { // correspond à l'attaque de RollPartAttaque (dans rollDta.current.attaque) label: label, - attaque: { - // correspond aux actions d'attaques dans RdDActor.listActionsAttaque - name: label, - // action: 'attaque', - arme: attackerRoll.arme, - comp: attackerRoll.competence, - main: RdDItemArme.getMainAttaque(attackerRoll.competence), - equipe: attackerRoll.arme.system.equipe, - // carac: { key: caracCode, value: caracValue }, - // dommagesArme: dommagesArme, - }, + // correspond aux actions d'attaques dans RdDActor.listActionsAttaque + name: label, + // action: 'attaque', + arme: attackerRoll.arme, + comp: attackerRoll.competence, + main: RdDItemArme.getMainAttaque(attackerRoll.competence), + equipe: attackerRoll.arme.system.equipe, + // carac: { key: caracCode, value: caracValue }, + // dommagesArme: dommagesArme, diff: attackerRoll.diffLibre, particuliere: attackerRoll.particuliere, tactique: RdDBonus.find(attackerRoll.tactique), diff --git a/module/roll/roll-dialog.mjs b/module/roll/roll-dialog.mjs index 2ecc326e..a49c510c 100644 --- a/module/roll/roll-dialog.mjs +++ b/module/roll/roll-dialog.mjs @@ -39,10 +39,13 @@ import { RollDialogAdapter } from "./roll-dialog-adapter.mjs"; import { ROLLDIALOG_SECTION } from "./roll-part.mjs"; import { ROLL_TYPE_COMP } from "./roll-constants.mjs"; 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(), @@ -51,14 +54,13 @@ const ALL_ROLL_TYPES = [ new RollTypeDefense(), new RollTypeSort(), new RollTypeMeditation(), + new RollTypeCuisine(), new RollTypeOeuvre(), new RollTypeJeu(), // new RollTypeResistance ?? // new RollTypeFixedCarac ?? ] -const BASIC_PARTS = new RollBasicParts() - const ROLL_PARTS = [ new RollPartActor(), new RollPartAction(), @@ -72,6 +74,7 @@ const ROLL_PARTS = [ new RollPartMeditation(), new RollPartSort(), new RollPartTache(), + new RollPartCuisine(), new RollPartOeuvre(), new RollPartJeu(), @@ -169,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() { } @@ -256,13 +267,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 rollData.selected = rollData.selected ?? {} rollData.type = rollData.type ?? {} rollData.type.retry = rollData.type.retry ?? false - BASIC_PARTS.restore(rollData) + RollBasicParts.restore(rollData) const potential = ALL_ROLL_TYPES.find(m => m.code == rollData.type.current)?.code const allowed = rollData.type.retry && potential ? [potential] - : (rollData.type.allowed ?? ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code)) - const rollType = allowed.find(c => c == rollData.type.current) ?? (allowed.length > 0 ? allowed[0].code : ROLL_TYPE_COMP); + : (rollData.type.allowed ?? ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code) ?? [ROLL_TYPE_COMP]) + const rollType = allowed.find(c => c == rollData.type.current) ?? allowed[0] rollData.type.allowed = allowed rollData.type.current = rollType @@ -272,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) }) @@ -282,13 +293,14 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 } static saveParts(rollData) { - const target = BASIC_PARTS.initFrom(rollData) + const target = RollBasicParts.initFrom(rollData) ROLL_PARTS.filter(p => p.isActive(rollData)) .forEach(p => p.storeClean(rollData, target)) target.attackerRoll = rollData.attackerRoll target.rolled = rollData.rolled target.result = rollData.result - target.done = target.done ?? {} + target.done = rollData.done ?? {} + target.dmg = rollData.dmg return target } @@ -303,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() @@ -324,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) { @@ -368,7 +380,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 } async buildHTMLTable(carac, diff) { - return await foundry.applications.handlebars.renderTemplate('roll-table', { carac, diff }) + return await renderTemplate('roll-table', { carac, diff }) } async _prepareContext() { @@ -376,7 +388,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 const types = ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)) .map(m => m.toTypeData(rollData)) - BASIC_PARTS.loadSurprises(rollData, this.getSelectedType().code) + RollBasicParts.loadSurprises(rollData, this.getSelectedType().code) rollData.type.label = this.getSelectedType()?.title(rollData) //TOCHECK: set type.label ? const visibleRollParts = RollDialog.getActiveParts(rollData) @@ -422,18 +434,22 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 } async roll() { - // ROLL_PARTS.filter(p => p.isActive(this.rollData)) - // .forEach(p => p.validate(this.rollData)) 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.rolled = await this.$rollDice(roll) - roll.result = this.getSelectedType(roll).getResult(roll) - console.info('RollDialog.roll:', roll) - await Promise.all(this.rollOptions.callbacks.map(async callback => await callback(roll))) - await this.chatRollResult.display(roll) + roll.choix = {} + roll.rolled = await RollDialogAdapter.rollDice(roll, this.rollTitle(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) this.rollOptions.onRollDone(this) } @@ -443,12 +459,9 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 roll.v2 = true } - async defaultCallback(rollData, rolled) { - await rollData.active.actor.appliquerAjoutExperience(rollData) - await rollData.active.actor.appliquerAppelMoral(rollData) + async defaultCallback(roll, rolled) { + await roll.active.actor.appliquerAjoutExperience(roll) + await roll.active.actor.appliquerAppelMoral(roll) } - async $rollDice(rollData) { - return await RollDialogAdapter.rollDice(rollData, this.rollTitle(rollData)) - } } diff --git a/module/roll/roll-part-attaque.mjs b/module/roll/roll-part-attaque.mjs index ca2f41e5..a9f69731 100644 --- a/module/roll/roll-part-attaque.mjs +++ b/module/roll/roll-part-attaque.mjs @@ -1,8 +1,8 @@ import { RdDBonus } from "../rdd-bonus.js" -import { ReglesOptionnelles } from "../settings/regles-optionnelles.js" -import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs" +import { DIFF, ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs" import { PART_CARAC } from "./roll-part-carac.mjs" import { PART_COMP } from "./roll-part-comp.mjs" +import { PART_DIFF } from "./roll-part-diff.mjs" import { RollPartSelect } from "./roll-part-select.mjs" import { ROLLDIALOG_SECTION } from "./roll-part.mjs" @@ -23,7 +23,8 @@ export class RollPartAttaque extends RollPartSelect { refs.attaques = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor)) refs.tactiques = TACTIQUES if (refs.attaques.length > 0) { - this.$selectAttaque(rollData) + const attaque = this.findAttaque(refs.attaques, this.getSaved(rollData)) + this.$selectAttaque(rollData, attaque?.key) } } @@ -33,19 +34,34 @@ export class RollPartAttaque extends RollPartSelect { } restore(rollData) { + const saved = this.getSaved(rollData) super.restore(rollData) - this.getCurrent(rollData).dmg = this.getSaved(rollData).dmg + if (saved.dmg) { + this.getCurrent(rollData).dmg = this.getSaved(rollData).dmg + } + } + + findAttaque(attaques, saved) { + return attaques.find(at => at.arme.id == saved?.arme?.id && + at.comp.id == saved?.comp?.id + ) } choices(refs) { return refs.attaques } static $extractAttaque(attaque, actor) { - return { - key: `${attaque.action}::${attaque.name}`, - label: attaque.name, - attaque: attaque, - tactique: TACTIQUES[0], - } + // const extracted = foundry.utils.mergeObject({ + // key: `${attaque.action}::${attaque.label}`, + // tactique: TACTIQUES[0] + // }, + // attaque + // ) + // return extracted + // extracted.initialDiff = attaque.comp?.system.default_diffLibre ?? 0 + attaque.key = `${attaque.action}::${attaque.label}` + attaque.tactique = TACTIQUES[0] + attaque.initialDiff = attaque.comp?.system.default_diffLibre ?? 0 + return attaque } prepareContext(rollData) { @@ -100,8 +116,14 @@ export class RollPartAttaque extends RollPartSelect { if (this.visible(rollData)) { const current = this.getCurrent(rollData) switch (part.code) { - case PART_CARAC: return part.filterCaracs(rollData, [current.attaque.carac.key]) - case PART_COMP: return part.filterComps(rollData, [current.attaque.comp?.name]) + case PART_CARAC: return part.filterCaracs(rollData, [current.carac.key]) + case PART_COMP: return part.filterComps(rollData, [current.comp.name]) + case PART_DIFF: { + if (current.initialDiff) { + part.setDiff(rollData, { type: DIFF.ATTAQUE, value: current.initialDiff }) + current.initialDiff = undefined + } + } } } return undefined 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-cuisine.mjs b/module/roll/roll-part-cuisine.mjs new file mode 100644 index 00000000..fa60128d --- /dev/null +++ b/module/roll/roll-part-cuisine.mjs @@ -0,0 +1,76 @@ +import { ITEM_TYPES } from "../constants.js" +import { CARACS } from "../rdd-carac.js" +import { ROLL_TYPE_CUISINE } from "./roll-constants.mjs" +import { PART_CARAC } from "./roll-part-carac.mjs" +import { PART_COMP } from "./roll-part-comp.mjs" +import { RollPartSelect } from "./roll-part-select.mjs" +import { ROLLDIALOG_SECTION } from "./roll-part.mjs" + +export const PART_CUISINE = "cuisine" + +export class RollPartCuisine extends RollPartSelect { + onReady() { + foundry.applications.handlebars.loadTemplates({ 'roll-oeuvre-recettecuisine': `systems/foundryvtt-reve-de-dragon/templates/roll/roll-oeuvre-recettecuisine.hbs` }) + } + + get code() { return PART_CUISINE } + get section() { return ROLLDIALOG_SECTION.CHOIX } + + isValid(rollData) { return rollData.active.actor.isPersonnage() } + visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_CUISINE) } + + loadRefs(rollData) { + const refs = this.getRefs(rollData) + refs.recettes = rollData.active.actor.items + .filter(it => it.type == ITEM_TYPES.recettecuisine) + .map(it => RollPartCuisine.$extractRecette(it, rollData.active.actor)) + if (refs.recettes.length > 0) { + this.$selectRecette(rollData) + } + } + + choices(refs) { return refs.recettes } + + static $extractRecette(recette, actor) { + return { + key: recette.id, + label: recette.name, + caracs: RollPartCuisine.getCaracs(recette), + qualite: recette.system.niveau, + value: -recette.system.niveau, + recette: recette, + comp: actor.getCompetence('Cuisine') + } + } + static getCaracs(recette){ + // TODO: permettre différentes caractéristiques pour la cuisine? + return [CARACS.ODORATGOUT, CARACS.EMPATHIE, CARACS.DEXTERITE] + } + + $selectRecette(rollData, key) { + this.selectByKey(rollData, key, 0) + } + + async _onRender(rollDialog, context, options) { + const selectRecette = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-recette"]`) + + selectRecette.addEventListener("change", e => { + const selectOptions = e.currentTarget.options + const index = selectOptions.selectedIndex + this.$selectRecette(rollDialog.rollData, selectOptions[index]?.value) + rollDialog.render() + }) + } + + impactOtherPart(part, rollData) { + if (this.visible(rollData)) { + const current = this.getCurrent(rollData) + switch (part.code) { + case PART_CARAC: return part.filterCaracs(rollData, current.caracs) + case PART_COMP: return part.filterComps(rollData, [current.comp?.name]) + } + } + return undefined + } + +} diff --git a/module/roll/roll-part-defense.mjs b/module/roll/roll-part-defense.mjs index 82d33c51..e03dcfb0 100644 --- a/module/roll/roll-part-defense.mjs +++ b/module/roll/roll-part-defense.mjs @@ -34,7 +34,7 @@ export class RollPartDefense extends RollPartSelect { .map(it => RollPartDefense.$extractEsquive(it, defenseur)) const parades = defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier())) - .map(it => RollPartDefense.$extractParade(it, attackerRoll?.attaque.arme, defenseur)) + .map(it => RollPartDefense.$extractParade(it, attackerRoll?.arme, defenseur)) refs.defenses = [...esquives, ...parades].filter(it => it != undefined) this.$selectDefense(rollData) @@ -113,17 +113,16 @@ export class RollPartDefense extends RollPartSelect { isArmeDisparate(rollData) { const armeDefense = this.getCurrent(rollData).arme if (armeDefense) { - const armeAttaque = rollData.attackerRoll?.attaque.arme + const armeAttaque = rollData.attackerRoll?.arme return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) == 'sign' } return false } getDiffDefense(rollData) { - const current = this.getCurrent(rollData) const refs = this.getRefs(rollData) if (refs.isDistance || !rollData.attackerRoll) { - // Déterminer la difficulté de parade + // TODO: Déterminer la difficulté de parade return { diff: 0, type: DIFF.LIBRE } } else { diff --git a/module/roll/roll-part-diff.mjs b/module/roll/roll-part-diff.mjs index 8ec99049..680dd9e8 100644 --- a/module/roll/roll-part-diff.mjs +++ b/module/roll/roll-part-diff.mjs @@ -51,10 +51,10 @@ export class RollPartDiff extends RollPart { ) } - setDiff(rollData, diffDefense) { + setDiff(rollData, diff) { const current = this.getCurrent(rollData) - current.value = diffDefense.diff - current.type = diffDefense.type + current.value = diff.diff + current.type = diff.type } getAjustements(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-oeuvre.mjs b/module/roll/roll-part-oeuvre.mjs index d59dd63a..a8dd637a 100644 --- a/module/roll/roll-part-oeuvre.mjs +++ b/module/roll/roll-part-oeuvre.mjs @@ -11,6 +11,7 @@ export const PART_OEUVRE = "oeuvre" const ARTS = [ { type: ITEM_TYPES.oeuvre, action: "interpréte l'oeuvre", competence: it => it.system.competence, caracs: it => [it.system.default_carac] }, + { type: ITEM_TYPES.musique, action: "joue le morceau:", competence: it => 'Musique', caracs: it => [CARACS.OUIE] }, { type: ITEM_TYPES.chant, action: "chante", competence: it => 'Chant', caracs: it => [CARACS.OUIE] }, { type: ITEM_TYPES.danse, action: "danse:", competence: it => 'Danse', caracs: it => { @@ -20,8 +21,6 @@ const ARTS = [ return caracs } }, - { type: ITEM_TYPES.musique, action: "joue le morceau:", competence: it => 'Musique', caracs: it => [CARACS.OUIE] }, - { type: ITEM_TYPES.recettecuisine, action: "cuisine le plat:", competence: it => 'Cuisine', caracs: it => [CARACS.ODORATGOUT] }, ] export class RollPartOeuvre extends RollPartSelect { 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-attaque.mjs b/module/roll/roll-type-attaque.mjs index 0bf07924..f761e9be 100644 --- a/module/roll/roll-type-attaque.mjs +++ b/module/roll/roll-type-attaque.mjs @@ -1,4 +1,5 @@ import { DIFF, ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs" +import { PART_ATTAQUE } from "./roll-part-attaque.mjs" import { RollType } from "./roll-type.mjs" export class RollTypeAttaque extends RollType { @@ -10,4 +11,5 @@ export class RollTypeAttaque extends RollType { onSelect(rollData) { this.setDiffType(rollData, DIFF.ATTAQUE) } + } \ No newline at end of file diff --git a/module/roll/roll-type-cuisine.mjs b/module/roll/roll-type-cuisine.mjs new file mode 100644 index 00000000..7ab82f6b --- /dev/null +++ b/module/roll/roll-type-cuisine.mjs @@ -0,0 +1,28 @@ +import { DIFF, ROLL_TYPE_CUISINE } from "./roll-constants.mjs" +import { PART_CUISINE } from "./roll-part-cuisine.mjs" +import { RollType } from "./roll-type.mjs" + +export class RollTypeCuisine extends RollType { + get code() { return ROLL_TYPE_CUISINE } + get name() { return `Interpréter une oeuvre` } + + visible(rollData) { return rollData.active.actor.isPersonnage() } + title(rollData) { + const current = rollData.current[PART_CUISINE] + return `prépare une recette: ${current.label}` + } + + onSelect(rollData) { + this.setDiffType(rollData, DIFF.AUCUN) + } + + getResult(rollData){ + const current = rollData.current[PART_CUISINE] + const qualite = rollData.rolled.isSuccess ? current.qualite : Math.min(current.qualite, current.comp.system.niveau) + return { + qualite: qualite + rollData.rolled.ptQualite + } + } + +} + 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 ef7547bc..124f503d 100644 --- a/module/roll/roll-type.mjs +++ b/module/roll/roll-type.mjs @@ -11,14 +11,13 @@ export class RollType { get name() { return this.code } get icon() { return `systems/foundryvtt-reve-de-dragon/assets/actions/${this.code}.svg` } get chatResultTemplate() { return `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-${this.code}.hbs` } - + toTypeData(rollData) { return { code: this.code, name: this.name, icon: this.icon, section: 'type', template: this.template, selected: this.isSelected(rollData) } } 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 } @@ -33,11 +32,10 @@ export class RollType { this.typeFromOpponents(rollData), rollData.selected[PART_DIFF].type ] - const type = possibleTypes.find(m => DEFAULT_DIFF_TYPES.includes(m)) ??DIFF.DEFAUT + const type = possibleTypes.find(m => DEFAULT_DIFF_TYPES.includes(m)) ?? DIFF.DEFAUT this.setDiffType(rollData, type) } - typeFromOpponents(rollData) { if (rollData.type.opposed) { if (rollData.type.resistance) { @@ -53,7 +51,7 @@ export class RollType { this.setRollDataType(rollData) } - getResult(rollData){ - return undefined - } + callbacks(rollOptions) { return [] } + + getResult(rollData) { return undefined } } diff --git a/module/rolldata-ajustements.js b/module/rolldata-ajustements-v1.js similarity index 100% rename from module/rolldata-ajustements.js rename to module/rolldata-ajustements-v1.js 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/module/settings/system-compendiums.js b/module/settings/system-compendiums.js index fe782aaf..f602b80a 100644 --- a/module/settings/system-compendiums.js +++ b/module/settings/system-compendiums.js @@ -1,5 +1,5 @@ import { ChatUtility } from "../chat-utility.js"; -import { HIDE_DICE, SYSTEM_RDD } from "../constants.js"; +import { HIDE_DICE, renderTemplate, SYSTEM_RDD } from "../constants.js"; import { Grammar } from "../grammar.js"; import { RdDItem } from "../item.js"; import { Misc } from "../misc.js"; diff --git a/module/sommeil/dialog-chateau-dormant.js b/module/sommeil/dialog-chateau-dormant.js index c01075dc..64218b62 100644 --- a/module/sommeil/dialog-chateau-dormant.js +++ b/module/sommeil/dialog-chateau-dormant.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "../constants.js"; export class DialogChateauDormant extends Dialog { @@ -9,7 +10,7 @@ export class DialogChateauDormant extends Dialog { motifStress: `Nuit du ${date}`, finChateauDormant: game.system.rdd.calendrier.getTimestampFinChateauDormant() }; - const html = await foundry.applications.handlebars.renderTemplate("systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-chateau-dormant.hbs", + const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-chateau-dormant.hbs", dialogData); new DialogChateauDormant(dialogData, html) diff --git a/module/sommeil/dialog-repos.js b/module/sommeil/dialog-repos.js index 7374a520..a7267e51 100644 --- a/module/sommeil/dialog-repos.js +++ b/module/sommeil/dialog-repos.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "../constants.js"; import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"; import { EffetsDraconiques } from "../tmr/effets-draconiques.js"; diff --git a/module/sommeil/dialog-stress.js b/module/sommeil/dialog-stress.js index 0b30c323..cdf7928e 100644 --- a/module/sommeil/dialog-stress.js +++ b/module/sommeil/dialog-stress.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "../constants.js"; export class DialogStress extends Dialog { diff --git a/module/time/rdd-calendrier.js b/module/time/rdd-calendrier.js index 1c1671c5..83eeee3a 100644 --- a/module/time/rdd-calendrier.js +++ b/module/time/rdd-calendrier.js @@ -4,7 +4,7 @@ import { RdDResolutionTable } from "../rdd-resolution-table.js"; import { RdDDice } from "../rdd-dice.js"; import { Misc } from "../misc.js"; import { DialogChronologie } from "../dialog-chronologie.js"; -import { HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js"; +import { HIDE_DICE, renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js"; import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"; import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js"; import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js"; diff --git a/module/tirage/fenetre-recherche-tirage.js b/module/tirage/fenetre-recherche-tirage.js index 932778d0..c72ee938 100644 --- a/module/tirage/fenetre-recherche-tirage.js +++ b/module/tirage/fenetre-recherche-tirage.js @@ -4,6 +4,7 @@ import { Misc } from "../misc.js"; import { CompendiumTableHelpers } from '../settings/system-compendiums.js'; import { RdDRaretes } from '../item/raretes.js'; import { Grammar } from '../grammar.js'; +import { renderTemplate } from '../constants.js'; const FILTER_GROUPS = [ { group: 'type', label: "Type d'objet" }, diff --git a/module/tmr-rencontres.js b/module/tmr-rencontres.js index 684fdf85..2281517e 100644 --- a/module/tmr-rencontres.js +++ b/module/tmr-rencontres.js @@ -1,3 +1,4 @@ +import { renderTemplate } from "./constants.js"; import { Grammar } from "./grammar.js"; import { Misc } from "./misc.js"; import { RdDDice } from "./rdd-dice.js"; @@ -102,7 +103,7 @@ export class TMRRencontres { /* -------------------------------------------- */ async $chatRolledRencontre(row, rencontre, tmr) { - const flavorContent = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll-rencontre.hbs', + const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll-rencontre.hbs', { roll: row.roll, rencontre, diff --git a/module/tmr/effets-rencontres.js b/module/tmr/effets-rencontres.js index 183c876c..675218a4 100644 --- a/module/tmr/effets-rencontres.js +++ b/module/tmr/effets-rencontres.js @@ -1,5 +1,6 @@ import { ExperienceLog, XP_TOPIC } from "../actor/experience-log.js"; import { ChatUtility } from "../chat-utility.js"; +import { renderTemplate } from "../constants.js"; import { Poetique } from "../poetique.js"; import { RdDDice } from "../rdd-dice.js"; import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"; diff --git a/module/voyage/dialog-fatigue-voyage.js b/module/voyage/dialog-fatigue-voyage.js index 18c96e41..d1adce19 100644 --- a/module/voyage/dialog-fatigue-voyage.js +++ b/module/voyage/dialog-fatigue-voyage.js @@ -1,4 +1,4 @@ -import { ITEM_TYPES } from "../constants.js" +import { ITEM_TYPES, renderTemplate } from "../constants.js" import { RdDItemCompetence } from "../item-competence.js" import { ChatUtility } from "../chat-utility.js" import { Misc } from "../misc.js" diff --git a/templates/actor/combat.hbs b/templates/actor/combat.hbs index 235327dc..68f686e4 100644 --- a/templates/actor/combat.hbs +++ b/templates/actor/combat.hbs @@ -9,15 +9,14 @@ {{#each combat as |action key|}}
  • + data-tooltip="{{action.label}}: niveau {{plusMoins action.comp.system.niveau}}"> {{#if action.arme.img}} {{/if}} - {{action.name}} + {{action.label}} - ({{action.comp.name}}) {{>"systems/foundryvtt-reve-de-dragon/templates/item/icon-arme-broken.hbs" action.arme}} {{plusMoins action.comp.system.niveau}} diff --git a/templates/actor/export-scriptarium/arme.hbs b/templates/actor/export-scriptarium/arme.hbs index 572c9367..6a784d64 100644 --- a/templates/actor/export-scriptarium/arme.hbs +++ b/templates/actor/export-scriptarium/arme.hbs @@ -2,8 +2,8 @@ data-item-id="{{attaque.arme._id}}" data-arme-name="{{attaque.arme.name}}" data-competence-name="{{attaque.comp.name}}"> - {{#if attaque.name}} - {{upperFirst attaque.name}} + {{#if attaque.label}} + {{upperFirst attaque.label}} {{else}}
    {{/if}} diff --git a/templates/chat-demande-defense-v1.hbs b/templates/chat-demande-defense-v1.hbs new file mode 100644 index 00000000..718e86dc --- /dev/null +++ b/templates/chat-demande-defense-v1.hbs @@ -0,0 +1,103 @@ +
    + {{#if (eq surprise 'totale')}} + {{defenderToken.name}} est totalement surpris + {{else if essais.defense}} + {{defenderToken.name}} doit : + {{else}} + {{defenderToken.name}} doit se défendre + {{~#if (eq surprise 'demi')}} avec une significative {{/if}} d'une attaque + {{~#if attaqueParticuliere}} particulière en + {{~#if (eq attaqueParticuliere 'finesse')}} finesse + {{else if (eq attaqueParticuliere 'force')}} force + {{else if (eq attaqueParticuliere 'rapidite')}} rapidité + {{/if~}} + {{/if}} de {{attackerToken.name}} ({{attaqueArme.name}}): + + {{/if}} + +
    + {{#unless (eq surprise 'totale')}} + {{#if essais.defense}} + {{#unless essais.defenseChance}} + {{#if (eq defender.type 'personnage')}} + + Faire appel à la chance + +
    + {{/if}} + {{#if (and (eq defender.type 'personnage') (gt defender.system.compteurs.destinee.value 0))}} + + Utiliser la destinée + +
    + {{/if}} + {{/unless}} + {{else}} + {{#if (settings-get 'rdd-advanced-roll-dialog-v2')}} + + Se défendre + {{#if (or (eq attaqueCategorie 'tir') (eq attaqueCategorie 'lancer'))}} + (difficulté à déterminer) + {{else}} + à {{diffLibre }} + {{/if}} + +
    + {{else}} + {{#each armes as |arme key|}} + + Parer avec {{arme.name}} + {{#if (or (eq ../attaqueCategorie 'tir') (eq ../attaqueCategorie 'lancer'))}} + (difficulté à déterminer) + {{else}}à {{../diffLibre }} + {{/if}} + {{#if (eq arme.typeParade 'sign')}} + ×½ + {{/if}} + {{#if arme.nbUsage}}(Utilisations : {{arme.nbUsage}}){{/if}} + +
    + {{/each}} + {{#if mainsNues}} + + Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}} + +
    + {{/if}} + {{#if (ne attaqueCategorie 'tir')}} + {{#each esquives as |esquive key|}} + + {{esquive.name}} + {{#if (or (eq ../attaqueCategorie 'tir') (eq ../attaqueCategorie 'lancer'))}} + (difficulté à déterminer) + {{else}}à {{../diffLibre }} + {{/if}} + {{#if esquive.nbUsage}}(Utilisations : {{esquive.nbUsage}}){{/if}} + +
    + {{/each}} + {{/if}} + {{/if}} + {{/if}} + {{/unless}} + + Encaisser à {{plusMoins dmg.total}} + {{#if (eq dmg.mortalite 'non-mortel')~}} + (non-mortel) ! + {{/if}} + +
    +
    \ No newline at end of file diff --git a/templates/chat-demande-defense.hbs b/templates/chat-demande-defense.hbs index 718e86dc..75d544be 100644 --- a/templates/chat-demande-defense.hbs +++ b/templates/chat-demande-defense.hbs @@ -1,103 +1,59 @@ -
    - {{#if (eq surprise 'totale')}} - {{defenderToken.name}} est totalement surpris - {{else if essais.defense}} - {{defenderToken.name}} doit : - {{else}} - {{defenderToken.name}} doit se défendre - {{~#if (eq surprise 'demi')}} avec une significative {{/if}} d'une attaque - {{~#if attaqueParticuliere}} particulière en - {{~#if (eq attaqueParticuliere 'finesse')}} finesse - {{else if (eq attaqueParticuliere 'force')}} force - {{else if (eq attaqueParticuliere 'rapidite')}} rapidité - {{/if~}} - {{/if}} de {{attackerToken.name}} ({{attaqueArme.name}}): - - {{/if}} - -
    - {{#unless (eq surprise 'totale')}} - {{#if essais.defense}} - {{#unless essais.defenseChance}} - {{#if (eq defender.type 'personnage')}} - - Faire appel à la chance - -
    - {{/if}} - {{#if (and (eq defender.type 'personnage') (gt defender.system.compteurs.destinee.value 0))}} - - Utiliser la destinée - -
    - {{/if}} - {{/unless}} - {{else}} - {{#if (settings-get 'rdd-advanced-roll-dialog-v2')}} - +
    + +
    + +
    +

    Défense de {{opponent.name}}

    +
    + +
    + {{#if (eq opponent.surprise.key 'totale')}} + {{opponent.name}} est totalement surpris + {{else}} + {{opponent.name}} doit se défendre + {{~#if (eq opponent.surprise.key 'demi')}} avec une significative {{/if}} d'une attaque + {{~#if particuliere}} particulière en + {{~#if (eq particuliere 'finesse')}} finesse + {{else if (eq particuliere 'force')}} force + {{else if (eq particuliere 'rapidite')}} rapidité + {{/if~}} + {{/if}} de {{active.name}} ({{current.attaque.label}}): + + {{/if}} +
    + +
    +
    + +
    + +
    +
    \ No newline at end of file diff --git a/templates/chat-infojet.hbs b/templates/chat-infojet.hbs index 696c607d..bc94b4c1 100644 --- a/templates/chat-infojet.hbs +++ b/templates/chat-infojet.hbs @@ -1,13 +1,11 @@ +{{log rolled}}
    {{rolled.caracValue}} à {{plusMoins rolled.finalLevel}} - {{#if (and rolled.factorHtml (gt rolled.factorHtml 1))}} - ×{{{rolled.factorHtml}}} - = {{rolled.score}}% - ×{{{rolled.factorHtml}}} - {{else}} = {{rolled.score}}% + {{#if (and rolled.factorHtml (ne rolled.factorHtml 1))}} + ×{{{rolled.factorHtml}}} {{/if}}
    diff --git a/templates/hud-actor-attaque.hbs b/templates/hud-actor-attaque.hbs index 4f841f16..8d650963 100644 --- a/templates/hud-actor-attaque.hbs +++ b/templates/hud-actor-attaque.hbs @@ -6,8 +6,8 @@
    - + data-tooltip="Attaque: {{action.label}}"> +
    {{/unless}} {{/each}} diff --git a/templates/hud-actor-combat.hbs b/templates/hud-actor-combat.hbs new file mode 100644 index 00000000..e3e921d4 --- /dev/null +++ b/templates/hud-actor-combat.hbs @@ -0,0 +1,34 @@ +{{log this}} +
    + +
    +
    + + +
    + + {{!--
    + +
    --}} + +
    + +
    + +
    +
    diff --git a/templates/hud-actor-init.hbs b/templates/hud-actor-init.hbs index a642e265..e4836c77 100644 --- a/templates/hud-actor-init.hbs +++ b/templates/hud-actor-init.hbs @@ -5,17 +5,17 @@
    - + data-tooltip="Initiative {{action.label}}"> +
    {{/each}} {{#each commandes as |commande key|}}
    - + data-tooltip="{{commande.label}}"> +
    {{/each}}
    diff --git a/templates/roll/result/chat-attaque.hbs b/templates/roll/result/chat-attaque.hbs index e69de29b..ba173d6c 100644 --- a/templates/roll/result/chat-attaque.hbs +++ b/templates/roll/result/chat-attaque.hbs @@ -0,0 +1,75 @@ +
    +
    + + + +
    + +
    + {{active.name}} attaque {{opponent.name}}: {{current.label}} +
    + +
    + {{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}} +
    {{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}} +
    + +
    +
    + {{#if rolled.isSuccess}} + + {{opponent.name}} doit se défendre à {{current.diff.value}}, + {{#if (eq current.dmg.mortalite 'empoignade')}} + ou {{active.name}} marquera un point d'empoignade + {{else if (eq current.dmg.mortalite 'non-mortel')}} + ou encaisser à {{plusMoins current.dmg.total}} (non-mortel) + {{else}} + {{!-- {{~#if (eq current.dmg.mortalite 'mortel')}} --}} + ou encaisser à {{plusMoins current.dmg.total}} + {{!-- {{~#if (eq current.dmg.mortalite 'cauchemar')}} --}} + {{!-- {{else}} + {{plusMoins dmg.total}} (entités de cauchemar) --}} + {{/if}} + + + {{#if show.recul}} + + + Si votre adversaire n'esquive pas cette {{show.recul.raison}}, il devra résister à l'impact ou reculer sous le choc! + + {{/if}} + {{#if (eq particuliere 'rapidite')}} + +
    Votre attaque rapide vous permet une deuxième attaque, ou une défense supplémentaire! +
    + {{/if}} + {{else}} + Votre attaque a échoué! + {{/if}} + {{#if (eq current.tactique 'charge')}} + + + C'est une charge, vos parades auront un -4 et vous ne pourrez pas esquiver! + + {{/if}} + {{#if (and (eq current.tactique 'feinte') rolled.isSuccess)}} + + + Votre feinte peut faire mouche! + + {{/if}} +

    + {{> "systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.hbs"}} +

    +
    + +
    + {{> 'partial-attaque-particuliere'}} + {{!-- TODO: maladresses --}} +
    + +
    + {{> 'partial-appel-chance'}} +
    + +
    \ No newline at end of file diff --git a/templates/roll/result/chat-comp.hbs b/templates/roll/result/chat-comp.hbs new file mode 100644 index 00000000..91d7323a --- /dev/null +++ b/templates/roll/result/chat-comp.hbs @@ -0,0 +1,23 @@ +
    +
    + + +
    +
    + {{active.name}} fait un jet de {{current.comp.label}} +
    + +
    + {{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}} +
    {{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}} +
    + +
    +

    Qualité {{rolled.ptQualite}}, points de tâche {{rolled.ptQualite}} +

    + {{> 'partial-info-appel-moral'}} +
    +
    + {{> 'partial-appel-chance'}} +
    +
    diff --git a/templates/roll/result/chat-competence.hbs b/templates/roll/result/chat-competence.hbs deleted file mode 100644 index e69de29b..00000000 diff --git a/templates/roll/result/chat-cuisine.hbs b/templates/roll/result/chat-cuisine.hbs new file mode 100644 index 00000000..3b17db80 --- /dev/null +++ b/templates/roll/result/chat-cuisine.hbs @@ -0,0 +1,29 @@ +
    +
    + + +
    +
    + {{active.name}} prépare une recette de niveau {{current.cuisine.recette.system.niveau}}: : {{current.cuisine.label}} ( +
    + +
    + {{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}} +
    {{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}} +
    + +
    +

    + {{active.name}} + {{#if rolled.isSuccess}}réussit la recette avec + {{else}}manque d'inspiration, le plat a + {{/if}} + une qualité de {{result.qualite}}. +

    + {{> 'partial-info-appel-moral'}} + {{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.cuisine.recette.system}} +
    +
    + {{> 'partial-appel-chance'}} +
    +
    diff --git a/templates/roll/result/chat-defense.hbs b/templates/roll/result/chat-defense.hbs index b9c078a1..975134a9 100644 --- a/templates/roll/result/chat-defense.hbs +++ b/templates/roll/result/chat-defense.hbs @@ -1,9 +1,8 @@ - -{{log this}}
    - + +
    @@ -30,20 +29,22 @@ {{!-- {{else}} {{/if}} --}} - {{#if attackerRoll.tactique}} -

    - {{#if (eq attackerRoll.tactique.key 'charge')}} + {{#if (eq attackerRoll.tactique.key 'charge')}} + C'était une charge, les parades de {{opponent.name}} auront un -4 et il ne pourra pas esquiver! - {{else if (eq attackerRoll.tactique.key 'feinte')}} + + {{/if}} + {{#if (eq attackerRoll.tactique.key 'feinte')}} + C'était une feinte! - {{/if}} -

    + {{/if}} {{> 'partial-info-appel-moral'}}
    + {{!-- TODO: maladresses --}} {{> 'partial-recul-choc'}} {{> 'partial-encaissement'}}
    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-oeuvre.hbs b/templates/roll/result/chat-oeuvre.hbs index f1b0b31e..f0300a72 100644 --- a/templates/roll/result/chat-oeuvre.hbs +++ b/templates/roll/result/chat-oeuvre.hbs @@ -1,8 +1,7 @@ -{{log this}}
    - +
    {{active.name}} {{current.oeuvre.art.action}}: {{current.oeuvre.label}} (de niveau {{current.oeuvre.oeuvre.system.niveau}}) 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/result/partial-attaque-particuliere.hbs b/templates/roll/result/partial-attaque-particuliere.hbs new file mode 100644 index 00000000..6a72b10f --- /dev/null +++ b/templates/roll/result/partial-attaque-particuliere.hbs @@ -0,0 +1,16 @@ +{{#if particulieres.length}} + {{#if (eq particulieres.length 1)}} + {{#each particulieres as |part|}} + + Attaque particulière {{part.descr}} + + {{/each}} + {{else}} + {{#each particulieres as |part|}} + + Choisir une particulière {{part.descr}} + + {{/each}} + {{/if}} +{{/if}} \ No newline at end of file diff --git a/templates/roll/roll-oeuvre-recettecuisine.hbs b/templates/roll/roll-oeuvre-recettecuisine.hbs index d6ba4bc6..0822fb49 100644 --- a/templates/roll/roll-oeuvre-recettecuisine.hbs +++ b/templates/roll/roll-oeuvre-recettecuisine.hbs @@ -1,3 +1,4 @@ + -{{> "systems/foundryvtt-reve-de-dragon/templates/partial-description.hbs" current.oeuvre.system}} +{{> "systems/foundryvtt-reve-de-dragon/templates/partial-description.hbs" current.recette.system}} diff --git a/templates/roll/roll-part-attaque.hbs b/templates/roll/roll-part-attaque.hbs index a9a90974..28b6e7dd 100644 --- a/templates/roll/roll-part-attaque.hbs +++ b/templates/roll/roll-part-attaque.hbs @@ -4,8 +4,8 @@ - {{#if current.attaque.arme}} - + {{#if current.arme}} + {{/if}} @@ -16,10 +16,10 @@ - {{#if (eq current.attaque.arme.system.mortalite 'empoignade')}} + {{#if (eq current.arme.system.mortalite 'empoignade')}} Empoignade, pas de dommages directs {{else}} - {{#if (and (ne current.attaque.arme.system.mortalite 'non-mortel') (eq current.dmg.penetration 0))}} + {{#if (and (ne current.arme.system.mortalite 'non-mortel') (eq current.dmg.penetration 0))}} {{/if}} Durée: 60 minutes -
    + +
    +