Version 12.0.16 - Le secret d'Astrobazzarh #717

Closed
VincentVk wants to merge 6 commits from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
24 changed files with 215 additions and 153 deletions

View File

@ -1,4 +1,9 @@
# 12.0 # 12.0
## 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 ## 12.0.15 - Le messager d'Astrobazzarh
- Correction des faces de dés personalisés dice-so-nice - Correction des faces de dés personalisés dice-so-nice
- Les messages de maladies ne sont plus publics - Les messages de maladies ne sont plus publics

View File

@ -220,18 +220,18 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
// Points de reve actuel // Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', true)) 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('.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 // Initiative pour l'arme
this.html.find('.arme-initiative a').click(async event => { 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) { if (combatant) {
let action = this._getEventArmeCombat(event); RdDCombatManager.rollInitiativeAction(combatant._id, this._getEventArmeCombat(event));
RdDCombatManager.rollInitiativeAction(combatant._id, action);
} else { } else {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat."); ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
} }
}); })
// Display TMR // Display TMR
this.html.find('.button-tmr').click(async event => this.actor.displayTMR("normal")) 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') { async appliquerAjoutExperience(rollData, hideChatMessage = 'show') {
if (!Misc.firstConnectedGM()){ if (!Misc.isFirstConnectedGM()){
return return
} }
hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM) hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
@ -3011,7 +3011,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onCreateOwnedDraconique(item, options, id) { async onCreateOwnedDraconique(item, options, id) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item)); let draconique = Draconique.all().find(it => it.match(item));
if (draconique) { if (draconique) {
await draconique.onActorCreateOwned(this, item) await draconique.onActorCreateOwned(this, item)
@ -3023,7 +3023,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onDeleteOwnedDraconique(item, options, id) { async onDeleteOwnedDraconique(item, options, id) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item)); let draconique = Draconique.all().find(it => it.match(item));
if (draconique) { if (draconique) {
await draconique.onActorDeleteOwned(this, item) await draconique.onActorDeleteOwned(this, item)
@ -3033,7 +3033,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onDeleteOwnedCaseTmr(item, options, id) { async onDeleteOwnedCaseTmr(item, options, id) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.isCase(item)); let draconique = Draconique.all().find(it => it.isCase(item));
if (draconique) { if (draconique) {
await draconique.onActorDeleteCaseTmr(this, item) await draconique.onActorDeleteCaseTmr(this, item)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -195,7 +195,7 @@ export class Misc {
return document 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 document
} }
return undefined return undefined
@ -206,14 +206,14 @@ export class Misc {
} }
static isOwnerPlayerOrUniqueConnectedGM(actor) { 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 * @returns true pour un seul utilisateur: le premier GM connecté par ordre d'id
*/ */
static isUniqueConnectedGM() { static isFirstConnectedGM() {
return game.user.id == Misc.firstConnectedGMId(); return game.user == Misc.firstConnectedGM();
} }
static firstConnectedGMId() { static firstConnectedGMId() {

View File

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

View File

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

View File

@ -37,7 +37,7 @@ export class RdDTokenHud {
// initiative // initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions); await RdDTokenHud.addExtensionHudInit(html, combatant, actions);
// combat // 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) { static async addExtensionHudCombat(html, combatant, token, actions) {
const hudData = { combatant, actions, commandes: [] }; const hudData = { combatant, token, actions, commandes: [] };
const controlIconTarget = html.find('.control-icon[data-action=target]'); const controlIconTarget = html.find('.control-icon[data-action=target]');
await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData, await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData,
(event) => { (event) => {
@ -80,7 +80,7 @@ export class RdDTokenHud {
combatant.actor.conjurerPossession(possession); combatant.actor.conjurerPossession(possession);
} }
else { 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); const diff = Math.abs(rollData.diffLibre);
let formula = RdDUtility.formuleEncaissement(diff, options) let formula = RdDUtility.formuleEncaissement(diff, options)
const roll = await RdDDice.roll(formula, options); const roll = await RdDDice.roll(formula, options);
RdDUtility.remplaceDeMinParDifficulte(roll, diff, 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) { 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 // 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 bonusDegatsDiffLibre = ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(rollData.diffLibre ?? 0) : 0
const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre
const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite); const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite);
const over20 = Math.max(jetTotal - 20, 0); const over20 = Math.max(jetTotal - 20, 0);
encaissement.dmg = rollData.dmg; encaissement.dmg = rollData.dmg
encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(this.type); if (ReglesOptionnelles.isUsing('localisation-aleatoire')){
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'; 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.dmg.bonusDegatsDiffLibre = bonusDegatsDiffLibre
encaissement.roll = roll; encaissement.roll = roll;
encaissement.armure = armure; encaissement.armure = armure;
@ -741,6 +746,15 @@ export class RdDUtility {
return undefined; 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) { static getSelectedActor(msgPlayer = undefined) {
if (canvas.tokens.controlled.length == 1) { if (canvas.tokens.controlled.length == 1) {
let token = canvas.tokens.controlled[0]; 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é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: '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: '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" }, { 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(), date: this.html.find('[name="joursAstrologie"]').val(),
userId: game.user.id userId: game.user.id
} }
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
game.system.rdd.calendrier.requestNombreAstral(socketData); game.system.rdd.calendrier.requestNombreAstral(socketData);
} else { } else {
game.socket.emit(SYSTEM_SOCKET_ID, { game.socket.emit(SYSTEM_SOCKET_ID, {

View File

@ -13,18 +13,21 @@ export class Targets {
static extractTokenData(target) { static extractTokenData(target) {
return { id: target?.id, name: target?.document.name, img: target?.document.texture.src ?? target?.actor.img ?? 'icons/svg/mystery-man.svg' }; 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) { static isTargetEntite(target) {
return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE; return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE;
} }
static async selectOneToken(onSelectTarget = target => { }) { static async selectOneTargetToken(onSelectTarget = target => { }) {
const targets = Targets.listTargets(); const targets = Targets.listTargets()
switch (targets.length) { switch (targets.length) {
case 0: return; case 0:
return
case 1: case 1:
onSelectTarget(targets[0]); onSelectTarget(targets[0])
return; return
default: default:
{ {
const selectData = { const selectData = {
@ -32,7 +35,7 @@ export class Targets {
label: "Choisir une seule des cibles", label: "Choisir une seule des cibles",
list: targets.map(it => Targets.extractTokenData(it)) 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() { constructor() {
super(); super();
this.timestamp = RdDTimestamp.getWorldTime(); this.timestamp = RdDTimestamp.getWorldTime();
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM if (Misc.isFirstConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp); RdDTimestamp.setWorldTime(this.timestamp);
this.rebuildNombresAstraux(); // Ensure always up-to-date this.rebuildNombresAstraux(); // Ensure always up-to-date
} }
@ -258,7 +258,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */ /* -------------------------------------------- */
async rebuildNombresAstraux() { async rebuildNombresAstraux() {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
const nombresAstraux = this.getNombresAstraux() const nombresAstraux = this.getNombresAstraux()
let newNombresAstraux = []; let newNombresAstraux = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) { for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
@ -337,7 +337,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */ /* -------------------------------------------- */
async requestNombreAstral(request) { async requestNombreAstral(request) {
const actor = game.actors.get(request.id); const actor = game.actors.get(request.id);
if (Misc.isUniqueConnectedGM()) { // Only once if (Misc.isFirstConnectedGM()) { // Only once
console.log(request); console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date); let jourDiff = this.getLectureAstrologieDifficulte(request.date);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat); 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%; height: 100%;
object-fit: contain; object-fit: contain;
} }
.chat-inline-icon {
border: 0;
padding: 1px;
vertical-align: text-top;
}
#sidebar-tabs { #sidebar-tabs {
flex: 0 0 28px; flex: 0 0 28px;

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
{{#if isGM}} {{#if isGM}}
{{log this}}
<span> <span>
{{#if (gt endurance 0)}} {{#if (gt endurance 0)}}
De plus, {{alias}} a perdu {{endurance}} points d'endurance De plus, {{alias}} a perdu {{endurance}} points d'endurance
@ -18,18 +17,18 @@
</h4> </h4>
<div> <div>
Jet d'encaissement de {{roll.total}} 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 (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> <br>
{{alias}} {{alias}}
{{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup {{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup
{{else if mort}}vient de mourir {{else if mort}}vient de mourir
{{else if blessure}} {{else if blessure}}
{{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}} {{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}}
{{else}}subit une contusion {{~else~}}subit une contusion
{{~/if~}} {{~/if~}}
{{else}}s'en sort sans une égratignure {{~else~}}s'en sort sans une égratignure
{{~/if~}} {{~/if~}}
{{~#unless (eq dmg.mortalite 'entiteincarnee')}} {{~#unless (eq dmg.mortalite 'entiteincarnee')}}
@ -39,11 +38,11 @@
{{/unless~}} {{/unless~}}
{{~#if (gt endurance 0)}} {{~#if (gt endurance 0)}}
{{~#if hasPlayerOwner}}, a perdu {{endurance}} points d'endurance {{~#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}}
{{#if (ne dmg.mortalite 'entiteincarnee')}} {{#if (ne dmg.mortalite 'entiteincarnee')}}
{{#if (gt endurance 1)}}et {{#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 hasPlayerOwner}}Jet d'endurance : {{jetEndurance}} / {{resteEndurance}}{{/if}}
{{/if}} {{/if}}
{{/if}} {{/if}}