Merge branch 'stabilisation-v1.2' into 'v1.2'

Fix des messages de passes d'armes en combat côté joueur

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!116
This commit is contained in:
Leratier Bretonnien 2021-01-15 19:24:21 +00:00
commit bd1c45e9c0
9 changed files with 129 additions and 119 deletions

View File

@ -409,7 +409,7 @@ export class RdDActor extends Actor {
async dormir(heures = 1) {
let message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: "Vous dormez " + heures + " heure" + (heures > 1 ? "s" : "")
content: this.name +": Vous dormez " + heures + " heure" + (heures > 1 ? "s" : "")
};
await this.recupereEndurance(message);
for (let i = 0; i < heures; i++) {
@ -2105,7 +2105,7 @@ export class RdDActor extends Actor {
}
// Notification au MJ
ChatMessage.create({ content: game.user.name + " est monté dans les TMR en mode : " + mode, whisper: ChatMessage.getWhisperRecipients("GM") });
ChatMessage.create({ content: this.name + " est monté dans les TMR en mode : " + mode, whisper: ChatMessage.getWhisperRecipients("GM") });
let data = {
fatigue: {

View File

@ -59,7 +59,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static getWhisperRecipientsAndGMs(name) {
return ChatMessage.getWhisperRecipients(name)
.concat(this.getUsers(user => user.isGM));
.concat(ChatMessage.getWhisperRecipients('GM'));
}
/* -------------------------------------------- */

View File

@ -64,7 +64,7 @@ export class RdDItemSheet extends ItemSheet {
data.actorId = this.actor._id;
}
data.bonusCaseList = RdDItemSort.getBonusCaseList(data, true);
data.isGM = game.user.isGM; // Pour vérouiller certaines éditions
data.isGM = game.user.isGM; // Pour verrouiller certaines éditions
return data;
}

View File

@ -28,9 +28,9 @@ export class RdDCombat {
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
case "msg_encaisser":
return RdDCombat.terminerPasseArmes(data);
return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense":
return RdDCombat.handleMsgDefense(sockmsg.data);
return RdDCombat.onMsgDefense(sockmsg.data);
}
}
@ -99,6 +99,36 @@ export class RdDCombat {
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) {
return new RdDCombat(attacker, defender, defenderTokenId, target)
@ -121,59 +151,36 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static handleMsgDefense(data) {
let defenderToken = canvas.tokens.get(data.defenderTokenId);
static onMsgEncaisser(data) {
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 (!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.");
return;
}
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);
game.system.rdd.combatStore.attaques[data.attackerId] = duplicate(data.rollData);
data.whisper = [game.user];
data.blind = true;
data.rollMode = "blindroll";
ChatMessage.create(data);
const defenderRoll = msgData.defenderRoll;
RdDCombat._storeAttaque(msgData.attackerId, defenderRoll.attackerRoll);
RdDCombat._storeDefense(defenderRoll);
}
}
}
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) {
let actorId = event.currentTarget.attributes['data-actorId'].value;
@ -218,11 +225,12 @@ export class RdDCombat {
/* -------------------------------------------- */
async onEvent(button, event) {
let attackerRoll = game.system.rdd.combatStore.attaques[this.attackerId];
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 defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value;
const armeParadeId = event.currentTarget.attributes['data-armeid']?.value;
@ -238,8 +246,8 @@ export class RdDCombat {
() => this.attaqueChanceuse(attackerRoll),
() => this._onEchecTotal(attackerRoll));
case '#appel-chance-defense': return this.defender.rollAppelChance(
() => this.defenseChanceuse(attackerRoll),
() => this.afficherOptionsDefense(attackerRoll, { defenseChance: true }));
() => this.defenseChanceuse(attackerRoll, defenderRoll),
() => this.afficherOptionsDefense(attackerRoll, defenderRoll, { defenseChance: true }));
case '#appel-destinee-attaque': return this.attacker.appelDestinee(
() => 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) {
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")
attackerRoll.essais.defenseChance = true;
attackerRoll.essais.defense = false;
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
this._sendMessageDefense(attackerRoll);
this._sendMessageDefense(attackerRoll, defenderRoll, attackerRoll.essais);
}
/* -------------------------------------------- */
defenseDestinee(attackerRoll) {
let defenderRoll = this._getDefense(attackerRoll.passeArme);
let defenderRoll = RdDCombat._getDefense(attackerRoll.passeArme);
if (defenderRoll) {
ui.notifications.info('Défense significative grâce à la destinée')
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");
this._sendMessageDefense(attackerRoll, essais);
this._sendMessageDefense(attackerRoll, defenderRoll, essais);
}
/* -------------------------------------------- */
@ -410,13 +401,15 @@ export class RdDCombat {
/* -------------------------------------------- */
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
const isMeleeDiffNegative = rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0;
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', {
alias: this.attacker.name,
attackerId: this.attackerId,
defenderTokenId: this.defenderTokenId,
isFinesse: isMeleeDiffNegative,
@ -432,7 +425,7 @@ export class RdDCombat {
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), this.defender.isEntiteCauchemar());
// Save rollData for defender
game.system.rdd.combatStore.attaques[this.attackerId] = duplicate(attackerRoll);
RdDCombat._storeAttaque(this.attackerId, attackerRoll);
attackerRoll.show = {
cible: this.target ? this.defender.data.name : 'la cible',
@ -450,29 +443,52 @@ export class RdDCombat {
}
/* -------------------------------------------- */
async _sendMessageDefense(attackerRoll, essais = {}) {
console.log("RdDCombat._sendMessageDefense", attackerRoll, essais, " / ", this.attacker, this.target, this.attackerId, attackerRoll.competence.data.categorie);
async _sendMessageDefense(attackerRoll, defenderRoll = undefined, essaisPrecedents = undefined) {
console.log("RdDCombat._sendMessageDefense", attackerRoll, defenderRoll, essaisPrecedents, " / ", this.attacker, this.target, this.attackerId, attackerRoll.competence.data.categorie);
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
mergeObject(attackerRoll.essais, essais, { overwrite: true });
if (essaisPrecedents) {
mergeObject(attackerRoll.essais, essaisPrecedents, { overwrite: true });
}
const paramDemandeDefense = {
passeArme: attackerRoll.passeArme,
essais: attackerRoll.essais,
// surprise: this.defender.getSurprise(true),
// surprise: attackerRoll.ajustements.attaqueDefenseurSurpris.used,
defender: this.defender,
attackerId: this.attackerId,
defenderTokenId: this.defenderTokenId,
mainsNues: attackerRoll.dmg.mortalite != 'mortel' && this.defender.getCompetence("Corps à corps"),
armes: this._filterArmesParade(this.defender.data.items, attackerRoll.competence, attackerRoll.arme),
diffLibre: attackerRoll.ajustements?.diffLibre?.value ?? 0,
attaqueParticuliere: attackerRoll.particuliere,
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) {
items = items.filter(item => (item.type == 'arme' && item.data.equipe) || (item.type == 'competencecreature' && item.data.isparade));
@ -492,7 +508,7 @@ export class RdDCombat {
/* -------------------------------------------- */
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
ChatMessage.create({
@ -536,7 +552,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async parade(attackerRoll, armeParadeId) {
let arme = this.defender.getArmeParade(armeParadeId);
console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme);
let rollData = this._prepareParade(attackerRoll, arme);
@ -616,11 +632,11 @@ export class RdDCombat {
async _onParadeNormale(defenderRoll) {
console.log("RdDCombat._onParadeNormale >>>", defenderRoll);
this._consumeDefense(defenderRoll.passeArme);
await this.computeRecul(defenderRoll);
await this.computeDeteriorationArme(defenderRoll);
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
RdDCombat._deleteDefense(defenderRoll.passeArme);
}
/* -------------------------------------------- */
@ -630,7 +646,7 @@ export class RdDCombat {
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-parade.html');
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, { defense: true });
this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true });
this._storeDefense(defenderRoll);
}
@ -691,8 +707,8 @@ export class RdDCombat {
/* -------------------------------------------- */
async _onEsquiveNormale(defenderRoll) {
console.log("RdDCombat._onEsquiveNormal >>>", defenderRoll);
this._consumeDefense(defenderRoll.passeArme);
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
RdDCombat._deleteDefense(defenderRoll.passeArme);
}
/* -------------------------------------------- */
@ -702,7 +718,7 @@ export class RdDCombat {
await RdDResolutionTable.displayRollData(defenderRoll, this.defender, 'chat-resultat-esquive.html');
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, { defense: true })
this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true })
this._storeDefense(defenderRoll);
}
@ -792,15 +808,8 @@ export class RdDCombat {
defenderTokenId = defenderTokenId || this.defenderTokenId;
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
let defenderRoll = this._consumeDefense(attackerRoll.passeArme);
if (!defenderRoll) {
defenderRoll = {
attackerRoll: attackerRoll,
show: {}
};
}
else if (RdDCombat.isEchecTotal(defenderRoll)) {
// TODO: echec total!!!
let defenderRoll = RdDCombat._getDefense(attackerRoll.passeArme);
if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) {
this._onEchecTotal(defenderRoll);
}
@ -810,12 +819,18 @@ export class RdDCombat {
await this.computeRecul(defenderRoll);
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", {
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) {
console.log(">>>>> MSG RECV", sockmsg);
switch (sockmsg.msg) {
case "msg_encaisser":
return RdDUtility._handleMsgEncaisser(sockmsg.data);
case "msg_gm_chat_message":
return ChatUtility.handleGMChatMessage(sockmsg.data);
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) {
RdDCombat.registerChatCallbacks(html);

View File

@ -114,7 +114,7 @@
<div class="flex-group-left flexcol">
<span><a class="lock-unlock-sheet"><img class="small-button-container"
src="systems/foundryvtt-reve-de-dragon/icons/{{#if data.editCaracComp}}unlocked.svg{{else}}locked.svg{{/if}}" alt="blocker/débloquer"
>{{#if data.editCaracComp}}Vérouiller{{else}}Dévérouiller{{/if}}</a></span>
>{{#if data.editCaracComp}}Verrouiller{{else}}Déverrouiller{{/if}}</a></span>
<ul class="carac-list alterne-list">
{{#each data.carac as |carac key|}}
{{#if carac.isLevelUp}}
@ -214,7 +214,7 @@
<div class="flexrow">
<span><a class="lock-unlock-sheet"><img class="small-button-container"
src="systems/foundryvtt-reve-de-dragon/icons/{{#if data.editCaracComp}}unlocked.svg{{else}}locked.svg{{/if}}" alt="blocker/débloquer"
>{{#if data.editCaracComp}}Vérouiller{{else}}Dévérouiller{{/if}}</a></span>
>{{#if data.editCaracComp}}Verrouiller{{else}}Déverrouiller{{/if}}</a></span>
<span><a id="show-hide-competences"><img class="small-button-container"
src="systems/foundryvtt-reve-de-dragon/icons/{{#if data.showCompNiveauBase}}no-filter.svg{{else}}filter.svg{{/if}}" alt="filter/montrer tout"
>{{#if data.showCompNiveauBase}}Montrer tout{{else}}Filtrer{{/if}}</a></span>

View File

@ -1,5 +1,5 @@
<div data-passearme="{{passeArme}}">
<h4 class="rdd-roll-part"><strong>Réussite particulière en attaque</strong></h4>
<h4 class="rdd-roll-part">{{alias}} réussit une attaque particulière!</strong></h4>
<br>
<a class="chat-card-button" id="particuliere-attaque" data-mode="force" data-attackerId="{{attackerId}}">
Attaquer en Force

View File

@ -5,7 +5,13 @@
<span><strong>{{defender.name}}</strong> doit :</span>
{{else}}
<span><strong>{{defender.name}}</strong> doit se défendre
{{#if (eq surprise 'demi')}} avec une significative {{/if}} :
{{~#if (eq surprise 'demi')}} avec une significative {{/if~}}
{{~#if attaqueParticuliere}} contre une <strong>particulière en
{{~#if (eq attaqueParticuliere 'finesse')}} finesse
{{else if (eq attaqueParticuliere 'force')}} force
{{else}} rapidité
{{/if~}}</strong>
{{/if~}} :
</span>
{{/if}}
<span class='chat-card-button-area'>

View File

@ -21,7 +21,7 @@
{{~#if (eq dmg.mortalite 'non-mortel')}}
<span class="rdd-roll-norm">({{numberFormat dmg.total decimals=0 sign=true}})</span> (dommages non-mortel)
{{else if (eq dmg.mortalite 'mortel')}}
<span class="rdd-roll-echec">{{numberFormat dmg.total decimals=0 sign=true}}</span>.
<span class="rdd-roll-echec">{{numberFormat dmg.total decimals=0 sign=true}}</span>
{{else}}
<span class="rdd-roll-etotal">{{numberFormat dmg.total decimals=0 sign=true}}</span> (entités de cauchemar)
{{~/if}}.