foundryvtt-reve-de-dragon/module/rdd-combat.js
Vincent Vandemeulebrouck 3ac2be74fd #97 particulière pour armes lentes
Pas de particulières en rapidité pour les armes lentes.

Le corps à corps est considéré comme rapide.

Standardise le corps à corps:  centraliser la construction d'un objet
pour le corps à corps

Convertion de compétences de créature en arme

Petit fix sur HUD: le click est sur le div uniquement (au lieu
du label avant, ou d'un mix avec un fix précédent...)
2021-01-04 00:31:10 +01:00

712 lines
28 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ChatUtility } from "./chat-utility.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDRollTables } from "./rdd-rolltables.js";
export class RdDCombat {
/* -------------------------------------------- */
static isActive() {
return true;
}
/* -------------------------------------------- */
static createUsingTarget(attacker) {
const target = RdDCombat.getTarget();
if (target == undefined) {
ui.notifications.warn((game.user.targets && game.user.targets.size > 1)
? "Vous devez choisir <strong>une seule</strong> cible à attaquer!"
: "Vous devez choisir une cible à attaquer!");
}
const defender = target ? target.actor : undefined;
const defenderTokenId = target ? target.data._id : undefined;
return this.create(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static getTarget() {
if (game.user.targets && game.user.targets.size == 1) {
for (let target of game.user.targets) {
return target;
}
}
return undefined;
}
/* -------------------------------------------- */
static create(attacker, defender, defenderTokenId, target = undefined) {
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static createForEvent(event) {
let attackerId = event.currentTarget.attributes['data-attackerId'].value;
let attacker = game.actors.get(attackerId);
const dataDefenderTokenId = event.currentTarget.attributes['data-defenderTokenId'];
if (dataDefenderTokenId) {
const defenderTokenId = dataDefenderTokenId.value;
let defenderToken = canvas.tokens.get(defenderTokenId);
let defender = defenderToken.actor;
return RdDCombat.create(attacker, defender, defenderTokenId);
}
return RdDCombat.createUsingTarget(attacker)
}
/* -------------------------------------------- */
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 ? sender.data._id : undefined,
defenderId: recipient ? recipient.data._id : undefined,
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;
let actor = game.actors.get(actorId);
actor.jetVie();
}
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of ['#parer-button', '#esquiver-button', '#particuliere-attaque', '#encaisser-button']) {
html.on("click", button, event => {
event.preventDefault();
RdDCombat.createForEvent(event).onEvent(button, event);
});
}
html.on("click", '#chat-jet-vie', event => {
event.preventDefault();
RdDCombat._callJetDeVie(event);
} );
}
/* -------------------------------------------- */
constructor(attacker, defender, defenderTokenId, target) {
this.attacker = attacker;
this.defender = defender;
this.target = target;
this.attackerId = this.attacker.data._id;
this.defenderId = this.defender.data._id;
this.defenderTokenId = defenderTokenId;
}
/* -------------------------------------------- */
async onEvent(button, event) {
let rollData = game.system.rdd.rollDataHandler[this.attackerId];
if (!rollData) {
ui.notifications.warn("Action automatisée impossible, le jet de l'attaquant a été perdu (suite à un raffraichissement?)")
return;
}
switch (button) {
case '#particuliere-attaque': return await this.choixParticuliere(rollData, event.currentTarget.attributes['data-mode'].value);
case '#parer-button': {
const armeId = event.currentTarget.attributes['data-armeid'];
return this.parade(rollData, armeId ? armeId.value : undefined);
}
case '#esquiver-button': return this.esquive(rollData);
case '#encaisser-button': return this.encaisser(rollData, event.currentTarget.attributes['data-defenderTokenId'].value);
}
}
/* -------------------------------------------- */
static isEchec(rollData) {
switch (rollData.surprise) {
case 'totale': return true;
}
return rollData.rolled.isEchec;
}
/* -------------------------------------------- */
static isEchecTotal(rollData) {
if (!rollData.attackerRoll && rollData.surprise) {
return rollData.rolled.isEchec;
}
return rollData.rolled.isETotal;
}
/* -------------------------------------------- */
static isParticuliere(rollData) {
if (!rollData.attackerRoll && rollData.surprise) {
return false;
}
return rollData.rolled.isPart;
}
/* -------------------------------------------- */
static isReussite(rollData) {
switch (rollData.surprise) {
case 'totale': return false;
}
return rollData.rolled.isSuccess;
}
/* -------------------------------------------- */
async attaque(competence, arme) {
if (!await this.accorderEntite('avant-attaque')) {
return;
}
let rollData = this._prepareAttaque(competence, arme);
console.log("RdDCombat.attaque >>>", rollData);
const dialog = await RdDRoll.create(this.attacker, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html',
options: { height: 540 }
}, {
name: 'jet-attaque',
label: 'Attaque: ' + (arme ? arme.name : competence.name),
callbacks: [
this.attacker.createCallbackExperience(),
{ condition: r => (RdDCombat.isReussite(r) && !RdDCombat.isParticuliere(r)), action: r => this._onAttaqueNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onAttaqueParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onAttaqueEchec(r) },
{ condition: RdDCombat.isEchecTotal, action: r => this._onAttaqueEchecTotal(r) },
]
});
dialog.render(true);
}
/* -------------------------------------------- */
_prepareAttaque(competence, arme) {
let rollData = {
coupsNonMortels: false,
competence: competence,
surprise: this.attacker.getSurprise(),
surpriseDefenseur: this.defender.getSurprise()
};
if (this.attacker.isCreature()) {
RdDItemCompetence.setRollDataCreature(rollData);
}
else if (arme) {
// Usual competence
rollData.arme = RdDItemArme.armeUneOuDeuxMains(arme, RdDItemCompetence.isArmeUneMain(competence));
}
else {
// sans armes: à mains nues
rollData.arme = RdDItemArme.mainsNues({niveau: competence.data.niveau});
}
return rollData;
}
/* -------------------------------------------- */
_onAttaqueParticuliere(rollData) {
console.log("RdDCombat.onAttaqueParticuliere >>>", rollData);
// Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum
let message = '<h4 class="rdd-roll-part"><strong>Réussite particulière en attaque</strong></h4>';
message += `<br><a class='chat-card-button' id='particuliere-attaque' data-mode='force' data-attackerId='${this.attackerId}'>Attaquer en Force</a>`;
if (rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0) {
if (rollData.arme.data.rapide) {
message += `<br><a class='chat-card-button' id='particuliere-attaque' data-mode='rapidite' data-attackerId='${this.attackerId}'>Attaquer en Rapidité</a>`;
}
message += `<br><a class='chat-card-button' id='particuliere-attaque' data-mode='finesse' data-attackerId='${this.attackerId}'>Attaquer en Finesse</a>`;
}
game.system.rdd.rollDataHandler[this.attackerId] = rollData;
// TODO: use a dialog?
ChatMessage.create({ content: message, whisper: ChatMessage.getWhisperRecipients(this.attacker.name) });
}
/* -------------------------------------------- */
async _onAttaqueNormale(rollData) {
console.log("RdDCombat.onAttaqueNormale >>>", rollData);
rollData.dmg = RdDBonus.dmg(rollData, this.attacker.getBonusDegat(), this.defender.isEntiteCauchemar());
// Save rollData for defender
game.system.rdd.rollDataHandler[this.attackerId] = duplicate(rollData);
rollData.show = {
cible: this.target ? this.defender.data.name : 'la cible',
isRecul: (rollData.particuliere == 'force' || rollData.tactique == 'charge')
}
await RdDResolutionTable.displayRollData(rollData, this.attacker.name, 'chat-resultat-attaque.html');
if (!await this.accorderEntite('avant-defense')) {
return;
}
if (this.target) {
this._sendMessageDefense(rollData);
}
}
/* -------------------------------------------- */
_sendMessageDefense(rollData) {
console.log("RdDCombat._sendMessageDefense", rollData, " / ", this.attacker, this.target, this.attackerId, rollData.competence.data.categorie);
let message = this._buildMessageDefense(rollData);
// encaisser
message += this._buildMessageEncaisser(rollData) + "</span>";
RdDCombat._sendRollMessage(this.attacker, this.defender, this.defenderTokenId, "msg_defense", message, rollData);
}
/* -------------------------------------------- */
_buildMessageDefense(rollData) {
let message = "<strong>" + this.defender.name + "</strong> doit se défendre :<span class='chat-card-button-area'>";
if (this.defender.getSurprise() != 'totale') {
// parades
for (const arme of this._filterArmesParade(this.defender.data.items, rollData.competence, rollData.arme)) {
message += "<br><a class='chat-card-button' id='parer-button' data-attackerId='" + this.attackerId + "' data-defenderTokenId='" + this.defenderTokenId + "' data-armeid='" + arme._id + "'>Parer avec " + arme.name + "</a>";
}
// corps à corps
if (rollData.dmg.mortalite != 'mortel' && this.defender.getCompetence("Corps à corps")) {
message += "<br><a class='chat-card-button' id='parer-button' data-attackerId='" + this.attackerId + "' data-defenderTokenId='" + this.defenderTokenId + "'>Parer à mains nues</a>";
}
// esquive
if (rollData.competence.data.categorie != 'tir') {
message += "<br><a class='chat-card-button' id='esquiver-button' data-attackerId='" + this.attackerId + "' data-defenderTokenId='" + this.defenderTokenId + "'>Esquiver</a>";
}
}
return message;
}
/* -------------------------------------------- */
_buildMessageEncaisser(rollData) {
return "<br><a class='chat-card-button' id='encaisser-button' data-attackerId='" + this.attackerId + "' data-defenderTokenId='" + this.defenderTokenId + "'>Encaisser à " + Misc.toSignedString(rollData.dmg.total) + " !</a>";
}
/* -------------------------------------------- */
_filterArmesParade(items, competence) {
items = items.filter(item => (item.type == 'arme' && item.data.equipe) || (item.type == 'competencecreature' && item.data.isparade));
switch (competence.data.categorie) {
case 'tir':
case 'lancer':
return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
default:
// Le fléau ne peut être paré quau bouclier p115
if (competence.name == "Fléau"){
return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
}
return items.filter(item => RdDItemArme.getCategorieParade(item));
}
}
/* -------------------------------------------- */
async _onAttaqueEchecTotal(rollData) {
// TODO: remplacer par un chat message pour laisser le joueur choisir un appel à la chance _avant_
// https://gitlab.com/LeRatierBretonnien/foundryvtt-reve-de-dragon/-/issues/85
console.log("RdDCombat.onEchecTotal >>>", rollData);
let chatOptions = {
content: "<strong>Echec total à l'attaque!</strong> "
+ await RdDRollTables.getMaladresse({ arme: rollData.arme && rollData.arme.data.categorie_parade != 'sans-armes' })
}
ChatUtility.chatWithRollMode(chatOptions, this.attacker.name)
}
/* -------------------------------------------- */
async _onAttaqueEchec(rollData) {
console.log("RdDCombat.onAttaqueEchec >>>", rollData);
await RdDResolutionTable.displayRollData(rollData, this.attacker.name, 'chat-resultat-attaque.html');
}
/* -------------------------------------------- */
async choixParticuliere(rollData, choix) {
console.log("RdDCombat.choixParticuliere >>>", rollData, choix);
rollData.particuliere = choix;
await this._onAttaqueNormale(rollData);
}
/* -------------------------------------------- */
async parade(attackerRoll, armeParadeId) {
let arme = RdDItemArme.getArmeData(armeParadeId ? this.defender.getOwnedItem(armeParadeId) : null);
console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme);
let rollData = this._prepareParade(attackerRoll, arme);
const dialog = await RdDRoll.create(this.defender, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html',
options: { height: 540 }
}, {
name: 'jet-parade',
label: 'Parade: ' + (arme ? arme.name : rollData.competence.name),
callbacks: [
this.defender.createCallbackExperience(),
{ condition: RdDCombat.isReussite, action: r => this._onParadeNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onParadeParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onParadeEchec(r) },
{ condition: RdDCombat.isEchecTotal, action: r => this._onParadeEchecTotal(r) },
]
});
dialog.render(true);
}
_prepareParade(attackerRoll, armeParade) {
const isCreature = this.defender.isCreature();
const compName = armeParade.data.competence;
const armeAttaque = attackerRoll.arme;
let rollData = {
forceValue: this.defender.getForceValue(),
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
competence: this.defender.getCompetence(compName),
arme: armeParade,
surprise: this.defender.getSurprise(),
needParadeSignificative: this.needParadeSignificative(attackerRoll, armeParade),
diviseur: this._getDiviseurSignificative(attackerRoll, armeParade),
needResist: this._needArmeResist(armeAttaque, armeParade),
carac: this.defender.data.data.carac,
show: {}
};
if (isCreature) {
RdDItemCompetence.setRollDataCreature(rollData);
}
return rollData;
}
/* -------------------------------------------- */
_getDiviseurSignificative(attackerRoll, armeParade = undefined) {
let facteurSign = this.defender.getDiviseurSignificative();
if (RdDCombat.isAttaqueFinesse(attackerRoll)) {
facteurSign *= 2;
}
if (this.needParadeSignificative(attackerRoll, armeParade)) {
facteurSign *= 2;
}
return Math.min(4, facteurSign);
}
static isAttaqueFinesse(attackerRoll) {
return attackerRoll && attackerRoll.particuliere == 'finesse';
}
needParadeSignificative(attackerRoll, armeParade) {
return attackerRoll.arme && armeParade &&
RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade);
}
/* -------------------------------------------- */
_needArmeResist(armeAttaque, armeParade) {
// Epées parant une arme de bois (cf. page 115 ), une résistance est nécessaire
let attCategory = RdDItemArme.getCategorieParade(armeAttaque);
let defCategory = RdDItemArme.getCategorieParade(armeParade);
return attCategory.match(/epees-/) && defCategory.match(/(haches|lances)/);
}
/* -------------------------------------------- */
_onParadeParticuliere(rollData) {
console.log("RdDCombat._onParadeParticuliere >>>", rollData);
if (!rollData.attackerRoll.isPart) {
// TODO: attaquant doit jouer résistance et peut être désarmé p132
ChatUtility.chatWithRollMode({
content: `L'attaquant doit jouer résistance et peut être désarmé (p132)`
}, this.defender.name)
}
}
/* -------------------------------------------- */
async _onParadeNormale(rollData) {
console.log("RdDCombat._onParadeNormale >>>", rollData);
await this.computeRecul(rollData);
await this.computeDeteriorationArme(rollData);
await RdDResolutionTable.displayRollData(rollData, this.defender.name, 'chat-resultat-parade.html');
}
/* -------------------------------------------- */
async _onParadeEchecTotal(rollData) {
// TODO: remplacer par un chat message pour laisser le joueur choisir un appel à la chance _avant_
// https://gitlab.com/LeRatierBretonnien/foundryvtt-reve-de-dragon/-/issues/85
console.log("RdDCombat._onParadeEchecTotal >>>", rollData);
let chatOptions = {
content: "<strong>Echec total à la parade!</strong> "
+ await RdDRollTables.getMaladresse({ arme: rollData.arme && rollData.arme.data.categorie_parade != 'sans-armes' })
}
ChatUtility.chatWithRollMode(chatOptions, this.defender.name)
}
/* -------------------------------------------- */
async _onParadeEchec(rollData) {
console.log("RdDCombat._onParadeEchec >>>", rollData);
await this.computeRecul(rollData);
await RdDResolutionTable.displayRollData(rollData, this.defender.name, 'chat-resultat-parade.html');
this._sendMessageEncaisser(rollData.attackerRoll);
}
/* -------------------------------------------- */
async esquive(attackerRoll) {
let esquive = this.defender.getCompetence("esquive");
if (esquive == undefined) {
ui.notifications.error(this.defender.name + " n'a pas de compétence 'esquive'");
return;
}
console.log("RdDCombat.esquive >>>", attackerRoll, esquive);
let rollData = this._prepareEsquive(attackerRoll, esquive);
const dialog = await RdDRoll.create(this.defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html' }, {
name: 'jet-esquive',
label: 'Esquiver',
callbacks: [
this.defender.createCallbackExperience(),
{ condition: RdDCombat.isReussite, action: r => this._onEsquiveNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onEsquiveParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onEsquiveEchec(r) },
{ condition: RdDCombat.isEchecTotal, action: r => this._onEsquiveEchecTotal(r) },
]
});
dialog.render(true);
}
/* -------------------------------------------- */
_prepareEsquive(attackerRoll, competence) {
let rollData = {
forceValue: this.defender.getForceValue(),
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
competence: competence,
surprise: this.defender.getSurprise(),
surpriseDefenseur: this.defender.getSurprise(),
diviseur: this._getDiviseurSignificative(attackerRoll),
carac: this.defender.data.data.carac,
show: {}
};
if (this.defender.isCreature()) {
RdDItemCompetence.setRollDataCreature(rollData);
}
return rollData;
}
/* -------------------------------------------- */
_onEsquiveParticuliere(rollData) {
console.log("RdDCombat._onEsquiveParticuliere >>>", rollData);
let chatOptions = {
content: "<strong>Vous pouvez esquiver une deuxième esquive!</strong>"
}
ChatUtility.chatWithRollMode(chatOptions, this.defender.name)
}
/* -------------------------------------------- */
async _onEsquiveNormale(rollData) {
console.log("RdDCombat._onEsquiveNormal >>>", rollData);
await RdDResolutionTable.displayRollData(rollData, this.defender.name, 'chat-resultat-esquive.html');
}
/* -------------------------------------------- */
async _onEsquiveEchecTotal(rollData) {
// TODO: remplacer par un chat message pour laisser le joueur choisir un appel à la chance _avant_
// https://gitlab.com/LeRatierBretonnien/foundryvtt-reve-de-dragon/-/issues/85
console.log("RdDCombat._onEsquiveEchecTotal >>>", rollData);
let chatOptions = {
content: "<strong>Echec total à l'esquive'!</strong> "
+ await RdDRollTables.getMaladresse({ arme: false })
}
ChatUtility.chatWithRollMode(chatOptions, this.defender.name)
}
/* -------------------------------------------- */
async _onEsquiveEchec(rollData) {
console.log("RdDCombat._onEsquiveEchec >>>", rollData);
await this.computeRecul(rollData);
await RdDResolutionTable.displayRollData(rollData, this.defender.name, 'chat-resultat-esquive.html');
this._sendMessageEncaisser(rollData.attackerRoll);
}
/* -------------------------------------------- */
async computeDeteriorationArme(rollData) {
const attackerRoll = rollData.attackerRoll;
// Est-ce une parade normale?
if (rollData.arme && attackerRoll && !rollData.rolled.isPart) {
// Est-ce que l'attaque est une particulière en force ou une charge
if (rollData.needResist || attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge') {
rollData.show = rollData.show || {}
const dmg = attackerRoll.dmg.dmgArme + attackerRoll.dmg.dmgActor;
let resistance = Misc.toInt(rollData.arme.data.resistance);
let msg = "";
// Jet de résistance de l'arme de parade (p.132)
let resistRoll = await RdDResolutionTable.rollData({
caracValue: resistance,
finalLevel: - dmg,
showDice: false
});
if (resistRoll.rolled.isSuccess) { // Perte de résistance
rollData.show.deteriorationArme = 'resiste';
} else {
resistance -= dmg;
if (resistance <= 0) {
this.defender.deleteEmbeddedEntity("OwnedItem", rollData.arme._id);
rollData.show.deteriorationArme = 'brise';
} else {
this.defender.updateEmbeddedEntity("OwnedItem", { _id: rollData.arme._id, 'data.resistance': resistance });
rollData.show.deteriorationArme = 'perte';
rollData.show.perteResistance = dmg;
}
}
// Si l'arme de parade n'est pas un bouclier, jet de désarmement (p.132)
if (resistance > 0 && !RdDItemArme.getCategorieParade(rollData.arme) == 'boucliers') {
let desarme = await RdDResolutionTable.rollData({
caracValue: this.defender.data.data.carac.force.value,
finalLevel: Misc.toInt(rollData.competence.data.niveau) - dmg,
showDice: false
});
rollData.show.desarme = desarme.rolled.isEchec;
if (desarme.rolled.isEchec) {
rollData.show.desarme = true;
}
}
}
}
}
/* -------------------------------------------- */
async computeRecul(rollData) { // Calcul du recul (p. 132)
const attaque = rollData.attackerRoll;
if (this._isAttaqueCauseRecul(attaque)) {
let impactRecul = this._computeImpactRecul(attaque);
const agilite = this.defender.data.data.carac.agilite.value;
let rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impactRecul, showDice: false });
if (rollRecul.isSuccess) {
rollData.show.recul = 'encaisse';
} else if (rollRecul.isETotal) {
rollData.show.recul = 'chute';
}
else {
let chute = await RdDResolutionTable.rollData({ caracValue: agilite, finalLevel: impactRecul, showDice: false });
rollData.show.recul = (chute.isSuccess)
? 'recul'
: 'chute';
}
}
}
_isAttaqueCauseRecul(attaque) {
return attaque.particuliere == 'force' || attaque.tactique == 'charge';
}
_computeImpactRecul(attaque) {
return Misc.toInt(this.defender.data.data.carac.taille.value) - (attaque.forceValue + attaque.arme.data.dommagesReels);
}
/* -------------------------------------------- */
_sendMessageEncaisser(rollData) {
let message = "<strong>" + this.defender.name + "</strong> doit:" + this._buildMessageEncaisser(rollData);
RdDCombat._sendRollMessage(this.attacker, this.defender, this.defenderTokenId, "msg_encaisser", message, rollData);
}
/* -------------------------------------------- */
encaisser(attackerRoll, defenderTokenId) {
defenderTokenId = defenderTokenId || this.defenderTokenId;
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
if (game.user.isGM) { // Current user is the GM -> direct access
attackerRoll.attackerId = this.attackerId;
attackerRoll.defenderTokenId = defenderTokenId;
this.defender.encaisserDommages(attackerRoll, this.attacker);
} else { // Emit message for GM
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_encaisser",
data: { attackerId: this.attackerId, defenderTokenId: defenderTokenId }
});
}
}
/* -------------------------------------------- */
/* retourne true si on peut continuer, false si on ne peut pas continuer */
async accorderEntite(when = 'avant-encaissement') {
if (when != game.settings.get("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar")
|| this.defender == undefined
|| !this.defender.isEntiteCauchemar()
|| this.defender.isEntiteCauchemarAccordee(this.attacker)) {
return true;
}
let rolled = await RdDResolutionTable.roll(this.attacker.getReveActuel(), - Number(this.defender.data.data.carac.niveau.value));
let message = {
content: "Jet de points actuels de rêve à " + rolled.finalLevel + RdDResolutionTable.explain(rolled) + "<br>",
whisper: ChatMessage.getWhisperRecipients(this.attacker.name)
};
if (rolled.isSuccess) {
await this.defender.setEntiteReveAccordee(this.attacker);
message.content += this.attacker.name + " s'est accordé avec " + this.defender.name;
}
else {
message.content += this.attacker.name + " n'est pas accordé avec " + this.defender.name;
}
ChatMessage.create(message);
return rolled.isSuccess;
}
/* -------------------------------------------- */
static async displayActorCombatStatus( actor ) {
let rollMode = game.settings.get("core", "rollMode");
let rollData = {
alias: actor.name,
etatGeneral: actor.getEtatGeneral(),
isSonne: actor.getSonne(),
blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(),
actorId: actor.data._id,
isGrave: false,
isCritique: false
}
if ( actor.countBlessuresByName("critiques") > 0 ) { // Pour éviter le cumul grave + critique
rollData.isCritique = true;
} else if ( actor.countBlessuresByName("graves") > 0) {
rollData.isGrave = true;
}
let content = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html`, rollData);
ChatUtility.createChatMessage({ content: content }, rollMode, actor.name);
}
/* -------------------------------------------- */
static updateCombatRound( combat, data) {
if (combat.data.round != 0 && combat.turns && combat.data.active) {
let turn = combat.turns.find(t => t.tokenId == combat.current.tokenId);
this.displayActorCombatStatus( turn.actor );
// TODO Playaudio ??
}
}
}