Gestion des choix de combat par flags

Utiliser des flags sur les ChatMessage de choix de combat permet:

- de conserver les informations même après un refresh
- de simplifier un peu (pas besoin de gérer un stockage custom)
This commit is contained in:
Vincent Vandemeulebrouck 2022-01-29 23:33:31 +01:00
parent 3abaf4e944
commit c0442f607c
2 changed files with 43 additions and 118 deletions

View File

@ -1,5 +1,7 @@
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { SYSTEM_SOCKET_ID } from "./constants.js"; import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
export const MESSAGE_DATA = 'message-data';
/** /**
* Class providing helper methods to get the list of users, and * Class providing helper methods to get the list of users, and
@ -160,4 +162,20 @@ export class ChatUtility {
} }
} }
static async setMessageData(chatMessage, key, data) {
if (data) {
await chatMessage.setFlag(SYSTEM_RDD, key, JSON.stringify(data));
}
}
static getMessageData(chatMessage, key) {
const json = chatMessage.getFlag(SYSTEM_RDD, key);
return json ? JSON.parse(json) : undefined;
}
static getChatMessage(event) {
const chatMessageId = $(event.currentTarget).closest('.chat-message').attr('data-message-id');
return game.messages.get(chatMessageId);
}
} }

View File

@ -371,19 +371,10 @@ export class RdDCombatManager extends Combat {
export class RdDCombat { export class RdDCombat {
static init() { static init() {
this.initStorePasseArmes();
Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) }); Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) });
Hooks.on("preDeleteCombat", (combat, options, userId) => { RdDCombat.onPreDeleteCombat(combat, options, userId); }); Hooks.on("preDeleteCombat", (combat, options, userId) => { RdDCombat.onPreDeleteCombat(combat, options, userId); });
} }
/* -------------------------------------------- */
static initStorePasseArmes() {
game.system.rdd.combatStore = {
attaques: {},
defenses: {}
};
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static onSocketMessage(sockmsg) { static onSocketMessage(sockmsg) {
switch (sockmsg.msg) { switch (sockmsg.msg) {
@ -391,8 +382,6 @@ export class RdDCombat {
return RdDCombat.onMsgEncaisser(sockmsg.data); return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense": case "msg_defense":
return RdDCombat.onMsgDefense(sockmsg.data); return RdDCombat.onMsgDefense(sockmsg.data);
case "msg_combat_passearme":
return RdDCombat.onMsgPasseArme(sockmsg.data);
} }
} }
@ -405,22 +394,11 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
static onPreDeleteCombat(combat, options, userId) { static onPreDeleteCombat(combat, options, userId) {
if (game.user.isGM) { if (Misc.isUniqueConnectedGM()) {
combat.cleanItemUse(); combat.cleanItemUse();
ChatUtility.removeChatMessageContaining(`<div data-combatid="${combat.id}" data-combatmessage="actor-turn-summary">`) ChatUtility.removeChatMessageContaining(`<div data-combatid="${combat.id}" data-combatmessage="actor-turn-summary">`)
/* game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
* TODO: support de plusieurs combats parallèles .forEach(it => it.delete());
* il faudrait avoir un id de combat en plus de celui de passe d'armes
*/
for (const key in game.system.rdd.combatStore.attaques) {
const attackerRoll = game.system.rdd.combatStore.attaques[key];
ChatUtility.removeChatMessageContaining(`<div data-passearme="${attackerRoll.passeArme}">`);
}
for (const key in game.system.rdd.combatStore.defenses) {
const defenderRoll = game.system.rdd.combatStore.defenses[key];
ChatUtility.removeChatMessageContaining(`<div data-passearme="${defenderRoll.passeArme}">`);
}
RdDCombat.initStorePasseArmes();
} }
} }
@ -471,61 +449,6 @@ export class RdDCombat {
return undefined; return undefined;
} }
/* -------------------------------------------- */
static messagePasseArme(data) {
// TODO: store required info for combat in the chat message presenting choices?
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_combat_passearme", data: data });
RdDCombat.onMsgPasseArme(data);
}
/* -------------------------------------------- */
static onMsgPasseArme(data) {
switch (data.actionPasseArme) {
case "store-attaque":
game.system.rdd.combatStore.attaques[data.id] = data.rollData;
break;
case "store-defense":
game.system.rdd.combatStore.defenses[data.id] = data.rollData;
break;
case "delete-attaque":
delete game.system.rdd.combatStore.attaques[data.id];
break;
case "delete-defense":
delete game.system.rdd.combatStore.defenses[data.id];
break;
}
}
/* -------------------------------------------- */
static _storeAttaque(attackerId, attackerRoll) {
RdDCombat.messagePasseArme({ actionPasseArme: "store-attaque", id: attackerId, rollData: attackerRoll });
}
/* -------------------------------------------- */
static _getAttaque(attackerId) {
return game.system.rdd.combatStore.attaques[attackerId];
}
/* -------------------------------------------- */
static _deleteAttaque(attackerId) {
RdDCombat.messagePasseArme({ actionPasseArme: "delete-attaque", id: attackerId });
}
/* -------------------------------------------- */
static _storeDefense(passeArme, defenderRoll) {
RdDCombat.messagePasseArme({ actionPasseArme: "store-defense", id: passeArme, rollData: defenderRoll });
}
/* -------------------------------------------- */
static _getDefense(passeArme) {
return game.system.rdd.combatStore.defenses[passeArme];
}
/* -------------------------------------------- */
static _deleteDefense(passeArme) {
RdDCombat.messagePasseArme({ actionPasseArme: "delete-defense", id: passeArme });
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static create(attacker, defender, defenderTokenId, target = undefined) { static create(attacker, defender, defenderTokenId, target = undefined) {
return new RdDCombat(attacker, defender, defenderTokenId, target) return new RdDCombat(attacker, defender, defenderTokenId, target)
@ -547,7 +470,7 @@ export class RdDCombat {
static onMsgEncaisser(data) { static onMsgEncaisser(data) {
let defender = canvas.tokens.get(data.defenderTokenId).actor; let defender = canvas.tokens.get(data.defenderTokenId).actor;
if (Misc.isOwnerPlayerOrUniqueConnectedGM()) { if (Misc.isOwnerPlayerOrUniqueConnectedGM()) {
let attackerRoll = RdDCombat._getAttaque(data.attackerId); // Retrieve the rolldata from the store let attackerRoll = data.attackerRoll;
let attacker = data.attackerId ? game.actors.get(data.attackerId) : null; let attacker = data.attackerId ? game.actors.get(data.attackerId) : null;
defender.encaisserDommages(attackerRoll, attacker); defender.encaisserDommages(attackerRoll, attacker);
@ -563,10 +486,8 @@ export class RdDCombat {
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
if (rddCombat) { if (rddCombat) {
const defenderRoll = msg.defenderRoll; const defenderRoll = msg.defenderRoll;
RdDCombat._storeAttaque(msg.attackerId, defenderRoll.attackerRoll);
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
rddCombat.removeChatMessageActionsPasseArme(defenderRoll.passeArme); rddCombat.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
rddCombat._chatMessageDefense(msg.paramChatDefense); rddCombat._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
} }
} }
} }
@ -621,12 +542,10 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onEvent(button, event) { async onEvent(button, event) {
const attackerRoll = RdDCombat._getAttaque(this.attackerId); const chatMessage = ChatUtility.getChatMessage(event);
if (!attackerRoll) { const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
ui.notifications.warn("Action automatisée impossible, le jet de l'attaquant a été perdu (suite à un raffraichissement?)") const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll') ;
return; console.log('RdDCombat', attackerRoll, defenderRoll);
}
const defenderRoll = game.system.rdd.combatStore.defenses[attackerRoll.passeArme];
const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value; const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value;
const armeParadeId = event.currentTarget.attributes['data-armeid']?.value; const armeParadeId = event.currentTarget.attributes['data-armeid']?.value;
@ -637,7 +556,7 @@ export class RdDCombat {
case '#particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value); case '#particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '#parer-button': return this.parade(attackerRoll, armeParadeId); case '#parer-button': return this.parade(attackerRoll, armeParadeId);
case '#esquiver-button': return this.esquive(attackerRoll, compId, competence); case '#esquiver-button': return this.esquive(attackerRoll, compId, competence);
case '#encaisser-button': return this.encaisser(attackerRoll, defenderTokenId); case '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll, defenderTokenId);
case '#echec-total-attaque': return this._onEchecTotal(attackerRoll); case '#echec-total-attaque': return this._onEchecTotal(attackerRoll);
case '#appel-chance-attaque': return this.attacker.rollAppelChance( case '#appel-chance-attaque': return this.attacker.rollAppelChance(
@ -650,7 +569,7 @@ export class RdDCombat {
() => this.attaqueSignificative(attackerRoll), () => this.attaqueSignificative(attackerRoll),
() => { }); () => { });
case '#appel-destinee-defense': return this.defender.appelDestinee( case '#appel-destinee-defense': return this.defender.appelDestinee(
() => this.defenseDestinee(attackerRoll), () => this.defenseDestinee(defenderRoll),
() => { }); () => { });
} }
} }
@ -680,8 +599,7 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
defenseDestinee(attackerRoll) { defenseDestinee(defenderRoll) {
let defenderRoll = RdDCombat._getDefense(attackerRoll.passeArme);
if (defenderRoll) { if (defenderRoll) {
ui.notifications.info('Défense significative grâce à la destinée') ui.notifications.info('Défense significative grâce à la destinée')
RdDResolutionTable.significativeRequise(defenderRoll.rolled); RdDResolutionTable.significativeRequise(defenderRoll.rolled);
@ -819,8 +737,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _onAttaqueParticuliere(rollData) { async _onAttaqueParticuliere(rollData) {
RdDCombat._storeAttaque(this.attackerId, rollData);
const isMeleeDiffNegative = (rollData.competence.type == 'competencecreature' || rollData.selectedCarac.label == "Mêlée") && rollData.diffLibre < 0; const isMeleeDiffNegative = (rollData.competence.type == 'competencecreature' || rollData.selectedCarac.label == "Mêlée") && rollData.diffLibre < 0;
// force toujours, sauf empoignade // force toujours, sauf empoignade
// finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum // finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum
@ -838,8 +755,8 @@ export class RdDCombat {
else if (!isForce && !isFinesse && isRapide) { else if (!isForce && !isFinesse && isRapide) {
return await this.choixParticuliere(rollData, "rapidite"); return await this.choixParticuliere(rollData, "rapidite");
} }
ChatMessage.create({ const choixParticuliere = await ChatMessage.create({
alias: this.attacker.name, alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html', { content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html', {
@ -852,6 +769,7 @@ export class RdDCombat {
passeArme: rollData.passeArme passeArme: rollData.passeArme
}) })
}); });
ChatUtility.setMessageData(choixParticuliere, 'attacker-roll', rollData);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -860,10 +778,6 @@ export class RdDCombat {
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), this.defender.isEntiteCauchemar()); attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), this.defender.isEntiteCauchemar());
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} } let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
// Save rollData for defender
RdDCombat._storeAttaque(this.attackerId, attackerRoll);
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
attackerRoll.show = { attackerRoll.show = {
cible: this.target ? this.defender.data.name : 'la cible', cible: this.target ? this.defender.data.name : 'la cible',
isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge') isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
@ -918,7 +832,7 @@ export class RdDCombat {
}; };
if (Misc.isUniqueConnectedGM()) { if (Misc.isUniqueConnectedGM()) {
await this._chatMessageDefense(paramChatDefense); await this._chatMessageDefense(paramChatDefense, defenderRoll);
} }
else { else {
this._socketSendMessageDefense(paramChatDefense, defenderRoll); this._socketSendMessageDefense(paramChatDefense, defenderRoll);
@ -926,14 +840,16 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async _chatMessageDefense(paramDemandeDefense) { async _chatMessageDefense(paramDemandeDefense, defenderRoll) {
ChatMessage.create({ const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs) // message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)), speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker.name, alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.defender.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.defender.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense), content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense),
}); });
// flag pour garder les jets d'attaque/defense
ChatUtility.setMessageData(choixDefense, 'defender-roll', defenderRoll);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -974,10 +890,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _onAttaqueEchecTotal(attackerRoll) { async _onAttaqueEchecTotal(attackerRoll) {
const choixEchecTotal = await ChatMessage.create({
RdDCombat._storeAttaque(this.attackerId, attackerRoll);
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', { content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
attackerId: this.attackerId, attackerId: this.attackerId,
@ -986,6 +899,7 @@ export class RdDCombat {
essais: attackerRoll.essais essais: attackerRoll.essais
}) })
}); });
ChatUtility.setMessageData(choixEchecTotal, 'attacker-roll', attackerRoll);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -1105,7 +1019,6 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme); this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true }); this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true });
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -1177,7 +1090,6 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme); this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true }) this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true })
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -1282,15 +1194,10 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async encaisser(attackerRoll, defenderTokenId) { async encaisser(attackerRoll, defenderRoll, defenderTokenId) {
defenderTokenId = defenderTokenId || this.defenderTokenId; defenderTokenId = defenderTokenId || this.defenderTokenId;
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId); console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
let defenderRoll = RdDCombat._getDefense(attackerRoll.passeArme);
if (!defenderRoll) {
ui.notifications.warn("Cette passe d'arme est déjà terminée!")
return;
}
if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) { if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) {
this._onEchecTotal(defenderRoll); this._onEchecTotal(defenderRoll);
} }