Compare commits

...

26 Commits

Author SHA1 Message Date
484f02277b Merge pull request 'Version 10.0.30' (#567) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: public/foundryvtt-reve-de-dragon#567
2022-10-21 22:18:05 +02:00
Vincent Vandemeulebrouck
0ef9123cd5 Version 10.0.30
* Fix de bug de socket qui pouvait conduire à un plantage de Foundry,
  par exemple lors d'un achat par un joueur
* Fix de l'édition des sorts des personnages: la sauvegarde fonctionne
  de nouveau
* Ajout d'une demande de confirmation lors d'un refoulement: l'action
  refouler est très proche de la suppression, un mauvais clic est vite
  arrivé
2022-10-21 22:10:38 +02:00
Vincent Vandemeulebrouck
e6b71faa02 Passer un async à game.socket.on()
Corrige le problème causé par l'update d'un objet pendant le traitement
d'un message du socket, qui peut échouer quand le socket est occupé,
et cause donc un rejet du message du socket, qui est réessayé en boucle
infinier.

Ce qui devrait résoudre le problème d'achat/vente qui fait planter le
MJ et le serveur Foundry.
2022-10-21 22:05:35 +02:00
Vincent Vandemeulebrouck
9a1a464cb6 Whisper vers l'actor
Plutôt que le game.user.name, pour que les messages soient envoyés
au joueur même si c'est le MJ qui agit pour le user
2022-10-21 02:41:32 +02:00
Vincent Vandemeulebrouck
32af9bf1b4 Ajout de confirmation pour refoulement 2022-10-21 02:41:32 +02:00
Vincent Vandemeulebrouck
362fd964d0 Fix: édition de sorts d'Actor
L'édition était impossible parce que le formData ne contient pas
de noeud system, mais des propriétés 'system.portee', ...
2022-10-21 02:41:32 +02:00
e4f9b0f589 New 10.0.29 release 2022-10-19 07:43:10 +02:00
975e525fd5 Merge pull request 'v10.0.29: Fix empilement' (#566) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: public/foundryvtt-reve-de-dragon#566
2022-10-19 07:41:55 +02:00
Vincent Vandemeulebrouck
296bff2c5d v10.0.29
Fix de l'empilement
2022-10-17 21:21:45 +02:00
Vincent Vandemeulebrouck
3e65bcb848 Fix: empilement de nouveau possible
Les objets empilables peuvent de nouveau être empilés.

Un message indique en cas de drop sur un objet non empilable qui
n'est pas un conteneur que l'objet est déplacé dans le conteneur parent
ou dans les objets portés.
2022-10-17 21:20:52 +02:00
8bb2afe83e Merge pull request 'Bugfixes' (#565) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: public/foundryvtt-reve-de-dragon#565
2022-10-14 22:52:08 +02:00
Vincent Vandemeulebrouck
37e5b3c0aa Version 10.0.28 2022-10-14 21:47:34 +02:00
Vincent Vandemeulebrouck
e1aecb05c3 Fix: Organisation boutons/messages d'équipement
Le message de surencombrement rendait la présentation moche
2022-10-14 00:58:26 +02:00
Vincent Vandemeulebrouck
168127d8fe Fix: tirage dans une table ne fonctionnait plus
Régression bête, qui empêchait aussi d'afficher le résultat d'un échec
contre un rêve de dragon
2022-10-14 00:48:05 +02:00
Vincent Vandemeulebrouck
21ec043e98 Fix: alignement des queues avec le reste
Sans l'onglet Haut rêve, les queues n'étaient pas bien alignées.
2022-10-14 00:46:42 +02:00
Vincent Vandemeulebrouck
00b3b7f9b3 Fix: Affichage ajustement encaissement
Pour les encaissements hors combat, rappel de l'ajustement
d'encaissement choisi
2022-10-14 00:45:53 +02:00
Vincent Vandemeulebrouck
d1be242791 Minor: optimisation de monnaies
pas d'updates pour les monnaies non affectées
2022-10-14 00:43:37 +02:00
Vincent Vandemeulebrouck
fa75828bc1 Fix: régression lancer de sorts
A cause du duplicate, les voies n'étaient plus des RdDItem, du coup,
la méthode isCompetence de la recherche de compétences ne marche
pas...

Et le duplicate était inutile de toutes façons.
2022-10-14 00:42:35 +02:00
232f414a62 Ajout distance 2022-10-09 13:44:40 +02:00
14e76ac631 Merge pull request 'v10' (#564) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: public/foundryvtt-reve-de-dragon#564
2022-10-09 13:43:27 +02:00
Vincent Vandemeulebrouck
43763dbe3a Jets d'encaissement validés par le MJ
* ajout d'une option pour activer la validation par le MJ
* lors d'un jet d'encaissement, une fenêtre s'ouvre chez le MJ
  avec le résultat d'encaissement
* le MJ peut changer le jet d'encaissement
* si le MJ annule, l'encaissement n'a pas lieu
* Attention, si plusieurs MJ, un seul doit valider, sinon
  encaissements multiples
2022-10-09 02:19:33 +02:00
Vincent Vandemeulebrouck
81ae15a6a2 Simuler les lancers de dés
Lorsqu'on force le résultat des dés, on force l'affichage d'un lancer
donnant ce résultat avec DiceSoNice
2022-10-08 17:37:39 +02:00
Vincent Vandemeulebrouck
6dc7272ef6 Corrections mineures
Plus besoin de vérifier game.versions pour utiliser game.users
Plus besoin d'une indirection Misc.getUsers

defender.system.name => defender.name
2022-10-08 17:37:32 +02:00
Vincent Vandemeulebrouck
d75eef1926 Correction configuration affichage prix
L'option de configuration étaity toujours vrai quand vue par le MJ
2022-10-08 17:37:32 +02:00
Vincent Vandemeulebrouck
18039e905b Compétences & herbes personalisées
* permettre d'ajouter des compétences dans un monde, qui seront
  ajoutés aux acteurs créés dans ce monde
* les herbes de repos/soins du monde sont bien considérées comme
  des herbes pour les potions
2022-10-08 17:37:32 +02:00
Vincent Vandemeulebrouck
6d0e5321a2 Nommage homogène 2022-10-08 11:56:52 +02:00
25 changed files with 567 additions and 309 deletions

View File

@@ -33,8 +33,9 @@ import { RollDataAjustements } from "./rolldata-ajustements.js";
import { DialogItemAchat } from "./dialog-item-achat.js"; import { DialogItemAchat } from "./dialog-item-achat.js";
import { RdDItem } from "./item.js"; import { RdDItem } from "./item.js";
import { RdDPossession } from "./rdd-possession.js"; import { RdDPossession } from "./rdd-possession.js";
import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js"; import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE, HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDConfirm } from "./rdd-confirm.js"; import { RdDConfirm } from "./rdd-confirm.js";
import { DialogValidationEncaissement } from "./dialog-validation-encaissement.js";
const POSSESSION_SANS_DRACONIC = { const POSSESSION_SANS_DRACONIC = {
img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp', img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp',
@@ -120,7 +121,7 @@ export class RdDActor extends Actor {
} }
if (isPersonnage) { if (isPersonnage) {
const competences = await RdDUtility.loadCompendium(RdDItemCompetence.actorCompendium(actorData.type)); const competences = await RdDUtility.loadItems(it => it.isCompetencePersonnage(), 'foundryvtt-reve-de-dragon.competences');
actorData.items = competences.map(i => i.toObject()); actorData.items = competences.map(i => i.toObject());
actorData.items = actorData.items.concat(Monnaie.monnaiesStandard()); actorData.items = actorData.items.concat(Monnaie.monnaiesStandard());
} }
@@ -343,7 +344,7 @@ export class RdDActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getDraconicList() { getDraconicList() {
return this.items.filter(it => it.type == 'competence' && it.system.categorie == 'draconic') return this.items.filter(it => it.isCompetencePersonnage() && it.system.categorie == 'draconic')
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getBestDraconic() { getBestDraconic() {
@@ -392,7 +393,7 @@ export class RdDActor extends Actor {
potionImg: potion.img potionImg: potion.img
} }
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html`, messageData) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html`, messageData)
}); });
} }
@@ -800,7 +801,7 @@ export class RdDActor extends Actor {
rollData.poesie = await Poetique.getExtrait(); rollData.poesie = await Poetique.getExtrait();
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, rollData) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, rollData)
}); });
} }
@@ -945,7 +946,7 @@ export class RdDActor extends Actor {
message += "<br>" + troncName; message += "<br>" + troncName;
} }
ChatMessage.create({ ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients(game.user.name), whisper: ChatMessage.getWhisperRecipients(this.name),
content: message content: message
}); });
} }
@@ -1186,8 +1187,8 @@ export class RdDActor extends Actor {
const src = this.getObjet(srcId); const src = this.getObjet(srcId);
const dest = this.getObjet(destId); const dest = this.getObjet(destId);
const cible = this.getContenantOrParent(dest); const cible = this.getContenantOrParent(dest);
const messageEquipementDifferent = item.messageEquipementDifferent(dest); const [empilable, message] = item.isEquipementEmpilable(dest);
if (dest && !messageEquipementDifferent) { if (empilable) {
await this.regrouperEquipementsSimilaires(item, dest); await this.regrouperEquipementsSimilaires(item, dest);
result = false; result = false;
} }
@@ -1195,9 +1196,11 @@ export class RdDActor extends Actor {
else if (!cible || this.conteneurPeutContenir(cible, item)) { else if (!cible || this.conteneurPeutContenir(cible, item)) {
await this.enleverDeConteneur(item, src, params.onEnleverConteneur); await this.enleverDeConteneur(item, src, params.onEnleverConteneur);
await this.ajouterDansConteneur(item, cible, params.onAjouterDansConteneur); await this.ajouterDansConteneur(item, cible, params.onAjouterDansConteneur);
} if (message && !dest.isConteneur()) {
else { ui.notifications.info(cible
ui.notifications.info(messageEquipementDifferent); ? `${message}<br>${item.name} a été déplacé dans: ${cible.name}`
: `${message}<br>${item.name} a été sorti du conteneur`);
}
} }
} }
} }
@@ -1423,9 +1426,21 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async actionRefoulement(item) { async actionRefoulement(item) {
const refoulement = item?.system.refoulement ?? 0; const refoulement = item?.system.refoulement ?? 0;
if (refoulement>0){ if (refoulement>0) {
await this.ajouterRefoulement(refoulement); RdDConfirm.confirmer({
await item.delete(); settingConfirmer: "confirmation-refouler",
content: `<p>Prennez-vous le risque de refouler ${item.name} pour ${refoulement} points de refoulement ?</p>`,
title: 'Confirmer la refoulement',
buttonLabel: 'Refouler',
onAction: async () => {
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `${this.name} a refoulé une queue ${item.nname} pour ${refoulement} points de refoulement`
});
await this.ajouterRefoulement(refoulement);
await item.delete();
}
})
} }
} }
@@ -1448,7 +1463,7 @@ export class RdDActor extends Actor {
await this.createEmbeddedDocuments('Item', [souffle]); await this.createEmbeddedDocuments('Item', [souffle]);
if (options.chat) { if (options.chat) {
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: this.name + " subit un Souffle de Dragon : " + souffle.name content: this.name + " subit un Souffle de Dragon : " + souffle.name
}); });
} }
@@ -1468,7 +1483,7 @@ export class RdDActor extends Actor {
await this.createEmbeddedDocuments('Item', [queue]); await this.createEmbeddedDocuments('Item', [queue]);
if (options.chat) { if (options.chat) {
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: this.name + " subit une Queue de Dragon : " + queue.name content: this.name + " subit une Queue de Dragon : " + queue.name
}); });
} }
@@ -1506,7 +1521,7 @@ export class RdDActor extends Actor {
let tmr = await TMRUtility.getTMRAleatoire(tmr => accessible(tmr) && !innaccessible.includes(tmr.coord)); let tmr = await TMRUtility.getTMRAleatoire(tmr => accessible(tmr) && !innaccessible.includes(tmr.coord));
ChatMessage.create({ ChatMessage.create({
content: `${raison} : ré-insertion aléatoire.`, content: `${raison} : ré-insertion aléatoire.`,
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name) whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name)
}); });
await this.forcerPositionTMRInconnue(tmr); await this.forcerPositionTMRInconnue(tmr);
return tmr; return tmr;
@@ -1676,7 +1691,7 @@ export class RdDActor extends Actor {
const result = await this._jetEndurance(this.system.sante.endurance.value) const result = await this._jetEndurance(this.system.sante.endurance.value)
const message = { const message = {
content: "Jet d'Endurance : " + result.roll.total + " / " + endurance + "<br>", content: "Jet d'Endurance : " + result.roll.total + " / " + endurance + "<br>",
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}; };
if (result.sonne) { if (result.sonne) {
message.content += `${this.name} a échoué son Jet d'Endurance et devient Sonné`; message.content += `${this.name} a échoué son Jet d'Endurance et devient Sonné`;
@@ -1726,7 +1741,7 @@ export class RdDActor extends Actor {
} }
const message = { const message = {
content: msgText, content: msgText,
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}; };
ChatMessage.create(message); ChatMessage.create(message);
} }
@@ -1835,7 +1850,7 @@ export class RdDActor extends Actor {
const jetMoral = await this._jetDeMoral(situation); const jetMoral = await this._jetDeMoral(situation);
const finMessage = (jetMoral.succes ? messageReussi : messageManque) ?? (jetMoral.ajustement == 0 ? "Vous gardez votre moral" : jetMoral.ajustement > 0 ? "Vous gagnez du moral" : "Vous perdez du moral"); const finMessage = (jetMoral.succes ? messageReussi : messageManque) ?? (jetMoral.ajustement == 0 ? "Vous gardez votre moral" : jetMoral.ajustement > 0 ? "Vous gagnez du moral" : "Vous perdez du moral");
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `${finMessage} - jet ${jetMoral.succes ? "réussi" : "manqué"} en situation ${situation} (${jetMoral.jet}/${jetMoral.difficulte}).` content: `${finMessage} - jet ${jetMoral.succes ? "réussi" : "manqué"} en situation ${situation} (${jetMoral.jet}/${jetMoral.difficulte}).`
}); });
return jetMoral.ajustement; return jetMoral.ajustement;
@@ -2130,7 +2145,7 @@ export class RdDActor extends Actor {
}; };
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-transformer-stress.html`, stressRollData) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-transformer-stress.html`, stressRollData)
}); });
@@ -2292,12 +2307,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
computeDraconicAndSortIndex(sortList) { computeDraconicAndSortIndex(sortList) {
let draconicList = this.getDraconicList() let draconicList = this.getDraconicList();
.map(it => {
it = duplicate(it)
it.system.defaut_carac = "reve";
return it;
});
for (let sort of sortList) { for (let sort of sortList) {
let draconicsSort = this.getDraconicsSort(draconicList, sort).map(it => it.name); let draconicsSort = this.getDraconicsSort(draconicList, sort).map(it => it.name);
for (let index = 0; index < draconicList.length && sort.system.listIndex == undefined; index++) { for (let index = 0; index < draconicList.length && sort.system.listIndex == undefined; index++) {
@@ -2317,7 +2327,7 @@ export class RdDActor extends Actor {
case "detection d'aura": case "detection d'aura":
return draconicList; return draconicList;
case "annulation de magie": case "annulation de magie":
return draconicList.filter(it => !Grammar.toLowerCaseNoAccent(it.name).includes('thanatos')); return draconicList.filter(it => !RdDItemCompetence.isThanatos(it));
} }
return [RdDItemCompetence.getVoieDraconic(draconicList, sort.system.draconic)]; return [RdDItemCompetence.getVoieDraconic(draconicList, sort.system.draconic)];
} }
@@ -2386,7 +2396,7 @@ export class RdDActor extends Actor {
} }
ChatMessage.create({ ChatMessage.create({
content: "Vous êtes sous le coup d'une Mauvaise Rencontre en Persective." + addMsg, content: "Vous êtes sous le coup d'une Mauvaise Rencontre en Persective." + addMsg,
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}); });
} }
return rencSpecial; return rencSpecial;
@@ -2398,7 +2408,7 @@ export class RdDActor extends Actor {
if (countInertieDraconique > 0) { if (countInertieDraconique > 0) {
ChatMessage.create({ ChatMessage.create({
content: `Vous êtes sous le coup d'Inertie Draconique : vous perdrez ${countInertieDraconique + 1} cases de Fatigue par déplacement au lieu d'une.`, content: `Vous êtes sous le coup d'Inertie Draconique : vous perdrez ${countInertieDraconique + 1} cases de Fatigue par déplacement au lieu d'une.`,
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}); });
} }
return countInertieDraconique + 1; return countInertieDraconique + 1;
@@ -2410,7 +2420,7 @@ export class RdDActor extends Actor {
await this.reveActuelIncDec(-1); await this.reveActuelIncDec(-1);
ChatMessage.create({ ChatMessage.create({
content: "Vous êtes sous le coup d'un Péage : l'entrée sur cette case vous a coûté 1 Point de Rêve (déduit automatiquement).", content: "Vous êtes sous le coup d'un Péage : l'entrée sur cette case vous a coûté 1 Point de Rêve (déduit automatiquement).",
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}); });
} }
} }
@@ -3017,7 +3027,7 @@ export class RdDActor extends Actor {
// Cas de désir lancinant, pas d'expérience sur particulière // Cas de désir lancinant, pas d'expérience sur particulière
ChatMessage.create({ ChatMessage.create({
content: `Vous souffrez au moins d'un Désir Lancinant, vous ne pouvez pas gagner d'expérience sur une Particulière tant que le désir n'est pas assouvi`, content: `Vous souffrez au moins d'un Désir Lancinant, vous ne pouvez pas gagner d'expérience sur une Particulière tant que le désir n'est pas assouvi`,
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}); });
return undefined; return undefined;
} }
@@ -3147,7 +3157,7 @@ export class RdDActor extends Actor {
if (countMonteeLaborieuse > 0) { if (countMonteeLaborieuse > 0) {
ChatMessage.create({ ChatMessage.create({
content: `Vous êtes sous le coup d'une Montée Laborieuse : vos montées en TMR coûtent ${countMonteeLaborieuse} Point de Rêve de plus.`, content: `Vous êtes sous le coup d'une Montée Laborieuse : vos montées en TMR coûtent ${countMonteeLaborieuse} Point de Rêve de plus.`,
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}); });
} }
return countMonteeLaborieuse; return countMonteeLaborieuse;
@@ -3187,7 +3197,7 @@ export class RdDActor extends Actor {
if (this.getReveActuel() < minReveValue) { if (this.getReveActuel() < minReveValue) {
ChatMessage.create({ ChatMessage.create({
content: `Vous n'avez les ${minReveValue} Points de Reve nécessaires pour monter dans les Terres Médianes`, content: `Vous n'avez les ${minReveValue} Points de Reve nécessaires pour monter dans les Terres Médianes`,
whisper: ChatMessage.getWhisperRecipients(game.user.name) whisper: ChatMessage.getWhisperRecipients(this.name)
}); });
return; return;
} }
@@ -3330,20 +3340,39 @@ export class RdDActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async encaisserDommages(rollData, attacker = undefined, defenderRoll = undefined) { async encaisserDommages(rollData, attacker = undefined, show = undefined) {
if (attacker && !await attacker.accorder(this, 'avant-encaissement')) { if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
return; return;
} }
this.validerEncaissement(rollData, show);
}
console.log("encaisserDommages", rollData) async validerEncaissement(rollData, show) {
if (ReglesOptionelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
RdDActor.remoteActorCall({
actorId: this.id,
method: 'validerEncaissement',
args: [rollData, show]
});
return;
}
const armure = await this.computeArmure(rollData);
if (ReglesOptionelles.isUsing('validation-encaissement-gr')) {
DialogValidationEncaissement.validerEncaissement(this, rollData, armure, show, (encaissement, show) => this._appliquerEncaissement(encaissement, show));
}
else {
let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
await this._appliquerEncaissement(encaissement, show)
}
}
async _appliquerEncaissement(encaissement, show) {
let santeOrig = duplicate(this.system.sante); let santeOrig = duplicate(this.system.sante);
let encaissement = await this.jetEncaissement(rollData);
this.ajouterBlessure(encaissement); // Will upate the result table this.ajouterBlessure(encaissement); // Will upate the result table
const perteVie = this.isEntite() const perteVie = this.isEntite()
? { newValue: 0 } ? { newValue: 0 }
: await this.santeIncDec("vie", - encaissement.vie); : await this.santeIncDec("vie", -encaissement.vie);
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, encaissement.critiques > 0); const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, encaissement.critiques > 0);
this.computeEtatGeneral(); this.computeEtatGeneral();
@@ -3356,7 +3385,7 @@ export class RdDActor extends Actor {
jetEndurance: perteEndurance.jetEndurance, jetEndurance: perteEndurance.jetEndurance,
endurance: santeOrig.endurance.value - perteEndurance.newValue, endurance: santeOrig.endurance.value - perteEndurance.newValue,
vie: this.isEntite() ? 0 : (santeOrig.vie.value - perteVie.newValue), vie: this.isEntite() ? 0 : (santeOrig.vie.value - perteVie.newValue),
show: defenderRoll?.show ?? {} show: show ?? {}
}); });
await ChatUtility.createChatWithRollMode(this.name, { await ChatUtility.createChatWithRollMode(this.name, {
@@ -3374,65 +3403,6 @@ export class RdDActor extends Actor {
} }
} }
/* -------------------------------------------- */
async jetEncaissement(rollData) {
let formula = "2d10";
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "min" + valeurMin;
}
}
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-ajout-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "+" + valeurMin;
}
}
let roll = await RdDDice.roll(formula);
// 1 dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre-simple')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
if (roll.terms[0].results[0].result < valeurMin) {
roll.terms[0].results[0].result = valeurMin;
} else if (roll.terms[0].results[1].result < valeurMin) {
roll.terms[0].results[1].result = valeurMin;
}
roll._total = roll.terms[0].results[0].result + roll.terms[0].results[1].result;
}
}
const armure = await this.computeArmure(rollData);
const jetTotal = roll.total + rollData.dmg.total - armure;
let encaissement = RdDUtility.selectEncaissement(jetTotal, rollData.dmg.mortalite)
let 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.roll = roll;
encaissement.armure = armure;
encaissement.total = jetTotal;
encaissement.vie = await RdDActor._evaluatePerte(encaissement.vie, over20);
encaissement.endurance = await RdDActor._evaluatePerte(encaissement.endurance, over20);
encaissement.penetration = rollData.arme?.system.penetration ?? 0;
return encaissement;
}
/* -------------------------------------------- */
static async _evaluatePerte(formula, over20) {
let perte = new Roll(formula, { over20: over20 });
await perte.evaluate({ async: true });
return perte.total;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
ajouterBlessure(encaissement) { ajouterBlessure(encaissement) {
if (this.type == 'entite') return; // Une entité n'a pas de blessures if (this.type == 'entite') return; // Une entité n'a pas de blessures
@@ -3699,8 +3669,7 @@ export class RdDActor extends Actor {
actorId: achat.vendeurId ?? achat.acheteurId, actorId: achat.vendeurId ?? achat.acheteurId,
method: 'achatVente', method: 'achatVente',
args: [achat] args: [achat]
}, });
);
return; return;
} }
@@ -3746,7 +3715,7 @@ export class RdDActor extends Actor {
system: mergeObject(vente.item.system, { quantite: isItemEmpilable ? achat.quantiteTotal : undefined }), system: mergeObject(vente.item.system, { quantite: isItemEmpilable ? achat.quantiteTotal : undefined }),
} }
let listeAchat = isItemEmpilable ? [achatData] : Array.from({ length: achat.quantiteTotal }, (_, i) => achatData) let listeAchat = isItemEmpilable ? [achatData] : Array.from({ length: achat.quantiteTotal }, (_, i) => achatData)
let items = await acheteur.createEmbeddedDocuments("Item", listeAchat) let items = await acheteur.createEmbeddedDocuments("Item", listeAchat);
if (achat.choix.consommer && vente.item.type == 'nourritureboisson') { if (achat.choix.consommer && vente.item.type == 'nourritureboisson') {
achat.choix.doses = achat.choix.nombreLots; achat.choix.doses = achat.choix.nombreLots;
await acheteur.consommerNourritureboisson(items[0], achat.choix); await acheteur.consommerNourritureboisson(items[0], achat.choix);
@@ -3974,7 +3943,7 @@ export class RdDActor extends Actor {
this.bonusRecuperationPotion = potionData.system.herbeBonus; this.bonusRecuperationPotion = potionData.system.herbeBonus;
} }
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-soin.html`, potionData) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-soin.html`, potionData)
}); });
} }
@@ -4007,7 +3976,7 @@ export class RdDActor extends Actor {
this.bonusRepos = potionData.system.herbeBonus; this.bonusRepos = potionData.system.herbeBonus;
} }
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-repos.html`, potionData) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-repos.html`, potionData)
}); });
} }
@@ -4045,7 +4014,7 @@ export class RdDActor extends Actor {
this.diminuerQuantiteObjet(herbeData._id, herbeData.nbBrins); this.diminuerQuantiteObjet(herbeData._id, herbeData.nbBrins);
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html`, messageData) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html`, messageData)
}); });
} }
@@ -4070,7 +4039,7 @@ export class RdDActor extends Actor {
} }
} }
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-generique.html`, potionData) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-generique.html`, potionData)
}); });
} }
@@ -4140,7 +4109,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { async onPreUpdateItem(item, change, options, id) {
if (item.type == 'competence' && item.system.defaut_carac && item.system.xp) { if (item.isCompetencePersonnage() && item.system.defaut_carac && item.system.xp) {
await this.checkCompetenceXP(item.name, item.system.xp); await this.checkCompetenceXP(item.name, item.system.xp);
} }
} }
@@ -4205,7 +4174,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
notifyGestionTeteSouffleQueue(item, manualMessage = true) { notifyGestionTeteSouffleQueue(item, manualMessage = true) {
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `${this.name} a reçu un/une ${item.type}: ${item.name}, qui ${manualMessage ? "n'est pas" : "est"} géré(e) automatiquement. ${manualMessage ? manualMessage : ''}` content: `${this.name} a reçu un/une ${item.type}: ${item.name}, qui ${manualMessage ? "n'est pas" : "est"} géré(e) automatiquement. ${manualMessage ? manualMessage : ''}`
}); });
} }

