From 706aa657b1457c4d9a43d6ec7436588cffae5125 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Mon, 1 Dec 2025 00:51:44 +0100 Subject: [PATCH] =?UTF-8?q?M=C3=A9thode=20rollPossession=20pour=20attaque?= =?UTF-8?q?=20possession?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout d'une méthode pour ouvrir le dialogue en mode possession - gestion des information de possession - fenêtre défense possession - préparation des messages de tchat --- module/actor/base-actor-reve.js | 24 +++- module/rdd-possession-v2.mjs | 132 ++++++++++++++++++ module/rdd-possession.js | 21 +-- module/roll/chat-roll-result.mjs | 17 ++- module/roll/roll-dialog.mjs | 5 +- module/roll/roll-part-possession.mjs | 24 ++-- module/roll/roll-type-possession.mjs | 25 ++-- module/roll/roll-type.mjs | 1 + template.json | 6 +- templates/chat-demande-defense-possession.hbs | 39 ++++++ ...on.hbs => chat-resultat-possession-v1.hbs} | 0 templates/item/possession-sheet.hbs | 1 - templates/roll/result/chat-possession.hbs | 8 +- templates/roll/roll-dialog.hbs | 2 +- templates/roll/roll-part-possession.hbs | 20 ++- 15 files changed, 270 insertions(+), 55 deletions(-) create mode 100644 module/rdd-possession-v2.mjs create mode 100644 templates/chat-demande-defense-possession.hbs rename templates/{chat-resultat-possession.hbs => chat-resultat-possession-v1.hbs} (100%) diff --git a/module/actor/base-actor-reve.js b/module/actor/base-actor-reve.js index b9a50ff4..84c70c78 100644 --- a/module/actor/base-actor-reve.js +++ b/module/actor/base-actor-reve.js @@ -26,11 +26,12 @@ import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES_CREATURES } fr 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, DIFFS, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE } from "../roll/roll-constants.mjs"; +import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, DIFFS, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_POSSESSION, 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"; import { RdDInitiative } from "../initiative.mjs"; import { RdDItemCompetenceCreature } from "../item-competencecreature.js"; +import { ACTIONS_POSSESSION, RdDPossessionV2 } from "../rdd-possession-v2.mjs"; /** * Classe de base pour les acteurs disposant de rêve (donc, pas des objets) @@ -598,6 +599,27 @@ export class RdDBaseActorReve extends RdDBaseActor { }) } + rollPossession() { + Targets.selectOneTargetToken(async target => { + const selectedToken = RdDUtility.getSelectedToken(this) + RollDialog.create( + { + ids: { + actorId: this.id, + actorTokenId: selectedToken.id, + opponentId: target.actor.id, + opponentTokenId: target.id + }, + type: { allowed: [ROLL_TYPE_POSSESSION], current: ROLL_TYPE_POSSESSION, possession: { action: ACTIONS_POSSESSION.ATTAQUE } }, + passeArme: foundry.utils.randomID(16), + }, + { + onRollDone: RollDialog.onRollDoneClose, + callbacks: [async (roll) => await RdDPossessionV2.chatMessageDefensePossession(roll)] + }) + }) + } + verifierForceMin(item) { } /* -------------------------------------------- */ diff --git a/module/rdd-possession-v2.mjs b/module/rdd-possession-v2.mjs new file mode 100644 index 00000000..7c2da7bc --- /dev/null +++ b/module/rdd-possession-v2.mjs @@ -0,0 +1,132 @@ +import { ITEM_TYPES, RDD_CONFIG } from "./constants.js"; +import { RdDRollResult } from "./rdd-roll-result.js"; +import { RollBasicParts } from "./roll/roll-basic-parts.mjs"; +import { ChatUtility } from "./chat-utility.js"; +import RollDialog from "./roll/roll-dialog.mjs"; + +export const ACTIONS_POSSESSION = { + ATTAQUE: 'attaque', + POSSEDER: 'posseder', + CONJURER: 'conjurer', + DEFENSE: 'defense', + DEFENSE_POSSESSION: 'defense-possession', + DEFENSE_CONJURATION: 'defense-conjuration' +} + +export class RdDPossessionV2 { + + static init() { + } + + static $isInverse(entite, victime) { + return !entite.isEntiteNonIncarnee() && victime.isEntiteNonIncarnee() + } + + /* -------------------------------------------- */ + static findPossession(entite, victime) { + if (RdDPossessionV2.$isInverse(entite, victime)) { + return RdDPossessionV2.findPossession(victime, entite) + } + return victime.itemTypes[ITEM_TYPES.possession].find(poss => poss.system.entiteid == entite.id) + } + + static async createPossession(entite, victime) { + if (RdDPossessionV2.$isInverse(entite, victime)) { + return await RdDPossessionV2.createPossession(victime, entite) + } + const existing = RdDPossessionV2.findPossession(entite, victime) + if (!existing) { + await victime.createEmbeddedDocuments('Item', [{ + name: `Possession de ${entite.name}`, + type: ITEM_TYPES.possession, + img: RDD_CONFIG.icons.possession, + system: { entiteid: entite.id, victimeid: victime.id, compteur: 0 } + }]) + } + } + + static getPossessionDetails(rollData, action) { + const isEntite = rollData.active.actor.isEntiteNonIncarnee() && !rollData.opponent.actor.isEntiteNonIncarnee() + const itemPossession = RdDPossessionV2.findPossession(rollData.active.actor, rollData.opponent.actor) + const compteur = itemPossession?.system.compteur ?? 0 + const isAttaque = RdDPossessionV2.isAttaque(action) + action = RdDPossessionV2.$getAction(isAttaque, isEntite) + + return { + action: action, + isEntite: isEntite, + isAttaque: isAttaque, + isPersonnage: rollData.active.actor.isPersonnage(), + isCompteurPossession: Math.sign(compteur) >= 0, + compteur: Math.abs(compteur) + } + } + + static $getAction(isAttaque, isEntite) { + if (isAttaque) { + return isEntite ? ACTIONS_POSSESSION.POSSEDER : ACTIONS_POSSESSION.CONJURER + } + return isEntite ? ACTIONS_POSSESSION.DEFENSE_CONJURATION : ACTIONS_POSSESSION.DEFENSE_POSSESSION + } + + static actionTitle(action) { + switch (action) { + case ACTIONS_POSSESSION.POSSEDER: return "tente de posséder" + case ACTIONS_POSSESSION.CONJURER: return "tente de conjurer" + case ACTIONS_POSSESSION.DEFENSE_POSSESSION: return "résiste à la possession de" + case ACTIONS_POSSESSION.DEFENSE_CONJURATION: return "résiste à la conjuration de" + case ACTIONS_POSSESSION.DEFENSE: return "résiste à " + } + return "lutte contre" + } + + static isAttaque(action) { + return ![ACTIONS_POSSESSION.DEFENSE, ACTIONS_POSSESSION.DEFENSE_POSSESSION, ACTIONS_POSSESSION.DEFENSE_CONJURATION].includes(action) + } + + static async addPointPossession(entite, victime, add) { + if (RdDPossessionV2.$isInverse(entite, victime)) { + return await RdDPossessionV2.addPointPossession(victime, entite, /* negate?*/ add) + } + const existing = RdDPossessionV2.findPossession(entite, victime) + if (!existing) { + return + } + const points = add ? 1 : -1 + const compteur = (existing.system.compteur ?? 0) + points + await victime.updateEmbeddedDocuments('Item', [{ id: existing.id, 'system.compteur': compteur }]) + } + + static callbacksOnDefensePossession() { + return [async roll => { + + }] + } + + static async chatMessageDefensePossession(attackerRoll) { + + const defense = RollBasicParts.prepareDefense(attackerRoll) + defense.type = { + possession: RdDPossessionV2.getPossessionDetails(defense, ACTIONS_POSSESSION.DEFENSE) + } + + const chatDemandeDefense = await ChatMessage.create({ + // message privé: du défenseur à lui même (et aux GMs) + speaker: ChatMessage.getSpeaker({ actor: defense.active.actor, token: defense.active.token }), + alias: attackerRoll.active.name, + whisper: ChatUtility.getOwners(defense.active.actor), + content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense-possession.hbs', defense) + }); + ChatUtility.setMessageData(chatDemandeDefense, 'demande-defense', true) + // // flag pour garder les jets d'attaque/defense + ChatUtility.setMessageData(chatDemandeDefense, 'rollData', { + ids: defense.ids, + attackerRoll: RollDialog.saveParts(attackerRoll), + passeArme: defense.passeArme + }) + } + + static async chatMarquerPointPossession(roll) { + } + +} \ No newline at end of file diff --git a/module/rdd-possession.js b/module/rdd-possession.js index c8cade05..dab70705 100644 --- a/module/rdd-possession.js +++ b/module/rdd-possession.js @@ -18,9 +18,9 @@ export class RdDPossession { /* -------------------------------------------- */ static searchPossessionFromEntite(attacker, defender) { - let poss = attacker.items.find(poss => poss.type == ITEM_TYPES.possession && poss.system.victime.actorid == defender.id); + let poss = attacker.itemTypes[ITEM_TYPES.possession].find(poss => poss.system.victime.actorid == defender.id); if (!poss) { - poss = defender.items.find(poss => poss.type == ITEM_TYPES.possession && poss.system.victime.actorid == defender.id); + poss = defender.itemTypes[ITEM_TYPES.possession].find(poss => poss.system.victime.actorid == defender.id); } return poss && foundry.utils.duplicate(poss) || undefined; } @@ -132,7 +132,7 @@ export class RdDPossession { } const possession = (rollData.isECNIDefender ? rollData.attacker : rollData.defender).getPossession(rollData.possession.system.possessionid) RdDPossession.storePossessionAttaque(possession, rollData) - await RdDRollResult.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.hbs'); + await RdDRollResult.displayRollData(rollData, rollData.defender, 'chat-resultat-possession-v1.hbs'); } /* -------------------------------------------- */ @@ -172,7 +172,7 @@ export class RdDPossession { rollData.possession = possession RdDPossession.$updateEtatPossession(rollData.possession) - await RdDRollResult.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.hbs') + await RdDRollResult.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession-v1.hbs') if (rollData.possession.isPosseder || rollData.possession.isConjurer) { // conjuration await victime.deleteEmbeddedDocuments("Item", [rollData.possession._id]) @@ -226,15 +226,18 @@ export class RdDPossession { /* -------------------------------------------- */ static async createPossession(attacker, defender) { return await Item.create({ - name: "Possession en cours de " + attacker.name, type: 'possession', - img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp", + name: `Possession de ${entite.name}`, + type: ITEM_TYPES.possession, + img: RDD_CONFIG.icons.possession, system: { - description: "", typepossession: attacker.name, + typepossession: attacker.name, possede: false, possessionid: foundry.utils.randomID(16), + entiteid: attacker.id, + victimeid: defender.id, + compteur: 0, entite: { actorid: attacker.id }, - victime: { actorid: defender.id }, - compteur: 0 + victime: { actorid: defender.id } } }, { diff --git a/module/roll/chat-roll-result.mjs b/module/roll/chat-roll-result.mjs index 4e892d6b..d55eab81 100644 --- a/module/roll/chat-roll-result.mjs +++ b/module/roll/chat-roll-result.mjs @@ -53,19 +53,19 @@ export default class ChatRollResult { prepareDisplay(roll) { roll.done = roll.done ?? {} roll.show = roll.show ?? {} - roll.show.chance = this.isAppelChancePossible(roll) - roll.show.encaissement = this.isShowEncaissement(roll) - roll.show.recul = this.getRecul(roll) - roll.show.maladresse = this.getMaladresse(roll) + roll.show.chance = this.$isAppelChancePossible(roll) + roll.show.encaissement = this.$isShowEncaissement(roll) + roll.show.recul = this.$getRecul(roll) + roll.show.maladresse = this.$getMaladresse(roll) } - isAppelChancePossible(roll) { + $isAppelChancePossible(roll) { return roll.active.actor.isPersonnage() && roll.rolled.isEchec && RdDCarac.isActionPhysique(roll.current.carac?.key) } - isShowEncaissement(roll) { + $isShowEncaissement(roll) { switch (roll.type.current) { case ROLL_TYPE_DEFENSE: return roll.rolled.isEchec @@ -73,7 +73,7 @@ export default class ChatRollResult { return false } - getMaladresse(roll) { + $getMaladresse(roll) { switch (roll.type.current) { case ROLL_TYPE_DEFENSE: if (roll.rolled.isETotal) { @@ -91,7 +91,7 @@ export default class ChatRollResult { return undefined } - getRecul(roll, defender = roll.active.actor, attacker = roll.opponent?.actor) { + $getRecul(roll, defender = roll.active.actor, attacker = roll.opponent?.actor) { switch (roll.type.current) { case ROLL_TYPE_DEFENSE: { @@ -195,7 +195,6 @@ export default class ChatRollResult { this.getCombat(reRoll)?.doRollDefense(reRoll, callbacks) break case ROLL_TYPE_ATTAQUE: - // TODO this.getCombat(reRoll)?.doRollAttaque(reRoll, callbacks) break default: { diff --git a/module/roll/roll-dialog.mjs b/module/roll/roll-dialog.mjs index d5863d87..7787212c 100644 --- a/module/roll/roll-dialog.mjs +++ b/module/roll/roll-dialog.mjs @@ -370,10 +370,11 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 selectType() { - const selectedType = this.getSelectedType(); - this.rollData.type.label = selectedType.title(this.rollData) + const selectedType = this.getSelectedType() + selectedType.prepare(this.rollData) selectedType.setRollDataType(this.rollData) selectedType.onSelect(this.rollData) + this.rollData.type.label = selectedType.title(this.rollData) ROLL_PARTS.find(it => it.code == PART_CARAC).filterCaracs(this.rollData) ROLL_PARTS.find(it => it.code == PART_COMP).filterComps(this.rollData) diff --git a/module/roll/roll-part-possession.mjs b/module/roll/roll-part-possession.mjs index b33a7ab5..7a0f857c 100644 --- a/module/roll/roll-part-possession.mjs +++ b/module/roll/roll-part-possession.mjs @@ -1,5 +1,6 @@ import { CATEGORIES_COMPETENCES_CREATURES } from "../item/base-items.js" import { CARACS } from "../rdd-carac.js" +import { RdDPossessionV2 } from "../rdd-possession-v2.mjs" import { ROLL_TYPE_POSSESSION } from "./roll-constants.mjs" import { PART_CARAC } from "./roll-part-carac.mjs" import { PART_COMP } from "./roll-part-comp.mjs" @@ -20,14 +21,11 @@ export class RollPartPossession extends RollPartSelect { loadRefs(rollData) { const refs = this.getRefs(rollData) - const selected = this.getSelected(rollData) refs.all = this.$getActorConjurations(rollData) - refs.isPersonnage = rollData.active.actor.isPersonnage() - refs.isENI = rollData.active.actor.isEntiteNonIncarnee() - this.$selectPossession(rollData) } + choices(refs) { return refs.all } $getActorConjurations(rollData) { @@ -38,7 +36,7 @@ export class RollPartPossession extends RollPartSelect { static extractPossession(comp) { return { key: comp.id ?? comp.name, - label: `${comp.system.categorie == CATEGORIES_COMPETENCES_CREATURES.possession.key ? 'Possession': 'Conjuration'} (${comp.name})`, + label: `${comp.system.categorie == CATEGORIES_COMPETENCES_CREATURES.possession.key ? 'Possession' : 'Conjuration'} (${comp.name})`, value: comp.system.niveau, comp: comp } @@ -47,18 +45,26 @@ export class RollPartPossession extends RollPartSelect { prepareContext(rollData) { this.$selectPossession(rollData) } + getAjustements(rollData) { return [] } - async _onRender(rollDialog, context, options) { - const select = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-possession"]`) + async _onRender(rollDialog, context, options) { + const rollData = rollDialog.rollData + const select = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-possession"]`) + const button = rollDialog.element.querySelector(`roll-section[name="${this.code}"] button[name="creer-possession"]`) select?.addEventListener("change", e => { const selectOptions = e.currentTarget.options const index = selectOptions.selectedIndex - this.$selectPossession(rollDialog.rollData, selectOptions[index]?.value) + this.$selectPossession(rollData, selectOptions[index]?.value) rollDialog.render() }) + button?.addEventListener("click", async e => { + e.preventDefault() + await RdDPossessionV2.createPossession(rollData.active.actor, rollData.opponent.actor) + rollDialog.render() + }) } $selectPossession(rollData, key) { @@ -69,7 +75,7 @@ export class RollPartPossession extends RollPartSelect { if (this.visible(rollData)) { const current = this.getCurrent(rollData) switch (part.code) { - case PART_CARAC: return part.filterCaracs(rollData, [this.getRefs(rollData).isPersonnage ? CARACS.REVE_ACTUEL : CARACS.REVE]) + case PART_CARAC: return part.filterCaracs(rollData, [rollData.type.possession.isPersonnage ? CARACS.REVE_ACTUEL : CARACS.REVE]) case PART_COMP: return part.filterComps(rollData, [current.comp?.name]) } } diff --git a/module/roll/roll-type-possession.mjs b/module/roll/roll-type-possession.mjs index 64f1a64a..b9438a74 100644 --- a/module/roll/roll-type-possession.mjs +++ b/module/roll/roll-type-possession.mjs @@ -1,33 +1,28 @@ +import { RdDPossessionV2 } from "../rdd-possession-v2.mjs" import { DIFF, ROLL_TYPE_POSSESSION } from "./roll-constants.mjs" import { RollType } from "./roll-type.mjs" - -export const TYPE_POSSESSION = { - POSSEDER: 'posseder', - CONJURER: 'conjurer', - DEFENSE: 'defense' -} export class RollTypePossession extends RollType { get code() { return ROLL_TYPE_POSSESSION } - get name() { return `Posséder` } + get name() { return "Posséder" } + + prepare(rollData) { + rollData.type.possession = RdDPossessionV2.getPossessionDetails(rollData, rollData.type.possession?.action) + } title(rollData) { - const isEntite = this.isEntite(rollData) - if (this.isDefense(rollData)) { - return `résiste à la ${isEntite ? 'conjuration' : 'possession'} de` - } - return `tente de ${isEntite ? 'posséder' : 'conjurer'}` + return RdDPossessionV2.actionTitle(rollData.type.possession.action) } onSelect(rollData) { - this.setDiffType(rollData, this.isDefense(rollData) ? DIFF.DEFENSE : DIFF.ATTAQUE) + this.setDiffType(rollData, this.isAttaque(rollData) ? DIFF.ATTAQUE : DIFF.DEFENSE) } isEntite(rollData) { return rollData.active.actor.isEntiteNonIncarnee() } - isDefense(rollData) { - return rollData.type.possession == TYPE_POSSESSION.DEFENSE + isAttaque(rollData) { + return RdDPossessionV2.isAttaque(rollData.type.possession.action) } } \ No newline at end of file diff --git a/module/roll/roll-type.mjs b/module/roll/roll-type.mjs index 193ae420..44b85035 100644 --- a/module/roll/roll-type.mjs +++ b/module/roll/roll-type.mjs @@ -16,6 +16,7 @@ export class RollType { return { code: this.code, name: this.name, icon: this.icon, section: 'type', template: this.template, selected: this.isSelected(rollData) } } + prepare(rollData){} isAllowed(rollData) { return rollData.type.allowed == undefined || rollData.type.allowed.includes(this.code) } visible(rollData) { return true } title(rollData) { return this.code } diff --git a/template.json b/template.json index 7fbff462..b35d9e21 100644 --- a/template.json +++ b/template.json @@ -648,10 +648,11 @@ "immobilise": false }, "possession": { - "templates": ["description"], "typepossession": "", "possede": false, "possessionid": "", + "entiteid": "", + "compteur": 0, "entite": { "actorid": "", "diffLibre": 0, @@ -661,8 +662,7 @@ "actorid": "", "diffLibre": 0, "finesse": false - }, - "compteur": 0 + } }, "blessure": { "templates": ["temporel"], diff --git a/templates/chat-demande-defense-possession.hbs b/templates/chat-demande-defense-possession.hbs new file mode 100644 index 00000000..a15baa9e --- /dev/null +++ b/templates/chat-demande-defense-possession.hbs @@ -0,0 +1,39 @@ +
+
+ +
+ +
+

