Fix échange de messages de combat

Les actions des joueurs n'étaient pas toujours possibles
à cause de tests multijoueurs insuffisants
This commit is contained in:
Vincent Vandemeulebrouck 2021-01-15 19:10:50 +01:00
parent 9ee1b0250d
commit 7a668eb1cf
2 changed files with 113 additions and 108 deletions

View File

@ -28,9 +28,9 @@ export class RdDCombat {
static onSocketMessage(sockmsg) { static onSocketMessage(sockmsg) {
switch (sockmsg.msg) { switch (sockmsg.msg) {
case "msg_encaisser": case "msg_encaisser":
return RdDCombat.terminerPasseArmes(data); return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense": case "msg_defense":
return RdDCombat.handleMsgDefense(sockmsg.data); return RdDCombat.onMsgDefense(sockmsg.data);
} }
} }
@ -99,6 +99,36 @@ export class RdDCombat {
return undefined; return undefined;
} }
/* -------------------------------------------- */
static _storeAttaque(attackerId, attackerRoll) {
game.system.rdd.combatStore.attaques[attackerId] = duplicate(attackerRoll);
}
/* -------------------------------------------- */
static _getAttaque(attackerId) {
return game.system.rdd.combatStore.attaques[attackerId];
}
/* -------------------------------------------- */
static _deleteAttaque(attackerId) {
delete game.system.rdd.combatStore.attaques[attackerId];
}
/* -------------------------------------------- */
static _storeDefense(defenderRoll) {
game.system.rdd.combatStore.defenses[defenderRoll.passeArme] = duplicate(defenderRoll);
}
/* -------------------------------------------- */
static _getDefense(passeArme) {
return game.system.rdd.combatStore.defenses[passeArme];
}
/* -------------------------------------------- */
static _deleteDefense(passeArme) {
delete game.system.rdd.combatStore.defenses;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
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)
@ -121,59 +151,36 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static handleMsgDefense(data) { static onMsgEncaisser(data) {
let defenderToken = canvas.tokens.get(data.defenderTokenId); let attackerRoll = RdDCombat._getAttaque(data.attackerId); // Retrieve the rolldata from the store
if (game.user.id === data.gmId) { // Seul le GM effectue l'encaissement sur la fiche
let attacker = data.attackerId ? game.actors.get(data.attackerId) : null;
let defender = canvas.tokens.get(data.defenderTokenId).actor;
defender.encaisserDommages(attackerRoll, attacker);
}
RdDCombat._deleteDefense(attackerRoll.passeArme);
RdDCombat._deleteAttaque(data.attackerId);
}
/* -------------------------------------------- */
static onMsgDefense(msgData) {
let defenderToken = canvas.tokens.get(msgData.defenderTokenId);
if (defenderToken) { if (defenderToken) {
if (!game.user.isGM && game.user.character == undefined) { // vérification / sanity check if (!game.user.isGM && !game.user.character) { // vérification / sanity check
ui.notifications.error("Le joueur " + game.user.name + " n'est connecté à aucun personnage. Impossible de continuer."); ui.notifications.error("Le joueur " + game.user.name + " n'est connecté à aucun personnage. Impossible de continuer.");
return; return;
} }
if ((game.user.isGM && !defenderToken.actor.hasPlayerOwner) || (defenderToken.actor.hasPlayerOwner && (game.user.character.id == defenderToken.actor.data._id))) { if ((game.user.isGM && !defenderToken.actor.hasPlayerOwner) || (defenderToken.actor.hasPlayerOwner && (game.user.character.id == defenderToken.actor.data._id))) {
//console.log("User is pushing message...", game.user.name); const defenderRoll = msgData.defenderRoll;
game.system.rdd.combatStore.attaques[data.attackerId] = duplicate(data.rollData); RdDCombat._storeAttaque(msgData.attackerId, defenderRoll.attackerRoll);
data.whisper = [game.user]; RdDCombat._storeDefense(defenderRoll);
data.blind = true;
data.rollMode = "blindroll";
ChatMessage.create(data);
} }
} }
} }
static terminerPasseArmes(data) {
if (game.user.isGM) { // Seul le GM nettoie le stockage des données de combat
let attackerRoll = game.system.rdd.combatStore.attaques[data.attackerId]; // Retrieve the rolldata from the store
game.system.rdd.combatStore.attaques[data.attackerId] = undefined;
game.system.rdd.combatStore.defenses[attackerRoll.passeArme] = undefined;
}
}
/* -------------------------------------------- */
static _sendRollMessage(sender, recipient, defenderTokenId, topic, message, rollData) {
let chatMessage = {
content: message,
whisper: ChatUtility.getWhisperRecipients("blindroll", recipient.name),
};
// envoyer le message au destinataire
if (!game.user.isGM || recipient.hasPlayerOwner) {
let data = {
attackerId: sender?.data._id,
defenderId: recipient?.data._id,
defenderTokenId: defenderTokenId,
rollData: duplicate(rollData),
rollMode: true
};
mergeObject(data, chatMessage);
game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: topic, data: data });
} else {
chatMessage.whisper = [game.user];
}
if (game.user.isGM) { // Always push the message to the MJ
ChatMessage.create(chatMessage);
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static _callJetDeVie(event) { static _callJetDeVie(event) {
let actorId = event.currentTarget.attributes['data-actorId'].value; let actorId = event.currentTarget.attributes['data-actorId'].value;
@ -218,11 +225,12 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onEvent(button, event) { async onEvent(button, event) {
let attackerRoll = game.system.rdd.combatStore.attaques[this.attackerId]; const attackerRoll = RdDCombat._getAttaque(this.attackerId);
if (!attackerRoll) { if (!attackerRoll) {
ui.notifications.warn("Action automatisée impossible, le jet de l'attaquant a été perdu (suite à un raffraichissement?)") ui.notifications.warn("Action automatisée impossible, le jet de l'attaquant a été perdu (suite à un raffraichissement?)")
return; return;
} }
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;
@ -238,8 +246,8 @@ export class RdDCombat {
() => this.attaqueChanceuse(attackerRoll), () => this.attaqueChanceuse(attackerRoll),
() => this._onEchecTotal(attackerRoll)); () => this._onEchecTotal(attackerRoll));
case '#appel-chance-defense': return this.defender.rollAppelChance( case '#appel-chance-defense': return this.defender.rollAppelChance(
() => this.defenseChanceuse(attackerRoll), () => this.defenseChanceuse(attackerRoll, defenderRoll),
() => this.afficherOptionsDefense(attackerRoll, { defenseChance: true })); () => this.afficherOptionsDefense(attackerRoll, defenderRoll, { defenseChance: true }));
case '#appel-destinee-attaque': return this.attacker.appelDestinee( case '#appel-destinee-attaque': return this.attacker.appelDestinee(
() => this.attaqueSignificative(attackerRoll), () => this.attaqueSignificative(attackerRoll),
() => { }); () => { });
@ -249,23 +257,6 @@ export class RdDCombat {
} }
} }
/* -------------------------------------------- */
_consumeDefense(passeArme) {
let defenderRoll = this._getDefense(passeArme);
game.system.rdd.combatStore.defenses[passeArme] = undefined;
return defenderRoll;
}
/* -------------------------------------------- */
_getDefense(passeArme) {
return game.system.rdd.combatStore.defenses[passeArme];
}
/* -------------------------------------------- */
_storeDefense(defenderRoll) {
game.system.rdd.combatStore.defenses[defenderRoll.passeArme] = defenderRoll;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
attaqueChanceuse(attackerRoll) { attaqueChanceuse(attackerRoll) {
ui.notifications.info("L'attaque est rejouée grâce à la chance") ui.notifications.info("L'attaque est rejouée grâce à la chance")
@ -282,17 +273,17 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
defenseChanceuse(attackerRoll) { defenseChanceuse(attackerRoll, defenderRoll) {
ui.notifications.info("La défense est rejouée grâce à la chance") ui.notifications.info("La défense est rejouée grâce à la chance")
attackerRoll.essais.defenseChance = true; attackerRoll.essais.defenseChance = true;
attackerRoll.essais.defense = false; attackerRoll.essais.defense = false;
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme); this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
this._sendMessageDefense(attackerRoll); this._sendMessageDefense(attackerRoll, defenderRoll, attackerRoll.essais);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
defenseDestinee(attackerRoll) { defenseDestinee(attackerRoll) {
let defenderRoll = this._getDefense(attackerRoll.passeArme); 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.forceSignificative(defenderRoll.rolled); RdDResolutionTable.forceSignificative(defenderRoll.rolled);
@ -310,9 +301,9 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
afficherOptionsDefense(attackerRoll, essais) { afficherOptionsDefense(attackerRoll, defenderRoll, essais) {
ui.notifications.info("La chance n'est pas avec vous"); ui.notifications.info("La chance n'est pas avec vous");
this._sendMessageDefense(attackerRoll, essais); this._sendMessageDefense(attackerRoll, defenderRoll, essais);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -410,13 +401,15 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _onAttaqueParticuliere(rollData) { async _onAttaqueParticuliere(rollData) {
game.system.rdd.combatStore.attaques[this.attackerId] = duplicate(rollData); RdDCombat._storeAttaque(this.attackerId, rollData);
// Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum // Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum
const isMeleeDiffNegative = rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0; const isMeleeDiffNegative = rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0;
ChatMessage.create({ ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients(this.attacker.name), alias: 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', {
alias: this.attacker.name,
attackerId: this.attackerId, attackerId: this.attackerId,
defenderTokenId: this.defenderTokenId, defenderTokenId: this.defenderTokenId,
isFinesse: isMeleeDiffNegative, isFinesse: isMeleeDiffNegative,
@ -432,7 +425,7 @@ 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());
// Save rollData for defender // Save rollData for defender
game.system.rdd.combatStore.attaques[this.attackerId] = duplicate(attackerRoll); RdDCombat._storeAttaque(this.attackerId, attackerRoll);
attackerRoll.show = { attackerRoll.show = {
cible: this.target ? this.defender.data.name : 'la cible', cible: this.target ? this.defender.data.name : 'la cible',
@ -450,11 +443,13 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async _sendMessageDefense(attackerRoll, essais = {}) { async _sendMessageDefense(attackerRoll, defenderRoll = undefined, essaisPrecedents = undefined) {
console.log("RdDCombat._sendMessageDefense", attackerRoll, essais, " / ", this.attacker, this.target, this.attackerId, attackerRoll.competence.data.categorie); console.log("RdDCombat._sendMessageDefense", attackerRoll, defenderRoll, essaisPrecedents, " / ", this.attacker, this.target, this.attackerId, attackerRoll.competence.data.categorie);
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme); this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
mergeObject(attackerRoll.essais, essais, { overwrite: true }); if (essaisPrecedents) {
mergeObject(attackerRoll.essais, essaisPrecedents, { overwrite: true });
}
const paramDemandeDefense = { const paramDemandeDefense = {
passeArme: attackerRoll.passeArme, passeArme: attackerRoll.passeArme,
essais: attackerRoll.essais, essais: attackerRoll.essais,
@ -467,11 +462,33 @@ export class RdDCombat {
attaqueParticuliere: attackerRoll.particuliere, attaqueParticuliere: attackerRoll.particuliere,
dmg: attackerRoll.dmg dmg: attackerRoll.dmg
}; };
let message = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense);
RdDCombat._sendRollMessage(this.attacker, this.defender, this.defenderTokenId, "msg_defense", message, attackerRoll); RdDCombat._sendRollMessage(this.attacker, this.defender, this.defenderTokenId,
defenderRoll ?? { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} },
essaisPrecedents != undefined);
let chatMessage = {
alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.defender.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense),
};
ChatMessage.create(chatMessage);
} }
/* -------------------------------------------- */
static _sendRollMessage(sender, recipient, defenderTokenId, defenderRoll, selfMessage) {
// envoyer le message au destinataire
if (!selfMessage || !game.user.isGM || recipient.hasPlayerOwner) {
let data = {
attackerId: sender?.data._id,
defenderId: recipient?.data._id,
defenderTokenId: defenderTokenId,
defenderRoll: duplicate(defenderRoll),
rollMode: true
};
game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: "msg_defense", data: data });
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
_filterArmesParade(items, competence) { _filterArmesParade(items, competence) {
items = items.filter(item => (item.type == 'arme' && item.data.equipe) || (item.type == 'competencecreature' && item.data.isparade)); items = items.filter(item => (item.type == 'arme' && item.data.equipe) || (item.type == 'competencecreature' && item.data.isparade));
@ -491,7 +508,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _onAttaqueEchecTotal(attackerRoll) { async _onAttaqueEchecTotal(attackerRoll) {
game.system.rdd.combatStore.attaques[this.attackerId] = duplicate(attackerRoll); RdDCombat._storeAttaque(this.attackerId, attackerRoll);
// Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum // Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum
ChatMessage.create({ ChatMessage.create({
@ -535,7 +552,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async parade(attackerRoll, armeParadeId) { async parade(attackerRoll, armeParadeId) {
let arme = this.defender.getArmeParade(armeParadeId); let arme = this.defender.getArmeParade(armeParadeId);
console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme); console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme);
let rollData = this._prepareParade(attackerRoll, arme); let rollData = this._prepareParade(attackerRoll, arme);
@ -615,11 +632,11 @@ export class RdDCombat {
async _onParadeNormale(defenderRoll) { async _onParadeNormale(defenderRoll) {
console.log("RdDCombat._onParadeNormale >>>", defenderRoll); console.log("RdDCombat._onParadeNormale >>>", defenderRoll);
this._consumeDefense(defenderRoll.passeArme);
await this.computeRecul(defenderRoll); await this.computeRecul(defenderRoll);
await this.computeDeteriorationArme(defenderRoll); await this.computeDeteriorationArme(defenderRoll);
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html'); await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
RdDCombat._deleteDefense(defenderRoll.passeArme);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -629,7 +646,7 @@ export class RdDCombat {
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html'); await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme); this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, { defense: true }); this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true });
this._storeDefense(defenderRoll); this._storeDefense(defenderRoll);
} }
@ -690,8 +707,8 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _onEsquiveNormale(defenderRoll) { async _onEsquiveNormale(defenderRoll) {
console.log("RdDCombat._onEsquiveNormal >>>", defenderRoll); console.log("RdDCombat._onEsquiveNormal >>>", defenderRoll);
this._consumeDefense(defenderRoll.passeArme);
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html'); await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
RdDCombat._deleteDefense(defenderRoll.passeArme);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -701,7 +718,7 @@ export class RdDCombat {
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html'); await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme); this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, { defense: true }) this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true })
this._storeDefense(defenderRoll); this._storeDefense(defenderRoll);
} }
@ -791,15 +808,8 @@ export class RdDCombat {
defenderTokenId = defenderTokenId || this.defenderTokenId; defenderTokenId = defenderTokenId || this.defenderTokenId;
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId); console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
let defenderRoll = this._consumeDefense(attackerRoll.passeArme); let defenderRoll = RdDCombat._getDefense(attackerRoll.passeArme);
if (!defenderRoll) { if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) {
defenderRoll = {
attackerRoll: attackerRoll,
show: {}
};
}
else if (RdDCombat.isEchecTotal(defenderRoll)) {
// TODO: echec total!!!
this._onEchecTotal(defenderRoll); this._onEchecTotal(defenderRoll);
} }
@ -809,12 +819,18 @@ export class RdDCombat {
await this.computeRecul(defenderRoll); await this.computeRecul(defenderRoll);
this.defender.encaisserDommages(attackerRoll, this.attacker); this.defender.encaisserDommages(attackerRoll, this.attacker);
} else { // Emit message for GM } else { // envoi à un GM: les joueurs n'ont pas le droit de modifier les personnages qu'ils ne possèdent pas
game.socket.emit("system.foundryvtt-reve-de-dragon", { game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_encaisser", msg: "msg_encaisser",
data: { attackerId: this.attackerId, defenderTokenId: defenderTokenId } data: {
attackerId: this.attackerId,
defenderTokenId: defenderTokenId,
attackerRoll: attackerRoll,
gmId: game.users.entities.find(u => u.isGM)?.id,
}
}); });
} }
RdDCombat._deleteDefense(attackerRoll.passeArme);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -635,8 +635,6 @@ export class RdDUtility {
static onSocketMesssage(sockmsg) { static onSocketMesssage(sockmsg) {
console.log(">>>>> MSG RECV", sockmsg); console.log(">>>>> MSG RECV", sockmsg);
switch (sockmsg.msg) { switch (sockmsg.msg) {
case "msg_encaisser":
return RdDUtility._handleMsgEncaisser(sockmsg.data);
case "msg_gm_chat_message": case "msg_gm_chat_message":
return ChatUtility.handleGMChatMessage(sockmsg.data); return ChatUtility.handleGMChatMessage(sockmsg.data);
case "msg_sync_time": case "msg_sync_time":
@ -749,15 +747,6 @@ export class RdDUtility {
}); });
} }
/* -------------------------------------------- */
static _handleMsgEncaisser(data) {
if (game.user.isGM) { // Seul le GM effectue l'encaissement sur la fiche
let defender = canvas.tokens.get(data.defenderTokenId).actor;
let attacker = data.attackerId ? game.actors.get(data.attackerId) : null;
defender.encaisserDommages(attackerRoll, attacker);
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async chatListeners(html) { static async chatListeners(html) {
RdDCombat.registerChatCallbacks(html); RdDCombat.registerChatCallbacks(html);