Méthode rollPossession pour attaque possession

- 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
This commit is contained in:
2025-12-05 20:12:09 +01:00
parent fd3f988a4f
commit 706aa657b1
15 changed files with 270 additions and 55 deletions
+23 -1
View File
@@ -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) { }
/* -------------------------------------------- */
+132
View File
@@ -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) {
}
}
+12 -9
View File
@@ -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 }
}
},
{
+8 -9
View File
@@ -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: {
+3 -2
View File
@@ -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)
+15 -9
View File
@@ -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,16 +45,24 @@ 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()
})
}
@@ -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])
}
}
+10 -15
View File
@@ -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)
}
}
+1
View File
@@ -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 }
+3 -3
View File
@@ -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"],
@@ -0,0 +1,39 @@
<div class="roll-chat" data-passearme="{{passeArme}}">
<div class="chat-img">
<img src="{{active.img}}" data-tooltip="{{active.name}}" />
</div>
<div class="chat-header">
<h4>Défense de {{active.name}}</h4>
</div>
<div class="chat-resume">
{{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}}
<p>Points de {{#if type.possession.isCompteurPossession}}possession{{else}}conjuration{{/if}}: {{type.possession.compteur}}</p>
</div>
<div class="chat-details">
</div>
<div class="chat-actions">
<a class='chat-card-button button-defense-possession'
data-attackerId='{{ids.opponentId}}'
data-attackerTokenId='{{ids.opponentTokenId}}'
data-defenderId='{{ids.actorId}}'
data-defenderTokenId='{{ids.actorTokenId}}'
>
{{#if type.possession.isEntite}}
Résister à la conjuration
{{else}}
Résister à la possession
{{/if}}
</a>
</div>
</div>
-1
View File
@@ -14,6 +14,5 @@
<label for="system.compteur">Compteur</label>
<input class="attribute-value" type="text" name="system.compteur" value="{{system.compteur}}" data-dtype="Number"/>
</div>
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-item-description.hbs"}}
</section>
</form>
+7 -1
View File
@@ -1,10 +1,11 @@
{{log 'roll-chat-possession' this}}
<div class="roll-chat">
<div class="chat-img">
<img src="{{active.img}}" data-tooltip="{{active.name}}" />
<img src="{{current.comp.comp.img}}" data-tooltip="{{current.comp.label}}" />
</div>
<div class="chat-header">
{{active.name}} possession {{opponent.name}}: {{current}}
{{active.name}} {{type.label}} {{opponent.name}}
</div>
<div class="chat-resume">
@@ -15,6 +16,11 @@
</div>
<div class="chat-actions">
{{#if roll.type.possession.isAttaque}}
proposer defense
{{else}}
proposer marquer points
{{/if}}
</div>
<div class="chat-buttons">
+15 -3
View File
@@ -1,7 +1,19 @@
<roll-part-img>
<img src="{{current.comp.img}}" data-tooltip="{{current.comp.label}}" />
</roll-part-img>
<subline>
<img src="{{current.comp.img}}" data-tooltip="{{current.comp.label}}" />
<select name="select-possession" {{#if (or rollData.type.retry (eq refs.all.length 1))}}disabled{{/if}}>
<select name="select-possession" {{#if (or rollData.type.retry (eq refs.all.length 1))}}disabled{{/if}}>
{{selectOptions refs.all selected=current.key valueAttr="key" labelAttr="label"}}
</select>
</select>
</subline>
<roll-part-detail>
<subline>
<p>Points de {{#if rollData.type.possession.isCompteurPossession}}possession{{else}}conjuration{{/if}}: {{rollData.type.possession.compteur}}</p>
</subline>
<subline>
<button name="creer-possession">Créer possession</button>
</subline>
</roll-part-detail>