View File

@@ -129,7 +129,7 @@ export class ChatUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getUsers(filter) { static getUsers(filter) {
return Misc.getUsers().filter(filter).map(user => user.id); return game.users.filter(filter).map(user => user.id);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@@ -0,0 +1,70 @@
import { HIDE_DICE, SHOW_DICE } from "./constants.js";
import { RdDUtility } from "./rdd-utility.js";
/**
* Extend the base Dialog entity by defining a custom window to perform roll.
* @extends {Dialog}
*/
export class DialogValidationEncaissement extends Dialog {
static async validerEncaissement(actor, rollData, armure, show, onEncaisser) {
let encaissement = await RdDUtility.jetEncaissement(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,
show: show
});
const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, show, onEncaisser);
dialog.render(true);
}
/* -------------------------------------------- */
constructor(html, actor, rollData, armure, encaissement, show, onEncaisser) {
// Common conf
let buttons = {
"valider": { label: "Valider", callback: html => this.validerEncaissement() },
"annuler": { label: "Annuler", callback: html => { } },
};
let dialogConf = {
title: "Validation d'encaissement",
content: html,
buttons: buttons,
default: "valider"
}
let dialogOptions = {
classes: ["rdddialog"],
width: 350,
height: 290
}
// Select proper roll dialog template and stuff
super(dialogConf, dialogOptions);
this.actor = actor
this.rollData = rollData;
this.armure = armure;
this.encaissement = encaissement;
this.show = show;
this.onEncaisser = onEncaisser;
this.forceDiceResult = {total: encaissement.roll.result };
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
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});
$('label.encaissement-total').text(this.encaissement.total);
$('label.encaissement-blessure').text(this.encaissement.blessures)
});
}
async validerEncaissement() {
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
this.onEncaisser(this.encaissement, this.show)
}
}

