Améliorations combat #532

Merged
vincent.vandeme merged 1 commits from v1.5-combat into v1.5 2022-01-30 16:10:28 +01:00
2 changed files with 43 additions and 118 deletions

View File

@ -1,5 +1,7 @@
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
@ -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 {
static init() {
this.initStorePasseArmes();
Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) });
Hooks.on("preDeleteCombat", (combat, options, userId) => { RdDCombat.onPreDeleteCombat(combat, options, userId); });
}
/* -------------------------------------------- */
static initStorePasseArmes() {
game.system.rdd.combatStore = {
attaques: {},
defenses: {}
};
}
/* -------------------------------------------- */
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
@ -391,8 +382,6 @@ export class RdDCombat {
return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense":
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) {
if (game.user.isGM) {
if (Misc.isUniqueConnectedGM()) {
combat.cleanItemUse();
ChatUtility.removeChatMessageContaining(`<div data-combatid="${combat.id}" data-combatmessage="actor-turn-summary">`)
/*
* TODO: support de plusieurs combats parallèles
* 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();
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
.forEach(it => it.delete());
}
}
@ -471,61 +449,6 @@ export class RdDCombat {
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) {
return new RdDCombat(attacker, defender, defenderTokenId, target)
@ -547,7 +470,7 @@ export class RdDCombat {
static onMsgEncaisser(data) {
let defender = canvas.tokens.get(data.defenderTokenId).actor;
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;
defender.encaisserDommages(attackerRoll, attacker);
@ -563,10 +486,8 @@ export class RdDCombat {
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
if (rddCombat) {
const defenderRoll = msg.defenderRoll;
RdDCombat._storeAttaque(msg.attackerId, defenderRoll.attackerRoll);
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
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) {
const attackerRoll = RdDCombat._getAttaque(this.attackerId);
if (!attackerRoll) {
ui.notifications.warn("Action automatisée impossible, le jet de l'attaquant a été perdu (suite à un raffraichissement?)")
return;
}
const defenderRoll = game.system.rdd.combatStore.defenses[attackerRoll.passeArme];
const chatMessage = ChatUtility.getChatMessage(event);
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll') ;
console.log('RdDCombat', attackerRoll, defenderRoll);
const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.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 '#parer-button': return this.parade(attackerRoll, armeParadeId);
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 '#appel-chance-attaque': return this.attacker.rollAppelChance(
@ -650,7 +569,7 @@ export class RdDCombat {
() => this.attaqueSignificative(attackerRoll),
() => { });
case '#appel-destinee-defense': return this.defender.appelDestinee(
() => this.defenseDestinee(attackerRoll),
() => this.defenseDestinee(defenderRoll),
() => { });
}
}
@ -680,8 +599,7 @@ export class RdDCombat {
}
/* -------------------------------------------- */
defenseDestinee(attackerRoll) {
let defenderRoll = RdDCombat._getDefense(attackerRoll.passeArme);
defenseDestinee(defenderRoll) {
if (defenderRoll) {
ui.notifications.info('Défense significative grâce à la destinée')
RdDResolutionTable.significativeRequise(defenderRoll.rolled);
@ -819,8 +737,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async _onAttaqueParticuliere(rollData) {
RdDCombat._storeAttaque(this.attackerId, rollData);
const isMeleeDiffNegative = (rollData.competence.type == 'competencecreature' || rollData.selectedCarac.label == "Mêlée") && rollData.diffLibre < 0;
// force toujours, sauf empoignade
// 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) {
return await this.choixParticuliere(rollData, "rapidite");
}
ChatMessage.create({
const choixParticuliere = await ChatMessage.create({
alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html', {
@ -852,6 +769,7 @@ export class RdDCombat {
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());
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
// Save rollData for defender
RdDCombat._storeAttaque(this.attackerId, attackerRoll);
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
attackerRoll.show = {
cible: this.target ? this.defender.data.name : 'la cible',
isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
@ -918,7 +832,7 @@ export class RdDCombat {
};
if (Misc.isUniqueConnectedGM()) {
await this._chatMessageDefense(paramChatDefense);
await this._chatMessageDefense(paramChatDefense, defenderRoll);
}
else {
this._socketSendMessageDefense(paramChatDefense, defenderRoll);
@ -926,14 +840,16 @@ export class RdDCombat {
}
/* -------------------------------------------- */
async _chatMessageDefense(paramDemandeDefense) {
ChatMessage.create({
async _chatMessageDefense(paramDemandeDefense, defenderRoll) {
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.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.defender.name),
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) {
RdDCombat._storeAttaque(this.attackerId, attackerRoll);
ChatMessage.create({
const choixEchecTotal = await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
attackerId: this.attackerId,
@ -986,6 +899,7 @@ export class RdDCombat {
essais: attackerRoll.essais
})
});
ChatUtility.setMessageData(choixEchecTotal, 'attacker-roll', attackerRoll);
}
/* -------------------------------------------- */
@ -1105,7 +1019,6 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true });
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
}
/* -------------------------------------------- */
@ -1177,7 +1090,6 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
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;
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)) {
this._onEchecTotal(defenderRoll);
}