Défense de {{active.name}}

+
+ +
+ {{log 'chat-resultat-possession' this}} + {{#if type.possession.isEntite}} + {{!-- {{#if (eq mode "attaque")}} + {{attacker.name}} tente de {{#if isECNIDefender}}conjurer la possession de{{else}}posséder{{/if}} {{defender.name}} + {{else}} + {{defender.name}} tente de {{#if isECNIDefender}}résister à{{else}}conjurer la possession de{{/if}} {{attacker.name}} + {{/if}} --}} + {{/if}} +

Points de {{#if type.possession.isCompteurPossession}}possession{{else}}conjuration{{/if}}: {{type.possession.compteur}}

+
+ +
+
+ + +
diff --git a/templates/chat-resultat-possession.hbs b/templates/chat-resultat-possession-v1.hbs similarity index 100% rename from templates/chat-resultat-possession.hbs rename to templates/chat-resultat-possession-v1.hbs diff --git a/templates/item/possession-sheet.hbs b/templates/item/possession-sheet.hbs index ffdea2e8..dfff8092 100644 --- a/templates/item/possession-sheet.hbs +++ b/templates/item/possession-sheet.hbs @@ -14,6 +14,5 @@ - {{>"systems/foundryvtt-reve-de-dragon/templates/partial-item-description.hbs"}} diff --git a/templates/roll/result/chat-possession.hbs b/templates/roll/result/chat-possession.hbs index 44377d02..35de8780 100644 --- a/templates/roll/result/chat-possession.hbs +++ b/templates/roll/result/chat-possession.hbs @@ -1,10 +1,11 @@ +{{log 'roll-chat-possession' this}}
- {{active.name}} possession {{opponent.name}}: {{current}} + {{active.name}} {{type.label}} {{opponent.name}}
@@ -15,6 +16,11 @@
+ {{#if roll.type.possession.isAttaque}} + proposer defense + {{else}} + proposer marquer points + {{/if}}
diff --git a/templates/roll/roll-dialog.hbs b/templates/roll/roll-dialog.hbs index 748460bc..54714a20 100644 --- a/templates/roll/roll-dialog.hbs +++ b/templates/roll/roll-dialog.hbs @@ -12,4 +12,4 @@ {{#each templates as |template|}}{{> 'roll-section' rollData=@root.rollData currentsection='ajustements'}}{{/each}} {{> 'roll-button' }} - + \ No newline at end of file diff --git a/templates/roll/roll-part-possession.hbs b/templates/roll/roll-part-possession.hbs index 62ac46f8..49f83080 100644 --- a/templates/roll/roll-part-possession.hbs +++ b/templates/roll/roll-part-possession.hbs @@ -1,7 +1,19 @@ + + + + - - + + + +

Points de {{#if rollData.type.possession.isCompteurPossession}}possession{{else}}conjuration{{/if}}: {{rollData.type.possession.compteur}}

+
+ + + + +