View File

@@ -57,8 +57,8 @@ const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item { export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static actorCompendium(actorType) { static actorCompendium(actorType = undefined) {
return compendiumCompetences[actorType]; return compendiumCompetences[actorType ?? 'personnage'];
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -84,27 +84,36 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getVoieDraconic(competences, voie) { static getVoieDraconic(competences, voie) {
return RdDItemCompetence.findCompetence(competences.filter(it => RdDItemCompetence.isDraconic(it)), voie); return RdDItemCompetence.findFirstItem(competences, voie, {
preFilter: it => it.isCompetence() && RdDItemCompetence.isDraconic(it),
description: 'Draconic',
});
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static isCompetenceArme(competence) { static isCompetenceArme(competence) {
switch (competence.system.categorie) { if (competence.isCompetence()) {
case 'melee': switch (competence.system.categorie) {
return competence.name != 'Esquive'; case 'melee':
case 'tir': return !Grammar.toLowerCaseNoAccent(competence.name).includes('esquive');
case 'lancer': case 'tir':
return true; case 'lancer':
return true;
}
} }
return false; return false;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static isArmeUneMain(competence) { static isArmeUneMain(competence) {
return competence.name.toLowerCase().includes("1 main"); return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("1 main");
} }
static isArme2Main(competence) { static isArme2Main(competence) {
return competence.name.toLowerCase().includes("2 main"); return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("2 main");
}
static isThanatos(competence) {
return competence.isCompetencePersonnage() && Grammar.toLowerCaseNoAccent(competence.name).includes('thanatos');
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -133,7 +142,7 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static computeXP(competence) { static computeXP(competence) {
const factor = competence.name.includes('Thanatos') ? 2 : 1; // Thanatos compte double ! const factor = RdDItemCompetence.isThanatos(competence) ? 2 : 1; // Thanatos compte double !
const xpNiveau = RdDItemCompetence.computeDeltaXP(competence.system.base, competence.system.niveau ?? competence.system.base); const xpNiveau = RdDItemCompetence.computeDeltaXP(competence.system.base, competence.system.niveau ?? competence.system.base);
const xp = competence.system.xp ?? 0; const xp = competence.system.xp ?? 0;
const xpSort = competence.system.xp_sort ?? 0; const xpSort = competence.system.xp_sort ?? 0;
@@ -213,21 +222,18 @@ export class RdDItemCompetence extends Item {
if (idOrName == undefined) { if (idOrName == undefined) {
return undefined; return undefined;
} }
options = mergeObject(options, { options = mergeObject(options, { preFilter: it => it.isCompetence(), description: 'compétence' }, {overwrite: false});
preFilter: it => RdDItemCompetence.isCompetence(it), return RdDItemCompetence.findFirstItem(list, idOrName, options);
description: 'compétence',
});
return list.find(it => it.id == idOrName && RdDItemCompetence.isCompetence(it))
?? Misc.findFirstLike(idOrName, list, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static findCompetences(list, name) { static findCompetences(list, name) {
return Misc.findAllLike(name, list, { filter: it => RdDItemCompetence.isCompetence(it), description: 'compétence' }); return Misc.findAllLike(name, list, { filter: it => it.isCompetence(), description: 'compétence' });
} }
static isCompetence(item) { static findFirstItem(list, idOrName, options) {
return item.type == 'competence' || item.type == 'competencecreature'; return list.find(it => it.id == idOrName && options.preFilter(it))
?? Misc.findFirstLike(idOrName, list, options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@@ -71,9 +71,12 @@ export class Monnaie {
let updates = []; let updates = [];
let parValeur = Misc.classifyFirst(monnaies, it => it.system.valeur_deniers); let parValeur = Misc.classifyFirst(monnaies, it => it.system.valeur_deniers);
for (let valeur of [1000, 100, 10, 1]) { for (let valeur of [1000, 100, 10, 1]) {
if (parValeur[valeur]) { const itemPiece = parValeur[valeur];
const piecesDeCetteValeur = Math.floor(reste / valeur); const piecesDeCetteValeur = Math.floor(reste / valeur);
updates.push({ _id: parValeur[valeur].id, 'system.quantite': piecesDeCetteValeur }); if (itemPiece) {
if (piecesDeCetteValeur != itemPiece.system.quantite) {
updates.push({ _id: parValeur[valeur].id, 'system.quantite': piecesDeCetteValeur });
}
reste -= piecesDeCetteValeur*valeur; reste -= piecesDeCetteValeur*valeur;
} }
} }

View File

@@ -90,11 +90,10 @@ export class RdDItemSheet extends ItemSheet {
if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') { if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') {
formData.caracList = duplicate(game.system.model.Actor.personnage.carac) formData.caracList = duplicate(game.system.model.Actor.personnage.carac)
formData.caracList["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve) formData.caracList["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve)
formData.competences = await RdDUtility.loadCompendium('foundryvtt-reve-de-dragon.competences') formData.competences = await RdDUtility.loadItems(it => it.isCompetencePersonnage(), RdDItemCompetence.actorCompendium(this.actor?.type))
} }
if (this.item.type == 'arme') { if (this.item.type == 'arme') {
formData.competences = await RdDUtility.loadCompendium('foundryvtt-reve-de-dragon.competences', it => RdDItemCompetence.isCompetenceArme(it)); formData.competences = await RdDUtility.loadItems(it => RdDItemCompetence.isCompetenceArme(it), RdDItemCompetence.actorCompendium(this.actor?.type))
console.log(formData.competences)
} }
if (this.item.type == 'recettecuisine') { if (this.item.type == 'recettecuisine') {
formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, {async: true}) formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, {async: true})
@@ -115,7 +114,7 @@ export class RdDItemSheet extends ItemSheet {
formData.system.prdate = this.dateUpdated; formData.system.prdate = this.dateUpdated;
this.dateUpdated = undefined; this.dateUpdated = undefined;
} }
RdDHerbes.updatePotionData(formData); await RdDHerbes.updatePotionData(formData);
} }
if (formData.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) { if (formData.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) {
formData.isIngredientPotionBase = true; formData.isIngredientPotionBase = true;
@@ -160,7 +159,7 @@ export class RdDItemSheet extends ItemSheet {
html.find(".categorie").change(event => this._onSelectCategorie(event)); html.find(".categorie").change(event => this._onSelectCategorie(event));
html.find('.sheet-competence-xp').change((event) => { html.find('.sheet-competence-xp').change((event) => {
if (this.item.type == 'competence') { if (this.item.isCompetencePersonnage()) {
RdDUtility.checkThanatosXP(this.item.name); RdDUtility.checkThanatosXP(this.item.name);
} }
}); });
@@ -251,9 +250,9 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
/** @override */ /** @override */
_updateObject(event, formData) { // Deprecated en v0.8 à clarifier _updateObject(event, formData) {
// Données de bonus de cases ? // Données de bonus de cases ?
formData = RdDItemSort.buildBonusCaseStringFromFormData(formData); formData['system.bonuscase'] = RdDItemSort.buildBonusCaseStringFromFormData(formData.bonusValue, formData.caseValue);
return this.item.update(formData); return this.item.update(formData);
} }

View File

@@ -63,26 +63,21 @@ export class RdDItemSort extends Item {
/** Met à jour les données de formulaire /** Met à jour les données de formulaire
* si static des bonus de cases sont présents * si static des bonus de cases sont présents
* */ * */
static buildBonusCaseStringFromFormData( formData ) { static buildBonusCaseStringFromFormData( bonuses, cases ) {
if ( formData.bonusValue ) { if ( bonuses ) {
let list = []; let list = [];
let caseCheck = {}; let caseCheck = {};
for(let i=0; i<formData.bonusValue.length; i++) { for (let i=0; i<bonuses.length; i++) {
let coord = formData.caseValue[i] || 'A1'; let coord = cases[i]?.toUpperCase() || 'A1';
coord = coord.toUpperCase(); let bonus = bonuses[i] || 0;
if ( TMRUtility.verifyTMRCoord( coord ) ) { // Sanity check if ( TMRUtility.verifyTMRCoord( coord ) && bonus > 0 && caseCheck[coord] == undefined ) {
let bonus = formData.bonusValue[i] || 0; caseCheck[coord] = bonus;
if ( bonus > 0 && caseCheck[coord] == undefined ) { list.push( coord+":"+bonus );
caseCheck[coord] = bonus;
list.push( coord+":"+bonus );
}
} }
} }
formData.bonusValue = undefined; return list.toString();
formData.caseValue = undefined;
formData.system.bonuscase = list.toString(); // Reset
} }
return formData; return undefined;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@@ -21,7 +21,7 @@ const typesObjetsOeuvres = ["oeuvre", "recettecuisine", "musique", "chant", "dan
const typesObjetsDraconiques = ["queue", "ombre", "souffle", "tete", "signedraconique", "sortreserve"] const typesObjetsDraconiques = ["queue", "ombre", "souffle", "tete", "signedraconique", "sortreserve"]
const typesObjetsConnaissance = ["meditation", "recettealchimique", "sort"] const typesObjetsConnaissance = ["meditation", "recettealchimique", "sort"]
const typesObjetsEffet = ["possession", "poison", "maladie"] const typesObjetsEffet = ["possession", "poison", "maladie"]
const typesObjetsCompetence = ["competence", "compcreature"] const typesObjetsCompetence = ["competence", "competencecreature"]
const encBrin = 0.00005; // un brin = 1 décigramme = 1/10g = 1/10000kg = 1/20000 enc const encBrin = 0.00005; // un brin = 1 décigramme = 1/10g = 1/10000kg = 1/20000 enc
const encPepin = 0.0007; /* un pépin de gemme = 1/10 cm3 = 1/1000 l = 3.5/1000 kg = 7/2000 kg = 7/1000 enc const encPepin = 0.0007; /* un pépin de gemme = 1/10 cm3 = 1/1000 l = 3.5/1000 kg = 7/2000 kg = 7/1000 enc
densité 3.5 (~2.3 à 4, parfois plus) -- https://www.juwelo.fr/guide-des-pierres/faits-et-chiffres/ densité 3.5 (~2.3 à 4, parfois plus) -- https://www.juwelo.fr/guide-des-pierres/faits-et-chiffres/
@@ -29,7 +29,7 @@ densité 3.5 (~2.3 à 4, parfois plus) -- https://www.juwelo.fr/guide-des-pierre
export const defaultItemImg = { export const defaultItemImg = {
competence: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp", competence: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
compcreature: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp", competencecreature: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
arme: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/epee_gnome.webp", arme: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/epee_gnome.webp",
armure: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/armure_plaques.webp", armure: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/armure_plaques.webp",
conteneur: "systems/foundryvtt-reve-de-dragon/icons/objets/sac_a_dos.webp", conteneur: "systems/foundryvtt-reve-de-dragon/icons/objets/sac_a_dos.webp",
@@ -77,6 +77,9 @@ export class RdDItem extends Item {
return typesObjetsOeuvres return typesObjetsOeuvres
} }
isCompetencePersonnage() {
return this.type == 'competence'
}
isCompetence() { isCompetence() {
return typesObjetsCompetence.includes(this.type) return typesObjetsCompetence.includes(this.type)
} }
@@ -180,8 +183,8 @@ export class RdDItem extends Item {
this.system.magique = categorie.includes('enchante'); this.system.magique = categorie.includes('enchante');
if (this.system.magique) { if (this.system.magique) {
if (categorie.includes('soin') || categorie.includes('repos')) { if (categorie.includes('soin') || categorie.includes('repos')) {
// TODO: utiliser calculePointsRepos / calculePointsGuerison // TODO: utiliser calculPointsRepos / calculPointsGuerison
this.system.puissance = RdDHerbes.calculePuissancePotion(this); this.system.puissance = RdDHerbes.calculPuissancePotion(this);
} }
} }
} }
@@ -239,30 +242,32 @@ export class RdDItem extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
// détermine si deux équipements sont similaires: de même type, et avec les même champs hormis la quantité // détermine si deux équipements sont similaires: de même type, et avec les même champs hormis la quantité
messageEquipementDifferent(other) { isEquipementEmpilable(other) {
if (!other || !this.isEquipement()) return undefined; if (!other || !this.isEquipement()) {
return [false, undefined];
}
let message = undefined;
if (this.system.quantite == undefined) { if (this.system.quantite == undefined) {
message = `Impossible de regrouper des ${this.type}, ils ne sont pas empilables`; return [false, `Impossible de regrouper des ${this.type}, ils ne sont pas empilables`];
} }
else if (this.type != other.type) { else if (this.type != other.type) {
message = `Impossible de regrouper des ${this.type} avec des ${other.type}`; return [false, `Impossible de regrouper des ${this.type} avec des ${other.type}`];
} }
else if (this.name != other.name) { else if (this.name != other.name) {
message = `Impossible de regrouper ${this.name} avec ${other.name}`; return [false, `Impossible de regrouper ${this.name} avec ${other.name}`];
} }
else { else {
const differences = Object.entries(this.system) const differences = Object.entries(this.system)
.filter(([key, value]) => !['quantite', 'cout'].includes(key) && value != other.system[key]); .filter(([key, value]) => !['quantite', 'cout', 'encTotal'].includes(key) && value != other.system[key]);
if (differences.length > 0) { if (differences.length > 0) {
message = `Impossible de regrouper les ${this.type} ${this.name}: `; let message = `Impossible de regrouper les ${this.type} ${this.name}: `;
for (const [key, value] of differences) { for (const [key, value] of differences) {
message += `<br>${key}: ${value} vs ${other.system[key]}`; message += `<br>${key}: ${value} vs ${other.system[key]}`;
} }
return [false, message];
} }
} }
return message; return [true, undefined];
} }
async proposerVente() { async proposerVente() {

View File

@@ -112,16 +112,12 @@ export class Misc {
return Misc.firstConnectedGM()?.id ?? game.user.id; return Misc.firstConnectedGM()?.id ?? game.user.id;
} }
static getUsers() {
return game.version ? game.users : game.users.entities;
}
static getActiveUser(id) { static getActiveUser(id) {
return Misc.getUsers().find(u => u.id == id && u.active); return game.users.find(u => u.id == id && u.active);
} }
static firstConnectedGM() { static firstConnectedGM() {
return Misc.getUsers().filter(u => u.isGM && u.active).sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active); return game.users.filter(u => u.isGM && u.active).sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
} }
@@ -142,7 +138,7 @@ export class Misc {
/* -------------------------------------------- */ /* -------------------------------------------- */
static findPlayer(name) { static findPlayer(name) {
return Misc.findFirstLike(name, Misc.getUsers(), { description: 'joueur' }); return Misc.findFirstLike(name, game.users, { description: 'joueur' });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@@ -721,12 +721,12 @@ export class RdDCombat {
} }
} }
isVisible(token, defender) { isVisible(token, defenderToken) {
return canvas.effects.visibility.testVisibility(defender.center, { object: token }) return canvas.effects.visibility.testVisibility(defenderToken.center, { object: token })
} }
distance(t, defenderToken) { distance(token, defenderToken) {
return Number(canvas.grid.measureDistances([{ ray: new Ray(t.center, defenderToken.center) }], { gridSpaces: false })).toFixed(1); return Number(canvas.grid.measureDistances([{ ray: new Ray(token.center, defenderToken.center) }], { gridSpaces: false })).toFixed(1);
} }
_ajustementPortee(dist, arme) { _ajustementPortee(dist, arme) {
@@ -878,7 +878,7 @@ export class RdDCombat {
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), this.defender.isEntite()); attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), 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.system.name : 'la cible', cible: this.target ? this.defender.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');
@@ -1303,7 +1303,7 @@ export class RdDCombat {
attackerRoll.defenderTokenId = defenderTokenId; attackerRoll.defenderTokenId = defenderTokenId;
await this.computeRecul(defenderRoll); await this.computeRecul(defenderRoll);
this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll); this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show);
} }
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, {

View File

@@ -132,18 +132,15 @@ export class RdDDice {
} }
} }
static async roll(formula, options = { showDice: SHOW_DICE, rollMode: undefined }) { static async rollTotal(formula, options = { showDice: HIDE_DICE }) {
const roll = new Roll(formula); return (await RdDDice.roll(formula, options)).total;
await roll.evaluate({ async: true });
if (options.showDice != HIDE_DICE) {
await this.showDiceSoNice(roll, options.rollMode ?? game.settings.get("core", "rollMode"));
}
return roll;
} }
static async rollTotal(formula, options = { showDice: HIDE_DICE}) { static async roll(formula, options = { showDice: SHOW_DICE, rollMode: undefined }) {
const roll = await RdDDice.roll(formula, options); const roll = new Roll(RdDDice._formulaOrFake(formula, options));
return roll.total; await roll.evaluate({ async: true });
await this.showDiceSoNice(roll, options);
return roll;
} }
static async rollOneOf(array) { static async rollOneOf(array) {
@@ -160,27 +157,106 @@ export class RdDDice {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async showDiceSoNice(roll, rollMode) { static async showDiceSoNice(roll, options) {
if (game.modules.get("dice-so-nice")?.active) { if (options.showDice == HIDE_DICE || !game.modules.get("dice-so-nice")?.active || !game.dice3d) {
if (game.dice3d) { return;
let whisper = null; }
let blind = false;
rollMode = rollMode ?? game.settings.get("core", "rollMode"); let { whisper, blind } = RdDDice._getWhisperBlind(options);
switch (rollMode) { if (options.forceDiceResult?.total) {
case "blindroll": //GM only let terms = await RdDDice._getForcedTerms(options);
blind = true; if (terms) {
case "gmroll": //GM + rolling player await game.dice3d.show({ throws: [{ dice: terms }] })
whisper = ChatUtility.getUsers(user => user.isGM); return;
break;
case "roll": //everybody
whisper = ChatUtility.getUsers(user => user.active);
break;
case "selfroll":
whisper = [game.user.id];
break;
}
await game.dice3d.showForRoll(roll, game.user, true, whisper, blind);
} }
} }
await game.dice3d.showForRoll(roll, game.user, true, whisper, blind);
}
static _formulaOrFake(formula, options) {
if (options?.forceDiceResult?.total) {
options.forceDiceResult.formula = formula;
return options.forceDiceResult.total.toString()
}
return formula;
}
static async _getForcedTerms(options) {
const total = options.forceDiceResult.total;
switch (options.forceDiceResult.formula) {
case '1d100':
return terms1d100(total);
case "2d10":
return await terms2d10(total);
}
return undefined;
function terms1d100(total) {
const unites = total % 10;
const dizaines = Math.floor(total / 10);
return [{
resultLabel: dizaines * 10,
d100Result: total,
result: dizaines,
type: "d100",
vectors: [],
options: {}
},
{
resultLabel: unites,
d100Result: total,
result: unites,
type: "d10",
vectors: [],
options: {}
}];
}
async function terms2d10(total) {
if (total>20 || total<2) { return undefined }
let first = await RdDDice.d10();
let second = Math.min(total-first, 10);
first = Math.max(first, total-second);
return [{
resultLabel:first,
result: first,
type: "d10",
vectors: [],
options: {}
},
{
resultLabel: second,
result: second,
type: "d10",
vectors: [],
options: {}
}];
}
}
static async d10() {
let roll = new Roll('1d10');
await roll.evaluate({ async: true });
return roll.total;
}
static _getWhisperBlind(options) {
let whisper = null;
let blind = false;
let rollMode = options.rollMode ?? game.settings.get("core", "rollMode");
switch (rollMode) {
case "blindroll": //GM only
blind = true;
case "gmroll": //GM + rolling player
whisper = ChatUtility.getUsers(user => user.isGM);
break;
case "roll": //everybody
whisper = ChatUtility.getUsers(user => user.active);
break;
case "selfroll":
whisper = [game.user.id];
break;
}
return { whisper, blind };
} }
} }

View File

@@ -1,76 +1,75 @@
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
import { RdDCalendrier } from "./rdd-calendrier.js"; import { RdDCalendrier } from "./rdd-calendrier.js";
import { Grammar } from "./grammar.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDHerbes extends Item { export class RdDHerbes extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static isHerbeSoin( botaniqueItem ) { static async initializeHerbes() {
return botaniqueItem.categorie == 'Soin'; this.herbesSoins = await RdDHerbes.listCategorieHerbes('Soin');
} this.herbesRepos = await RdDHerbes.listCategorieHerbes('Repos');
/* -------------------------------------------- */
static isHerbeRepos( botaniqueItem ) {
return botaniqueItem.categorie == 'Repos';
} }
/* -------------------------------------------- */ static async listCategorieHerbes(categorie) {
static async initializeHerbes( ) { return await RdDUtility.loadItems(
this.herbesSoins = await RdDUtility.loadCompendium('foundryvtt-reve-de-dragon.botanique', item => this.isHerbeSoin(item)); it => it.type == 'herbe' && it.system.categorie.toLowerCase() == categorie.toLowerCase(),
this.herbesRepos = await RdDUtility.loadCompendium('foundryvtt-reve-de-dragon.botanique', item => this.isHerbeRepos(item)); 'foundryvtt-reve-de-dragon.botanique');
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static buildHerbesList(listeHerbes, max) { static buildHerbesList(listeHerbes, max) {
let list = {} let list = {}
for ( let herbe of listeHerbes) { for (let herbe of listeHerbes) {
let brins = max - herbe.system.niveau; let brins = max - herbe.system.niveau;
list[herbe.system.name] = `${herbe.system.name} (Bonus: ${herbe.system.niveau}, Brins: ${brins})`; list[herbe.name] = `${herbe.name} (Bonus: ${herbe.system.niveau}, Brins: ${brins})`;
} }
list['Autre'] = 'Autre (Bonus: variable, Brins: variable)' list['Autre'] = 'Autre (Bonus: variable, Brins: variable)'
return list; return list;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static updatePotionData( formData ) { static async updatePotionData(formData) {
formData.herbesSoins = this.buildHerbesList(this.herbesSoins, 12); formData.isSoins = formData.system.categorie.includes('Soin');
formData.herbesRepos = this.buildHerbesList(this.herbesRepos, 7); formData.isRepos = formData.system.categorie.includes('Repos');
if (formData.isSoins) {
RdDHerbes.calculBonusHerbe(formData, this.herbesSoins, 12);
}
if (formData.isRepos) {
RdDHerbes.calculBonusHerbe(formData, this.herbesRepos, 7);
}
formData.herbesSoins = RdDHerbes.buildHerbesList(this.herbesSoins, 12);
formData.herbesRepos = RdDHerbes.buildHerbesList(this.herbesRepos, 7);
formData.jourMoisOptions = RdDCalendrier.buildJoursMois(); formData.jourMoisOptions = RdDCalendrier.buildJoursMois();
formData.dateActuelle = game.system.rdd.calendrier.getDateFromIndex(); formData.dateActuelle = game.system.rdd.calendrier.getDateFromIndex();
formData.splitDate = game.system.rdd.calendrier.getNumericDateFromIndex(formData.system.prdate); formData.splitDate = game.system.rdd.calendrier.getNumericDateFromIndex(formData.system.prdate);
if (formData.system.categorie.includes('Soin') ) {
formData.isHerbe = true;
this.computeHerbeBonus(formData, this.herbesSoins, 12);
} else if (formData.system.categorie.includes('Repos')) {
formData.isRepos = true;
this.computeHerbeBonus(formData, this.herbesRepos, 7);
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static calculePuissancePotion( potion ) { static calculPuissancePotion(potion) {
return potion.system.herbebonus * potion.system.pr; return potion.system.herbebonus * potion.system.pr;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static calculePointsRepos( potion ) { static calculPointsRepos(potion) {
return potion.system.herbebonus * potion.system.pr; return potion.system.herbebonus * potion.system.pr;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static calculePointsGuerison( potion ){ static calculPointsGuerison(potion) {
return potion.system.herbebonus * potion.system.pr; return potion.system.herbebonus * potion.system.pr;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static computeHerbeBonus( formData, herbesList, max) { static calculBonusHerbe(formData, herbesList, max) {
if ( Number(formData.system.herbebrins) ) { if (Number(formData.system.herbebrins)) {
let herbe = herbesList.find(item => item.name.toLowerCase() == formData.system.herbe.toLowerCase() ); let herbe = herbesList.find(item => item.name.toLowerCase() == formData.system.herbe.toLowerCase());
if( herbe ) { if (herbe) {
let brinsBase = max - herbe.system.niveau; const brinsRequis = max - herbe.system.niveau;
formData.system.herbebonus = Math.max(herbe.system.niveau - Math.max(brinsBase - formData.system.herbebrins, 0), 0); const brinsManquants = Math.max(brinsRequis - formData.system.herbebrins, 0);
formData.system.herbebonus = Math.max(herbe.system.niveau - brinsManquants, 0);
} }
} }
} }
} }

View File

@@ -148,7 +148,7 @@ Hooks.once("init", async function () {
}; };
/* -------------------------------------------- */ /* -------------------------------------------- */
game.socket.on(SYSTEM_SOCKET_ID, sockmsg => { game.socket.on(SYSTEM_SOCKET_ID, async (sockmsg) => {
console.log(">>>>> MSG RECV", sockmsg); console.log(">>>>> MSG RECV", sockmsg);
try { try {
RdDUtility.onSocketMessage(sockmsg); RdDUtility.onSocketMessage(sockmsg);

View File

@@ -142,10 +142,8 @@ export class RdDResolutionTable {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async rollChances(chances, diviseur, forceDiceResult = -1) { static async rollChances(chances, diviseur, forceDiceResult = -1) {
if (forceDiceResult <= 0 || forceDiceResult > 100) { chances.forceDiceResult = forceDiceResult <= 0 || forceDiceResult > 100 ? undefined : {total: forceDiceResult};
forceDiceResult = -1; chances.roll = await RdDDice.rollTotal( "1d100", chances);
}
chances.roll = await RdDDice.rollTotal((forceDiceResult == -1) ? "1d100" : `${forceDiceResult}`, chances);
mergeObject(chances, this.computeReussite(chances, chances.roll, diviseur), { overwrite: true }); mergeObject(chances, this.computeReussite(chances, chances.roll, diviseur), { overwrite: true });
return chances; return chances;
} }

View File

@@ -1,4 +1,4 @@
import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE } from "./constants.js"; import { ENTITE_BLURETTE, ENTITE_INCARNE} from "./constants.js";
/** /**
* Extend the base Dialog entity by defining a custom window to perform roll. * Extend the base Dialog entity by defining a custom window to perform roll.
@@ -33,7 +33,7 @@ export class RdDEncaisser extends Dialog {
let dialogOptions = { let dialogOptions = {
classes: ["rdddialog"], classes: ["rdddialog"],
width: 320, width: 320,
height: 240 height: 260
} }
// Select proper roll dialog template and stuff // Select proper roll dialog template and stuff
@@ -51,6 +51,7 @@ export class RdDEncaisser extends Dialog {
this.actor.encaisserDommages({ this.actor.encaisserDommages({
dmg: { dmg: {
total: Number(this.modifier), total: Number(this.modifier),
ajustement: Number(this.modifier),
encaisserSpecial: this.encaisserSpecial, encaisserSpecial: this.encaisserSpecial,
loc: { result: 0, label: "" }, loc: { result: 0, label: "" },
mortalite: mortalite mortalite: mortalite

View File

@@ -144,6 +144,7 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onAction(action, html) { async onAction(action, html) {
this.rollData.forceDiceResult = Number.parseInt($('#force-dice-result').val()) ?? -1;
await RdDResolutionTable.rollData(this.rollData); await RdDResolutionTable.rollData(this.rollData);
console.log("RdDRoll -=>", this.rollData, this.rollData.rolled); console.log("RdDRoll -=>", this.rollData, this.rollData.rolled);
this.actor.setRollWindowsOpened(false); this.actor.setRollWindowsOpened(false);

View File

@@ -2,16 +2,22 @@ export class RdDRollTables {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async genericGetTableResult(tableName, toChat) { static async genericGetTableResult(tableName, toChat) {
let table = game.tables.find(table => table.name.toLowerCase() == tableName.toLowerCase()) let table = RdDRollTables.getWorldTable(tableName) ?? (await RdDRollTables.getSystemTable(tableName));
if ( !table) {
const pack = game.packs.get("foundryvtt-reve-de-dragon.tables-diverses");
const index = await pack.getIndex();
const entry = index.find(e => e.name === tableName);
table = await pack.getDocument(entry._id);
}
const draw = await table.draw({ displayChat: toChat, rollMode: "gmroll"}); const draw = await table.draw({ displayChat: toChat, rollMode: "gmroll"});
//console.log("RdDRollTables", tableName, toChat, ":", draw); //console.log("RdDRollTables", tableName, toChat, ":", draw);
return draw.results.length > 0 ? draw.results[0] : undefined; return draw.results.length > 0 ? draw.results[0] : undefined;
}
static getWorldTable(tableName) {
return game.tables.find(table => table.name.toLowerCase() == tableName.toLowerCase());
}
static async getSystemTable(tableName) {
const pack = game.packs.get("foundryvtt-reve-de-dragon.tables-diverses");
const index = await pack.getIndex();
const entry = index.find(e => e.name === tableName);
return await pack.getDocument(entry._id);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@@ -13,6 +13,7 @@ import { Monnaie } from "./item-monnaie.js";
import { RdDPossession } from "./rdd-possession.js"; import { RdDPossession } from "./rdd-possession.js";
import { RdDNameGen } from "./rdd-namegen.js"; import { RdDNameGen } from "./rdd-namegen.js";
import { RdDConfirm } from "./rdd-confirm.js"; import { RdDConfirm } from "./rdd-confirm.js";
import { RdDActor } from "./actor.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
// This table starts at 0 -> niveau -10 // This table starts at 0 -> niveau -10
@@ -213,6 +214,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html', 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html', 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-encaisser.html', 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-encaisser.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-meditation.html', 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-meditation.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', 'systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-alchimie.html', 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-alchimie.html',
@@ -652,7 +654,68 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static selectEncaissement(degats, mortalite) { static async jetEncaissement(rollData, armure, options = { showDice: HIDE_DICE }) {
let formula = "2d10";
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "min" + valeurMin;
}
}
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-ajout-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "+" + valeurMin;
}
}
let roll = await RdDDice.roll(formula, options);
// 1 dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre-simple')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
if (roll.terms[0].results[0].result < valeurMin) {
roll.terms[0].results[0].result = valeurMin;
} else if (roll.terms[0].results[1].result < valeurMin) {
roll.terms[0].results[1].result = valeurMin;
}
roll._total = roll.terms[0].results[0].result + roll.terms[0].results[1].result;
}
}
return await RdDUtility.prepareEncaissement(rollData, roll, armure);
}
/* -------------------------------------------- */
static async prepareEncaissement(rollData, roll, armure) {
const jetTotal = roll.total + rollData.dmg.total - armure;
let encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite);
let 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.roll = roll;
encaissement.armure = armure;
encaissement.total = jetTotal;
encaissement.vie = await RdDUtility._evaluatePerte(encaissement.vie, over20);
encaissement.endurance = await RdDUtility._evaluatePerte(encaissement.endurance, over20);
encaissement.penetration = rollData.arme?.system.penetration ?? 0;
encaissement.blessures = (
encaissement.critiques> 0 ? "Critique":
encaissement.graves> 0 ? "Grave":
encaissement.legeres> 0 ? "Légère":
encaissement.eraflures>0 ? "Contusions/Eraflures":
'Aucune'
);
return encaissement;
}
/* -------------------------------------------- */
static _selectEncaissement(degats, mortalite) {
const table = definitionsEncaissement[mortalite] === undefined ? definitionsEncaissement["mortel"] : definitionsEncaissement[mortalite]; const table = definitionsEncaissement[mortalite] === undefined ? definitionsEncaissement["mortel"] : definitionsEncaissement[mortalite];
for (let encaissement of table) { for (let encaissement of table) {
if ((encaissement.minimum === undefined || encaissement.minimum <= degats) if ((encaissement.minimum === undefined || encaissement.minimum <= degats)
@@ -663,6 +726,13 @@ export class RdDUtility {
return duplicate(table[0]); return duplicate(table[0]);
} }
/* -------------------------------------------- */
static async _evaluatePerte(formula, over20) {
let perte = new Roll(formula, { over20: over20 });
await perte.evaluate({ async: true });
return perte.total;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static currentFatigueMalus(value, max) { static currentFatigueMalus(value, max) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) { if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
@@ -683,17 +753,28 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async loadCompendiumData(compendium) { static async loadItems(filter, compendium) {
const pack = game.packs.get(compendium); let items = game.items.filter(filter);
return await pack?.getDocuments() ?? []; if (compendium) {
const ids = items.map(it => it.id);
const names = items.map(it => it.name.toLowerCase());
items = items.concat(await RdDUtility.loadCompendium(compendium, it => !ids.includes(it.id) && !names.includes(it.name.toLowerCase()) && filter(it)));
}
return items;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async loadCompendium(compendium, filter = item => true) { static async loadCompendium(compendium, filter = it => true) {
let compendiumData = await RdDUtility.loadCompendiumData(compendium); let compendiumData = await RdDUtility.loadCompendiumData(compendium);
return compendiumData.filter(filter); return compendiumData.filter(filter);
} }
/* -------------------------------------------- */
static async loadCompendiumData(compendium) {
const pack = game.packs.get(compendium);
return await pack?.getDocuments() ?? [];
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async responseNombreAstral(callData) { static async responseNombreAstral(callData) {
let actor = game.actors.get(callData.id); let actor = game.actors.get(callData.id);

View File

@@ -2,29 +2,33 @@ import { SYSTEM_RDD } from "./constants.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
const listeReglesOptionelles = [ const listeReglesOptionelles = [
{ name: 'recul', group: 'Règles de combat', 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" },
{ name: 'resistanceArmeParade', group: 'Règles de combat', 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" },
{ name: 'deteriorationArmure', group: 'Règles de combat', 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" },
{ name: 'defenseurDesarme', group: 'Règles de combat', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" }, { group: 'Règles de combat', name: 'defenseurDesarme', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" },
{ name: 'categorieParade', group: 'Règles de combat', descr: "Le défenseur doit obtenir une significative en cas de parade avec des armes de catégories différentes" }, { group: 'Règles de combat', name: 'categorieParade', descr: "Le défenseur doit obtenir une significative en cas de parade avec des armes de catégories différentes" },
{ name: 'tripleSignificative', group: 'Règles de combat', descr: "En cas de demi-surprise, d'attaque particulière en finesse, et de catégories d'armes différentes, le défenseur doit obtenir 1/8 des chances de succès" }, { group: 'Règles de combat', name: 'tripleSignificative', descr: "En cas de demi-surprise, d'attaque particulière en finesse, et de catégories d'armes différentes, le défenseur doit obtenir 1/8 des chances de succès" },
{ name: 'degat-minimum-malus-libre-simple', group: 'Règles de combat', descr: "Le malus libre d'attaque remplace une des valeurs de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, si 1 résultat est inférieur à 4, alors il devient 4.", default: false }, { group: 'Règles de combat', name: 'degat-minimum-malus-libre-simple', descr: "Le malus libre d'attaque remplace une des valeurs de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, si le plus petit dé est inférieur à 4, alors il devient 4.", default: false },
{ name: 'degat-minimum-malus-libre', group: 'Règles de combat', descr: "Le malus libre d'attaque remplace une valeur de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, tout résultat inférieur à 4 devient 4.", default: false }, { group: 'Règles de combat', name: 'degat-minimum-malus-libre', descr: "Le malus libre d'attaque remplace une valeur de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, tout résultat inférieur à 4 devient 4.", default: false },
{ name: 'degat-ajout-malus-libre', group: 'Règles de combat', descr: "Le malus libre d'attaque s'ajoute au jet d'encaissement et aux autres bonus. Exemple : la difficulté libre de l'attaquant est de -4. Le jet d'encaissement est effectué à 2d10+4, plus les bonus de situation et d'armes.", default: false }, { group: 'Règles de combat', name: 'degat-ajout-malus-libre', descr: "Le malus libre d'attaque s'ajoute au jet d'encaissement et aux autres bonus. Exemple : la difficulté libre de l'attaquant est de -4. Le jet d'encaissement est effectué à 2d10+4, plus les bonus de situation et d'armes.", default: false },
{ name: 'astrologie', group: 'Règles générales', descr: "Appliquer les ajustements astrologiques aux jets de chance et aux rituels"}, { group: 'Règles de combat', name: 'validation-encaissement-gr', descr: "Le Gardien des Rêves doit valider les jets d'encaissement et peut les changer.", default: false },
{ name: 'afficher-prix-joueurs', group: 'Règles générales', descr: "Afficher le prix de l'équipement des joueurs", uniquementJoueur: true},
{ name: 'appliquer-fatigue', group: 'Règles générales', descr: "Appliquer les règles de fatigue"}, { group: 'Règles générales', name: 'astrologie', descr: "Appliquer les ajustements astrologiques aux jets de chance et aux rituels"},
{ name: 'afficher-colonnes-reussite', group: 'Règles générales', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false }, { group: 'Règles générales', name: 'afficher-prix-joueurs', descr: "Afficher le prix de l'équipement des joueurs", uniquementJoueur: true},
{ name: 'confirmation-tmr', group: 'Confirmations', descr: "Confirmer pour monter dans les TMR", scope: "client"}, { group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"},
{ name: 'confirmation-vider', group: 'Confirmations', descr: "Confirmer pour vider l'équipement", scope: "client"}, { group: 'Règles générales', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false },
{ name: 'confirmation-supprimer-lien-acteur', group: 'Confirmations', descr: "Confirmer pour détacher un animal/suivant/véhicule", scope: "client"},
{ name: 'confirmation-supprimer-equipement', group: 'Confirmations', descr: "Confirmer la suppression des équipements", scope: "client"}, { group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"},
{ name: 'confirmation-supprimer-oeuvre', group: 'Confirmations', descr: "Confirmer la suppression des oeuvres", scope: "client"}, { group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"},
{ name: 'confirmation-supprimer-connaissance', group: 'Confirmations', descr: "Confirmer la suppression des connaissances", scope: "client"}, { group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},
{ name: 'confirmation-supprimer-draconique', group: 'Confirmations', descr: "Confirmer la suppression des queues, souffles, têtes", scope: "client"}, { group: 'Confirmations', name: 'confirmation-supprimer-lien-acteur', descr: "Confirmer pour détacher un animal/suivant/véhicule", scope: "client"},
{ name: 'confirmation-supprimer-effet', group: 'Confirmations', descr: "Confirmer la suppression des effets", scope: "client"}, { group: 'Confirmations', name: 'confirmation-supprimer-equipement', descr: "Confirmer la suppression des équipements", scope: "client"},
{ name: 'confirmation-supprimer-competence', group: 'Confirmations', descr: "Confirmer la suppression des compétences", scope: "client"}, { group: 'Confirmations', name: 'confirmation-supprimer-oeuvre', descr: "Confirmer la suppression des oeuvres", scope: "client"},
{ name: 'confirmation-supprimer-autres', group: 'Confirmations', descr: "Confirmer la suppression des autres types d'Objets", scope: "client"}, { group: 'Confirmations', name: 'confirmation-supprimer-connaissance', descr: "Confirmer la suppression des connaissances", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-draconique', descr: "Confirmer la suppression des queues, souffles, têtes", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-effet', descr: "Confirmer la suppression des effets", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-competence', descr: "Confirmer la suppression des compétences", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-autres', descr: "Confirmer la suppression des autres types d'Objets", scope: "client"},
]; ];
const uniquementJoueur = listeReglesOptionelles.filter(it => it.uniquementJoueur).map(it=>it.name); const uniquementJoueur = listeReglesOptionelles.filter(it => it.uniquementJoueur).map(it=>it.name);
@@ -73,7 +77,7 @@ export class ReglesOptionelles extends FormApplication {
const regles = listeReglesOptionelles.filter(it => game.user.isGM || it.scope == "client").map(it => { const regles = listeReglesOptionelles.filter(it => game.user.isGM || it.scope == "client").map(it => {
it = duplicate(it); it = duplicate(it);
it.id = ReglesOptionelles._getIdRegle(it.name); it.id = ReglesOptionelles._getIdRegle(it.name);
it.active = ReglesOptionelles.isUsing(it.name); it.active = ReglesOptionelles.isSet(it.name);
return it; return it;
}); });
formData.regles = regles; formData.regles = regles;
@@ -85,11 +89,11 @@ export class ReglesOptionelles extends FormApplication {
if (game.user.isGM && uniquementJoueur.includes(name)) { if (game.user.isGM && uniquementJoueur.includes(name)) {
return true; return true;
} }
return game.settings.get(SYSTEM_RDD, ReglesOptionelles._getIdRegle(name)); return ReglesOptionelles.isSet(name);
} }
static isSet(name) { static isSet(name) {
return ReglesOptionelles.isUsing(name); return game.settings.get(SYSTEM_RDD, ReglesOptionelles._getIdRegle(name));
} }
static set(name, value) { static set(name, value) {

View File

@@ -1,6 +1,14 @@
{ {
"id": "foundryvtt-reve-de-dragon", "id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon", "title": "Rêve de Dragon",
"version": "10.0.30",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-10.0.30.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v10/system.json",
"compatibility": {
"minimum": "10",
"verified": "10",
"maximum": "10"
},
"description": "Rêve de Dragon RPG for FoundryVTT", "description": "Rêve de Dragon RPG for FoundryVTT",
"authors": [ "authors": [
{ {
@@ -34,11 +42,6 @@
], ],
"url": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/", "url": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/",
"license": "LICENSE.txt", "license": "LICENSE.txt",
"version": "10.0.26",
"compatibility": {
"minimum": "10",
"verified": "10.287"
},
"esmodules": [ "esmodules": [
"module/rdd-main.js" "module/rdd-main.js"
], ],
@@ -332,8 +335,6 @@
} }
], ],
"socket": true, "socket": true,
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v10/system.json",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-10.0.26.zip",
"gridDistance": 1, "gridDistance": 1,
"gridUnits": "m", "gridUnits": "m",
"primaryTokenAttribute": "sante.vie", "primaryTokenAttribute": "sante.vie",

View File

@@ -4,7 +4,7 @@
{{#if (and queues.length ombres.length)}} et {{/if}} {{#if (and queues.length ombres.length)}} et {{/if}}
{{#if ombres.length}}Ombres de Thanatos{{/if}} {{#if ombres.length}}Ombres de Thanatos{{/if}}
</h3> </h3>
<ul class="flex-group-left"> <ul class="item-list">
{{#each queues as |queue key|}} {{#each queues as |queue key|}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/dragon-queue.html" queue=queue key=key}} {{> "systems/foundryvtt-reve-de-dragon/templates/actor/dragon-queue.html" queue=queue key=key}}
{{/each}} {{/each}}

View File

@@ -2,15 +2,15 @@
<h4>Equipement</h4> <h4>Equipement</h4>
<span class="item-name"> <span class="item-name">
Encombrement: {{numberFormat calc.encTotal decimals=2}} (max: {{system.attributs.encombrement.value}}){{#if calc.surEncombrementMessage}}<b>{{calc.surEncombrementMessage}}</b>{{/if}}
{{#if (regle-optionnelle 'afficher-prix-joueurs')}}
&hyphen; Valeur: {{numberFormat calc.prixTotalEquipement decimals=2}} Sols
{{/if}}
&hyphen;
<a class="chat-card-button creer-un-objet">Nouvel objet</a> <a class="chat-card-button creer-un-objet">Nouvel objet</a>
{{#if options.isGM}} {{#if options.isGM}}
<a class="chat-card-button nettoyer-conteneurs">Tout vider</a> <a class="chat-card-button nettoyer-conteneurs">Tout vider</a>
{{/if}} {{/if}}
{{#if calc.surEncombrementMessage}}<b>{{calc.surEncombrementMessage}}</b> &hyphen;{{/if}}
Encombrement: {{numberFormat calc.encTotal decimals=2}} (max: {{system.attributs.encombrement.value}})
{{#if (regle-optionnelle 'afficher-prix-joueurs')}}
&hyphen; Valeur: {{numberFormat calc.prixTotalEquipement decimals=2}} Sols
{{/if}}
</span> </span>
<ul class="item-list alterne-list"> <ul class="item-list alterne-list">
<li class="competence-header flexrow"> <li class="competence-header flexrow">

View File

@@ -1,8 +1,8 @@
<form class="encaisse-roll-dialog"> <form class="encaisse-roll-dialog">
<h2 class="encaisserdialog" id="encaisserTitle"></h2> <h2 class="encaisserdialog" id="encaisserTitle"></h2>
<div class="form-group"> <div class="flexrow">
<label class="competence-label">Modificateurs aux Dommages:</label> <label>Modificateurs aux Dommages:</label>
<select class="competence-value" name="modificateurDegats" id="modificateurDegats" data-dtype="number"> <select class="competence-value flex-shrink" name="modificateurDegats" id="modificateurDegats" data-dtype="number">
{{#select modificateurDegats}} {{#select modificateurDegats}}
{{#each ajustementsEncaissement as |key|}} {{#each ajustementsEncaissement as |key|}}
<option value={{key}}>{{numberFormat key decimals=0 sign=true}}</option> <option value={{key}}>{{numberFormat key decimals=0 sign=true}}</option>
@@ -10,9 +10,9 @@
{{/select}} {{/select}}
</select> </select>
</div> </div>
<div class="form-group"> <div class="flexcol">
<label class="competence-label">Cas particuliers:</label> <label >Cas particuliers:</label>
<select class="competence-value" name="encaisserSpecial" id="encaisserSpecial" data-dtype="String"> <select name="encaisserSpecial" id="encaisserSpecial" data-dtype="String">
<option value="aucun">Aucun</option> <option value="aucun">Aucun</option>
<option value="noarmure">Ne pas compter les Armures</option> <option value="noarmure">Ne pas compter les Armures</option>
<option value="chute">Chute : Limiter les armures à 2 PA</option> <option value="chute">Chute : Limiter les armures à 2 PA</option>

View File

@@ -0,0 +1,48 @@
<form class="dialog-validation-encaissement">
<div class="flexrow flex-center">
<div class="flex-shrink"><img class="chat-icon" src="icons/svg/bones.svg" alt="Encaisser des dommages" /></div>
<div><h4>Encaissement de {{actor.name}}</h4></div>
<div class="flex-shrink"><img class="chat-icon" src="{{actor.img}}" title="{{actor.name}}" alt="{{actor.name}}" /></div>
</div>
<ul>
<li class="flexrow flex-group-left">
<label>Jet d'encaissement</label>
<input class="encaissement-roll-result" type="number" value="{{encaissement.roll.result}}" min="2" max="20" data-dtype="Number" />
</li>
<li class="flexrow flex-group-left">
<label>Total</label>
<span class="tooltip tooltip-dotted">
<label class="encaissement-total">{{encaissement.total}}</label>
<div class="tooltiptext ttt-ajustements">
<div>Armure: {{encaissement.armure}}</div>
{{#if rollData.dmg.penetration}}
<div>Pénétration: -{{rollData.dmg.penetration}}</div>
{{/if}}
<hr>
{{#if encaissement.dmg.total}}
<div>+dom encaissement: {{numberFormat rollData.dmg.total decimals=0 sign=true}}</div>
{{/if}}
{{#if rollData.dmg.dmgArme}}
<div>+dom arme: {{numberFormat rollData.dmg.dmgArme decimals=0 sign=true}}</div>
{{/if}}
{{#if rollData.dmg.dmgActor}}
<div>+dom attaquant: {{numberFormat rollData.dmg.dmgActor decimals=0 sign=true}}</div>
{{/if}}
{{#if rollData.dmg.dmgParticuliere}}
<div>+dom particulière: {{numberFormat rollData.dmg.dmgParticuliere decimals=0 sign=true}}</div>
{{/if}}
{{#if rollData.dmg.dmgTactique}}
<div>+dom tactique: {{numberFormat rollData.dmg.dmgTactique decimals=0 sign=true}}</div>
{{/if}}
{{#if rollData.dmg.dmgSurprise}}
<div>+dom surprise: {{numberFormat rollData.dmg.dmgSurprise decimals=0 sign=true}}</div>
{{/if}}
</div>
</span>
</li>
<li class="flexrow flex-group-left">
<label>Blessure ({{rollData.dmg.mortalite}})</label>
<label class="encaissement-blessure">{{encaissement.blessures}}</label>
</li>
</ul>
</form>

View File

@@ -41,7 +41,7 @@
{{/select}} {{/select}}
</select> </select>
</div> </div>
{{#if isHerbe}} {{#if isSoins}}
<div class="form-group"> <div class="form-group">
<label>Herbe</label> <label>Herbe</label>
<select name="system.herbe" class="herbe" data-dtype="String"> <select name="system.herbe" class="herbe" data-dtype="String">
@@ -86,7 +86,7 @@
<label for="xp">Permanente ? </label> <label for="xp">Permanente ? </label>
<input class="attribute-value" type="checkbox" name="system.prpermanent" {{#if system.prpermanent}}checked{{/if}}/> <input class="attribute-value" type="checkbox" name="system.prpermanent" {{#if system.prpermanent}}checked{{/if}}/>
</div> </div>
{{#if isHerbe}} {{#if isSoins}}
<div class="form-group"> <div class="form-group">
<label for="xp">Points de guérison </label> <label for="xp">Points de guérison </label>
<label for="xp">{{pointsGuerison}}</label> <label for="xp">{{pointsGuerison}}</label>