Compare commits

...

9 Commits

Author SHA1 Message Date
d1b1b2a341 Portrait dans les feuilles simplifiées 2024-10-26 23:10:45 +02:00
e05753fc0b Fix: niveau de compétence non défini
- quand le niveau de compétence n'était pas défini, la feuille de
personnage simplifiée et l'export scriptarium ne marchaient pas
- on pouvait saisitr une compétence avec un niveau non défini
2024-10-26 23:08:13 +02:00
3de6179e9b Move to v12.0.17 2024-10-26 23:08:13 +02:00
a7e7eec261 Utilisation du nom du Nom du token
suite (pour les encaissements)
2024-10-26 01:53:38 +02:00
5c78ecb64e Localisation des blessures en regle optionnelle 2024-10-26 01:33:20 +02:00
c150dee6de Amélioration message encaissement 2024-10-26 01:20:03 +02:00
c8afb6e98a Afficher le nom du token au lieu du nom d'Acteur
Dans les messages d'automatisation de combat, le nom des
tokens est utilisé au lieu d'utiliser le nom de l'acteur.

Ceci permet de ne pas dévoiler un nom générique (Villageois)
si le token a un nom personnalisé.
2024-10-26 00:10:47 +02:00
2429f5b20a Version 12.0.16 2024-10-23 23:51:19 +02:00
4eabb58a89 Fix: Message uniquement MJ
L'utilisation de firstConnectedGM renvoie le GM, pas true si
un GM est connecté.
2024-10-23 23:44:49 +02:00
28 changed files with 243 additions and 168 deletions

View File

@ -1,4 +1,12 @@
# 12.0
## 12.0.18 - A la barbe d'Astrobazzarh
- Ajout du portrait sur les feuilles simplifiées
## 12.0.16 - Le secret d'Astrobazzarh
- Fix: les jets envoyés messages uniquement au MJ ne sont plus envoyés à tous les autres joueurs (et dupliqués)
- Les noms affichés dans les automatisations de combat sont maintenant ceux des tokens plutôt que ceux des acteurs
- Ajout d'une option pour la localisation des blessures
## 12.0.15 - Le messager d'Astrobazzarh
- Correction des faces de dés personalisés dice-so-nice
- Les messages de maladies ne sont plus publics

View File

@ -220,18 +220,18 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
// Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', true))
this.html.find('.empoignade-label a').click(async event => RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor)))
this.html.find('.roll-arme').click(async event => this.actor.rollArme(foundry.utils.duplicate(this._getEventArmeCombat(event))))
this.html.find('.roll-arme').click(async event => this.actor.rollArme(foundry.utils.duplicate(this._getEventArmeCombat(event)), 'competence'))
// Initiative pour l'arme
this.html.find('.arme-initiative a').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id);
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id)
if (combatant) {
let action = this._getEventArmeCombat(event);
RdDCombatManager.rollInitiativeAction(combatant._id, action);
RdDCombatManager.rollInitiativeAction(combatant._id, this._getEventArmeCombat(event));
} else {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
}
});
})
// Display TMR
this.html.find('.button-tmr').click(async event => this.actor.displayTMR("normal"))

View File

@ -1548,7 +1548,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') {
if (!Misc.firstConnectedGM()){
if (!Misc.isFirstConnectedGM()){
return
}
hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
@ -3011,7 +3011,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async onCreateOwnedDraconique(item, options, id) {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item));
if (draconique) {
await draconique.onActorCreateOwned(this, item)
@ -3023,7 +3023,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async onDeleteOwnedDraconique(item, options, id) {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item));
if (draconique) {
await draconique.onActorDeleteOwned(this, item)
@ -3033,7 +3033,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async onDeleteOwnedCaseTmr(item, options, id) {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.isCase(item));
if (draconique) {
await draconique.onActorDeleteCaseTmr(this, item)

View File

@ -327,14 +327,15 @@ export class RdDBaseActorReve extends RdDBaseActor {
const competence = this.getCompetence(idOrName);
let rollData = { carac: this.system.carac, competence: competence, arme: options.arme }
if (competence.type == ITEM_TYPES.competencecreature) {
const token = RdDUtility.getSelectedToken(this)
const arme = RdDItemCompetenceCreature.armeCreature(competence)
if (arme && options.tryTarget && Targets.hasTargets()) {
Targets.selectOneToken(target => {
Targets.selectOneTargetToken(target => {
if (arme.action == "possession") {
RdDPossession.onAttaquePossession(target, this, competence)
}
else {
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
}
});
return;
@ -364,7 +365,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
* @returns
*/
rollArme(arme, categorieArme = "competence") {
rollArme(arme, categorieArme, token) {
token = token ?? RdDUtility.getSelectedToken(this)
const compToUse = this.$getCompetenceArme(arme, categorieArme)
if (!RdDItemArme.isArmeUtilisable(arme)) {
ui.notifications.warn(`Arme inutilisable: ${arme.name} a une résistance de 0 ou moins`)
@ -385,7 +387,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
return
}
Targets.selectOneToken(target => {
Targets.selectOneTargetToken(target => {
if (Targets.isTargetEntite(target)) {
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
return
@ -395,7 +397,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
if (competence.isCompetencePossession()) {
return RdDPossession.onAttaquePossession(target, this, competence);
}
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme);
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme);
})
}
@ -413,48 +415,47 @@ export class RdDBaseActorReve extends RdDBaseActor {
/* -------------------------------------------- */
async encaisser() { await RdDEncaisser.encaisser(this) }
async encaisserDommages(rollData, attacker = undefined, show = undefined) {
async encaisserDommages(rollData, attacker = undefined, show = undefined, attackerToken = undefined, defenderToken = undefined) {
if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
return;
}
const armure = await this.computeArmure(rollData);
if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
await this.encaisserDommagesValidationGR(rollData, armure, attacker?.id, show);
await this.encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken);
}
else {
const jet = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
await this.$onEncaissement(jet, show, attacker);
const jet = await RdDUtility.jetEncaissement(this, rollData, armure, { showDice: SHOW_DICE });
await this.$onEncaissement(jet, show, attackerToken, defenderToken)
}
}
async encaisserDommagesValidationGR(rollData, armure, attackerId, show) {
async encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken) {
if (!game.user.isGM) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'encaisserDommagesValidationGR',
args: [rollData, armure, attackerId, show]
});
args: [rollData, armure, show, attackerToken, defenderToken]
})
} else {
const attacker = game.actors.get(attackerId);
DialogValidationEncaissement.validerEncaissement(this, rollData, armure,
jet => this.$onEncaissement(jet, show, attacker));
jet => this.$onEncaissement(jet, show, attackerToken, defenderToken));
}
}
async $onEncaissement(jet, show, attacker) {
await this.onAppliquerJetEncaissement(jet, attacker);
await this.$afficherEncaissement(jet, show);
async $onEncaissement(jet, show, attackerToken, defenderToken) {
await this.onAppliquerJetEncaissement(jet, attackerToken);
await this.$afficherEncaissement(jet, show, defenderToken);
}
async onAppliquerJetEncaissement(encaissement, attacker) { }
async onAppliquerJetEncaissement(encaissement, attackerToken) { }
async $afficherEncaissement(encaissement, show) {
async $afficherEncaissement(encaissement, show, defenderToken) {
foundry.utils.mergeObject(encaissement, {
alias: this.name,
alias: defenderToken?.name ?? this.name,
hasPlayerOwner: this.hasPlayerOwner,
show: show ?? {}
});
}, {overwrite: false});
await ChatUtility.createChatWithRollMode(
{

View File

@ -89,9 +89,9 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */
async onAppliquerJetEncaissement(encaissement, attacker) {
async onAppliquerJetEncaissement(encaissement, attackerToken) {
const santeOrig = foundry.utils.duplicate(this.system.sante);
const blessure = await this.ajouterBlessure(encaissement, attacker); // Will update the result table
const blessure = await this.ajouterBlessure(encaissement, attackerToken); // Will update the result table
const perteVie = await this.santeIncDec("vie", -encaissement.vie);
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique());
@ -169,7 +169,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */
async ajouterBlessure(encaissement, attacker = undefined) {
async ajouterBlessure(encaissement, attackerToken = undefined) {
if (encaissement.gravite < 0) return;
if (encaissement.gravite > 0) {
while (this.countBlessures(it => it.system.gravite == encaissement.gravite) >= RdDItemBlessure.maxBlessures(encaissement.gravite) && encaissement.gravite <= 6) {
@ -181,7 +181,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
}
}
const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attacker);
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken);
if (blessure.isCritique()) {
encaissement.endurance = endActuelle;
}

View File

@ -218,7 +218,7 @@ export class RdDBaseActor extends Actor {
}
async creerObjetParMJ(object) {
if (!Misc.isUniqueConnectedGM()) {
if (!Misc.isFirstConnectedGM()) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
@ -335,7 +335,7 @@ export class RdDBaseActor extends Actor {
ui.notifications.info("Inutile de se vendre à soi-même");
return;
}
if (!Misc.isUniqueConnectedGM()) {
if (!Misc.isFirstConnectedGM()) {
RdDBaseActor.remoteActorCall({
actorId: achat.vendeurId ?? achat.acheteurId,
method: 'achatVente',

View File

@ -74,7 +74,7 @@ export class RdDEntite extends RdDBaseActorReve {
return [STATUSES.StatusComma].includes(effectId);
}
async onAppliquerJetEncaissement(encaissement, attacker) {
async onAppliquerJetEncaissement(encaissement, attackerToken) {
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
foundry.utils.mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue,

View File

@ -327,7 +327,8 @@ export class Mapping {
const txtByCategories = Object.values(byCategories)
.map(it => it.competencesParNiveau)
.map(byNiveau => {
const niveaux = Object.keys(byNiveau).map(it => Number(it)).sort(Misc.ascending())
const niveaux = Object.keys(byNiveau)
.map(it => Number(it)).sort(Misc.ascending())
if (niveaux.length == 0) {
return ''
}

View File

@ -49,7 +49,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static onRemoveMessages(socketData) {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
if (socketData.part) {
const toDelete = game.messages.filter(it => it.content.includes(socketData.part));
toDelete.forEach(it => it.delete());
@ -63,7 +63,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static removeMessages(socketData) {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
ChatUtility.onRemoveMessages(socketData);
}
else {
@ -161,7 +161,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static handleGMChatMessage(socketData) {
console.log("blindMessageToGM", socketData);
if (Misc.firstConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
ChatMessage.create({
user: game.user.id,
whisper: ChatUtility.getGMs(),

View File

@ -8,14 +8,13 @@ import { RdDUtility } from "./rdd-utility.js";
export class DialogValidationEncaissement extends Dialog {
static async validerEncaissement(actor, rollData, armure, onEncaisser) {
let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: HIDE_DICE });
const encaissement = await RdDUtility.jetEncaissement(actor, rollData, armure, { showDice: HIDE_DICE });
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.html', {
actor: actor,
rollData: rollData,
encaissement: encaissement
});
const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser);
dialog.render(true);
new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser).render(true);
}
/* -------------------------------------------- */
@ -56,14 +55,14 @@ export class DialogValidationEncaissement extends Dialog {
this.html = html;
this.html.find('input.encaissement-roll-result').keyup(async event => {
this.forceDiceResult.total = event.currentTarget.value;
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: HIDE_DICE, forceDiceResult: this.forceDiceResult});
this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.rollData, this.armure, { showDice: HIDE_DICE, forceDiceResult: this.forceDiceResult});
this.html.find('label.encaissement-total').text(this.encaissement.total);
this.html.find('label.encaissement-blessure').text(this.encaissement.blessures)
});
}
async onValider() {
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
this.onEncaisser(this.encaissement)
}
}

View File

@ -190,7 +190,7 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static isNiveauBase(item) {
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.type);
return item.system.niveau == undefined || Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.type);
}
/* -------------------------------------------- */

View File

@ -267,10 +267,17 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
if (this.item.type == 'sort') {
// Données de bonus de cases ?
formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue);
switch (this.item.type) {
case ITEM_TYPES.sort:
// Données de bonus de cases ?
formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue)
break
case ITEM_TYPES.competence:
if (formData['system.niveau'] == undefined) {
formData['system.niveau'] = formData['system.base']
}
}
return this.item.update(formData);
}

View File

@ -195,7 +195,7 @@ export class Misc {
return document
}
}
else if (Misc.isUniqueConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) {
else if (Misc.isFirstConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) {
return document
}
return undefined
@ -206,14 +206,14 @@ export class Misc {
}
static isOwnerPlayerOrUniqueConnectedGM(actor) {
return Misc.isOwnerPlayer(actor) ?? Misc.isUniqueConnectedGM();
return Misc.isOwnerPlayer(actor) ?? Misc.isFirstConnectedGM();
}
/**
* @returns true pour un seul utilisateur: le premier GM connecté par ordre d'id
*/
static isUniqueConnectedGM() {
return game.user.id == Misc.firstConnectedGMId();
static isFirstConnectedGM() {
return game.user == Misc.firstConnectedGM();
}
static firstConnectedGMId() {

View File

@ -53,7 +53,7 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
async onPreDeleteCombat() {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
await this.finDeRound({ terminer: true })
ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
@ -291,7 +291,7 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
static processPremierRoundInit() {
// Check if we have the whole init !
if (Misc.isUniqueConnectedGM() && game.combat.current.round == 1) {
if (Misc.isFirstConnectedGM() && game.combat.current.round == 1) {
let initMissing = game.combat.combatants.find(it => !it.initiative);
if (!initMissing) { // Premier round !
for (let combatant of game.combat.combatants) {
@ -454,10 +454,10 @@ export class RdDCombat {
/* -------------------------------------------- */
static combatNouveauTour(combat) {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId);
if (turn?.actor) {
RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token.id);
RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token);
// TODO Playaudio for player??
}
}
@ -469,52 +469,51 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static rddCombatTarget(target, attacker) {
const defender = target?.actor;
const defenderTokenId = target?.id;
return new RdDCombat(attacker, defender, defenderTokenId, target)
static rddCombatTarget(target, attacker, attackerToken) {
return new RdDCombat(attacker, attackerToken?.id, target?.actor, target?.id, target)
}
/* -------------------------------------------- */
static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) {
const attacker = game.actors.get(attackerId);
let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined;
static rddCombatForAttackerAndDefender(attackerId, attackerTokenId, defenderTokenId) {
const attacker = game.actors.get(attackerId)
const defenderToken = defenderTokenId ? canvas.tokens.get(defenderTokenId) : undefined
let defender = defenderToken?.actor;
let target = undefined
if (!defenderTokenId || !defender) {
console.warn(`RdDCombat.rddCombatForAttackerAndDefender: appel avec defenderTokenId ${defenderTokenId} incorrect, ou pas de defender correspondant`);
target = Targets.getTarget()
if (!target) {
return;
return
}
defenderTokenId = target.id;
defender = target.actor;
if (!defenderTokenId || !defender) {
return;
return
}
}
return new RdDCombat(attacker, defender, defenderTokenId, target)
return new RdDCombat(attacker, attackerTokenId, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static onMsgEncaisser(msg) {
let defender = canvas.tokens.get(msg.defenderTokenId).actor;
if (Misc.isOwnerPlayerOrUniqueConnectedGM()) {
let attackerRoll = msg.attackerRoll;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
let defender = canvas.tokens.get(msg.defenderToken.id).actor;
defender.encaisserDommages(attackerRoll, attacker);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
defender.encaisserDommages(attackerRoll, attacker, msg.attackerToken);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
}
/* -------------------------------------------- */
static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderTokenId);
if (defenderToken && Misc.isUniqueConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme);
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
let defenderToken = canvas.tokens.get(msg.defenderToken.id)
if (defenderToken && Misc.isFirstConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id)
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme)
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
}
}
@ -545,6 +544,7 @@ export class RdDCombat {
html.on("click", button, event => {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-attackerTokenId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) {
rddCombat.onEvent(button, event);
@ -560,22 +560,38 @@ export class RdDCombat {
}
/* -------------------------------------------- */
constructor(attacker, defender, defenderTokenId, target) {
constructor(attacker, attackerTokenId, defender, defenderTokenId, target) {
this.attacker = attacker
this.defender = defender
this.target = target
this.attackerId = this.attacker.id
this.defenderId = this.defender.id
this.attackerTokenId = attackerTokenId
this.defenderTokenId = defenderTokenId
this.attackerToken = RdDCombat.$extractAttackerTokenData(attacker, attackerTokenId)
this.defenderToken = RdDCombat.$extractDefenderTokenData(defender, defenderTokenId, target)
}
static $extractAttackerTokenData(attacker, attackerTokenId) {
const token = canvas.tokens.get(attackerTokenId);
return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(attackerTokenId, attacker)
}
static $extractDefenderTokenData(defender, defenderTokenId, target) {
if (target) {
return Targets.extractTokenData(target)
}
const token = canvas.tokens.get(defenderTokenId);
return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(defenderTokenId, defender)
}
/* -------------------------------------------- */
async onEvent(button, event) {
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;
const competence = event.currentTarget.attributes['data-competence']?.value;
@ -585,7 +601,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, defenderRoll, defenderTokenId);
case '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll);
case '#echec-total-attaque': return this._onEchecTotal(attackerRoll);
case '#appel-chance-attaque': return this.attacker.rollAppelChance(
@ -702,7 +718,7 @@ export class RdDCombat {
})
}
else {
const defenderToken = canvas.tokens.get(this.defenderTokenId);
const defenderToken = canvas.tokens.get(this.defenderTokenId)
const dist = this.distance(_token, defenderToken)
const isVisible = this.isVisible(_token, defenderToken)
const portee = this._ajustementPortee(dist, rollData.arme)
@ -769,7 +785,7 @@ export class RdDCombat {
}
RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
let rollData = this._prepareAttaque(competence, arme);
let rollData = this._prepareAttaque(competence, arme)
console.log("RdDCombat.attaque >>>", rollData);
if (arme) {
this.attacker.verifierForceMin(arme);
@ -795,15 +811,18 @@ export class RdDCombat {
dialog.render(true);
}
/* -------------------------------------------- */
_prepareAttaque(competence, arme) {
let rollData = {
alias: this.attackerToken.name,
passeArme: foundry.utils.randomID(16),
mortalite: arme?.system.mortalite,
competence: competence,
surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
targetToken: Targets.extractTokenData(this.target),
sourceToken: this.attackerToken,
targetToken: this.defenderToken,
essais: {}
};
@ -848,9 +867,10 @@ export class RdDCombat {
alias: this.attacker.name,
whisper: ChatUtility.getOwners(this.attacker),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html', {
alias: this.attacker.name,
alias: this.attackerToken.name,
attackerId: this.attackerId,
defenderTokenId: this.defenderTokenId,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
isForce: isForce,
isFinesse: isFinesse,
isRapide: isRapide,
@ -867,7 +887,7 @@ export class RdDCombat {
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite());
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
attackerRoll.show = {
cible: this.target ? this.defender.name : 'la cible',
cible: this.defenderToken?.name ?? 'la cible',
isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
}
await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
@ -908,7 +928,8 @@ export class RdDCombat {
attacker: this.attacker,
attackerId: this.attackerId,
esquives: esquives,
defenderTokenId: this.defenderTokenId,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
mainsNues: attackerRoll.dmg.mortalite != 'mortel' && corpsACorps,
armes: this._filterArmesParade(this.defender, attackerRoll.competence, attackerRoll.arme),
diffLibre: attackerRoll.ajustements?.diffLibre?.value ?? 0,
@ -919,7 +940,7 @@ export class RdDCombat {
dmg: attackerRoll.dmg,
};
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
await this._chatMessageDefense(paramChatDefense, defenderRoll);
}
else {
@ -932,7 +953,7 @@ export class RdDCombat {
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,
alias: this.attackerToken.name,
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense),
});
@ -946,8 +967,9 @@ export class RdDCombat {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_defense", data: {
attackerId: this.attacker?.id,
attackerToken: this.attackerToken,
defenderId: this.defender?.id,
defenderTokenId: this.defenderTokenId,
defenderToken: this.defenderToken,
defenderRoll: defenderRoll,
paramChatDefense: paramChatDefense,
rollMode: true
@ -980,7 +1002,8 @@ export class RdDCombat {
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
attackerId: this.attackerId,
attacker: this.attacker,
defenderTokenId: this.defenderTokenId,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
essais: attackerRoll.essais
})
});
@ -1052,8 +1075,11 @@ export class RdDCombat {
/* -------------------------------------------- */
_prepareParade(attackerRoll, armeParade, competenceParade) {
let defenderRoll = {
alias: this.defenderToken?.name,
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
attackerRoll: attackerRoll,
competence: this.defender.getCompetence(competenceParade),
arme: armeParade,
@ -1133,8 +1159,11 @@ export class RdDCombat {
/* -------------------------------------------- */
_prepareEsquive(attackerRoll, competence) {
let rollData = {
alias: this.defenderToken?.name,
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
attackerRoll: attackerRoll,
competence: competence,
surprise: this.defender.getSurprise(true),
@ -1276,9 +1305,8 @@ export class RdDCombat {
}
/* -------------------------------------------- */
async encaisser(attackerRoll, defenderRoll, defenderTokenId) {
defenderTokenId = defenderTokenId || this.defenderTokenId;
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
async encaisser(attackerRoll, defenderRoll) {
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderRoll);
if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) {
this._onEchecTotal(defenderRoll);
@ -1286,18 +1314,19 @@ export class RdDCombat {
if (Misc.isOwnerPlayerOrUniqueConnectedGM(this.defender)) {
attackerRoll.attackerId = this.attackerId;
attackerRoll.defenderTokenId = defenderTokenId;
attackerRoll.defenderTokenId = this.defenderToken.id;
await this.computeRecul(defenderRoll);
await this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show);
await this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show, this.attackerToken, this.defenderToken);
}
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_SOCKET_ID, {
msg: "msg_encaisser",
data: {
attackerId: this.attackerId,
defenderTokenId: defenderTokenId,
attackerRoll: attackerRoll
attackerRoll: attackerRoll,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken
}
});
}
@ -1305,28 +1334,28 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static async displayActorCombatStatus(combat, actor, tokenId) {
static async displayActorCombatStatus(combat, actor, token) {
let formData = {
combatId: combat._id,
alias: actor.name,
alias: token.name ?? actor.name,
etatGeneral: actor.getEtatGeneral(),
isSonne: actor.getSonne(),
blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(),
actorId: actor.id,
actor: actor,
tokenId: tokenId,
tokenId: token.id,
isGrave: actor.countBlessures(it => it.isGrave()) > 0,
isCritique: actor.countBlessures(it => it.isCritique()) > 0
}
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs`, formData),
alias: actor.name
alias: token.name ?? actor.name
})
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData),
whisper: ChatUtility.getOwners(actor),
alias: actor.name
alias: token.name ?? actor.name
});
}
}

View File

@ -278,7 +278,7 @@ export class SystemReveDeDragon {
/* Foundry VTT Initialization */
/* -------------------------------------------- */
game.system.rdd.calendrier = new RdDCalendrier()
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
new Migrations().migrate()
this.messageDeBienvenue()
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {

View File

@ -72,6 +72,6 @@ export class RdDEncaisser extends Dialog {
encaisserSpecial: this.encaisserSpecial,
mortalite: mortalite
}
});
})
}
}

View File

@ -37,7 +37,7 @@ export class RdDTokenHud {
// initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions);
// combat
await RdDTokenHud.addExtensionHudCombat(html, combatant, actions);
await RdDTokenHud.addExtensionHudCombat(html, combatant, token, actions);
}
@ -68,8 +68,8 @@ export class RdDTokenHud {
});
}
static async addExtensionHudCombat(html, combatant, actions) {
const hudData = { combatant, actions, commandes: [] };
static async addExtensionHudCombat(html, combatant, token, actions) {
const hudData = { combatant, token, actions, commandes: [] };
const controlIconTarget = html.find('.control-icon[data-action=target]');
await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData,
(event) => {
@ -80,7 +80,7 @@ export class RdDTokenHud {
combatant.actor.conjurerPossession(possession);
}
else {
combatant.actor.rollArme(action);
combatant.actor.rollArme(action, 'competence', token)
}
});
}

View File

@ -563,14 +563,14 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async jetEncaissement(rollData, armure, options = { showDice: HIDE_DICE }) {
static async jetEncaissement(actor, rollData, armure, options = { showDice: HIDE_DICE }) {
const diff = Math.abs(rollData.diffLibre);
let formula = RdDUtility.formuleEncaissement(diff, options)
const roll = await RdDDice.roll(formula, options);
RdDUtility.remplaceDeMinParDifficulte(roll, diff, options);
return await RdDUtility.prepareEncaissement(rollData, roll, armure);
return await RdDUtility.prepareEncaissement(actor, rollData, roll, armure);
}
static remplaceDeMinParDifficulte(roll, diff, options) {
@ -602,15 +602,20 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async prepareEncaissement(rollData, roll, armure) {
static async prepareEncaissement(actor, rollData, roll, armure) {
// La difficulté d'ataque s'ajoute aux dégâts
const bonusDegatsDiffLibre = ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(rollData.diffLibre ?? 0) : 0
const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre
const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite);
const over20 = Math.max(jetTotal - 20, 0);
encaissement.dmg = rollData.dmg;
encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(this.type);
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;';
encaissement.dmg = rollData.dmg
if (ReglesOptionnelles.isUsing('localisation-aleatoire')){
encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(actor.type)
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'
}
else{
encaissement.dmg.loc = {label:''}
}
encaissement.dmg.bonusDegatsDiffLibre = bonusDegatsDiffLibre
encaissement.roll = roll;
encaissement.armure = armure;
@ -741,6 +746,15 @@ export class RdDUtility {
return undefined;
}
static getSelectedToken(actor) {
if (canvas.tokens.controlled.length > 0) {
const tokens = canvas.tokens.controlled
.filter(it => it.actor.id == actor.id)
return tokens[0]
}
return undefined
}
static getSelectedActor(msgPlayer = undefined) {
if (canvas.tokens.controlled.length == 1) {
let token = canvas.tokens.controlled[0];

View File

@ -11,6 +11,7 @@ const listeReglesOptionnelles = [
{ group: 'Récupération', name: 'recuperation-moral', descr: "Le moral revient vers 0 durant Château Dormant"},
{ group: 'Règles de combat', name: 'localisation-aleatoire', descr: "Proposer une localisation aléatoire des blessures" },
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
{ group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },

View File

@ -186,7 +186,7 @@ export class AppAstrologie extends Application {
date: this.html.find('[name="joursAstrologie"]').val(),
userId: game.user.id
}
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
game.system.rdd.calendrier.requestNombreAstral(socketData);
} else {
game.socket.emit(SYSTEM_SOCKET_ID, {

View File

@ -13,18 +13,21 @@ export class Targets {
static extractTokenData(target) {
return { id: target?.id, name: target?.document.name, img: target?.document.texture.src ?? target?.actor.img ?? 'icons/svg/mystery-man.svg' };
}
static buildActorTokenData(tokenId, actor) {
return { id: tokenId, name: actor.name, img: actor.img ?? 'icons/svg/mystery-man.svg' };
}
static isTargetEntite(target) {
return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE;
}
static async selectOneToken(onSelectTarget = target => { }) {
const targets = Targets.listTargets();
static async selectOneTargetToken(onSelectTarget = target => { }) {
const targets = Targets.listTargets()
switch (targets.length) {
case 0: return;
case 0:
return
case 1:
onSelectTarget(targets[0]);
return;
onSelectTarget(targets[0])
return
default:
{
const selectData = {
@ -32,7 +35,7 @@ export class Targets {
label: "Choisir une seule des cibles",
list: targets.map(it => Targets.extractTokenData(it))
};
DialogSelect.select(selectData, onSelectTarget);
DialogSelect.select(selectData, onSelectTarget)
}
}
}

View File

@ -48,7 +48,7 @@ export class RdDCalendrier extends Application {
constructor() {
super();
this.timestamp = RdDTimestamp.getWorldTime();
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
if (Misc.isFirstConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp);
this.rebuildNombresAstraux(); // Ensure always up-to-date
}
@ -258,7 +258,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async rebuildNombresAstraux() {
if (Misc.isUniqueConnectedGM()) {
if (Misc.isFirstConnectedGM()) {
const nombresAstraux = this.getNombresAstraux()
let newNombresAstraux = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
@ -337,7 +337,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async requestNombreAstral(request) {
const actor = game.actors.get(request.id);
if (Misc.isUniqueConnectedGM()) { // Only once
if (Misc.isFirstConnectedGM()) { // Only once
console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);

View File

@ -1561,6 +1561,11 @@ div.control-icon.token-hud-icon {
height: 100%;
object-fit: contain;
}
.chat-inline-icon {
border: 0;
padding: 1px;
vertical-align: text-top;
}
#sidebar-tabs {
flex: 0 0 28px;

View File

@ -1,8 +1,8 @@
{
"id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon",
"version": "12.0.15",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-12.0.15.zip",
"version": "12.0.17",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-12.0.17.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json",
"changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"compatibility": {

View File

@ -1,11 +1,17 @@
<form class="{{cssClass}}" autocomplete="off" >
<section class="sheet-header">
<div class="flexrow">
<div class="flex-group-left flex-grow-0-5">
<h1 class="charname">{{name}}
</h1>
<section class="sheet-header flexrow">
<div class="flex-grow-0-5">
<img class="profile-img" src="{{img}}" data-edit="img" data-tooltip="{{name}}" />
</div>
<div class="flexcol flex-grow-2">
<div>
<h1 class="charname">{{name}}</h1>
</div>
<div class="flex-group-right flex-grow-3">
<div>
<a class="button-appel-chance"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/appel-chance.svg" data-tooltip="Appel à la chance"/></a>
<a class="button-encaissement"><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a>
<a class="button-ethylisme"><img class="button-img" src="icons/svg/tankard.svg" data-tooltip="Boire"/></a>
<a class="button-repos"><img class="button-img" src="icons/svg/sleep.svg" data-tooltip="Se reposer"/></a>
{{#if system.attributs.hautrevant.value}}
<a class="button-tmr" data-tooltip="Montée dans les Terres M&eacute;dianes !" {{#if hautreve.isDemiReve}}disabled{{/if}}>
<img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-normal.svg"/>
@ -17,10 +23,6 @@
<img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-view.svg"/>
</a>
{{/if}}
<a class="button-appel-chance"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/appel-chance.svg" data-tooltip="Appel à la chance"/></a>
<a class="button-encaissement"><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a>
<a class="button-ethylisme"><img class="button-img" src="icons/svg/tankard.svg" data-tooltip="Boire"/></a>
<a class="button-repos"><img class="button-img" src="icons/svg/sleep.svg" data-tooltip="Se reposer"/></a>
{{#if @root.options.isGM}}
<a class="button-remise-a-neuf"><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a>
{{/if}}

View File

@ -3,20 +3,22 @@
<br>
{{#if (eq attacker.type 'personnage')}}
{{#unless essais.attaqueChance}}
<a class='chat-card-button' id='appel-chance-attaque' data-attackerId='{{attackerId}}'
data-defenderTokenId='{{defenderTokenId}}'>Faire appel à la chance</a>
<a class='chat-card-button' id='appel-chance-attaque'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Faire appel à la chance</a>
</a>
<br>
{{/unless}}
{{#if (gt attacker.system.compteurs.destinee.value 0)}}
<a class='chat-card-button' id='appel-destinee-attaque' data-attackerId='{{attackerId}}'
data-defenderTokenId='{{defenderTokenId}}'>Utiliser la destinée</a>
<a class='chat-card-button' id='appel-destinee-attaque'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Utiliser la destinée</a>
</a>
<br>
{{/if}}
{{/if}}
<a class='chat-card-button' id='echec-total-attaque' data-attackerId='{{attackerId}}'
data-defenderTokenId='{{defenderTokenId}}'>
<a class='chat-card-button' id='echec-total-attaque'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Tirer la maladresse !
</a>
</div>

View File

@ -1,17 +1,17 @@
<div data-passearme="{{passeArme}}">
{{#if (eq surprise 'totale')}}
<span><strong>{{defender.name}}</strong> est totalement surpris</span>
<span><strong>{{defenderToken.name}}</strong> est totalement surpris</span>
{{else if essais.defense}}
<span><strong>{{defender.name}}</strong> doit :</span>
<span><strong>{{defenderToken.name}}</strong> doit :</span>
{{else}}
<span><strong>{{defender.name}}</strong> doit se défendre
<span><strong>{{defenderToken.name}}</strong> doit se défendre
{{~#if (eq surprise 'demi')}} avec une significative {{/if}} d'une attaque
{{~#if attaqueParticuliere}} <strong>particulière en
{{~#if (eq attaqueParticuliere 'finesse')}} finesse
{{else if (eq attaqueParticuliere 'force')}} force
{{else if (eq attaqueParticuliere 'rapidite')}} rapidité
{{/if~}}</strong>
{{/if}} de {{attacker.name}} ({{attaqueArme.name}}):
{{/if}} de {{attackerToken.name}} ({{attaqueArme.name}}):
</span>
{{/if}}
<span class='chat-card-button-area'>
@ -20,15 +20,17 @@
{{#if essais.defense}}
{{#unless essais.defenseChance}}
{{#if (eq defender.type 'personnage')}}
<a class='chat-card-button' id='appel-chance-defense' data-attackerId='{{attackerId}}'
data-defenderTokenId='{{defenderTokenId}}'>Faire appel à la chance</a>
<a class='chat-card-button' id='appel-chance-defense'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Faire appel à la chance</a>
</a>
<br>
{{/if}}
{{#if (eq defender.type 'personnage')}}
{{#if (gt defender.system.compteurs.destinee.value 0)}}
<a class='chat-card-button' id='appel-destinee-defense' data-attackerId='{{attackerId}}'
data-defenderTokenId='{{defenderTokenId}}'>Utiliser la destinée</a>
<a class='chat-card-button' id='appel-destinee-defense'
data-attackerId='{{attackerId}}' data-attackerTokenId='{{attackerToken.id}}' data-defenderTokenId='{{defenderToken.id}}'>
Utiliser la destinée</a>
</a>
<br>
{{/if}}
@ -36,24 +38,26 @@
{{/unless}}
{{else}}
{{#each armes as |arme key|}}
<a class='chat-card-button' id='parer-button' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderTokenId}}'
data-armeid='{{arme._id}}'>
<a class='chat-card-button' id='parer-button'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
data-armeid='{{arme._id}}'>
Parer avec {{arme.name}} à {{../diffLibre }}{{#if arme.system.nbUsage}} (Utilisations : {{arme.system.nbUsage}}){{/if}}
</a>
<br>
{{/each}}
{{#if mainsNues}}
<a class='chat-card-button' id='parer-button' data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderTokenId}}'
data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
<a class='chat-card-button' id='parer-button'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'
data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
Parer à mains nues à {{diffLibre}}{{#if arme.system.nbUsage}} (Utilisations : {{arme.system.nbUsage}}){{/if}}
</a>
<br>
{{/if}}
{{#if (ne attaqueCategorie 'tir')}}
{{#each esquives as |esquive key|}}
<a class='chat-card-button' id='esquiver-button' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderTokenId}}'
data-compid='{{esquive._id}}' data-competence='{{esquive.name}}'>
{{log 'esquive' esquive}}
<a class='chat-card-button' id='esquiver-button'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
data-compid='{{esquive._id}}' data-competence='{{esquive.name}}'>
{{esquive.name}} à {{../diffLibre}} {{#if esquive.system.nbUsage}} (Utilisations : {{esquive.system.nbUsage}}){{/if}}
</a>
<br>
@ -61,8 +65,8 @@
{{/if}}
{{/if}}
{{/unless}}
<a class='chat-card-button' id='encaisser-button' data-attackerId='{{attackerId}}'
data-defenderTokenId='{{defenderTokenId}}'>
<a class='chat-card-button' id='encaisser-button'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Encaisser à {{plusMoins dmg.total}}
{{#if (eq dmg.mortalite 'non-mortel')~}}
(non-mortel) !

View File

@ -1,5 +1,4 @@
{{#if isGM}}
{{log this}}
<span>
{{#if (gt endurance 0)}}
De plus, {{alias}} a perdu {{endurance}} points d'endurance
@ -18,18 +17,18 @@
</h4>
<div>
Jet d'encaissement de {{roll.total}}
{{#unless (eq armure 0)}}, l'armure a protègé de {{armure}}
{{~#unless (eq armure 0)}}, l'armure a protègé de {{armure}}
{{~#unless (eq penetration 0)}} (pénétration de {{penetration}}){{/unless}}
{{~/unless}}, total: <span class="rdd-roll-echec">{{total}}</span>
{{~/unless~}}, total: <span class="rdd-roll-echec">{{total}}</span>
<br>
{{alias}}
{{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup
{{else if mort}}vient de mourir
{{else if blessure}}
{{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}}
{{else}}subit une contusion
{{~else~}}subit une contusion
{{~/if~}}
{{else}}s'en sort sans une égratignure
{{~else~}}s'en sort sans une égratignure
{{~/if~}}
{{~#unless (eq dmg.mortalite 'entiteincarnee')}}
@ -39,11 +38,11 @@
{{/unless~}}
{{~#if (gt endurance 0)}}
{{~#if hasPlayerOwner}}, a perdu {{endurance}} points d'endurance
{{#if (ne vie 0)}}, <span class="rdd-roll-echec">{{vie}} points de vie</span>{{/if}}
{{~#if (ne vie 0)}}, <span class="rdd-roll-echec">{{vie}} points de vie</span>{{/if~}}
{{/if}}
{{#if (ne dmg.mortalite 'entiteincarnee')}}
{{#if (gt endurance 1)}}et
{{#if sonne}}est <strong>sonné</strong><img class="chat-icon" src="icons/svg/stoned.svg" data-tooltip="charge" height="16" width="16" /> jusqu'à la fin du prochain round{{else}}n'est pas sonné{{/if}}!
{{#if sonne}}est <strong>sonné</strong><img class="chat-inline-icon" src="icons/svg/stoned.svg" data-tooltip="charge" height="16" width="16" /> jusqu'à la fin du prochain round{{else}}n'est pas sonné{{/if}}!
{{#if hasPlayerOwner}}Jet d'endurance : {{jetEndurance}} / {{resteEndurance}}{{/if}}
{{/if}}
{{/if}}