Compare commits

...

60 Commits

Author SHA1 Message Date
f220e3a394 Merge pull request 'Version 13.0.37' (#803) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 1m28s
Reviewed-on: https, #803
2026-05-02 16:37:40 +02:00
1d9348b701 Version 13.0.37 2026-05-02 16:36:51 +02:00
d96dff988e Merge pull request 'isNewerVersion' (#802) from feature/v13-corrections into v13
Reviewed-on: https, #802
2026-05-02 16:35:21 +02:00
9966a33524 Correction Migration avec Foundry14
Accès à méthode isNewerVersion à travers foundry.utils
2026-05-02 16:32:19 +02:00
7fb0ee1659 await ChatMessage.create
ChatMessage.create est async, il faut donc de préférence
l'appeler avec un await.

Des effets secondaires avaient lieu (ordre de messages, updates
ultérieurs parfois pas pris en compte)
2026-05-02 00:42:45 +02:00
d15d0989a3 Remise à zéro du refoulement après souffle 2026-05-01 23:56:17 +02:00
704c99e418 Correction: modification de coeurs 2026-05-01 23:38:05 +02:00
9ccc068333 Amélioration du moral
Les bon moments sont affichés en tooltips sur le moral

Simplification moralIncDec: Utilisation d'un seul update
2026-05-01 23:20:58 +02:00
6b0a6a268e Merge pull request '13.0.36 - Les rêveries d'Illisys' (#801) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 1m45s
Reviewed-on: https, #801
2026-04-30 18:25:47 +02:00
c7da930556 Boutons dans les listes d'équipement
Affichage amélioré en évitant les retours à la ligne
Meilleur affichage des -/+
2026-04-30 00:05:31 +02:00
e9e2eba9b5 Support v14: commandes dans chatMessage en html 2026-04-29 22:53:23 +02:00
423dcaf53e Cuisiner depuis l'équipement 2026-04-29 22:53:23 +02:00
a01086ff28 Amélioration champs input
- ajout de min et max quand utile
- ordre et regroupement des attributs:
   - préférence pour name en premier
   - regroupement de class/type/data-dtype
   - regroupement value/min/max
2026-04-29 22:53:22 +02:00
76e651cf19 Corrections mineures 2026-04-28 19:21:00 +02:00
eaee50511a Fix v14: attaque à distance 2026-04-28 19:10:38 +02:00
d1832917bc Message de dommage sur entité
Plutôt que de dire "ne subit aucun dommage", une gravité de blessure
indicative est donnée
2026-04-28 19:09:08 +02:00
277799088f Affichage des jeux 2026-04-28 19:07:24 +02:00
0398fbdbd1 Fix: Pas d'affichage de feuille sur encaissement 2026-04-28 19:07:00 +02:00
4faa9b6b54 Merge pull request 'Quelques corrections mineures' (#800) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 1m40s
Reviewed-on: https, #800
2026-04-26 22:38:40 +02:00
43a09f3a99 Compatibilité v14 2026-04-26 22:37:49 +02:00
ec8fc795f4 Affichage état & blessures
Les icônes d'effets sont affichées en premier.

Affichage des contusions. Les blessures sont affichées en
dernier, par gravité décroissante
2026-04-26 22:37:49 +02:00
8b46ea3681 Ajout de tokens de créatures 2026-04-26 22:37:49 +02:00
3ce33eeea3 simplification pre-update checkCompetence 2026-04-26 22:37:48 +02:00
5c4292882f Correction des updates multiples
Utilisation d'un render sur timer pour forcer le réaffichage
2026-04-26 22:37:48 +02:00
fb150753e0 Suppression image de fond "players"
Adaptation theme Foundry v13
2026-04-26 22:37:48 +02:00
fa54865369 Amélioration des tirages
- option pour ne pas afficher la table source
- les tirages de rencontres sont entre joueur et MJ
2026-04-26 22:37:48 +02:00
200e35b7b7 Filtrage des actions des items des commerces 2026-04-26 22:37:48 +02:00
76fb385c69 Visibilité des tirages dans le compendium
Les tirages de queues, têtes, ... ne concernent plus tout le monde,
mais le joueur et le MJ
2026-04-26 22:37:48 +02:00
ef30e76449 Fix: recul contre entité de cauchemar 2026-04-26 22:37:47 +02:00
10aba8bd58 Fix: sortie d'objet de conteneur 2026-04-26 22:37:47 +02:00
0aedff9d4b Merge pull request 'v13.0.34 - La saumuche d'Illysis' (#799) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 2m2s
Reviewed-on: https, #799
2026-04-07 19:23:26 +02:00
6062d0428c Fix: difficulté d'annulation de magie 2026-04-03 22:16:50 +02:00
eebca509bd Fix: affichage appel moral sur sorts 2026-04-03 22:16:50 +02:00
9934fe9191 Le pluriel de jeu est jeux 2026-04-03 22:16:50 +02:00
e6c4f7990a Correction: qualité des improvisations de cuisine 2026-04-03 22:16:50 +02:00
956fecdd82 Demi-rêve masqué, masquer les rencontres
Pour éviter de trahir l'emplacement du demi-rêve...
2026-04-03 22:16:50 +02:00
afcd200913 Merge pull request 'v13.0.33 - L'ébriété d'Illysis' (#798) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 1m31s
Reviewed-on: https, #798
2026-03-26 23:11:26 +01:00
f3e7cc38a7 Message de Vaisseau 2026-03-24 20:45:54 +01:00
bcc1ec6a37 Pas de perte d'endurance quand éméché 2026-03-24 20:45:54 +01:00
4141eeaa4a Cleanup: gererExperience=>ajoutExperience 2026-03-24 20:45:54 +01:00
d80efba092 Corrections relecture IA 2026-03-24 20:45:54 +01:00
a768419029 Merge pull request '## 13.0.32 - Le surpoids d'Illysis' (#797) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 2m42s
Reviewed-on: https, #797
2026-03-09 09:47:19 +01:00
dfc73fb96d Fix: affichage blessures et pertes
Les pertes et la récupération sont correctement gérées (en ne
faisant qu'un seul render pour le personnage)
2026-03-07 20:38:28 +01:00
9bb9a3b0eb Icône "sonné" inline dans le tchat 2026-03-07 20:38:28 +01:00
3f014faf02 Pas de perte endurance blessures graves soignées 2026-03-07 20:38:12 +01:00
6f30913a8f Visibilité ajustement conditions 2026-03-07 20:38:11 +01:00
b326484797 Fix: malus encombrement 2026-03-07 20:38:11 +01:00
fd96be439e Merge pull request '13.0.31 - Les choix multiples d'Illysis' (#796) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 1m57s
Reviewed-on: https, #796
2026-03-04 09:01:53 +01:00
238d99fa9b Fix: dommages armes 1/2 mains à deux mains 2026-03-03 18:14:58 +01:00
490de5882b Fix: valeurs par défaut après la sélection 2026-03-03 17:58:52 +01:00
4fc06a449c Merge pull request '13.0.30 - Le pansement d'Illysis' (#795) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 1m27s
Reviewed-on: https, #795
2026-02-18 00:43:46 +01:00
929d6af173 Saisie de valeurs négatives 2026-02-17 00:36:08 +01:00
e15ed9d05d Jet de compétence avec défauts
Utilisation de carac par défaut et difficulté par défaut à l'ouverture
de la fenêtre de jet depuis une compétence
2026-02-16 23:58:45 +01:00
3699bc19b8 Fix: soins d'un joueur à l'autre
Les données dans rollData ne pouvaient pas être serialisées
2026-02-06 13:42:27 +01:00
15510b99d8 Merge pull request 'feature/v13-corrections' (#794) from feature/v13-corrections into v13
All checks were successful
Release Creation / build (release) Successful in 2m45s
Reviewed-on: https, #794
2026-01-19 21:44:59 +01:00
fa30705989 Affichage de l'XP des sorts et caractéristiques 2026-01-17 03:45:42 +01:00
04f550dd21 Fix jet de résistance V2
sans cométence s'il n'y a pas de sélection
2026-01-16 08:47:51 +01:00
850cae3979 Attaques V2 depuis onglet combat 2026-01-16 08:47:50 +01:00
7b514d5159 Background arme inutilisable 2026-01-14 23:55:03 +01:00
d78ede4f59 Fix missing import 2026-01-14 23:47:01 +01:00
198 changed files with 1998 additions and 1667 deletions

View File

@@ -1,12 +1,77 @@
# 13.0
## 13.0.37 - Le bonheur des zyglutes d'Illisys
- Corrections v14
- correction du problème liè à Foundry 14 qui peut empêcher d'utiliser les fenêtres de jets à cause de migrations mal effectuées
- Les bon moments sont affichés en tooltip sur le moral
- Les modifications de coeurs fonctionnent de nouveau
- Le refoulement est remis à zéro après avoir refoulé et reçu un souffle
## 13.0.36 - Les rêveries d'Illisys
- Corrections v14
- les attaques à distance n'empèchent plus la fenêtre d'attaque de s'ouvrir
- les commandes (/help, ...) fonctionnent en v14
- Les feuilles d'acteurs ne s'ouvrent plus lors d'un changement (par exemple en cas d'encaissement)
- Les jeux sont correctement affichés
- Ajout d'une gravité de blessure sur les encaissement d'entités
- Amélioration de champs numériques: ajout de min et max quand c'est utile
- correction pour cuisiner depuis l'équipement
- les boutons dans les listes d'équipement sont mieux affichés sans retour à la ligne
## 13.0.35 - Les travaux d'Illisys
- Correction du recul contre une entité de cauchemar (qui utilise le rêve comme force)
- Correction erreur lors de la suppression d'un objet d'un conteneur
- Meilleure gestion des messages publics/GM, en particulier pour les tirage dans les compendiums
- Filtrage des boutons pour les acteurs non personnages (pour éviter de faire manger l'auberge...)
## 13.0.34 - La saumuche d'Illysis
- la qualité des "improvisations du moment" se base sur le niveau du cuisinier
- l'appel au moral n'est pas affiché à l'ouverture d'une fenêtre de jets de sorts (ou de tâche intellectuelle)
- la difficulté variable de l'annulation de magie est bien prise en compte
- correction erreur lors de la suppression d'un objet d'un conteneur
## 13.0.33 - L'ébriété d'Illysis
- le stade éméché ne cause plus de perte d'endurance
## 13.0.32 - Le surpoids d'Illysis
- Le malus d'encombrement sur jet d'Agilité avec Natation ou Acrobatie peuvent être changés, et sont correctement arrondis
- L'ajustement de condition est plus visible sur les résultats de jets de dés
- Les blessures graves soignées ne font pas perdre d'endurance par round
- Les blessures et pertes correspondantes (vie, endurance, effets sonné, ...) s'affichent correctement
- la remise à neuf ne provoque pas d'erreur pour certains effets déjà supprimés
## 13.0.31 - Les choix multiples d'Illysis
- les défauts de caractéristique/difficulté des compétences ne sont pris que si aucun autre choix n'est fait
- lors d'une attaque à deux mains avec une arme à une ou deux mains, les dommages à deux mains sont bien utilisés
## 13.0.30 - Le pansement d'Illysis
- les soins d'un joueur à l'autre fonctionne de nouveau
- la fenêtre de jet de compétence s'ouvre avec la caractéristique et la difficulté par défaut
- on peut saisir des valeurs négatives au clavier en sélectionnant les conditions/difficultés
## 13.0.29 - Le tricorne d'Illysis
- gestion des attaques avec jets V2 depuis l'onglet de combat
- les jets de résistance en mode V2 fonctionnent sans sélection de compétence
- affichage de l'expérience correspondant aux sorts pour aider à la création
- affichage de l'équivallent d'expérience des caractéristiques
## 13.0.28 - La quadrature d'Illysis
- Les ajustements de portée sont calculés pour les attaques à distance
- L'appel au moral dans le tchat ne déplace plus les boutons d'appel à la chance
- Correction d'apparence V13
- la fenêtre de choix des status utilisés est affichée correctement
- la fenêtrre d'astrologir MJ est affichée correctement
- la fenêtre d'astrologie MJ est affichée correctement
## 13.0.27 - Les lunettes d'Illysis

View File

@@ -893,7 +893,7 @@ body {
max-width: 1.4rem;
max-height: 1.4rem;
border: 1px;
background: center / contain no-repeat url("../../icons/templates/icone_parchement_vierge.webp");
background: center / contain no-repeat url("../icons/templates/icone_parchement_vierge.webp");
}
.system-foundryvtt-reve-de-dragon .sheet-header .header-compteurs {
width: calc(60% - 110px - 1rem);
@@ -1120,49 +1120,27 @@ body {
text-align: left;
}
.system-foundryvtt-reve-de-dragon .equipement-nom {
flex-grow: 3;
flex-grow: 4;
flex-shrink: 2;
margin: 0;
justify-content: center;
text-align: left;
display: ruby;
}
.system-foundryvtt-reve-de-dragon .equipement-valeur {
margin: 0;
flex-grow: 1.5;
flex-grow: 1;
flex-shrink: 1;
text-align: center;
}
.system-foundryvtt-reve-de-dragon .equipement-detail {
margin: 0;
flex-grow: 1;
align-items: center;
justify-content: center;
text-align: center;
}
.system-foundryvtt-reve-de-dragon span.equipement-detail-buttons {
margin: 0;
flex-grow: 1.5;
flex-shrink: 1;
align-items: center;
justify-content: center;
text-align: center;
display: flex;
flex-direction: row;
}
.system-foundryvtt-reve-de-dragon .equipement-button {
margin: 0;
flex-grow: 0.5;
align-items: center;
justify-content: center;
text-align: center;
}
.system-foundryvtt-reve-de-dragon :is(.item-actions-controls, .equipement-actions) {
margin: 0;
flex-grow: 1.2;
align-items: end;
justify-content: flex-end;
text-align: right;
}
.system-foundryvtt-reve-de-dragon .liste-equipement :is(.equipement-actions, .item-actions-controls) {
flex-grow: 2;
min-width: max-content;
}
.system-foundryvtt-reve-de-dragon .blessure-control {
flex-grow: 1;
@@ -1526,9 +1504,20 @@ body {
.system-foundryvtt-reve-de-dragon .competence-list .item-controls.hidden-controls {
display: none !important;
}
.system-foundryvtt-reve-de-dragon .competence-header .item-actions-controls {
flex-shrink: 2;
flex-grow: 2;
}
.system-foundryvtt-reve-de-dragon .item-actions-controls,
.system-foundryvtt-reve-de-dragon .item-controls {
vertical-align: super;
margin: 0;
flex-grow: 1;
flex-shrink: 1;
align-items: end;
text-align: right;
min-width: max-content;
flex-basis: fit-content;
vertical-align: baseline;
}
.system-foundryvtt-reve-de-dragon .item-actions-controls img,
.system-foundryvtt-reve-de-dragon .item-controls img {
@@ -2379,8 +2368,9 @@ body {
}
.system-foundryvtt-reve-de-dragon .chat-inline-icon {
border: 0;
padding: 1px;
padding: 0 0.2rem;
vertical-align: text-top;
display: inline;
}
.system-foundryvtt-reve-de-dragon .actor-img-small {
max-width: 1.5rem;
@@ -2498,12 +2488,6 @@ body {
background: #1e1914;
border: 1px solid #482e1c;
}
.system-foundryvtt-reve-de-dragon #players {
border-image: url(../assets/ui/footer-button.webp) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
background: #1e1914;
}
.system-foundryvtt-reve-de-dragon #navigation #scene-list .scene.nav-item.active {
background: #482e1c;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 136 KiB

BIN
icons/creatures/glou_t.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -1,5 +1,6 @@
<h3>Joyeuses fêtes</h3>
<h3>L'oeuf de Dragon</h3>
<p>
VincentVK, LeRatierBretonnien et toute l'équipe de Scriptrarium vous
souhaitent de joyeuses et oniriques fêtes !
On raconte que si le premier Vaisseau, on trouve un oeuf couvé par
un lapin, il faut bien s'occuper dudit lapin, car quatre mois plus tard
pourrait bien éclore un dragon!
</p>

View File

@@ -136,7 +136,7 @@
max-height: 1.4rem;
border: 1px;
background: center / contain no-repeat
url("../../icons/templates/icone_parchement_vierge.webp");
url("../icons/templates/icone_parchement_vierge.webp");
}
.sheet-header .header-compteurs {
@@ -393,49 +393,27 @@
text-align: left;
}
.equipement-nom {
flex-grow: 3;
flex-grow: 4;
flex-shrink: 2;
margin: 0;
justify-content: center;
text-align: left;
display: ruby;
}
.equipement-valeur {
margin: 0;
flex-grow: 1.5;
flex-grow: 1;
flex-shrink: 1;
text-align: center;
}
.equipement-detail {
margin: 0;
flex-grow: 1;
align-items: center;
justify-content: center;
text-align: center;
}
span.equipement-detail-buttons {
margin: 0;
flex-grow: 1.5;
flex-shrink: 1;
align-items: center;
justify-content: center;
text-align: center;
display: flex;
flex-direction: row;
}
.equipement-button {
margin: 0;
flex-grow: 0.5;
align-items: center;
justify-content: center;
text-align: center;
}
:is(.item-actions-controls, .equipement-actions) {
margin: 0;
flex-grow: 1.2;
align-items: end;
justify-content: flex-end;
text-align: right;
}
.liste-equipement :is(.equipement-actions, .item-actions-controls) {
flex-grow: 2;
min-width: max-content;
}
.blessure-control {
@@ -848,12 +826,20 @@
.competence-list .item-controls.hidden-controls {
display: none !important;
}
.competence-header .item-actions-controls{
flex-shrink: 2;
flex-grow: 2;
}
.item-actions-controls,
.item-controls {
vertical-align: super;
// a {
// }
margin: 0;
flex-grow: 1;
flex-shrink: 1;
align-items: end;
text-align: right;
min-width: max-content;
flex-basis: fit-content;
vertical-align: baseline;
img {
vertical-align: text-bottom;
display: inline;
@@ -1776,8 +1762,9 @@
}
.chat-inline-icon {
border: 0;
padding: 1px;
padding: 0 0.2rem;
vertical-align: text-top;
display: inline;
}
.actor-img-small {
@@ -1911,13 +1898,6 @@
border: 1px solid rgba(72, 46, 28, 1);
}
#players {
border-image: url(../assets/ui/footer-button.webp) 10 repeat;
border-image-width: 4px;
border-image-outset: 0px;
background: rgba(30, 25, 20, 1);
}
#navigation #scene-list .scene.nav-item.active {
background: rgba(72, 46, 28, 1);
}

View File

@@ -19,6 +19,7 @@ import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
import { MORAL } from "./moral/apprecier.mjs";
import { RdDItemSort } from "./item-sort.js";
/* -------------------------------------------- */
/**
@@ -50,8 +51,9 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
foundry.utils.mergeObject(formData.calc, {
surenc: this.actor.computeMalusSurEncombrement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).label,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
blessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
caracTotalXp: RdDCarac.computeTotalXp(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "",
malusArmure: this.actor.getMalusArmure()
})
@@ -61,9 +63,13 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
if (formData.type == ACTOR_TYPES.personnage) {
formData.options.mainsDirectrices = MAINS_DIRECTRICES;
formData.byCateg = Misc.classify(formData.competences, it => it.system.categorie)
formData.calc.comptageArchetype = RdDItemCompetence.computeResumeArchetype(formData.competences);
formData.calc.competenceXPTotal = RdDItemCompetence.computeTotalXP(formData.competences);
formData.calc.fatigue = RdDUtility.calculFatigueHtml(formData.system.sante.fatigue.value, formData.system.sante.endurance.max);
foundry.utils.mergeObject(formData.calc, {
comptageArchetype: RdDItemCompetence.computeResumeArchetype(formData.competences),
competenceXPTotal: RdDItemCompetence.computeTotalXP(formData.competences),
sortsXPTotal: RdDItemSort.computeTotalXP(this.actor.itemTypes[ITEM_TYPES.sort]),
fatigue: RdDUtility.calculFatigueHtml(formData.system.sante.fatigue.value, formData.system.sante.endurance.max)
})
formData.competences.forEach(item => {
item.system.isHidden = this.options.recherche
@@ -273,7 +279,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
// On pts de reve change
this.html.find('.pointsreve-value').change(async event => await this.actor.update({ "system.reve.reve.value": event.currentTarget.value }))
this.html.find('.seuil-reve-value').change(async event => await this.actor.setPointsDeSeuil(event.currentTarget.value))
this.html.find('.seuil-reve-value').change(async event => await this.actor.update({ "system.reve.seuil.value": event.currentTarget.value }))
this.html.find('.stress-test').click(async event => await this.actor.transformerStress())
this.html.find('.moral-malheureux').click(async event => await this.actor.jetDeMoral(MORAL.MALHEUREUX))

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@ import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES_CREATURES } fr
import { RollDataAjustements } from "../rolldata-ajustements-v1.js";
import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
import RollDialog from "../roll/roll-dialog.mjs";
import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE } from "../roll/roll-constants.mjs";
import { DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE } from "../roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
import { PART_COMP } from "../roll/roll-part-comp.mjs";
import { RdDInitiative } from "../initiative.mjs";
@@ -56,8 +56,6 @@ export class RdDBaseActorReve extends RdDBaseActor {
getTaille() { return Misc.toInt(this.system.carac.taille?.value) }
getConstitution() { return this.getReve() }
getVie() { return this.getReve() }
getCaracVie() { return { [CARACS.VIE]: { label: "Vie", value: this.getVieMax(), type: "number" } } }
getForce() { return this.getReve() }
getAgilite() { return this.getForce() }
@@ -156,11 +154,11 @@ export class RdDBaseActorReve extends RdDBaseActor {
async computeArmure(dmg) { return this.getProtectionNaturelle() }
async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
async ajoutExperience(rollData, hideChatMessage = 'show') { }
computeResumeBlessure() { }
computeResumeBlessure() { return []}
countBlessures(filter = it => !it.isContusion()) { return 0 }
async santeIncDec(name, inc, isCritique = false) { }
async santeIncDec(name, inc, isCritique) { }
async finDeRound(options = { terminer: false }) {
await this.finDeRoundSuppressionEffetsTermines(options)
@@ -171,10 +169,11 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
async finDeRoundSuppressionEffetsTermines(options) {
for (let effect of this.getEffects()) {
const effects = this.getEffects();
for (let effect of effects) {
if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) {
await effect.delete();
ChatMessage.create({ content: `${this.getAlias()} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` });
await effect.delete()
await ChatMessage.create({ content: `${this.getAlias()} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` })
}
}
}
@@ -257,15 +256,15 @@ export class RdDBaseActorReve extends RdDBaseActor {
if (competence) {
function getFieldPath(fieldName) {
switch (fieldName) {
case "niveau": return 'system.niveau';
case "dommages": return 'system.dommages';
case "carac_value": return 'system.carac_value';
case "niveau": return 'system.niveau'
case "dommages": return 'system.dommages'
case "carac_value": return 'system.carac_value'
}
return undefined
}
const path = getFieldPath(fieldName);
const path = getFieldPath(fieldName)
if (path) {
await competence.update({ [path]: value });
await competence.update({ [path]: value })
}
}
}
@@ -297,7 +296,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
/* -------------------------------------------- */
createCallbackExperience() {
return { action: r => this.appliquerAjoutExperience(r) }
return { action: r => this.ajoutExperience(r) }
}
/* -------------------------------------------- */
@@ -306,7 +305,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
return { action: r => this.appliquerAppelMoral(r) }
}
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
async ajoutExperience(rollData, hideChatMessage = 'show') { }
async appliquerAppelMoral(rollData) { }
async _onCloseRollDialog(html) { }
@@ -373,13 +372,11 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
RollDataAjustements.calcul(rollData, this);
await RdDResolutionTable.rollData(rollData);
this.gererExperience(rollData);
this.ajoutExperience(rollData);
await RdDResolutionTable.displayRollData(rollData, this)
return rollData.rolled;
}
gererExperience(rollData) { }
/* -------------------------------------------- */
async roll() {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
@@ -484,17 +481,15 @@ export class RdDBaseActorReve extends RdDBaseActor {
async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(idOrName);
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: options.arme ? ATTAQUE_ROLL_TYPES : DEFAULT_ROLL_TYPES },
type: { allowed: DEFAULT_ROLL_TYPES },
selected: {
carac: competence.type == ITEM_TYPES.competencecreature ? { key: competence.name } : undefined,
comp: { key: competence.name },
diff: { type: options.arme ? DIFF.ATTAQUE : DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 },
attaque: options.arme ? { arme: { key: options.arme.id } } : undefined
diff: { type: DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 },
}
}
return await RollDialog.create(rollData)
@@ -566,7 +561,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
type: {
allowed: [ROLL_TYPE_ATTAQUE], current: ROLL_TYPE_ATTAQUE
}
};
}
return await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
}
})
@@ -575,16 +570,18 @@ export class RdDBaseActorReve extends RdDBaseActor {
/** --------------------------------------------
* @param {*} arme item d'arme/compétence de créature
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
* @param {*} maniement catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
* @returns
*/
rollArme(arme, categorieArme = 'competence', token = undefined) {
async rollArme(arme, maniement = 'competence', token = undefined) {
token = token ?? RdDUtility.getSelectedToken(this)
const compToUse = RdDItemArme.getCompetenceArme(arme, categorieArme)
const compToUse = RdDItemArme.getCompetenceArme(arme, maniement)
if (!RdDItemArme.isUtilisable(arme)) {
ui.notifications.warn(`Arme inutilisable: ${arme.name} non équipée ou avec une résistance de 0 ou moins`)
return
}
if (!Targets.hasTargets()) {
RdDConfirm.confirmer({
settingConfirmer: "confirmer-combat-sans-cible",
@@ -596,7 +593,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
onAction: async () => {
this.rollCompetence(compToUse, { tryTarget: false, arme: arme })
}
});
})
return
}
@@ -606,11 +603,12 @@ export class RdDBaseActorReve extends RdDBaseActor {
return
}
const competence = this.getCompetence(compToUse)
if (competence.isCompetencePossession()) {
return RdDPossession.onAttaquePossession(target, this, competence);
const comp = this.getCompetence(compToUse)
if (comp.isCompetencePossession()) {
// TODO: vérifier si c'est possible, sinon simplifier
return RdDPossession.onAttaquePossession(target, this, comp);
}
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme);
RdDCombat.rddCombatTarget(target, this, token).attaque(comp, arme, maniement)
})
}
@@ -618,7 +616,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
RdDPossessionV2.rollAttaquePossession(this)
}
verifierForceMin(item) { }
async verifierForceMin(item) { }
/* -------------------------------------------- */
async encaisser() { await RdDEncaisser.encaisser(this) }
@@ -657,36 +655,31 @@ export class RdDBaseActorReve extends RdDBaseActor {
jet => this.$onEncaissement(jet, show, attackerToken, defenderToken));
}
}
async $onEncaissement(jet, show, attackerToken, defenderToken) {
await this.onAppliquerJetEncaissement(jet, attackerToken);
await this.$afficherEncaissement(jet, show, defenderToken);
}
async onAppliquerJetEncaissement(encaissement, attackerToken) { }
async $afficherEncaissement(encaissement, show, defenderToken) {
async $onEncaissement(encaissement, show, attackerToken, defenderToken) {
await this.onAppliquerJetEncaissement(encaissement, attackerToken);
foundry.utils.mergeObject(encaissement, {
alias: defenderToken?.name ?? this.getAlias(),
hasPlayerOwner: this.hasPlayerOwner,
show: show ?? {}
}, { overwrite: false });
await ChatUtility.createChatWithRollMode(
await ChatMessage.create(ChatUtility.adaptVisibility(
{
roll: encaissement.roll,
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.hbs', encaissement)
},
this
)
{ actor: this }))
if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) {
encaissement = foundry.utils.duplicate(encaissement)
encaissement.isGM = true
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getGMs(),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.hbs', encaissement)
});
})
}
}
@@ -733,7 +726,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.hbs')
await this.appliquerAjoutExperience(rollData, true)
await this.ajoutExperience(rollData, true)
return rolled.isSuccess;
}

View File

@@ -36,7 +36,7 @@ export class RdDBaseActorSangSheet extends RdDBaseActorReveSheet {
async jetEndurance() {
const endurance = this.actor.getEnduranceActuelle()
const result = await this.actor.jetEndurance(endurance);
ChatMessage.create({
await ChatMessage.create({
content: `Jet d'Endurance : ${result.jetEndurance} / ${endurance}
<br>${this.actor.name} a ${result.sonne ? 'échoué' : 'réussi'} son Jet d'Endurance ${result.sonne ? 'et devient Sonné' : ''}`,
whisper: ChatUtility.getOwners(this.actor)

View File

@@ -8,6 +8,7 @@ import { RdDItemBlessure } from "../item/blessure.js";
import { ChatUtility } from "../chat-utility.js";
import { Misc } from "../misc.js";
import { RdDBaseActor } from "./base-actor.js";
import { CARACS } from "../rdd-carac.js";
/**
* Classe de base pour les acteurs qui peuvent subir des blessures
@@ -16,6 +17,14 @@ import { RdDBaseActor } from "./base-actor.js";
*/
export class RdDBaseActorSang extends RdDBaseActorReve {
async _preUpdate(changed, options, user) {
const updatedEndurance = changed?.system?.sante?.endurance
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
}
prepareActorData() {
this.system.sante.vie.max = Math.ceil((this.getTaille() + this.getConstitution()) / 2)
this.system.sante.vie.value = Math.min(this.system.sante.vie.value, this.system.sante.vie.max)
@@ -70,51 +79,64 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
return Math.min(0, Math.floor(this.getEncombrementMax() - this.encTotal));
}
countBlessures(filter) { return this.itemTypes[ITEM_TYPES.blessure].filter(filter).length }
isDead() { return this.system.sante.vie.value < -this.getSConst() }
nbBlessuresLegeres() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isLegere()).length }
nbBlessuresGraves() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isGrave()).length }
nbBlessuresCritiques() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isCritique()).length }
/* -------------------------------------------- */
computeResumeBlessure() {
const nbLegeres = this.nbBlessuresLegeres()
const nbGraves = this.nbBlessuresGraves()
const nbCritiques = this.nbBlessuresCritiques()
function descBlessure(count, name) {
return count > 0 ? [`${count} ${name}${count > 1 ? "s" : ""}`] : []
}
if (nbLegeres + nbGraves + nbCritiques == 0) {
return "Aucune blessure";
}
let resume = "Blessures:";
if (nbLegeres > 0) {
resume += " " + nbLegeres + " légère" + (nbLegeres > 1 ? "s" : "");
}
if (nbGraves > 0) {
if (nbLegeres > 0)
resume += ",";
resume += " " + nbGraves + " grave" + (nbGraves > 1 ? "s" : "");
}
if (nbCritiques > 0) {
if (nbGraves > 0 || nbLegeres > 0)
resume += ",";
resume += " une CRITIQUE !";
}
return resume;
const nbContusions = this.countBlessures(it => it.isContusion())
const nbLegeres = this.countBlessures(it => it.isLegere())
const nbGraves = this.countBlessures(it => it.isGrave())
const nbCritiques = this.countBlessures(it => it.isCritique())
return [
... (nbCritiques > 0 ? ['une CRITIQUE !'] : []),
...descBlessure(nbGraves, 'grave'),
...descBlessure(nbLegeres, 'légère'),
...descBlessure(nbContusions, 'contusion'),
]
}
blessuresASoigner() { return [] }
async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
async ajoutExperience(rollData, hideChatMessage = 'show') { }
/* -------------------------------------------- */
async onAppliquerJetEncaissement(encaissement, attackerToken) {
const santeOrig = foundry.utils.duplicate(this.system.sante);
const blessure = await this.ajouterBlessure(encaissement, attackerToken); // Will update the result table
const perteVie = await this.santeIncDec("vie", -encaissement.vie);
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique());
const blessure = this.nouvelleBlessure(encaissement.gravite, {
localisation: encaissement.dmg?.loc.label ?? '',
origine: attackerToken?.name ?? ''
})
if (blessure.system.gravite == encaissement.gravite) {
blessure.system.vie = encaissement.vie
blessure.system.endurance = encaissement.endurance
}
else { // aggravation du fait du nombre de blessures
blessure.system.vie = blessure.system.vie
const rollPerteEndurance = new Roll(blessure.system.endurance)
await rollPerteEndurance.evaluate()
blessure.system.endurance = rollPerteEndurance.total
}
const isCritique = blessure.system.gravite >= 6;
if (isCritique) {
blessure.system.endurance = this.getEnduranceActuelle()
}
// Will update the result table
if (blessure.system.gravite > 6) {
this.setEffect(STATUSES.StatusComma, true)
encaissement.mort = "à seconde blessure critique"
}
const perteVie = await this.santeIncDec("vie", -encaissement.vie)
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, isCritique)
await this.createEmbeddedDocuments('Item', [blessure])
foundry.utils.mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue,
@@ -123,7 +145,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
endurance: perteEndurance.perte,
vie: santeOrig.vie.value - perteVie.newValue,
blessure: blessure
});
})
}
/* -------------------------------------------- */
@@ -141,12 +163,13 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
let minValue = name == "vie" ? -this.getSConst() - 1 : 0;
result.newValue = Math.max(minValue, Math.min(compteur.value + inc, compteur.max));
//console.log("New value ", inc, minValue, result.newValue);
let fatigue = 0;
if (name == "endurance") {
if (result.newValue == 0 && inc < 0 && !isCritique) { // perte endurance et endurance devient 0 (sauf critique) -> -1 vie
sante.vie.value--;
result.perteVie = true;
if (result.newValue == 0 && inc < 0 && !isCritique) {
// perte endurance et endurance devient 0 (sauf critique) -> -1 vie
sante.vie.value--
result.perteVie = true
}
result.newValue = Math.max(0, result.newValue);
if (inc > 0) { // le max d'endurance s'applique seulement à la récupération
@@ -163,34 +186,39 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) {
sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this.getFatigueMin());
}
await this.update({ "system.sante": sante }, { render: true })
await this.update({ "system.sante": sante })
if (perteEndurance > 1) {
// Peut-être sonné si 2 points d'endurance perdus d'un coup
foundry.utils.mergeObject(result, await this.jetEndurance(result.newValue));
} else if (name == "endurance" && inc > 0) {
await this.setSonne(false);
await this.setSonne(false)
}
if (this.isDead()) {
await this.setEffect(STATUSES.StatusComma, true);
await this.setEffect(STATUSES.StatusComma, true)
}
return result
}
async onRollTachePremiersSoins(blessureId, rollData, soigneurId) {
async callbackPremiersSoins(blessureId, rollData, soigneurId) {
await this.onRollTachePremiersSoins(blessureId,
rollData.v2 ? rollData.current.tache.tache : rollData.tache,
rollData.rolled.isETotal,
soigneurId)
}
async onRollTachePremiersSoins(blessureId, tache, isETotal, soigneurId) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'onRollTachePremiersSoins', args: [blessureId, rollData, soigneurId]
method: 'onRollTachePremiersSoins', args: [blessureId, tache, isETotal, soigneurId]
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && !blessure.system.premierssoins.done) {
const tache = rollData.v2 ? rollData.current.tache.tache : rollData.tache
if (rollData.rolled.isETotal) {
if (isETotal) {
await blessure.update({
'system.difficulte': blessure.system.difficulte - 1,
'system.premierssoins.tache': Math.max(0, tache.system.points_de_tache_courant)
@@ -250,14 +278,6 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
}
async onUpdateActor(update, options, actorId) {
const updatedEndurance = update?.system?.sante?.endurance
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
await super.onUpdateActor(update, options, actorId)
}
async onCreateItem(item, options, id) {
await this.changeItemEffects(item)
await super.onCreateItem(item, options, id)
@@ -276,12 +296,12 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
async changeItemEffects(item) {
switch (item.type) {
case ITEM_TYPES.blessure:
await this.changeStateBleeding();
await this.changeStateBleeding()
break;
case ITEM_TYPES.maladie:
case ITEM_TYPES.poison:
await this.changeStateMalade();
break;
await this.changeStateMalade()
break
}
}
@@ -300,32 +320,15 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
}
/* -------------------------------------------- */
async ajouterBlessure(encaissement, attackerToken = undefined) {
if (encaissement.gravite < 0) return;
if (encaissement.gravite > 0) {
while (this.countBlessures(it => it.system.gravite == encaissement.gravite) >= RdDItemBlessure.maxBlessures(encaissement.gravite) && encaissement.gravite <= 6) {
nouvelleBlessure(gravite, options = { origine: undefined, localisation: '' }) {
if (gravite < 0) return
if (gravite > 0) {
while (this.countBlessures(it => it.system.gravite == gravite) >= RdDItemBlessure.maxBlessures(gravite) && gravite <= 6) {
// Aggravation
encaissement.gravite += 2
if (encaissement.gravite > 2) {
encaissement.vie += 2;
}
gravite += 2
}
}
const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken);
if (blessure.isCritique()) {
encaissement.endurance = endActuelle
}
if (blessure.isMort()) {
this.setEffect(STATUSES.StatusComma, true);
encaissement.mort = true;
ChatMessage.create({
content: `<img class="chat-icon" src="icons/svg/skull.svg" data-tooltip="charge" />
<strong>${this.getAlias()} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>`
});
}
return blessure;
return RdDItemBlessure.prepareBlessure(gravite, options);
}
async supprimerBlessure({ gravite }) {
@@ -337,8 +340,8 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
async supprimerBlessures(filterToDelete) {
const toDelete = this.filterItems(filterToDelete, ITEM_TYPES.blessure)
.map(it => it.id);
await this.deleteEmbeddedDocuments('Item', toDelete);
.map(it => it.id)
await this.deleteEmbeddedDocuments('Item', toDelete)
}
countBlessures(filter = it => !it.isContusion()) {
@@ -348,19 +351,17 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */
async jetDeVie() {
if (this.isDead()) {
ChatMessage.create({
return await ChatMessage.create({
content: `Jet de Vie: ${this.getAlias()} est déjà mort, ce n'est pas la peine d'en rajouter !!!!!`,
whisper: ChatUtility.getOwners(this)
})
return
}
const jetDeVie = await RdDDice.roll("1d20");
const sConst = this.getSConst();
const vie = this.system.sante.vie.value;
const isCritique = this.nbBlessuresCritiques() > 0;
const isGrave = this.nbBlessuresGraves();
const isEchecTotal = jetDeVie.total == 20;
const isCritique = this.countBlessures(it => it.isCritique()) > 0
const isGrave = this.countBlessures(it => it.isGrave())
const isEchecTotal = jetDeVie.total == 20
const isSuccess = jetDeVie.total == 1 || jetDeVie.total <= vie;
const perte = isSuccess ? 0 : 1 + (isEchecTotal ? vie + sConst : 0)
const prochainJet = (jetDeVie.total == 1 && vie > 0 ? 20 : 1) * (isCritique ? 1 : isGrave > 0 ? sConst : 0)
@@ -378,10 +379,10 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
else if (prochainJet > 0) {
msgText += `<br>Prochain jet de vie dans ${prochainJet} ${isCritique ? 'round' : 'minute'}${prochainJet > 1 ? 's' : ''} ${isCritique ? '(état critique)' : '(état grave)'}`
}
ChatMessage.create({
return await ChatMessage.create({
content: msgText,
whisper: ChatUtility.getOwners(this)
});
})
}
/* -------------------------------------------- */
@@ -389,13 +390,14 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
const jetEndurance = (await RdDDice.roll("1d20")).total;
const sonne = jetEndurance == 20 || jetEndurance > (resteEndurance ?? this.system.sante.endurance.value)
if (sonne) {
await this.setSonne();
await this.setSonne(true)
}
return { jetEndurance, sonne }
}
async finDeRoundBlessures() {
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length;
const nbGraves = this.itemTypes[ITEM_TYPES.blessure]
.filter(it => it.isGrave() && !it.system.premierssoins.done).length;
if (nbGraves > 0) {
// Gestion blessure graves : -1 pt endurance par blessure grave
await this.santeIncDec("endurance", -nbGraves);
@@ -404,8 +406,9 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
async setSonne(sonne = true) {
if (!game.combat && sonne) {
// TODO: vérifier si comportement toujours valable
ui.notifications.info(`${this.getAlias()} est hors combat, il ne reste donc pas sonné`);
return;
return
}
await this.setEffect(STATUSES.StatusStunned, sonne)
}

View File

@@ -49,11 +49,9 @@ export class RdDBaseActor extends Actor {
static init() {
Handlebars.registerHelper('actor-isFeminin', actor => actor.isFeminin())
Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
Hooks.on("updateItem", (item, updates, options, id) => Misc.documentIfResponsible(item.parent)?.onUpdateItem(item, updates, options, id))
Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id))
Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId))
}
static onSocketMessage(sockmsg) {
@@ -93,25 +91,11 @@ export class RdDBaseActor extends Actor {
}
static getRealActor(actorId, tokenId) {
if (tokenId) {
let token = canvas.tokens.get(tokenId)
if (token) {
return token.actor
}
}
return game.actors.get(actorId)
const actor = tokenId ? canvas.tokens.get(tokenId)?.actor : undefined
return actor ?? game.actors.get(actorId)
}
getAlias() {
if (this.token?.name != null && this.token != this.prototypeToken) {
return this.token.name
}
return this.name
}
isPersonnageJoueur() { return false }
static extractActorMin = (actor) => { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img }; };
static extractActorMin(actor) { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img } }
/**
* Cette methode surcharge Actor.create() pour ajouter si besoin des Items par défaut:
@@ -156,6 +140,15 @@ export class RdDBaseActor extends Actor {
super(docData, context);
}
getAlias() {
if (this.token?.name != null && this.token != this.prototypeToken) {
return this.token.name
}
return this.name
}
isPersonnageJoueur() { return false }
getCarac() {
return foundry.utils.duplicate(this.system.carac)
}
@@ -221,6 +214,41 @@ export class RdDBaseActor extends Actor {
}
}
async _preUpdate(changed, options, user) {
const updatedCarac = changed?.system?.carac
if (updatedCarac && (updatedCarac.force || updatedCarac.reve || updatedCarac.taille)) {
await this.setEffect(STATUSES.StatusSurEnc, this.isSurenc())
}
await this.delayedRenderSheet('_preUpdate', changed, options)
return super._preUpdate(changed, options, user)
}
_onUpdate(changed, options, userId) {
super._onUpdate(changed, options, userId)
if (userId == game.user.id){
this.delayedRenderSheet('_onUpdate', changed, options)
}
}
async delayedRenderSheet(caller, data, options) {
this.refreshDelayCounter = (this.refreshDelayCounter ?? 0)+1
if (this.refreshDelayCounter == 1) {
this.renderAfterDelay(this.refreshDelayCounter)
}
}
renderAfterDelay(currentCounter) {
setTimeout(async () => {
if (currentCounter == this.refreshDelayCounter) {
this.sheet?.render()
this.refreshDelayCounter = 0
}
else {
this.renderAfterDelay(this.refreshDelayCounter)
}
}, 30)
}
/* -------------------------------------------- */
prepareData() {
super.prepareData()
@@ -233,6 +261,7 @@ export class RdDBaseActor extends Actor {
prepareActorData() { }
async computeEtatGeneral() { }
/* -------------------------------------------- */
findPlayer() {
return game.users.players.find(player => player.active && player.character?.id == this.id);
@@ -285,14 +314,14 @@ export class RdDBaseActor extends Actor {
return this.getEffects().filter(it => it.statuses.has(effectId))
}
async setEffect(effectId, status) {
async setEffect(effectId, status, options = { render: true }) {
if (this.isEffectAllowed(effectId)) {
const effects = this.getEffectsByStatus(effectId)
if (!status && effects.length > 0) {
await this.deleteEmbeddedDocuments('ActiveEffect', effects.map(it => it.id), { render: true })
await this.deleteEmbeddedDocuments('ActiveEffect', effects.map(it => it.id), options)
}
if (status && effects.length == 0) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(effectId)], { render: true })
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(effectId)], options)
}
}
}
@@ -301,13 +330,13 @@ export class RdDBaseActor extends Actor {
this.removeEffects(it => it.id == id)
}
async removeEffects(filter = e => true) {
async removeEffects(filter = e => true, options = { render: true }) {
if (game.user.isGM) {
const ids = this.getEffects(filter)
.filter(it => this.canRemoveEffects(it))
.map(it => it.id)
if (ids.length > 0) {
await this.deleteEmbeddedDocuments('ActiveEffect', ids)
await this.deleteEmbeddedDocuments('ActiveEffect', ids, options)
}
}
@@ -326,16 +355,8 @@ export class RdDBaseActor extends Actor {
async updateCarac(caracName, to) {
}
async onUpdateActor(change, options, actorId) {
const updatedCarac = change?.system?.carac
if (updatedCarac && (updatedCarac.force || updatedCarac.reve || updatedCarac.taille)) {
console.log(' onUpdateActor', change, options, actorId)
await this.setEffect(STATUSES.StatusSurEnc, this.isSurenc())
}
}
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) {
}
@@ -352,13 +373,13 @@ export class RdDBaseActor extends Actor {
}
async _removeItemFromConteneur(item) {
const updates = this.findConteneur(item)
.map(conteneur => {
const nouveauContenu = conteneur.system.contenu.filter(id => id != item.id)
return { _id: conteneur.id, 'system.contenu': nouveauContenu }
})
if (updates.length > 0) {
await this.updateEmbeddedDocuments('Item', updates)
const conteneur = this.findConteneur(item);
if (conteneur) {
const nouveauContenu = conteneur.system.contenu.filter(id => id != item.id)
const updates = { _id: conteneur.id, 'system.contenu': nouveauContenu }
if (updates.length > 0) {
await this.updateEmbeddedDocuments('Item', updates)
}
}
}
@@ -429,7 +450,7 @@ export class RdDBaseActor extends Actor {
msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
}
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: msg
})
@@ -473,7 +494,7 @@ export class RdDBaseActor extends Actor {
await Monnaie.optimiserFortune(this, sols + this.getFortune());
RdDAudio.PlayContextAudio("argent"); // Petit son
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
})
@@ -527,7 +548,7 @@ export class RdDBaseActor extends Actor {
}
const chatAchatItem = foundry.utils.duplicate(achat.vente);
chatAchatItem.quantiteTotal = quantite;
ChatMessage.create({
await ChatMessage.create({
user: achat.userId,
speaker: { alias: (acheteur ?? vendeur).getAlias() },
whisper: ChatUtility.getOwners(this),
@@ -872,13 +893,14 @@ export class RdDBaseActor extends Actor {
system: { description: this.system.description }
}
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.hbs', chatData)
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
.then(async html => await ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
}
actionImpossible(action) {
ui.notifications.info(`${this.getAlias()} ne peut pas faire cette action: ${action}`)
}
ajoutExperience(rollData) { }
isAlcoolise() { return false }
async jetEthylisme() { this.actionImpossible("jet d'éthylisme") }
async rollAppelChance() { this.actionImpossible("appel à la chance") }

View File

@@ -15,8 +15,8 @@ export class RdDCreature extends RdDBaseActorSang {
}
async remiseANeuf() {
await this.removeEffects(e => true);
await this.supprimerBlessures(it => true);
await this.removeEffects(e => true)
await this.supprimerBlessures(it => true)
await this.update({
'system.sante.endurance.value': this.system.sante.endurance.max,
'system.sante.vie.value': this.system.sante.vie.max,

View File

@@ -18,6 +18,7 @@ export class RdDActorEntiteSheet extends RdDBaseActorReveSheet {
formData.niveau = this.actor.getNiveau()
delete formData.system.carac.niveau
formData.resonances = this.actor.system.sante.resonnance.actors.map(actorId => game.actors.get(actorId))
.filter(actor => actor != undefined)
.map(actor => { return { id: actor.id, name: actor.name, img: actor.img } })
return formData
}
@@ -75,6 +76,6 @@ export class RdDActorEntiteSheet extends RdDBaseActorReveSheet {
async resonanceDelete(actorId) {
console.log('Delete : ', actorId);
let newResonances = this.actor.system.sante.resonnance.actors.filter(id => id != actorId);
await this.actor.update({ 'system.sante.resonnance.actors': newResonances }, { renderSheet: false });
await this.actor.update({ 'system.sante.resonnance.actors': newResonances }, { render: false });
}
}

View File

@@ -43,9 +43,7 @@ export class RdDEntite extends RdDBaseActorReve {
async remiseANeuf() {
if (!this.isEntiteNonIncarnee()) {
await this.update({
'system.sante.endurance.value': this.system.sante.endurance.max
});
await this.update({ 'system.sante.endurance.value': this.system.sante.endurance.max })
}
await this.removeEffects(e => true)
}
@@ -85,12 +83,9 @@ export class RdDEntite extends RdDBaseActorReve {
if (this.isEntiteNonIncarnee()) {
return
}
encaissement.isEntiteIncarnee = true
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
foundry.utils.mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue,
endurance: perteEndurance.perte,
blessure: RdDItemBlessure.prepareBlessure(encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken)
})
foundry.utils.mergeObject(encaissement, { resteEndurance: perteEndurance.newValue, endurance: perteEndurance.perte })
}
isEntiteAccordee(attacker) {

View File

@@ -46,9 +46,9 @@ export class RdDActorExportSheet extends RdDActorSheet {
formData.export = this.getMappingValues(formData.context, this.actor)
formData.competences = this.getCompetences(CATEGORIES_COMPETENCES_BASE)
formData.draconic = this.getCompetences(CATEGORIES_DRACONIC)
const legeres = this.actor.nbBlessuresLegeres()
const graves = this.actor.nbBlessuresGraves()
const critiques = this.actor.nbBlessuresCritiques()
const legeres = this.actor.countBlessures(it => it.isLegere())
const graves = this.actor.countBlessures(it => it.isGrave())
const critiques = this.actor.countBlessures(it => it.isCritique())
formData.etat = {
surenc: this.actor.computeMalusSurEncombrement(),
fatigue: {
@@ -103,11 +103,10 @@ export class RdDActorExportSheet extends RdDActorSheet {
gravite: this.html.find(event.currentTarget).data('gravite')
})
)
this.html.find('.click-blessure-add').click(async event =>
await this.actor.ajouterBlessure({
gravite: this.html.find(event.currentTarget).data('gravite')
})
)
this.html.find('.click-blessure-add').click(async event => {
const blessure = this.actor.nouvelleBlessure(this.html.find(event.currentTarget).data('gravite'))
await actor.createEmbeddedDocuments('Item', [blessure])
})
this.html.find('.button-export').click(async event => await
ExportScriptarium.INSTANCE.exportActors([this.actor],
`${this.actor.uuid}-${this.actor.name}`

View File

@@ -16,7 +16,7 @@ export class RdDTextEditor {
$(html).on("click", '.roll-text', async event => await RdDTextEditor.rollText(event))
}
static async enrichHTML(text, object, options = {showlink:true}) {
static async enrichHTML(text, object, options = { showlink: true }) {
const context = {
text,
object,
@@ -70,9 +70,7 @@ export class RdDTextEditor {
options: { showLink: false }
},
param)
ChatMessage.create({
content: text
})
await ChatMessage.create({ content: text })
}
}
}

View File

@@ -92,48 +92,50 @@ export class ChatUtility {
}
/* -------------------------------------------- */
static async createChatWithRollMode(messageData, actor = undefined, rollMode = game.settings.get("core", "rollMode")) {
switch (rollMode) {
static adaptVisibility( messageData, options = { actor: undefined, rollMode: undefined }) {
foundry.utils.mergeObject(options, { rollMode: game.settings.get("core", "rollMode") }, { overwrite: false });
switch (options.rollMode) {
case "blindroll": // GM only
if (!game.user.isGM) {
ChatUtility.blindMessageToGM(messageData)
messageData.whisper = [game.user];
messageData.content = "Message envoyé en aveugle au Gardien"
messageData.whisper = [game.user]
messageData.content = "Message envoyé en aveugle au Gardien";
}
else {
messageData.whisper = ChatUtility.getGMs()
}
break
break;
case "gmroll":
messageData.whisper = actor ? ChatUtility.getOwners(actor) : ChatUtility.getUserAndGMs()
break
messageData.whisper = options.actor ? ChatUtility.getOwners(options.actor) : ChatUtility.getUserAndGMs()
break;
case "selfroll":
messageData.whisper = [game.user]
break
}
messageData.alias = messageData.alias ?? actor?.name ?? game.user.name
return await ChatMessage.create(messageData)
messageData.alias = messageData.alias ?? options.actor?.name ?? game.user.name
return messageData
}
static tellToUser(message) {
ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id] });
static async tellToUser(message) {
await ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id] });
}
static tellToGM(message) {
ChatMessage.create({
static async tellToGM(message) {
await ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getGMs()
});
})
}
static tellToUserAndGM(message) {
ChatMessage.create({
static async tellToUserAndGM(message) {
await ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getUserAndGMs()
})
}
static getOwners(document) {
return document ? game.users.filter(it => document.getUserLevel(it) == CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) : [game.user]
}
@@ -216,7 +218,7 @@ export class ChatUtility {
static async onRenderChatMessage(chatMessage, html, data) {
const rddTimestamp = chatMessage.getFlag(SYSTEM_RDD, 'rdd-timestamp')
const heureRdD = $(html).find('header.message-header .heure-rdd')
if (rddTimestamp && heureRdD.length==0) {
if (rddTimestamp && heureRdD.length == 0) {
const messageTimestamp = $(html).find('header.message-header .message-timestamp');
const timestamp = new RdDTimestamp(rddTimestamp);
const timestampData = timestamp.toCalendrier();

View File

@@ -71,28 +71,32 @@ export class RdDCoeur {
}
static async applyCoeurChateauDormant(actor, message) {
const newSuivants = foundry.utils.duplicate(actor.system.subacteurs.suivants)
let count = 0
newSuivants.forEach(async link => {
const suivant = game.actors.get(link.id)
const prochainCoeur = link.prochainCoeur ?? 0;
const coeurCourant = link.coeur ?? 0;
const diff = prochainCoeur - coeurCourant
if (diff < 0) {
await actor.moralIncDec(-4);
link.coeur = Math.max(0, coeurCourant - 1)
link.prochainCoeur = link.coeur
message.content += `<br>Votre c&oelig;ur brisé pour ${suivant.name} vous fait perdre 4 points de moral, il vous reste ${link.coeur} points de C&oelig;ur.`
count++
}
else if (diff > 0) {
link.coeur = Math.min(prochainCoeur, 4)
message.content += `<br>Votre c&oelig;ur bat fort, vous avez maintenant ${link.coeur} points de C&oelig;ur pour ${suivant.name}.`
link.prochainCoeur = link.coeur
count++
}
let ajustMoral = 0
const newSuivants = actor.system.subacteurs.suivants.filter(link => game.actors.get(link.id) != undefined)
.map(link => {
const suivant = game.actors.get(link.id)
const prochainCoeur = link.prochainCoeur ?? 0
const coeurCourant = link.coeur ?? 0
const diff = prochainCoeur - coeurCourant
if (diff < 0) {
ajustMoral -= 4
link.coeur = Math.max(0, coeurCourant - 1)
link.prochainCoeur = link.coeur
message.content += `<br>Votre c&oelig;ur brisé pour ${suivant.name} vous fait perdre 4 points de moral, il vous reste ${link.coeur} points de C&oelig;ur.`
count++
}
if (diff > 0) {
link.coeur = Math.min(prochainCoeur, 4)
message.content += `<br>Votre c&oelig;ur bat fort, vous avez maintenant ${link.coeur} points de C&oelig;ur pour ${suivant.name}.`
link.prochainCoeur = link.coeur
count++
}
return foundry.utils.duplicate(link)
})
if (ajustMoral != 0) {
await actor.moralIncDec(ajustMoral, 'Coeur')
}
)
if (count > 0) {
await actor.update({ 'system.subacteurs.suivants': newSuivants });
}

View File

@@ -48,7 +48,7 @@ export class Distance {
static isVisible(token, defenderToken) {
// TODO: regarder les StatusEffect aveuglé?
if (Distance.$isToken(token) && Distance.$isToken(defenderToken)) {
return canvas.effects.visibility.testVisibility(defenderToken.getCenterPoint(), { object: token })
return canvas.visibility.testVisibility(defenderToken.getCenterPoint(), { object: token })
}
return true
}

View File

@@ -7,7 +7,7 @@ import { TMRUtility } from "./tmr-utility.js";
export class DialogCreateSigneDraconique extends Dialog {
static async createSigneForActors() {
const signe = await RdDItemSigneDraconique.randomSigneDraconique({ephemere: true});
const signe = await RdDItemSigneDraconique.randomSigneDraconique({ ephemere: true });
let dialogData = {
signe: signe,
tmrs: TMRUtility.buildSelectionTypesTMR(signe.system.typesTMR),
@@ -36,25 +36,25 @@ export class DialogCreateSigneDraconique extends Dialog {
super(conf, options);
this.dialogData = dialogData;
}
async _onCreerSigneActeurs() {
await this.html.find("[name='signe.system.ephemere']").change();
await this.html.find(".signe-xp-sort").change();
this.validerSigne();
this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it.id))
.forEach(actor => this._createSigneForActor(actor, this.dialogData.signe));
await this.html.find("[name='signe.system.ephemere']").change()
await this.html.find(".signe-xp-sort").change()
this.validerSigne()
await Promise.all(this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it.id))
.map(async actor => await this._createSigneForActor(actor, this.dialogData.signe)))
}
async _createSigneForActor(actor, signe) {
actor.createEmbeddedDocuments("Item", [signe]);
ChatMessage.create({
await actor.createEmbeddedDocuments("Item", [signe]);
await ChatMessage.createChatMessage.create({
whisper: ChatUtility.getOwners(actor),
content: await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-actor.hbs", {
signe: signe,
alias: actor.getAlias()
})
});
})
}
validerSigne() {
@@ -67,7 +67,7 @@ export class DialogCreateSigneDraconique extends Dialog {
this.dialogData.signe.system.duree = this.html.find("[name='signe.system.duree']").val();
this.dialogData.signe.system.typesTMR = TMRUtility.buildListTypesTMRSelection(this.dialogData.tmrs);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
@@ -81,7 +81,7 @@ export class DialogCreateSigneDraconique extends Dialog {
}
async setSigneAleatoire() {
const newSigne = await RdDItemSigneDraconique.randomSigneDraconique({ephemere: true});
const newSigne = await RdDItemSigneDraconique.randomSigneDraconique({ ephemere: true });
this.html.find("[name='signe.name']").val(newSigne.name);
this.html.find("[name='signe.system.valeur.norm']").val(newSigne.system.valeur.norm);
@@ -92,7 +92,7 @@ export class DialogCreateSigneDraconique extends Dialog {
this.html.find("[name='signe.system.ephemere']").prop("checked", newSigne.system.ephemere);
this.dialogData.tmrs = TMRUtility.buildSelectionTypesTMR(newSigne.system.typesTMR);
this.dialogData.tmrs.forEach(t => {
this.html.find(`[data-tmr-name='${t.name}']`).prop( "checked", t.selected);
this.html.find(`[data-tmr-name='${t.name}']`).prop("checked", t.selected);
})
this.setEphemere(newSigne.system.ephemere);
}
@@ -113,7 +113,7 @@ export class DialogCreateSigneDraconique extends Dialog {
onSelectTmr(event) {
const tmrName = this.html.find(event.currentTarget)?.data("tmr-name");
const onTmr = this.dialogData.tmrs.find(it => it.name == tmrName);
if (onTmr){
if (onTmr) {
onTmr.selected = event.currentTarget.checked;
}
}

View File

@@ -0,0 +1,19 @@
export class RdDActiveEffect extends ActiveEffect {
async _preCreate(data, options, user) {
await this.parent?.delayedRenderSheet('_preCreateEffect', data, options)
return super._preCreate(data, options, user)
}
async _preUpdate(changed, options, user) {
await this.parent?.delayedRenderSheet('_preUpdateEffect', changed, options)
return super._preUpdate(changed, options, user)
}
async _preDelete(options, user) {
await this.parent?.delayedRenderSheet('_preDeleteEffect', { id: this.id }, options)
return super._preDelete(options, user)
}
}

View File

@@ -1,4 +1,4 @@
import { ChatUtility } from "./chat-utility.js";
import { Grammar } from "./grammar.js";
import { RdDInitiative } from "./initiative.mjs";
import { RdDItem } from "./item.js";
@@ -71,6 +71,36 @@ export class RdDItemCompetence extends RdDItem {
}
return undefined
}
async _preUpdate(changed, options, user) {
await this.checkCompetenceXP(changed.system?.xp)
return super._preUpdate(changed, options, user)
}
async checkCompetenceXP(newXP, display = true) {
if (this.actor?.isPersonnage() && newXP && newXP != this.system.xp && newXP > 0) {
const newNiv = this.system.niveau + 1
let needed = RdDItemCompetence.getCompetenceNextXp(newNiv)
if (newXP >= needed) {
const xpData = {
alias: this.actor.getAlias(),
competence: this.name,
niveau: newNiv,
xp: newXP,
archetype: this.system.niveau_archetype,
archetypeWarning: newNiv > this.system.niveau_archetype
}
if (display) {
await ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.hbs`, xpData)
})
}
return xpData
}
}
}
/* -------------------------------------------- */
static getLabelCategorie(category) {
return CATEGORIES_COMPETENCES[category].label;
@@ -136,7 +166,7 @@ export class RdDItemCompetence extends RdDItem {
return troncList;
}
}
return [];
return []
}
/* -------------------------------------------- */

View File

@@ -41,6 +41,11 @@ export class RdDItemSort extends RdDItem {
return value ? value.replace('variable', 'var') : ''
}
static computeTotalXP(sorts) {
return sorts.map(sort => RdDItemSort.isDifficulteVariable(sort) ? 70 : -10 * Misc.toInt(sort.system.difficulte))
.reduce(Misc.sum(), 0)
}
static isSortOnCoord(sort, coord) {
let tmr = TMRUtility.getTMR(coord)
const caseTMR = sort.system.caseTMR.toLowerCase();
@@ -148,10 +153,10 @@ export class RdDItemSort extends RdDItem {
}
/* -------------------------------------------- */
static incrementBonusCase(actor, sort, coord) {
static async incrementBonusCase(actor, sort, coord) {
let bonuscase = RdDItemSort.calculBonuscase(sort, coord)
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }]);
await actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }]);
}

View File

@@ -85,6 +85,10 @@ export const defaultItemImg = {
munition: "systems/foundryvtt-reve-de-dragon/icons/objets/fleche.webp"
}
const ITEM_TYPES_PLURIEL = {
[ITEM_TYPES.jeu]: 'jeux'
}
/* -------------------------------------------- */
export class RdDItem extends Item {
@@ -112,6 +116,13 @@ export class RdDItem extends Item {
return true;
}
static itemTypePluriel(type) {
if (ITEM_TYPES[type]) {
return ITEM_TYPES_PLURIEL[type] ?? (type + 's')
}
console.error(`Item type ${type} is undefined`)
return type
}
static async getCorrespondingItem(itemRef) {
if (itemRef.pack) {
return await SystemCompendiums.loadDocument(itemRef)
@@ -163,6 +174,21 @@ export class RdDItem extends Item {
super(docData, context);
}
async _preCreate(data, options, user) {
await this.parent?.delayedRenderSheet('_preCreateItem', data, options)
return super._preCreate(data, options, user)
}
async _preUpdate(changed, options, user) {
await this.parent?.delayedRenderSheet('_preUpdateItem', changed, options)
return super._preUpdate(changed, options, user)
}
async _preDelete(options, user) {
await this.parent?.delayedRenderSheet('_preDeleteItem', { id: this.id }, options)
return super._preDelete(options, user)
}
getUniteQuantite() {
switch (this.type) {
case ITEM_TYPES.monnaie: return "(Pièces)"
@@ -601,14 +627,14 @@ export class RdDItem extends Item {
system: { description: this.system.description },
properties: this.getProprietes(),
}
renderTemplate(this.getChatItemTemplate(), chatData).then(html => {
renderTemplate(this.getChatItemTemplate(), chatData).then(async html => {
let chatOptions = RdDUtility.chatDataSetup(html, modeOverride);
ChatMessage.create(chatOptions)
});
await ChatMessage.create(chatOptions)
})
}
getChatItemTemplate() {
return 'systems/foundryvtt-reve-de-dragon/templates/post-item.hbs';
return 'systems/foundryvtt-reve-de-dragon/templates/post-item.hbs'
}
static propertyIfDefined(name, val, condition = true) {

View File

@@ -21,7 +21,7 @@ export class RdDItemArmure extends RdDItem {
if (deterioration >= 10) {
deterioration -= 10;
protection = this.calculProtectionDeterioree();
ChatMessage.create({ content: `Votre armure ${this.name} s'est détériorée, elle protège maintenant de ${protection}` });
await ChatMessage.create({ content: `Votre armure ${this.name} s'est détériorée, elle protège maintenant de ${protection}` });
}
await this.update({
'system.deterioration': deterioration,

View File

@@ -19,7 +19,7 @@ const definitionsBlessures = [
{ type: "legere", gravite: 2, endurance: "1d6", vie: 0, label: 'Légère', max: 5, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp" },
{ type: "grave", gravite: 4, endurance: "2d6", vie: -2, label: 'Grave', max: 2, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp" },
{ type: "critique", gravite: 6, endurance: "-100", vie: -4, label: 'Critique', max: 1, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp" },
{ type: "mort", gravite: 8, label: 'Mort', max: 1, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/mort.webp" }
{ type: "mort", gravite: 8, endurance: "-100", vie: 0, label: 'Mort', max: 1, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/mort.webp" }
]
export class RdDItemBlessure extends RdDItem {
@@ -54,7 +54,7 @@ export class RdDItemBlessure extends RdDItem {
}
await this.createBlessure(actor, gravite)
ChatMessage.create({
await ChatMessage.create({
//TODO: hbs
content: `Blessure ${definition.label} appliquée à ${actor.name}<br>Perte d'endurance : ${lostEndurance} (${definition.endurance})<br>Perte de Vie : ${definition.vie}`,
whisper: ChatUtility.getOwners(actor)
@@ -71,13 +71,13 @@ export class RdDItemBlessure extends RdDItem {
return 0
}
static async createBlessure(actor, gravite, localisation = '', attackerToken = undefined) {
const blessure = RdDItemBlessure.prepareBlessure(gravite, localisation, attackerToken);
const blessures = await actor.createEmbeddedDocuments('Item', [blessure])
static async createBlessure(actor, gravite, options = { localisation: '', origine: '', render: true }) {
const blessure = RdDItemBlessure.prepareBlessure(gravite, options);
const blessures = await actor.createEmbeddedDocuments('Item', [blessure], options)
return blessures[0]
}
static prepareBlessure(gravite, localisation, attackerToken) {
static prepareBlessure(gravite, options = { localisation: '', origine: '' }) {
const definition = RdDItemBlessure.getDefinition(gravite);
return {
name: definition.label,
@@ -85,9 +85,11 @@ export class RdDItemBlessure extends RdDItem {
img: definition.icon,
system: {
gravite: gravite,
vie: definition.vie,
endurance: definition.endurance,
difficulte: -gravite,
localisation: localisation,
origine: attackerToken?.name ?? ""
localisation: options.localisation,
origine: options.origine
}
}
}
@@ -104,11 +106,9 @@ export class RdDItemBlessure extends RdDItem {
async updateTacheSoinBlessure(tache) {
if (tache) {
await tache.update({
system: {
itemId: this.id,
difficulte: Math.min(this.system.difficulte, tache.system.difficulte),
points_de_tache_courant: Math.max(0, this.system.premierssoins.tache)
}
'system.itemId': this.id,
'system.difficulte': Math.min(this.system.difficulte, tache.system.difficulte),
'system.points_de_tache_courant': Math.max(0, this.system.premierssoins.tache)
});
}
}
@@ -160,7 +160,7 @@ export class RdDItemBlessure extends RdDItem {
message.content += ` -- une blessure ${label} reste stable`;
}
}
await this.update(update);
await this.update(update)
}
}

View File

@@ -28,6 +28,7 @@ const _MONTRER = {
}
const _SPLIT = {
code: 'item-split', label: 'Séparer le goupe', icon: 'fa-solid fa-unlink',
actorFilter: actor => actor.isOwner,
filter: it => Misc.toInt(it.system.quantite) > 1 && it.parent?.type != ACTOR_TYPES.commerce,
action: (item, actor) => RdDSheetUtility.splitItem(item, actor)
}
@@ -45,6 +46,7 @@ const _DELETE = {
}
const _EQUIPER = {
code: 'item-equip', label: 'Equiper', icon: it => it.system.equipe ? 'fa-solid fa-hand-rock' : 'fa-regular fa-hand-paper',
actorFilter: actor => actor.isPersonnage(),
filter: it => !it.estContenu && it.isEquipable(),
action: (item, actor) => actor.equiperObjet(item)
}
@@ -52,26 +54,31 @@ const _EQUIPER = {
const _CUISINER = {
code: 'item-cuisiner', label: 'Cuisiner',
img: 'systems/foundryvtt-reve-de-dragon/assets/actions/cuisine.svg',
actorFilter: actor => actor.isPersonnage(),
filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0,
action: (item, actor) => actor.preparerNourriture(item)
}
const _MANGER_CRU = {
code: 'item-manger-cru', label: 'Manger cru', icon: 'fa-solid fa-drumstick-bite',
actorFilter: actor => actor.isPersonnage(),
filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0,
action: (item, actor) => actor.mangerNourriture(item)
}
const _MANGER = {
code: 'item-manger', label: 'Manger', icon: 'fa-solid fa-utensils',
actorFilter: actor => actor.isPersonnage(),
filter: it => !(it.system.boisson),
action: (item, actor) => actor.mangerNourriture(item)
}
const _BOIRE = {
code: 'item-boire', label: 'Boire', icon: 'fa-solid fa-glass-water',
actorFilter: actor => actor.isPersonnage(),
filter: it => it.system.boisson,
action: (item, actor) => actor.mangerNourriture(item)
}
const _DECOCTION = {
code: 'item-decoction', label: 'Décoction', icon: 'fa-solid fa-flask-vial',
actorFilter: actor => actor.isPersonnage(),
action: (item, actor) => actor.fabriquerDecoctionHerbe(item)
}
@@ -82,17 +89,20 @@ const _OUVRIR = {
}
const _LIRE = {
code: 'item-lire', label: 'Lire', icon: 'fa-solid fa-book-open',
actorFilter: actor => actor.isPersonnage(),
action: (item, actor) => actor.actionLire(item)
}
const _REFOULER = {
code: 'item-refouler', label: 'Refouler', icon: 'fa-solid fa-burst',
actorFilter: actor => actor.isPersonnage(),
filter: it => it.system.refoulement > 0,
action: (item, actor) => actor.actionRefoulement(item)
}
const _SORT_RESERVE = {
code: 'item-sortreserve-add', label: 'Ajouter en réserve', icon: 'fa-solid fa-sparkles',
actorFilter: actor => actor.isPersonnage(),
filter: it => game.user.isGM && !it.system.isrituel,
action: (item, actor) => actor.addSortReserve(item)
}
@@ -118,6 +128,7 @@ export class ItemAction {
static applies(action, item, options) {
return action && item
&& item.isActionAllowed(action.code)
&& (!action.actorFilter || (item.actor && action.actorFilter(item.actor)))
&& (!action.filter || action.filter(item))
&& (action.allowLimited || options.editable)
&& (!action.optionsFilter || action.optionsFilter(options))

View File

@@ -22,7 +22,7 @@ export class RdDItemMaladie extends RdDItem {
const souffrance = mal.system.identifie
? `de ${mal.name}`
: `d'un mal inconnu`
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(mal.actor),
content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !`
})

View File

@@ -77,41 +77,41 @@ export class RdDInventaireItemSheet extends RdDItemSheetV1 {
}
async onAddMilieu(event) {
const milieu = this.html.find('input.input-selection-milieu').val();
const milieu = this.html.find('input.input-selection-milieu').val()
if (!milieu) {
ui.notifications.warn(`Choisissez le milieu dans lequel se trouve le/la ${this.item.name}`);
ui.notifications.warn(`Choisissez le milieu dans lequel se trouve le/la ${this.item.name}`)
return
}
const list = this.item.getEnvironnements();
const exists = list.find(it => it.milieu == milieu);
const list = this.item.getEnvironnements()
const exists = list.find(it => it.milieu == milieu)
if (exists) {
ui.notifications.warn(`${this.item.name} a déjà une rareté ${exists.rarete} en ${milieu} (fréquence: ${exists.frequence})`);
return
}
const rarete = RdDRaretes.rareteFrequente();
const added = { milieu, rarete: rarete.code, frequence: rarete.frequence };
const rarete = RdDRaretes.rareteFrequente()
const added = { milieu, rarete: rarete.code, frequence: rarete.frequence }
const newList = [added, ...list].sort(Misc.ascending(it => it.milieu))
await this.item.update({ 'system.environnement': newList })
}
async onDeleteMilieu(event) {
const milieu = this.$getEventMilieu(event);
const milieu = this.$getEventMilieu(event)
if (milieu != undefined) {
const newList = this.item.getEnvironnements().filter(it => it.milieu != milieu)
.sort(Misc.ascending(it => it.milieu));
await this.item.update({ 'system.environnement': newList });
.sort(Misc.ascending(it => it.milieu))
await this.item.update({ 'system.environnement': newList })
}
}
async onChange(event, doMutation) {
const list = this.item.system.environnement;
const milieu = this.$getEventMilieu(event);
const updated = list.find(it => it.milieu == milieu);
const list = this.item.system.environnement
const milieu = this.$getEventMilieu(event)
const updated = list.find(it => it.milieu == milieu)
if (updated) {
doMutation(updated);
doMutation(updated)
const newList = [...list.filter(it => it.milieu != milieu), updated]
.sort(Misc.ascending(it => it.milieu));
await this.item.update({ 'system.environnement': newList });
.sort(Misc.ascending(it => it.milieu))
await this.item.update({ 'system.environnement': newList })
}
}

View File

@@ -20,7 +20,7 @@ export class RdDFauneItemSheet extends RdDInventaireItemSheet {
'system.actor.pack': linkedActor.pack,
'system.actor.id': linkedActor._id,
'system.actor.name': linkedActor.name
});
})
}
else {
ui.notifications.warn(`${linkedActor.name} ne provient pas d'un compendium.
@@ -32,7 +32,7 @@ export class RdDFauneItemSheet extends RdDInventaireItemSheet {
'system.actor.pack': '',
'system.actor.id': '',
'system.actor.name': ''
});
})
}
}

View File

@@ -16,22 +16,18 @@ class Migration {
async migrate() { }
async applyItemsUpdates(computeUpdates) {
await game.actors.forEach(async (actor) => {
await Promise.all(game.actors.map(actor => {
const actorItemUpdates = computeUpdates(actor.items).filter(it => it != undefined);
if (actorItemUpdates.length > 0) {
console.log(
this.code,
`Applying updates on actor ${actor.name} items`,
actorItemUpdates
);
await actor.updateEmbeddedDocuments("Item", actorItemUpdates);
console.log(this.code, `Applying updates on actor ${actor.name} items`, actorItemUpdates);
return actor.updateEmbeddedDocuments("Item", actorItemUpdates);
}
});
}))
const itemUpdates = computeUpdates(game.items).filter(it => it != undefined);
const itemUpdates = computeUpdates(game.items).filter(it => it != undefined)
if (itemUpdates.length > 0) {
console.log(this.code, "Applying updates on items", itemUpdates);
await Item.updateDocuments(itemUpdates);
console.log(this.code, "Applying updates on items", itemUpdates)
await Item.updateDocuments(itemUpdates)
}
}
}
@@ -58,13 +54,13 @@ class _1_5_34_migrationPngWebp {
//Migrate system png to webp
await Item.updateDocuments(itemsUpdates);
await Actor.updateDocuments(actorsUpdates);
game.actors.forEach(actor => {
if (actor.token?.img && actor.token.img.match(regexOldPngJpg)) {
actor.update({ "token.img": convertImgToWebp(actor.token.img) });
}
const actorItemsToUpdate = prepareDocumentsImgUpdate(actor.items);
actor.updateEmbeddedDocuments('Item', actorItemsToUpdate);
});
await Promise.all(game.actors
.filter(actor => actor.token?.img && actor.token.img.match(regexOldPngJpg))
.map(actor => actor.update({ "token.img": convertImgToWebp(actor.token.img) }))
)
await Promise.all(game.actors.map(actor =>
actor.updateEmbeddedDocuments('Item', prepareDocumentsImgUpdate(actor.items))
))
}
}
@@ -125,16 +121,15 @@ class _10_0_21_VehiculeStructureResistanceMax extends Migration {
get version() { return "10.0.21"; }
async migrate() {
await game.actors
.filter((actor) => actor.type == "vehicule")
.forEach(async (actor) => {
await actor.update({
await Promise.all(
game.actors.filter(actor => actor.type == "vehicule")
.map(actor => actor.update({
'system.etat.resistance.value': actor.system.resistance,
'system.etat.resistance.max': actor.system.resistance,
'system.etat.structure.value': actor.system.structure,
'system.etat.structure.max': actor.system.structure
})
});
}))
)
}
}
@@ -192,9 +187,9 @@ class _10_2_5_ArmesTirLancer extends Migration {
get version() { return "10.2.5"; }
migrateArmeTirLancer(it) {
let updates = foundry.utils.mergeObject({ _id: it.id }, this.getMapping(it).updates);
console.log(it.name, updates);
return updates;
const updates = foundry.utils.mergeObject({ _id: it.id }, this.getMapping(it).updates)
console.log(it.name, updates)
return updates
}
async migrate() {
@@ -359,7 +354,7 @@ class _10_4_6_ServicesEnCommerces extends Migration {
}
async transformInventaireCommerce(service) {
const serviceItems = (service.system.items ?? []);
const commerceItems = await Promise.all(serviceItems.map(async (it) => { return await this.transformToItemBoutique(it); }));
const commerceItems = await Promise.all(serviceItems.map((it) => this.transformToItemBoutique(it)))
return commerceItems.concat(Monnaie.monnaiesStandard());
}
@@ -439,7 +434,7 @@ class _10_7_0_MigrationBlessures extends Migration {
'system.blessures.graves.liste': [],
'system.blessures.critiques.liste': []
})
}));
}))
}
creerBlessure(gravite, graviteTexte, blessure, timestamp) {
const dateBlessure = timestamp.addJours(-blessure.jours);
@@ -791,6 +786,6 @@ export class Migrations {
}
compareVersions(a, b) {
return isNewerVersion(a.version, b.version) ? 1 : isNewerVersion(b.version, a.version) ? -1 : 0;
return foundry.utils.isNewerVersion(a.version, b.version) ? 1 : foundry.utils.isNewerVersion(b.version, a.version) ? -1 : 0;
}
}

View File

@@ -24,6 +24,13 @@ export class Misc {
return text.charAt(0).toLowerCase() + text.slice(1);
}
static stripHtml(html)
{
const tmp = document.createElement("DIV")
tmp.innerHTML = html
return tmp.textContent || tmp.innerText || ""
}
static toSignedString(number) {
const value = parseInt(number)
const isPositiveNumber = value != NaN && value > 0;

View File

@@ -168,7 +168,7 @@ export class Apprecier {
async rollMoral(moral = undefined) {
if (this.raisons.length > 0) {
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
content: 'Pas de jet de moral:' + Misc.concat(this.raisons.map(r => `<br> - ${r}`))
})
@@ -176,7 +176,6 @@ export class Apprecier {
}
moral = moral ?? this.appreciation.moral
// TODO: jet de moral
await this.actor.jetDeMoral(moral, this.appreciation.bonmoment)
}
}

View File

@@ -168,10 +168,18 @@ export class RdDCarac {
const total = Object.values(carac ?? {}).filter(c => !c.derivee)
.map(it => parseInt(it.value))
.reduce(Misc.sum(), 0);
const beauteSuperieur10 = Math.max((beaute ?? 10) - 10, 0);
const beauteSuperieur10 = Math.max((beaute ?? 10) - 10, 0)
return total + beauteSuperieur10;
}
static computeTotalXp(carac, beaute = undefined) {
const totalXp = Object.values(carac ?? {}).filter(c => !c.derivee)
.map(it => RdDCarac.getCaracXp(0, Misc.toInt(it.value)) + Misc.toInt(it.xp))
.reduce(Misc.sum(), 0);
const beauteXp = beaute > 10 ? RdDCarac.getCaracXp(10, beaute) : 0
return totalXp + beauteXp;
}
static levelUp(it) {
it.xpNext = RdDCarac.getCaracNextXp(it.value);
it.isLevelUp = (it.xp >= it.xpNext);
@@ -184,12 +192,16 @@ export class RdDCarac {
/* -------------------------------------------- */
static getCaracNextXp(value) {
const nextValue = Number(value) + 1;
value = Number(value);
const nextValue = value + 1;
// xp est le coût pour atteindre cette valeur, on regarde donc le coût de la valeur+1
return RdDCarac.getCaracXp(nextValue);
return RdDCarac.getCaracXp(value, nextValue)
}
static getCaracXp(targetValue) {
return RdDCarac.getCaracDerivee(targetValue)?.xp ?? 200;
static getCaracXp(from, to) {
return Array.from({ length: to - from },
(_, i) => from + i + 1)
.map(it => RdDCarac.getCaracDerivee(it)?.xp ?? 200)
.reduce(Misc.sum(), 0)
}
}

View File

@@ -63,6 +63,7 @@ export class RdDCombatManager extends Combat {
it.token.id == tokenId
)
}
static getRangInitiativeCombatant(actorId, tokenId) {
const combatant = RdDCombatManager.getCombatant(actorId, tokenId)
return combatant?.system.init?.rang
@@ -198,8 +199,8 @@ export class RdDCombatManager extends Combat {
<div>
Etant donné son ${action.name}, son initative pour ce premier round est désormais de ${initData.init}.
</div>`
ChatMessage.create({ content: msg });
game.combat.setInitiative(combatant._id, initData.init);
ChatMessage.create({ content: msg })
game.combat.setInitiative(combatant._id, initData.init)
}
}
}
@@ -387,7 +388,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static onMsgEncaisser(msg) {
let defender = canvas.tokens.get(msg.defenderToken.id).actor;
let defender = canvas.tokens.get(msg.defenderToken.id)?.actor;
if (Misc.isOwnerPlayer(defender)) {
let attackerRoll = msg.attackerRoll;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
@@ -466,24 +467,10 @@ export class RdDCombat {
this.defenderId = this.defender.id
this.attackerTokenId = attackerTokenId
this.defenderTokenId = defenderTokenId
this.attackerToken = RdDCombat.$extractAttackerTokenData(attacker, attackerTokenId)
this.defenderToken = RdDCombat.$extractDefenderTokenData(defender, defenderTokenId, target)
this.attackerToken = Targets.getTokenData(attacker, attackerTokenId)
this.defenderToken = Targets.getTokenData(defender, defenderTokenId, target)
}
static $extractAttackerTokenData(attacker, attackerTokenId) {
const token = canvas.tokens.get(attackerTokenId);
return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(attackerTokenId, attacker)
}
static $extractDefenderTokenData(defender, defenderTokenId, target) {
if (target) {
return Targets.extractTokenData(target)
}
const token = canvas.tokens.get(defenderTokenId);
return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(defenderTokenId, defender)
}
/* -------------------------------------------- */
async onEvent(button, event) {
const chatMessage = ChatUtility.getChatMessage(event);
@@ -518,8 +505,8 @@ export class RdDCombat {
/* -------------------------------------------- */
attaqueChanceuse(attackerRoll) {
ui.notifications.info("L'attaque est rejouée grâce à la chance")
attackerRoll.essais.attaqueChance = true;
this.attaque(attackerRoll, attackerRoll.arme);
attackerRoll.essais.attaqueChance = true
this.attaque(attackerRoll, attackerRoll.arme)
}
/* -------------------------------------------- */
@@ -623,7 +610,7 @@ export class RdDCombat {
async proposerAjustementTirLancer(rollData) {
if (['tir', 'lancer'].includes(rollData.competence.system.categorie)) {
if (this.defender.isEntiteBlurette()) {
ChatMessage.create({
await ChatMessage.create({
content: `<strong>La cible est une blurette, l'arme à distance sera perdue dans le blurêve`,
whisper: ChatUtility.getGMs()
})
@@ -638,7 +625,7 @@ export class RdDCombat {
},
Distance.ajustements(_token, defenderToken, { arme: rollData.arme, main: rollData.competence.system.categorie })
)
ChatMessage.create({
await ChatMessage.create({
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.hbs', info),
whisper: ChatUtility.getGMs()
})
@@ -646,11 +633,11 @@ export class RdDCombat {
}
}
async attaqueV2() {
async attaqueV2(options = undefined) {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return
}
await this.doRollAttaque({
const rollData = {
ids: {
actorId: this.attackerId,
actorTokenId: this.attackerTokenId,
@@ -658,8 +645,19 @@ export class RdDCombat {
opponentTokenId: this.defenderTokenId,
},
type: { allowed: ['attaque'], current: 'attaque' },
selected: {},
passeArme: foundry.utils.randomID(16),
})
}
if (options) {
rollData.selected = {
attaque: {
comp: { id: options.comp.id },
arme: { id: options.arme.id },
main: options.main
}
}
}
await this.doRollAttaque(rollData)
}
async doRollAttaque(rollData, callbacks = []) {
@@ -725,7 +723,7 @@ export class RdDCombat {
const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
speaker: ChatMessage.getSpeaker({ actor: this.defender, token: canvas.tokens.get(this.defenderTokenId) }),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', defenseData)
@@ -740,12 +738,14 @@ export class RdDCombat {
}
/* -------------------------------------------- */
async attaque(competence, arme) {
async attaque(competence, arme, main) {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return
}
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
return this.attacker.rollCompetence(competence.name, { arme: arme })
return this.attaqueV2(
{ comp: competence, arme: arme, main: main }
)
}
if (arme.system.cac == EMPOIGNADE) {
RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender)
@@ -754,9 +754,9 @@ export class RdDCombat {
RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
let rollData = this._prepareAttaque(competence, arme)
console.log("RdDCombat.attaque >>>", rollData);
console.log("RdDCombat.attaque >>>", rollData)
if (arme) {
this.attacker.verifierForceMin(arme);
await this.attacker.verifierForceMin(arme);
}
await this.proposerAjustementTirLancer(rollData)
@@ -929,7 +929,7 @@ export class RdDCombat {
async _chatMessageDefense(paramDemandeDefense, defenderRoll) {
const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
speaker: ChatMessage.getSpeaker({ actor: this.defender, token: canvas.tokens.get(this.defenderTokenId) }),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense-v1.hbs', paramDemandeDefense),
@@ -996,9 +996,9 @@ export class RdDCombat {
const arme = rollData.arme;
const avecArme = !['', 'sans-armes', 'armes-naturelles'].includes(arme?.system.categorie_parade ?? '');
const action = (rollData.attackerRoll ? (arme ? "la parade" : "l'esquive") : "l'attaque");
ChatUtility.createChatWithRollMode(
await ChatMessage.create(ChatUtility.adaptVisibility(
{ content: `<strong>Maladresse à ${action}!</strong> ` + await RdDRollTables.getMaladresse({ arme: avecArme }) },
this.defender)
{ actor: this.defender }))
}
/* -------------------------------------------- */
@@ -1122,9 +1122,9 @@ export class RdDCombat {
async infoAttaquantDesarme(rollData) {
if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) {
// TODO: attaquant doit jouer résistance et peut être désarmé p132
ChatUtility.createChatWithRollMode(
await ChatMessage.create(ChatUtility.adaptVisibility(
{ content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` },
this.defender)
{ actor: this.defender }))
}
}
@@ -1156,9 +1156,9 @@ export class RdDCombat {
console.log("RdDCombat._onParadeParticuliere >>>", defenderRoll);
if (!defenderRoll.attackerRoll.isPart) {
// TODO: attaquant doit jouer résistance et peut être désarmé p132
ChatUtility.createChatWithRollMode(
await ChatMessage.create(ChatUtility.adaptVisibility(
{ content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` },
this.defender)
{ actor: this.defender }))
}
}
/* -------------------------------------------- */
@@ -1243,9 +1243,9 @@ export class RdDCombat {
/* -------------------------------------------- */
async _onEsquiveParticuliere(defenderRoll) {
console.log("RdDCombat._onEsquiveParticuliere >>>", defenderRoll);
ChatUtility.createChatWithRollMode(
await ChatMessage.create(ChatUtility.adaptVisibility(
{ content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" },
this.defender);
{ actor: this.defender }))
}
/* -------------------------------------------- */

View File

@@ -27,20 +27,8 @@ const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
export class RdDCommands {
static init() {
const rddCommands = new RdDCommands();
game.system.rdd.commands = rddCommands;
Hooks.on("chatMessage", (html, content, msg) => {
if (content[0] == '/') {
let regExp = /(\S+)/g;
let commands = content.match(regExp);
if (rddCommands.processChatCommand(commands, content, msg)) {
return false;
}
}
return true;
});
game.system.rdd.commands = new RdDCommands()
Hooks.on("chatMessage", (chatLog, message, msg) => game.system.rdd.commands.onChatMessage(message, msg))
}
constructor() {
@@ -116,6 +104,7 @@ export class RdDCommands {
this.registerCommand({
path: ["/xp", "carac"], func: (content, msg, params) => this.getCoutXpCarac(msg, params),
descr: `Détermine le coût d'expérience pour augmenter une caractéristique. Exemples:
<br>/xp carac 12 15: coût pour passer de 12 15
<br>/xp carac 15: coût pour atteindre 15 (depuis 14)`
});
@@ -215,6 +204,17 @@ export class RdDCommands {
}
onChatMessage(message, msg = {}) {
const content = Misc.stripHtml(message)
if (content[0] == '/') {
const commands = content.match(/(\S+)/g)
if (this.processChatCommand(commands, content, msg)) {
return false
}
}
return true
}
/* -------------------------------------------- */
/* Manage chat commands */
processChatCommand(commandLine, content = '', msg = {}) {
@@ -367,7 +367,7 @@ export class RdDCommands {
const carac = params[0];
const competence = length > 1 ? '/' + Misc.join(params.slice(1, length), ' ') : ''
ChatMessage.create({ content: `@roll[${carac}${competence}/${diff}]` })
await ChatMessage.create({ content: `@roll[${carac}${competence}/${diff}]` })
}
}
@@ -440,13 +440,18 @@ export class RdDCommands {
/* -------------------------------------------- */
getCoutXpCarac(msg, params) {
if (params && params.length == 1) {
let to = Number(params[0]);
return RdDCommands._chatAnswer(msg, `Coût pour passer une caractéristique de ${to - 1} à ${to}: ${RdDCarac.getCaracXp(to)}`);
}
else {
return false;
if (params) {
if (params.length == 1) {
const to = Number(params[0])
return RdDCommands._chatAnswer(msg, `Coût pour passer une caractéristique de ${to - 1} à ${to}: ${RdDCarac.getCaracXp(to - 1, to)}`);
}
if (params.length == 2) {
const from = Number(params[0]);
const to = Number(params[1]);
return RdDCommands._chatAnswer(msg, `Coût pour passer une caractéristique de ${from} à ${to}: ${RdDCarac.getCaracXp(from, to)}`);
}
}
return false
}
async creerSignesDraconiques() {

View File

@@ -15,21 +15,28 @@ export class RdDEmpoignade {
}
/* -------------------------------------------- */
static getActorEmpoignade(actorId) {
const actor = game.actors.get(actorId)
if (actor == undefined) {
ui.notifications.warn(`Impossible de retrouver l'acteur ${actorId}, l'empoignade ne peut pas être continuée.`)
}
return actor
}
static isCombatantEmpoignade(actorId, tokenId) {
const combatant = RdDCombatManager.getCombatant(actorId, tokenId)
return MAP_PHASE.empoignade.rang == combatant?.system.init?.rang
return combatant && MAP_PHASE.empoignade.rang == combatant?.system.init?.rang
}
static async ajustementEmpoignade(attacker, defender, adjust = 1) {
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
if (empoignade?.system.empoigneurid == defender.id) {
let empoignade = RdDEmpoignade.getEmpoignade(defender, attacker)
empoignade = RdDEmpoignade.getEmpoignade(defender, attacker)
return await RdDEmpoignade.$ajustementEmpoignade(empoignade, defender, attacker, - adjust);
}
return await RdDEmpoignade.$ajustementEmpoignade(empoignade, attacker, defender, adjust);
}
static async $ajustementEmpoignade(empoignade, attacker, defender, adjust) {
const empId = empoignade?.system.empoignadeid ?? foundry.utils.randomID(16);
const empFin = (empoignade?.system.pointsemp ?? 0) + adjust
@@ -73,30 +80,32 @@ export class RdDEmpoignade {
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Corps à corps", "melee")
});
})
$(html).on("click", '.defense-empoignade-esquive', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Esquive", "derobee")
});
})
$(html).on("click", '.empoignade-poursuivre', event => {
let attackerId = event.currentTarget.attributes['data-attackerId'].value
let defenderId = event.currentTarget.attributes['data-defenderId'].value
RdDEmpoignade.onAttaqueEmpoignadeValidee(game.actors.get(attackerId), game.actors.get(defenderId))
});
const attacker = RdDEmpoignade.getActorFromEventTag(event, 'data-attackerId')
const defender = RdDEmpoignade.getActorFromEventTag(event, 'data-defenderId')
if (attacker && defender) {
RdDEmpoignade.onAttaqueEmpoignadeValidee(attacker, defender)
}
})
$(html).on("click", '.empoignade-entrainer-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.entrainerAuSol(rollData)
ChatUtility.remover(chatMessage)()
});
})
$(html).on("click", '.empoignade-projeter-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.projeterAuSol(rollData)
ChatUtility.remover(chatMessage)()
});
})
$(html).on("change", '.empoignade-perte-endurance', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
@@ -104,7 +113,11 @@ export class RdDEmpoignade {
RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value)
ChatUtility.remover(chatMessage)()
}
});
})
}
static getActorFromEventTag(event, tag) {
return RdDEmpoignade.getActorEmpoignade(event.currentTarget.attributes[tag]?.value ?? '<inconnu>')
}
/* -------------------------------------------- */
@@ -112,32 +125,31 @@ export class RdDEmpoignade {
// TODO: autoriser la perception? la comédie/séduction?
if (RdDEmpoignade.isEmpoignadeEnCours(actor)) {
ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.")
return true;
return true
}
return false;
return false
}
/* -------------------------------------------- */
static $storeRollEmpoignade(msg, rollData) {
RdDEmpoignade.$reduceActorToIds(rollData);
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData);
ChatUtility.setMessageData(msg, "empoignade-roll-data", RdDEmpoignade.$reduceActorToIds(rollData))
}
static $reduceActorToIds(rollData) {
rollData.attacker = { id: rollData.attacker.id };
rollData.defender = { id: rollData.defender.id };
rollData.attacker = { id: rollData.attacker.id }
rollData.defender = { id: rollData.defender.id }
return rollData
}
/* -------------------------------------------- */
static $readRollEmpoignade(msg) {
const rollData = ChatUtility.getMessageData(msg, 'empoignade-roll-data');
RdDEmpoignade.$replaceIdsWithActors(rollData);
return rollData
return RdDEmpoignade.$replaceIdsWithActors(ChatUtility.getMessageData(msg, 'empoignade-roll-data'))
}
static $replaceIdsWithActors(rollData) {
rollData.attacker = game.actors.get(rollData.attacker.id);
rollData.defender = game.actors.get(rollData.defender.id);
rollData.attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
rollData.defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
return rollData
}
/* -------------------------------------------- */
@@ -147,19 +159,21 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static getEmpoignadeById(actor, id) {
return actor.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.empoignadeid == id)
return actor?.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.empoignadeid == id)
}
/* -------------------------------------------- */
static getEmpoignade(attacker, defender) {
let emp = attacker.itemTypes[ITEM_TYPES.empoignade].find(it =>
(it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
(it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
)
if (emp) {
return foundry.utils.duplicate(emp);
if (attacker && defender) {
const empoignade = attacker.itemTypes[ITEM_TYPES.empoignade].find(it =>
(it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
(it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
)
if (empoignade) {
return foundry.utils.duplicate(empoignade)
}
}
return undefined;
return undefined
}
/* -------------------------------------------- */
@@ -180,10 +194,14 @@ export class RdDEmpoignade {
}
static isActionAutorisee(mode, attacker, defender) {
const acting = RdDEmpoignade.isActionDefenseur(mode) ? defender : attacker;
if (!defender || !attacker) {
return false
}
const acting = RdDEmpoignade.isActionDefenseur(mode) ? defender : attacker
if (acting.getUserLevel(game.user) < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
ui.notifications.warn(`Vous n'êtes pas autorisé à choisir l'action de ${acting.name}`)
return false;
return false
}
return true
}
@@ -205,14 +223,11 @@ export class RdDEmpoignade {
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const isNouvelle = empoignade == undefined;
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
//console.log("W.", empoignade, defender.hasArmeeMeleeEquipee())
if ((isNouvelle || empoignade.system.pointsemp == 0) && defender.hasArmeeMeleeEquipee()) {
ChatUtility.createChatWithRollMode(
{
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.hbs`, { attacker: attacker, defender: defender })
},
attacker
)
await ChatMessage.create(ChatUtility.adaptVisibility(
{ content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.hbs`, { attacker: attacker, defender: defender }) },
{ actor: attacker }
))
} else {
await this.onAttaqueEmpoignadeValidee(attacker, defender)
}
@@ -264,30 +279,34 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async onAttaqueEmpoignadeFromItem(empoignade) {
let attacker = game.actors.get(empoignade.system.empoigneurid)
let defender = game.actors.get(empoignade.system.empoigneid)
await this.onAttaqueEmpoignadeValidee(attacker, defender)
const attacker = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneurid)
const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
if (attacker && defender) {
await this.onAttaqueEmpoignadeValidee(attacker, defender)
}
}
static async onImmobilisation(attacker, empoignade) {
const defender = game.actors.get(empoignade.system.empoigneid)
const empDefenseur = defender.itemTypes[ITEM_TYPES.empoignade]
.find(it => it.system.empoignadeid == empoignade.system.empoignadeid);
await defender.updateEmbeddedDocuments('Item', [{
_id: empDefenseur.id,
'system.immobilise': true
}])
const rollData = {
mode: "immobilise",
empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetenceCorpsACorps()
const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
if (defender) {
const empDefenseur = defender.itemTypes[ITEM_TYPES.empoignade]
.find(it => it.system.empoignadeid == empoignade.system.empoignadeid);
await defender.updateEmbeddedDocuments('Item', [{
_id: empDefenseur.id,
'system.immobilise': true
}])
const rollData = {
mode: "immobilise",
empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetenceCorpsACorps()
}
const msg = await ChatMessage.create({
whisper: ChatUtility.getOwners(attacker),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.hbs`, rollData)
})
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
const msg = await ChatMessage.create({
whisper: ChatUtility.getOwners(attacker),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.hbs`, rollData)
})
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
@@ -304,30 +323,30 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async $onRollEmpoignade(rollData, isNouvelle = false) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
if (attacker && defender) {
if (rollData.rolled.isSuccess && isNouvelle) {
RdDEmpoignade.$createEtatEmpoignade(rollData.empoignade)
}
if (rollData.rolled.isSuccess && isNouvelle) {
RdDEmpoignade.$createEtatEmpoignade(rollData.empoignade)
rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
if (rollData.rolled.isPart) {
rollData.particuliere = "finesse";
}
let msg = await RdDRollResult.displayRollData(rollData, defender, 'chat-empoignade-resultat.hbs');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
if (rollData.rolled.isPart) {
rollData.particuliere = "finesse";
}
let msg = await RdDRollResult.displayRollData(rollData, defender, 'chat-empoignade-resultat.hbs');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async onDefenseEmpoignade(attackerRoll, mode, competenceName = "Corps à corps", carac = "melee") {
let attacker = game.actors.get(attackerRoll.attacker.id)
let defender = game.actors.get(attackerRoll.defender.id)
const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
return
}
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
if (!empoignade) {
@@ -367,7 +386,7 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async $onRollContrerLiberer(rollData) {
let empoignade = rollData.empoignade
const empoignade = rollData.empoignade
if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) {
empoignade.system.pointsemp++
@@ -389,35 +408,40 @@ export class RdDEmpoignade {
static async $createEtatEmpoignade(empoignade) {
console.log("CREATE Empoignade", empoignade)
let defender = game.actors.get(empoignade.system.empoigneid)
let attacker = game.actors.get(empoignade.system.empoigneurid)
const attacker = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneurid)
const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
// Creer l'empoignade sur attaquant/defenseur
await attacker.creerObjetParMJ(empoignade)
await defender.creerObjetParMJ(empoignade)
if (attacker && defender) {
// Creer l'empoignade sur attaquant et defenseur
await attacker.creerObjetParMJ(empoignade)
await defender.creerObjetParMJ(empoignade)
}
}
/* -------------------------------------------- */
static async $updateEtatEmpoignade(empoignade, attacker, defender) {
const belligerants = [
attacker ?? game.actors.get(empoignade.system.empoigneurid),
defender ?? game.actors.get(empoignade.system.empoigneid)]
const removeEmp = empoignade.system.pointsemp == 0
attacker = attacker ?? RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneurid)
defender = defender ?? RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
if (removeEmp) {
const emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
return await attacker.deleteEmbeddedDocuments('Item', [emp.id])
}
else {
await Promise.all(
belligerants.map(async belligerant => {
const emp = RdDEmpoignade.getEmpoignadeById(belligerant, empoignade.system.empoignadeid)
return await belligerant.updateEmbeddedDocuments('Item', [{
_id: emp.id,
"system.pointsemp": empoignade.system.pointsemp,
"system.ausol": empoignade.system.ausol
}])
}))
if (attacker && defender) {
const belligerants = [attacker, defender]
const removeEmp = empoignade.system.pointsemp == 0
if (removeEmp) {
const emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
return await attacker.deleteEmbeddedDocuments('Item', [emp.id])
}
else {
await Promise.all(
belligerants.map(async belligerant => {
const emp = RdDEmpoignade.getEmpoignadeById(belligerant, empoignade.system.empoignadeid)
return await belligerant.updateEmbeddedDocuments('Item', [{
_id: emp.id,
"system.pointsemp": empoignade.system.pointsemp,
"system.ausol": empoignade.system.ausol
}])
}))
}
}
}
@@ -425,19 +449,21 @@ export class RdDEmpoignade {
static async $deleteEmpoignade(empoignade) {
console.log("DELETE Empoignade", empoignade)
const defender = game.actors.get(empoignade.system.empoigneid)
const emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
await defender.deleteEmbeddedDocuments('Item', [emp.id])
const defender = RdDEmpoignade.getActorEmpoignade(empoignade.system.empoigneid)
if (defender) {
const emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
await defender.deleteEmbeddedDocuments('Item', [emp.id])
}
}
/* -------------------------------------------- */
static async entrainerAuSol(rollData) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
empoignade.system.ausol = true
await this.$updateEtatEmpoignade(empoignade)
@@ -445,14 +471,14 @@ export class RdDEmpoignade {
await attacker.setEffect(STATUSES.StatusProne, true);
await defender.setEffect(STATUSES.StatusProne, true);
let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.hbs');
const msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.hbs');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async projeterAuSol(rollData) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
@@ -467,8 +493,8 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async perteEndurance(rollData, perteMode) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
const attacker = RdDEmpoignade.getActorEmpoignade(rollData.attacker.id)
const defender = RdDEmpoignade.getActorEmpoignade(rollData.defender.id)
if (perteMode == "none" || !RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
@@ -501,11 +527,11 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async deleteLinkedEmpoignade(actorId, empoignade) {
let actorDeleteId = (actorId == empoignade.system.empoigneurid) ? empoignade.system.empoigneid : empoignade.system.empoigneurid
let actor = game.actors.get(actorDeleteId)
let emp = this.getEmpoignadeById(actor, empoignade.system.empoignadeid)
if (emp) {
await actor.deleteEmbeddedDocuments('Item', [emp.id])
const actorDeleteId = (actorId == empoignade.system.empoigneurid) ? empoignade.system.empoigneid : empoignade.system.empoigneurid
const actor = RdDEmpoignade.getActorEmpoignade(actorDeleteId)
const empoignadeOpposant = this.getEmpoignadeById(actor, empoignade.system.empoignadeid)
if (actor && empoignadeOpposant) {
await actor.deleteEmbeddedDocuments('Item', [empoignadeOpposant.id])
}
}

View File

@@ -101,7 +101,7 @@ export class RdDHotbar {
}
/** Roll macro */
static rollMacro(itemName, itemType, categorieArme = 'competence') {
static rollMacro(itemName, itemType, maniement = 'competence') {
const speaker = ChatMessage.getSpeaker();
let actor;
if (speaker.token) actor = game.actors.tokens[speaker.token];
@@ -117,10 +117,10 @@ export class RdDHotbar {
// Trigger the item roll
switch (item.type) {
case ITEM_TYPES.arme:
return actor.rollArme(item, categorieArme);
return actor.rollArme(item, maniement);
case ITEM_TYPES.competence:
if (item.isCorpsACorps()) {
switch (categorieArme) {
switch (maniement) {
case PUGILAT:
return actor.rollArme(RdDItemArme.pugilat(actor));
case EMPOIGNADE:
@@ -130,7 +130,7 @@ export class RdDHotbar {
return actor.rollCompetence(item);
case ITEM_TYPES.competencecreature:
return item.system.iscombat
? actor.rollArme(item, categorieArme)
? actor.rollArme(item, maniement)
: actor.rollCompetence(item);
}

View File

@@ -94,6 +94,7 @@ import ChatRollResult from "./roll/chat-roll-result.mjs"
import ExportPdf from "./actor/export-pdf/export-pdf.mjs"
import { DialogFlushByDate } from "./chat/dialog-flush-by-date.mjs"
import { Remote } from "./remote.mjs"
import { RdDActiveEffect } from "./effect/base-active-effect.js"
/**
* RdD system
@@ -199,6 +200,7 @@ export class SystemReveDeDragon {
// Define custom Entity classes
console.log(`Initializing Reve de Dragon Documents`)
CONFIG.Actor.documentClass = RdDBaseActor
CONFIG.ActiveEffect.documentClass = RdDActiveEffect
CONFIG.Item.documentClass = RdDItem
CONFIG.Item.dataModels = {
monnaie: models.RdDModelMonnaie,

View File

@@ -117,7 +117,7 @@ export class RdDMeteo {
meteo.nuage.description = RdDMeteo.nuage(meteo.nuage.force);
meteo.pluie.description = RdDMeteo.pluie(meteo.pluie.force);
ChatMessage.create({
await ChatMessage.create({
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-meteo.hbs', meteo),
whisper: ChatUtility.getGMs()
});

View File

@@ -4,12 +4,10 @@ import { renderTemplate } from "./constants.js";
export class RdDRollResult {
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.hbs') {
const chatMessage = await ChatUtility.createChatWithRollMode(
return await ChatMessage.create(ChatUtility.adaptVisibility(
{ content: await RdDRollResult.buildRollDataHtml(rollData, template) },
actor,
rollData.current?.rollmode?.key
)
return chatMessage
{ actor: actor, rollMode: rollData.current?.rollmode?.key }
))
}
static async buildRollDataHtml(rollData, template = 'chat-resultat-general.hbs') {

View File

@@ -34,9 +34,9 @@ export class RdDRollTables {
}
/* -------------------------------------------- */
static async getCompetence(toChat = false) {
static async getCompetence(toChat = false, rollMode = "gmroll") {
if (toChat == 'liste') {
return await RdDRollTables.listOrRoll('competences', 'Item', ['competence'], toChat, it => 1);
return await RdDRollTables.listOrRoll('competences', 'Item', ['competence'], toChat, rollMode, it => 1);
}
else {
return await RdDRollTables.drawItemFromRollTable("Détermination aléatoire de compétence", toChat);
@@ -44,56 +44,58 @@ export class RdDRollTables {
}
/* -------------------------------------------- */
static async getSouffle(toChat = false) {
return await RdDRollTables.listOrRoll('souffles-de-dragon', 'Item', ['souffle'], toChat);
static async getSouffle(toChat = false, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('souffles-de-dragon', 'Item', ['souffle'], toChat, rollMode);
}
/* -------------------------------------------- */
static async getQueue(toChat = false) {
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat);
static async getQueue(toChat = false, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat, rollMode);
}
static async getDesirLancinant(toChat = false) {
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat,
static async getDesirLancinant(toChat = false, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat, rollMode,
it => it.system.frequence,
it => it.system.categorie == 'lancinant');
}
static async getIdeeFixe(toChat = false) {
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat,
static async getIdeeFixe(toChat = false, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat, rollMode,
it => it.system.frequence,
it => it.system.categorie == 'ideefixe');
}
/* -------------------------------------------- */
static async getTeteHR(toChat = false) {
return await RdDRollTables.listOrRoll('tetes-de-dragon-pour-haut-revants', 'Item', ['tete'], toChat);
static async getTeteHR(toChat = false, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('tetes-de-dragon-pour-haut-revants', 'Item', ['tete'], toChat, rollMode);
}
/* -------------------------------------------- */
static async getTete(toChat = false) {
return await RdDRollTables.listOrRoll('tetes-de-dragon-pour-tous-personnages', 'Item', ['tete'], toChat);
static async getTete(toChat = false, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('tetes-de-dragon-pour-tous-personnages', 'Item', ['tete'], toChat, rollMode);
}
/* -------------------------------------------- */
static async getOmbre(toChat = false) {
return await RdDRollTables.listOrRoll('ombres-de-thanatos', 'Item', ['ombre'], toChat);
static async getOmbre(toChat = false, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('ombres-de-thanatos', 'Item', ['ombre'], toChat, rollMode);
}
/* -------------------------------------------- */
static async getTarot(toChat = true) {
return await RdDRollTables.listOrRoll('tarot-draconique', 'Item', ['tarot'], toChat);
static async getTarot(toChat = true, rollMode = "gmroll") {
return await RdDRollTables.listOrRoll('tarot-draconique', 'Item', ['tarot'], toChat, rollMode);
}
/* -------------------------------------------- */
static async listOrRoll(compendium, type, subTypes, toChat, itemFrequence = it => it.system.frequence, filter = it => true) {
static async listOrRoll(compendium, type, subTypes, toChat, rollMode,
itemFrequence = it => it.system.frequence,
filter = it => true) {
const table = new CompendiumTable(compendium, type, subTypes);
if (toChat == 'liste') {
return await table.toChatMessage(itemFrequence, filter);
}
const row = await table.getRandom(itemFrequence, filter);
if (row) {
await CompendiumTableHelpers.tableRowToChatMessage(row, type);
await CompendiumTableHelpers.tableRowToChatMessage(row, type, { rollMode: rollMode });
return row.document;
}
return undefined;

View File

@@ -49,7 +49,7 @@ export class RdDTMRDialog extends Dialog {
await PixiTMR.init()
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.hbs', tmrData);
if (tmrData.mode != 'visu' && !game.user.isGM) {
ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatUtility.getGMs() });
await ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatUtility.getGMs() });
}
return new RdDTMRDialog(html, actor, tmrData)
}
@@ -138,7 +138,7 @@ export class RdDTMRDialog extends Dialog {
this.html.find('img.tmr-move').click(event => this.deplacementTMR(this.html.find(event.currentTarget)?.data('move')));
// Gestion du cout de montée en points de rêve
await this.actor.reveActuelIncDec(this.calculCoutMonteeTMR());
await this.actor.reveActuelIncDec(await this.calculCoutMonteeTMR());
this.cumulFatigue += this.fatigueParCase;
// Le reste...
@@ -305,8 +305,8 @@ export class RdDTMRDialog extends Dialog {
await this.$checkQuitterTMR();
}
calculCoutMonteeTMR() {
return ((this.tmrdata.isRapide && !EffetsDraconiques.isDeplacementAccelere(this.actor)) ? -2 : -1) - this.actor.countMonteeLaborieuse();
async calculCoutMonteeTMR() {
return ((this.tmrdata.isRapide && !EffetsDraconiques.isDeplacementAccelere(this.actor)) ? -2 : -1) - (await this.actor.countMonteeLaborieuse())
}
/* -------------------------------------------- */
@@ -501,7 +501,7 @@ export class RdDTMRDialog extends Dialog {
rencData.poesie = { extrait: result.poesie, reference: result.reference };
rencData.message = this.$formatMessageRencontre(rencData, result.message);
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.hbs`, rencData)
});
@@ -653,7 +653,7 @@ export class RdDTMRDialog extends Dialog {
this.restoreTMRAfterAction()
if (myRoll == 7) {
ChatUtility.tellToUser(myRoll + ": Rencontre en " + coordTMR);
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, await this.actor.isMauvaiseRencontre())
} else {
ChatUtility.tellToUser(myRoll + ": Pas de rencontre en " + coordTMR);
return undefined;
@@ -708,7 +708,7 @@ export class RdDTMRDialog extends Dialog {
return;
}
rollData.poesie = await Poetique.getExtrait();
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.hbs`, rollData)
});
@@ -728,16 +728,16 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
isCaseHumide(tmr) {
if (!(TMRUtility.isCaseHumide(tmr) || this.isCaseHumideAdditionelle(tmr))) {
return false;
return false
}
if (this.isCaseMaitrisee(tmr.coord)) {
ChatMessage.create({
content: tmr.label + ": cette case humide est déja maitrisée grâce à votre Tête <strong>Quête des Eaux</strong>",
whisper: ChatUtility.getOwners(this.actor)
});
return false;
})
return false
}
return true;
return true
}
/* -------------------------------------------- */
@@ -747,16 +747,16 @@ export class RdDTMRDialog extends Dialog {
content: tmr.label + ": Vous êtes sous le coup d'une Impraticabilité des Ponts : ce pont doit être maîtrisé comme une case humide.",
whisper: ChatUtility.getOwners(this.actor)
});
return true;
return true
}
if (this.isCaseInondee(tmr.coord)) {
ChatMessage.create({
content: tmr.label + ": cette case est inondée, elle doit être maîtrisée comme une case humide.",
whisper: ChatUtility.getOwners(this.actor)
});
return true;
return true
}
return false;
return false
}
/* -------------------------------------------- */
@@ -825,7 +825,7 @@ export class RdDTMRDialog extends Dialog {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false })
}
rollData.poesie = await Poetique.getExtrait()
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.hbs`, rollData)
})
@@ -876,7 +876,7 @@ export class RdDTMRDialog extends Dialog {
const reserveSecurite = EffetsDraconiques.isReserveEnSecurite(this.actor);
const reserveExtensible = this.isReserveExtensible(coord);
if (!EffetsDraconiques.isUrgenceDraconique(this.actor) && (reserveSecurite || reserveExtensible)) {
ChatMessage.create({
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-demande-declencher-sort.hbs`, {
actor: this.actor,
sorts: sorts,
@@ -892,13 +892,13 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
lancerSortEnReserve(coord, sortId) {
async lancerSortEnReserve(coord, sortId) {
const sort = this.getSortsReserve(coord)
.find(it => it.id == sortId);
if (sort) {
this.processSortReserve(sort);
await this.processSortReserve(sort);
} else {
ChatMessage.create({
await ChatMessage.create({
content: "Une erreur est survenue : impossible de récupérer le sort en réserve demandé.",
whisper: ChatUtility.getOwners(this.actor)
});
@@ -1137,12 +1137,12 @@ export class RdDTMRDialog extends Dialog {
if (!token) {
return
}
if (this.demiReve === token && this.isDemiReveCache()) {
if (this.isDemiReveCache() && [EffetsDraconiques.rencontre.code(), EffetsDraconiques.demiReve.code()].includes(token.code)) {
return
}
this.pixiTMR.positionToken(token);
this.pixiTMR.positionToken(token)
if (!this.allTokens.includes(token)) {
this.allTokens.push(token);
this.allTokens.push(token)
}
}
}

View File

@@ -132,9 +132,9 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-derivee.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-creature.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-entitee.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-total.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/comp-creature.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/comp-possession.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-total.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/competence.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/xp-competences.hbs',
@@ -147,7 +147,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/taches.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/jeus.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/jeux.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/alchimie.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/astrologie.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/chirurgie.hbs',
@@ -173,6 +173,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-vehicules.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/tooltip-bonmoments.hbs',
//Items
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs',
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete.hbs',
@@ -300,6 +301,7 @@ export class RdDUtility {
Handlebars.registerHelper('grammar-un', str => Grammar.articleIndetermine(str));
Handlebars.registerHelper('grammar-accord', (genre, ...args) => Grammar.accord(genre, args));
Handlebars.registerHelper('json-stringify', object => JSON.stringify(object))
Handlebars.registerHelper('escapeHtml', object => foundry.utils.escapeHTML(object))
// math
Handlebars.registerHelper('math-sum', (...values) => values.slice(0, -1).reduce(Misc.sum(), 0))
@@ -483,7 +485,7 @@ export class RdDUtility {
static filterItemsPerTypeForSheet(formData, itemTypes) {
Object.values(ITEM_TYPES).forEach(t => {
formData[t + 's'] = Misc.arrayOrEmpty(itemTypes[t])
formData[RdDItem.itemTypePluriel(t)] = Misc.arrayOrEmpty(itemTypes[t])
itemTypes[t].forEach(item => item.actions = item.itemActions())
})
@@ -680,7 +682,7 @@ export class RdDUtility {
encaissement.dmg = dmg
if (ReglesOptionnelles.isUsing('localisation-aleatoire')) {
encaissement.dmg.loc = dmg.loc ?? await RdDUtility.getLocalisation(targetActor.type)
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps'
}
else {
encaissement.dmg.loc = { label: '' }
@@ -689,9 +691,9 @@ export class RdDUtility {
encaissement.armure = armure
encaissement.penetration = dmg.penetration
encaissement.total = jetTotal
encaissement.vie = await RdDUtility._evaluatePerte(encaissement.vie, over20);
encaissement.endurance = await RdDUtility._evaluatePerte(encaissement.endurance, over20);
return encaissement;
encaissement.vie = await RdDUtility._evaluatePerte(encaissement.vie, over20)
encaissement.endurance = await RdDUtility._evaluatePerte(encaissement.endurance, over20)
return encaissement
}
/* -------------------------------------------- */
@@ -750,12 +752,12 @@ export class RdDUtility {
actor.tmrApp.positionnerDemiReve(coord);
});
// Gestion spécifique des sorts en réserve multiples (ie têtes)
$(html).on("click", '.declencher-sort-reserve', event => {
$(html).on("click", '.declencher-sort-reserve', async event => {
let coord = event.currentTarget.attributes['data-tmr-coord'].value;
let sortId = event.currentTarget.attributes['data-sort-id'].value;
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
actor.tmrApp.lancerSortEnReserve(coord, sortId);
await actor.tmrApp.lancerSortEnReserve(coord, sortId);
// TODO: supprimer le message?
});
@@ -844,12 +846,12 @@ export class RdDUtility {
return game.user.character;
}
if (msgPlayer != undefined) {
msgPlayer += "<br>vous pouvez sélectionner un seul token lié à un personnage";
msgPlayer += "<br>vous pouvez sélectionner un seul token lié à un personnage"
msgPlayer += "<br>vous devez être connecté comme joueur avec un personnage sélectionné";
ui.notifications.warn(msgPlayer);
ChatMessage.create({ content: msgPlayer, whisper: [game.user] });
ChatMessage.create({ content: msgPlayer, whisper: [game.user] })
}
return undefined;
return undefined
}
static doWithSelectedActor(onSelected = () => { }, filter = actor => true) {
@@ -942,9 +944,9 @@ export class RdDUtility {
content: `<p>Etes vous certain de vouloir supprimer: ${item.name}?</p>`,
title: `Supprimer ${item.name}`,
buttonLabel: "Supprimer",
onAction: () => {
onAction: async () => {
console.log('Delete : ', itemId);
actor.deleteEmbeddedDocuments('Item', [itemId], { renderSheet: false });
await actor.deleteEmbeddedDocuments('Item', [itemId], { renderSheet: false });
}
};
if (item.isConteneurNonVide()) {
@@ -955,9 +957,9 @@ export class RdDUtility {
'deleteall': {
icon: '<i class="fas fa-check"></i>',
label: "Supprimer conteneur et contenu",
callback: () => {
callback: async () => {
console.log("Delete : ", itemId);
actor.deleteAllConteneur(itemId, { renderSheet: false });
await actor.deleteAllConteneur(itemId, { renderSheet: false });
}
}
});
@@ -995,10 +997,10 @@ export class RdDUtility {
}
/*-------------------------------------------- */
static checkThanatosXP(item) {
static async checkThanatosXP(item) {
if (item.isCompetencePersonnage() && item.name.includes('Thanatos')) {
let message = "Vous avez mis des points d'Expérience en Thanatos !<br>Vous devez réduire manuellement d'un même montant d'XP une autre compétence Draconique.";
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getUserAndGMs(),
content: message
});

View File

@@ -43,13 +43,13 @@ export default class ChatRollResult {
async display(roll, impacts) {
this.prepareDisplay(roll)
const chatMessage = await ChatUtility.createChatWithRollMode(
const chatMessage = await ChatMessage.create(ChatUtility.adaptVisibility(
{
content: await this.buildRollHtml(roll)
},
roll.active.actor,
roll.current?.rollmode?.key
)
))
await this.saveChatMessageRoll(chatMessage, roll, impacts)
return chatMessage
@@ -105,8 +105,8 @@ export default class ChatRollResult {
if (attaque &&
(roll.rolled.isEchec || !roll.current.defense.isEsquive) &&
(attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) {
const taille = defender.system.carac.taille.value
const impact = attacker.system.carac.force.value + roll.attackerRoll?.dmg.dmgArme
const taille = defender.getTaille()
const impact = attacker.getForce() + roll.attackerRoll?.dmg.dmgArme
return {
raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force',
taille: taille,

View File

@@ -218,7 +218,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
foundry.applications.handlebars.loadTemplates(ALL_ROLL_TYPES.map(m => m.chatResultTemplate))
foundry.applications.handlebars.loadTemplates(ROLL_PARTS.map(p => p.template))
ROLL_PARTS.forEach(p => p.onReady())
ROLL_PARTS.forEach(p => p.onReady(ROLL_PARTS))
Handlebars.registerHelper('roll-centered-array', (base, show) => {
show = Math.abs(show)
@@ -380,7 +380,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
this.rollData = rollData
this.rollOptions = {
callbacks: [
async r => await r.active.actor.appliquerAjoutExperience(r),
async r => await r.active.actor.ajoutExperience(r),
async r => await r.active.actor.appliquerAppelMoral(r),
...(rollOptions.callbacks ?? [])
],
@@ -479,16 +479,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
RollBasicParts.loadSurprises(rollData, this.getSelectedType().code)
rollData.type.label = this.getSelectedType()?.title(rollData)
const visibleRollParts = this.getActiveParts()
visibleRollParts.forEach(p => p.applyExternalImpacts(visibleRollParts, rollData))
this.setSpecialComp(visibleRollParts);
visibleRollParts.forEach(p => p.prepareContext(rollData))
this.getActiveParts().forEach(p => p.applyExternalImpacts(this.getActiveParts(), rollData))
this.setSpecialComp(this.getActiveParts());
this.getActiveParts().forEach(p => p.prepareContext(rollData))
RollDialog.calculAjustement(rollData)
const templates = visibleRollParts.map(p => p.toTemplateData())
const templates = this.getActiveParts().map(p => p.toTemplateData())
const context = await super._prepareContext()
return foundry.utils.mergeObject(
{
@@ -569,7 +566,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
}
async defaultCallback(roll, rolled) {
await roll.active.actor.appliquerAjoutExperience(roll)
await roll.active.actor.ajoutExperience(roll)
await roll.active.actor.appliquerAppelMoral(roll)
}

View File

@@ -1,4 +1,5 @@
import { Grammar } from "../grammar.js"
import { CARACS } from "../rdd-carac.js"
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { ROLL_TYPE_SORT } from "./roll-constants.mjs"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
@@ -20,7 +21,7 @@ export class RollPartAstrologique extends RollPartCheckbox {
}
isJetChance(rollData) {
return Grammar.includesLowerCaseNoAccent(rollData.current.carac.key, 'chance')
return Grammar.includesLowerCaseNoAccent(rollData.current.carac.key, CARACS.CHANCE)
}
$isUsingAstrologie() {

View File

@@ -52,11 +52,13 @@ export class RollPartAttaque extends RollPartSelect {
restore(rollData) {
const saved = this.getSaved(rollData) ?? {}
this.setCurrent(rollData, {
key: saved.key,
tactique: saved.tactique,
dmg: saved.dmg
})
if (saved.key) {
this.setCurrent(rollData, {
key: saved.key,
tactique: saved.tactique,
dmg: saved.dmg
})
}
}
store(rollData, targetData) {
@@ -70,9 +72,11 @@ export class RollPartAttaque extends RollPartSelect {
findAttaque(attaques, saved) {
return attaques.find(at => at.arme.id == saved?.arme?.id &&
at.comp.id == saved?.comp?.id
)
return attaques.find(it => it.key == saved.key) ??
attaques.find(it => it.arme.id == (saved?.arme?.id ?? it.arme.id)
&& it.comp.id == (saved?.comp?.id ?? it.comp.id)
&& it.main == (saved?.main ?? it.main)
)
}
choices(refs) { return refs.attaques }

View File

@@ -41,7 +41,7 @@ export class RollPartCheckbox extends RollPart {
}
getCheckboxLabelAjustement(rollData) {
return `${this.getCheckboxIcon(rollData)} ${this.getRefs(rollData).label}`
return `${this.getCheckboxIcon(rollData)} ${this.getCheckboxLabel(rollData)}`
}
async _onRender(rollDialog, context, options) {

View File

@@ -1,7 +1,9 @@
import { Grammar } from "../grammar.js"
import { Misc } from "../misc.js"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_DIFF } from "./roll-part-diff.mjs"
import { RollPartSelect } from "./roll-part-select.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
export const PART_COMP = "comp"
@@ -15,15 +17,21 @@ export class RollPartComp extends RollPartSelect {
get name() { return 'Compétences' }
get section() { return ROLLDIALOG_SECTION.COMP }
onReady(rollParts) {
this.rollPartCarac = rollParts.find(it => it.code == PART_CARAC)
this.rollPartDiff = rollParts.find(it => it.code == PART_DIFF)
}
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const selected = this.getSelected(rollData)
const all = this.$getActorComps(rollData)
if (selected.forced) {
refs.all = all.filter(comp => Grammar.equalsInsensitive(comp.label, selected.key))
const selectedComp = selected.key
if (selected.forced && selectedComp) {
refs.all = all.filter(comp => Grammar.equalsInsensitive(comp.label, selectedComp))
if (refs.all.length == 0) {
if (selected.key.length > 0) {
refs.all = all.filter(comp => Grammar.includesLowerCaseNoAccent(comp.label, selected.key))
if (selected.key && selected.key.length > 0) {
refs.all = all.filter(comp => Grammar.includesLowerCaseNoAccent(comp.label, selectedComp))
}
else {
refs.all = all.filter(comp => comp == SANS_COMPETENCE)
@@ -35,6 +43,13 @@ export class RollPartComp extends RollPartSelect {
}
refs.comps = refs.all
this.$selectComp(rollData)
if (rollData.type.current == PART_COMP && selectedComp) {
const current = this.getCurrent(rollData)
const selectedCarac = RollPart.getSelectedPart(rollData, PART_CARAC)
const selectedDiff = RollPart.getSelectedPart(rollData, PART_DIFF)
this.rollPartCarac.selectByKey(rollData, selectedCarac?.key ?? current.comp.system.defaut_carac)
this.rollPartDiff.setDiff(rollData, selectedDiff?.value ?? current.comp.system.default_diffLibre)
}
}
choices(refs) { return refs.comps }
@@ -71,6 +86,7 @@ export class RollPartComp extends RollPartSelect {
refs.comps.sort(sorting)
}
this.$selectComp(rollData)
}
prepareContext(rollData) {

View File

@@ -10,7 +10,7 @@ export class RollPartConditions extends RollPart {
settingMin() { return RollPart.settingKey(this, 'min') }
settingMax() { return RollPart.settingKey(this, 'max') }
onReady() {
onReady(rollParts) {
game.settings.register(SYSTEM_RDD, this.settingMin(),
{
name: "Malus maximal de conditions",
@@ -65,12 +65,7 @@ export class RollPartConditions extends RollPart {
async _onRender(rollDialog, context, options) {
const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`)
input?.addEventListener("input", e => this.onInputChange(e, rollDialog))
}
onInputChange(event, rollDialog) {
this.getCurrent(rollDialog.rollData).value = parseInt(event.currentTarget.value)
rollDialog.render()
input?.addEventListener("input", e => this.onNumericInputChange(e, rollDialog))
}
}

View File

@@ -9,7 +9,7 @@ import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
export const PART_CUISINE = "cuisine"
export class RollPartCuisine extends RollPartSelect {
onReady() {
onReady(rollParts) {
foundry.applications.handlebars.loadTemplates({ 'roll-oeuvre-recettecuisine': `systems/foundryvtt-reve-de-dragon/templates/roll/roll-oeuvre-recettecuisine.hbs` })
}
@@ -43,14 +43,13 @@ export class RollPartCuisine extends RollPartSelect {
const recettes = actor.items
.filter(it => it.type == ITEM_TYPES.recettecuisine)
.map(RollPartCuisine.$extractPreparationRecette)
.map(it => RollPartCuisine.$extractPreparationRecette(refs.cuisine, it))
const ingredientsBruts = actor.items
.filter(it => it.getUtilisationCuisine() == 'brut')
.map(RollPartCuisine.$extractPreparationBrut)
.map(it => RollPartCuisine.$extractPreparationBrut(refs.cuisine, it))
refs.preparations = [RollPartCuisine.$preparationBasique(), ...recettes, ...ingredientsBruts]
refs.preparations.forEach(p => p.comp = refs.cuisine)
refs.preparations = [RollPartCuisine.$preparationBasique(refs.cuisine), ...recettes, ...ingredientsBruts]
if (refs.preparations.length > 0) {
this.$selectPreparation(rollData)
this.$restoreSavedOptions(rollData)
@@ -73,7 +72,7 @@ export class RollPartCuisine extends RollPartSelect {
choices(refs) { return refs.preparations }
static $preparationBasique() {
static $preparationBasique(cuisine) {
return {
key: '',
label: "Improvisation du moment",
@@ -85,10 +84,12 @@ export class RollPartCuisine extends RollPartSelect {
proportionsMax: 50,
value: 0,
fabriquer: false,
qualite: cuisine.system.niveau,
comp: cuisine,
}
}
static $extractPreparationRecette(recette) {
static $extractPreparationRecette(cuisine, recette) {
const proportions = recette.system.sust ?? 1
return {
key: recette.id,
@@ -102,10 +103,12 @@ export class RollPartCuisine extends RollPartSelect {
value: -recette.system.niveau,
recette: recette,
fabriquer: true,
qualite: recette.system.niveau,
comp: cuisine,
}
}
static $extractPreparationBrut(ingredient) {
static $extractPreparationBrut(cuisine, ingredient) {
return {
key: ingredient.id,
label: ingredient.name + ' cuisiné',
@@ -118,6 +121,8 @@ export class RollPartCuisine extends RollPartSelect {
value: 0,
ingredient: ingredient,
fabriquer: true,
qualite: cuisine.system.niveau,
comp: cuisine,
}
}
@@ -150,7 +155,7 @@ export class RollPartCuisine extends RollPartSelect {
this.$selectPreparation(rollDialog.rollData, selectOptions[index]?.value)
rollDialog.render()
})
checkboxFabriquer?.addEventListener("change", e => {
current.fabriquer = e.currentTarget.checked
})

View File

@@ -73,12 +73,7 @@ export class RollPartDiff extends RollPart {
async _onRender(rollDialog, context, options) {
const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`)
input?.addEventListener("input", e => this.onInputChange(e, rollDialog))
}
onInputChange(event, rollDialog) {
this.getCurrent(rollDialog.rollData).value = parseInt(event.currentTarget.value)
rollDialog.render()
input?.addEventListener("input", e => this.onNumericInputChange(e, rollDialog))
}
}

View File

@@ -15,18 +15,32 @@ export class RollPartEncTotal extends RollPartCheckbox {
&& RdDItemCompetence.isMalusEncombrementTotal(rollData.current.comp?.key)
}
loadRefs(rollData) {
const refs = this.getRefs(rollData)
refs.malusEnc = - Math.floor(rollData.active.actor.getEncTotal())
const current = this.getCurrent(rollData)
current.value = refs.malusEnc
}
async _onRender(rollDialog, context, options) {
super._onRender(rollDialog, context, options)
const inputMalusEnc = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="malusenc"]`)
inputMalusEnc?.addEventListener("change", e => {
this.getCurrent(rollDialog.rollData).value = parseInt(e.currentTarget.value)
const malusEnc = Math.floor(e.currentTarget.value)
const rollData = rollDialog.rollData
const refs = this.getRefs(rollData)
const current = this.getCurrent(rollData)
if (refs.malusEnc == current.value) {
current.value = malusEnc
}
refs.malusEnc = malusEnc
rollDialog.render()
})
}
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.surenc}">` }
getCheckboxLabel(rollData) { return "Enc. total" }
getCheckboxValue(rollData) { return - rollData.active.actor.getEncTotal() }
getCheckboxValue(rollData) { return this.getCurrent(rollData).value }
}

View File

@@ -24,7 +24,7 @@ const ARTS = [
]
export class RollPartOeuvre extends RollPartSelect {
onReady() {
onReady(rollParts) {
ARTS.forEach(art => art.label = Misc.typeName('Item', art.type))
ARTS.map(it => `roll-oeuvre-${it.type}`)
.forEach(art =>

View File

@@ -12,7 +12,7 @@ export const PART_SORT = "sort"
export class RollPartSort extends RollPartSelect {
onReady() {
onReady(rollParts) {
// TODO: utiliser un hook pour écouter les déplacements dans les TMRs?
}
@@ -28,7 +28,8 @@ export class RollPartSort extends RollPartSelect {
this.setCurrent(rollData, {
key: saved.key,
isReserve: saved.isReserve,
ptreve: saved.ptreve
ptreve: saved.ptreve,
value: saved.value,
})
}
@@ -37,7 +38,8 @@ export class RollPartSort extends RollPartSelect {
this.setSaved(targetData, {
key: current.key,
isReserve: current.isReserve,
ptreve: current.ptreve
ptreve: current.ptreve,
value: current.value,
})
}
@@ -98,7 +100,7 @@ export class RollPartSort extends RollPartSelect {
const sort = { label: current.label, value: current.value }
const reserve = current.isReserve ? [{ label: `Mise en réserve en ${this.getCoord(rollData)}` }] : []
const bonusCase = current.bonusCase ? [{ label: `Bonus case +${current.bonusCase}%` }] : []
const reve = { label: `Rêve ${current.ptreve}` }
const reve = { label: `Dépense de rêve ${current.ptreve}` }
return [sort, ...bonusCase, reve, ...reserve]
}
return []
@@ -106,9 +108,12 @@ export class RollPartSort extends RollPartSelect {
$selectSort(rollData, values) {
const current = this.selectByKey(rollData, values.key)
if (values.ptreve) {
if (values.ptreve != undefined) {
current.ptreve = values.ptreve
}
if (current.isDiffVariable && values.value != undefined) {
current.value = values.value
}
if (values.isReserve != undefined) {
current.isReserve = values.isReserve
}

View File

@@ -1,5 +1,3 @@
import { ALL_ROLL_TYPES } from "./roll-dialog.mjs"
export const ROLLDIALOG_SECTION = {
ACTION: 'action',
CARAC: 'carac',
@@ -7,12 +5,11 @@ export const ROLLDIALOG_SECTION = {
CHOIX: 'choix',
CONDITIONS: 'conditions',
AJUSTEMENTS: 'ajustements',
}
}
export class RollPart {
static settingKey(rollPart, key) { return `roll-part-${rollPart.code}.${key}` }
get code() { throw new Error(`Pas dse code définie pour ${this}`) }
get name() { return this.code }
/** la section de la fenêtre ou le paramêtre apparaît */
@@ -20,30 +17,37 @@ export class RollPart {
get priority() { return 0 /* TODO */ }
/** le template handlebars pour affichage */
get template() { return `systems/foundryvtt-reve-de-dragon/templates/roll/roll-part-${this.code}.hbs` }
static getSelectedPart(rollData, code) {
return rollData.selected[code] ?? {}
}
static setSelectedPart(rollData, code, saved) {
rollData.selected[code] = saved
}
/** l'acteur actif du jet */
getActor(rollData) { return rollData.active.actor }
/** le conteneur de données du RollPart */
getRefs(rollData) {
return rollData.refs[this.code]
}
}
/** les informations de sélection du paramètre */
getCurrent(rollData) {
return rollData.current[this.code]
}
}
setCurrent(rollData, current) {
rollData.current[this.code] = current
}
}
/** les informations minimales représentant la sélection dans le rollData permettant de restaurer la fenêtre */
getSelected(rollData) { return this.getSaved(rollData) }
getSaved(rollData) {
return rollData.selected[this.code] ?? {}
}
return RollPart.getSelectedPart(rollData, this.code)
}
setSaved(rollData, saved) {
rollData.selected[this.code] = saved
}
RollPart.setSelectedPart(rollData, this.code, saved)
}
restore(rollData) { }
@@ -75,7 +79,7 @@ export class RollPart {
isValid(rollData) { return true }
visible(rollData) { return true }
onReady() { }
onReady(rollParts) { }
loadRefs(rollData) { }
prepareContext(rollData) { }
@@ -103,4 +107,13 @@ export class RollPart {
async _onRender(rollDialog, context, options) { }
getHooks() { return [] }
onNumericInputChange(event, rollDialog, setValue = value => this.getCurrent(rollDialog.rollData).value = value) {
if (isNaN(event.currentTarget.value) || event.currentTarget.value == "") {
return
}
setValue(parseInt(event.currentTarget.value))
rollDialog.render()
}
}

View File

@@ -21,9 +21,7 @@ export class RollTypeCuisine extends RollType {
getResult(rollData, impacts) {
const current = rollData.current[PART_CUISINE]
const diff = -current.value
const cuisine = rollData.refs[PART_CUISINE].cuisine
const qualite = rollData.rolled.ptQualite + (rollData.rolled.isSuccess ? diff : Math.min(diff, cuisine.system.niveau))
const qualite = rollData.rolled.ptQualite + current.qualite
const result = {
qualite: qualite,
@@ -49,6 +47,7 @@ export class RollTypeCuisine extends RollType {
}
return result
}
onApplyImpacts(roll, impacts) {
if (roll.result.plat) {
// le plat n'est pas créé immédiatement, il faut donc retrouver l'id

View File

@@ -5,8 +5,6 @@ const DEFAULT_DIFF_TYPES = [DIFF.LIBRE, DIFF.IMPOSEE, DIFF.DEFAUT]
export class RollType {
onReady() { }
get code() { throw new Error(`Pas de code défini pour ${this}`) }
get name() { return this.code }
get icon() { return `systems/foundryvtt-reve-de-dragon/assets/actions/${this.code}.svg` }

View File

@@ -25,6 +25,7 @@ const listeReglesOptionnelles = [
{ group: 'Affichage', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false },
{ group: 'Affichage', name: 'afficher-prix-joueurs', descr: "Afficher le prix de l'équipement des joueurs", uniquementJoueur: true},
{ group: 'Affichage', name: 'afficher-table-source', descr: "Afficher la table d'origine des tirages aléatoires", default: false },
{ group: 'Confirmations', name: 'confirmer-combat-sans-cible', descr: "Confirmer avant une attaque sans cible", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"},

View File

@@ -4,6 +4,7 @@ import { Grammar } from "../grammar.js";
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDDice } from "../rdd-dice.js";
import { ReglesOptionnelles } from "./regles-optionnelles.js";
const COMPENDIUM_SETTING_PREFIX = 'compendium-';
@@ -289,24 +290,28 @@ export class CompendiumTableHelpers {
}
/* -------------------------------------------- */
static async tableRowToChatMessage(row, type, options = { showSource: true }) {
static async tableRowToChatMessage(row, type, options = {}) {
if (options.showSource == undefined){
options.showSource = ReglesOptionnelles.isUsing('afficher-table-source')
}
if (!row) {
return;
return
}
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll.hbs', {
roll: row.roll,
document: row.document,
typeName: Misc.typeName(type, row.document?.type ?? 'objet'),
isGM: game.user.isGM,
options
});
const messageData = {
user: game.user.id,
rolls: [row.roll],
sound: CONFIG.sounds.dice,
content: flavorContent
};
await ChatUtility.createChatWithRollMode(messageData)
options: { showSource: options.showSource }
})
await ChatMessage.create(ChatUtility.adaptVisibility(
{
user: game.user.id,
rolls: [row.roll],
sound: CONFIG.sounds.dice,
content: flavorContent
},
{ rollMode: options.rollMode }))
}
/* -------------------------------------------- */
@@ -316,13 +321,14 @@ export class CompendiumTableHelpers {
typeName: typeName ?? Misc.typeName(type, subTypes[0]),
table,
isGM: game.user.isGM,
});
const messageData = {
user: game.user.id,
whisper: [game.user],
content: flavorContent
};
await ChatUtility.createChatWithRollMode(messageData)
})
await ChatMessage.create(ChatUtility.adaptVisibility(
{
user: game.user.id,
whisper: [game.user],
content: flavorContent
}
))
}
}

View File

@@ -107,7 +107,7 @@ export class AppAstrologie extends Application {
const detail = foundry.utils.duplicate(nombreAstral);
const timestamp = new RdDTimestamp({ indexDate: nombreAstral.index });
detail.date = { mois: timestamp.mois, jour: timestamp.jour + 1 };
detail.lectures.forEach(lecture => lecture.actorName = game.actors.get(lecture.actorId).name ?? "Inconnu");
detail.lectures.forEach(lecture => lecture.actorName = game.actors.get(lecture.actorId)?.getAlias() ?? "Inconnu");
return detail;
}

View File

@@ -9,10 +9,10 @@ export class DialogStress extends Dialog {
immediat: false,
actors: game.actors.filter(actor => actor.isPersonnageJoueur())
.map(actor => ({
id: actor.id,
name: actor.name,
selected: true
})
id: actor.id,
name: actor.name,
selected: true
})
)
};
@@ -22,7 +22,8 @@ export class DialogStress extends Dialog {
}
constructor(dialogData, html) {
const options = { classes: ["DialogStress"],
const options = {
classes: ["DialogStress"],
width: 400,
height: 'fit-content',
'z-index': 99999
@@ -49,9 +50,12 @@ export class DialogStress extends Dialog {
const stress = Number(this.html.find("form.rdddialogstress input[name='stress']").val());
const compteur = (this.html.find("form.rdddialogstress input[name='immediat']").prop("checked")) ? 'experience' : 'stress';
this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it.id))
.forEach(async actor => await actor.distribuerStress(compteur, stress, motif));
await Promise.all(
this.dialogData.actors.filter(it => it.selected)
.map(actor => game.actors.get(actor.id))
.filter(actor => actor != undefined)
.map(actor => actor.distribuerStress(compteur, stress, motif))
)
}
async onSelectActor(event) {

View File

@@ -10,13 +10,24 @@ export class Targets {
return Targets.listTargets().length > 0;
}
static getTokenData( actor, tokenId, target = undefined) {
if ( target){
return Targets.extractTokenData(target)
}
const token = canvas.tokens.get(tokenId);
return token == undefined
? Targets.buildActorTokenData(tokenId, actor)
: Targets.extractTokenData(token);
}
static extractTokenData(target) {
return {
id: target?.id,
name: target?.document.name,
img: target?.document.texture.src ?? target?.actor.img ?? 'icons/svg/mystery-man.svg'
}
}
}
}
static extractActorData(actor) {
return {
@@ -27,7 +38,7 @@ export class Targets {
}
static buildActorTokenData(tokenId, actor) {
return { id: tokenId, name: actor.name, img: actor.img ?? 'icons/svg/mystery-man.svg' };
return { id: tokenId, name: actor?.name?? "pas d'acteur", img: actor?.img ?? 'icons/svg/mystery-man.svg' };
}
static isTargetEntite(target) {

View File

@@ -280,7 +280,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async setNewTimestamp(newTimestamp) {
const oldTimestamp = this.timestamp;
await Promise.all(game.actors.map(async actor => await actor.onTimeChanging(oldTimestamp, newTimestamp)));
await Promise.all(game.actors.map(actor => actor.onTimeChanging(oldTimestamp, newTimestamp)))
RdDTimestamp.setWorldTime(newTimestamp);
if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionnelles.isUsing("chateau-dormant-gardien")) {
await DialogChateauDormant.create();
@@ -359,7 +359,7 @@ export class RdDCalendrier extends Application {
// Gestion expérience (si existante)
request.competence = actor.getCompetence('Astrologie')
request.selectedCarac = actor.system.carac["vue"];
actor.appliquerAjoutExperience(request, 'hide');
actor.ajoutExperience(request, 'hide');
}
}
else {

View File

@@ -38,7 +38,7 @@ export class TMRRencontres {
const frequence = it => it.system.frequence[tmrType];
const row = await this.table.getRandom(frequence, filtreMauvaise, forcedRoll);
if (row) {
await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item', { showSource: false });
await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item', { showSource: false, rollMode: "gmroll" });
}
return row?.document;

View File

@@ -90,21 +90,22 @@ export class Draconique {
*/
token(pixiTMR, linkData, coordTMR, type = undefined) {
const tooltip = this.tooltip(linkData);
return this._createToken(pixiTMR, linkData, coordTMR, type, tooltip);
return this._createToken(pixiTMR, linkData, coordTMR, type, tooltip)
}
tokens(pixiTMR, linkData, coordTMR, type = undefined) {
tokens(pixiTMR, linkData, coordTMR, code = undefined) {
const tooltip = this.tooltip(linkData);
return [this._createToken(pixiTMR, linkData, coordTMR, type, tooltip)];
return [this._createToken(pixiTMR, linkData, coordTMR, code, tooltip)]
}
_createToken(pixiTMR, linkData, coordTMR, type, tooltip) {
_createToken(pixiTMR, linkData, coordTMR, code, tooltip) {
const token = {
sprite: this.createSprite(pixiTMR),
coordTMR: coordTMR,
tooltip: tooltip
tooltip: tooltip,
code: code ?? this.code()
};
token[type ?? this.code()] = linkData;
token[code ?? this.code()] = linkData;
return token;
}

View File

@@ -103,7 +103,7 @@ export class EffetsRencontre {
tete: context.rolled.isPart,
poesie: await Poetique.getExtrait()
})
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(context.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.hbs`, context)
});
@@ -118,18 +118,18 @@ export class EffetsRencontre {
context.queues.push(await context.actor.ajouterQueue());
}
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(context.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.hbs`, context)
});
}
static experience_particuliere = async (dialog, context) => {
await context.actor.appliquerAjoutExperience(context)
await context.actor.ajoutExperience(context)
}
static regain_seuil = async (dialog, context) => {
await context.actor.regainPointDeSeuil()
await context.actor.recuperationSeuilReve()
}
static async $reinsertion(dialog, actor, filter) {

View File

@@ -27,13 +27,13 @@ export class PresentCites extends Draconique {
async _ajouterPresents(actor, tete) {
let existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord);
if (existants.length > 0) {
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(actor),
content: "Vous avez encore des présents dans des cités, vous devrez tirer une autre tête pour remplacer celle ci!"
})
}
else {
let cites = TMRUtility.filterTMR(it => it.type == 'cite');
let cites = TMRUtility.filterTMR(it => it.type == 'cite')
for (let tmr of cites) {
await this.createCaseTmr(actor, 'Présent', tmr, tete.id);
}
@@ -43,7 +43,7 @@ export class PresentCites extends Draconique {
async choisirUnPresent(casetmr, onChoixPresent) {
const presents = await game.system.rdd.rencontresTMR.getPresentsCite()
const buttons = {};
presents.forEach(r => buttons['present'+r.id] = { icon: '<i class="fas fa-check"></i>', label: r.name, callback: async () => onChoixPresent(r) });
presents.forEach(r => buttons['present' + r.id] = { icon: '<i class="fas fa-check"></i>', label: r.name, callback: async () => onChoixPresent(r) });
let dialog = new Dialog({
title: "Présent des cités",
content: `La ${this.tmrLabel(casetmr)} vous offre un présent, faites votre choix`,

View File

@@ -18,7 +18,7 @@ export class UrgenceDraconique extends Draconique {
if (coordSortsReserve.length == 0) {
// La queue se transforme en idée fixe
const ideeFixe = await RdDRollTables.getIdeeFixe();
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(actor),
content: `En l'absence de sorts en réserve, l'urgence draconique de ${actor.name} se transforme en ${ideeFixe.name}`
});

View File

@@ -174,7 +174,7 @@ export class DialogFatigueVoyage extends Dialog {
.filter(it => it.selected)
.forEach(async it => {
const perteFatigue = fatigueBase + it.ajustement
ChatMessage.create({
await ChatMessage.create({
whisper: ChatUtility.getOwners(it.actor),
content: await renderTemplate(
'systems/foundryvtt-reve-de-dragon/templates/voyage/chat-fatigue_voyage.hbs',

View File

@@ -8,7 +8,7 @@ items:
type: competencecreature
sort: 100000
flags: {}
img: systems/foundryvtt-reve-de-dragon/icons/competence_course.webp
img: systems/foundryvtt-reve-de-dragon/icons/creatures/glou_t.webp
effects: []
folder: null
system:
@@ -422,7 +422,7 @@ prototypeToken:
negative: false
priority: 0
texture:
src: icons/svg/mystery-man.svg
src: systems/foundryvtt-reve-de-dragon/icons/creatures/glou_t.webp
tint: '#ffffff'
scaleX: 1
scaleY: 1

View File

@@ -299,7 +299,7 @@ prototypeToken:
negative: false
priority: 0
texture:
src: systems/foundryvtt-reve-de-dragon/icons/creatures/sirene.svg
src: systems/foundryvtt-reve-de-dragon/icons/creatures/sirene_t.webp
tint: '#ffffff'
scaleX: 1
scaleY: 1

View File

@@ -348,7 +348,7 @@ prototypeToken:
negative: false
priority: 0
texture:
src: systems/foundryvtt-reve-de-dragon/icons/creatures/tigre-vert.svg
src: systems/foundryvtt-reve-de-dragon/icons/creatures/tigre-vert_t.webp
tint: '#ffffff'
scaleX: 1
scaleY: 1

View File

@@ -215,7 +215,7 @@ prototypeToken:
negative: false
priority: 0
texture:
src: systems/foundryvtt-reve-de-dragon/icons/creatures/chrasme_t.webp
src: systems/foundryvtt-reve-de-dragon/icons/creatures/chrasme_t-old.webp
tint: '#d53434'
scaleX: 0.8
scaleY: 0.8

View File

@@ -298,7 +298,7 @@ prototypeToken:
negative: false
priority: 0
texture:
src: systems/foundryvtt-reve-de-dragon/icons/creatures/zyglute.svg
src: systems/foundryvtt-reve-de-dragon/icons/creatures/zyglute_t.webp
tint: '#ffffff'
scaleX: 1
scaleY: 1

View File

@@ -1,13 +1,13 @@
{
"id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon",
"version": "13.0.21",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/releases/download/13.0.0/rddsystem.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/releases/download/13.0.0/system.json",
"version": "13.0.36",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/releases/download/13.0.36/rddsystem.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/releases/download/13.0.36/system.json",
"changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"compatibility": {
"minimum": "13",
"verified": "13"
"verified": "14"
},
"description": "Rêve de Dragon RPG for FoundryVTT",
"authors": [

View File

@@ -693,7 +693,9 @@
"bonus": 0
},
"localisation": "",
"origine": ""
"origine": "",
"vie": 0,
"endurance": "0"
},
"maladie": {
"templates": ["description", "temporel"],

View File

@@ -19,9 +19,11 @@
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs-creature.hbs"}}
<div class="flex-group-left header-etats">
<div class="flexcol">
<span>{{>"systems/foundryvtt-reve-de-dragon/templates/actor/header-effects.hbs"}}</span>
<span>{{system.compteurs.etat.label}}: {{system.compteurs.etat.value}}</span>
<span>{{calc.resumeBlessures}}</span>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/header-effects.hbs"}}
{{#each calc.blessures as |blessure|}}
<span>{{blessure}}</span>
{{/each}}
</div>
</div>
</div>

View File

@@ -122,7 +122,7 @@
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/taches.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/chirurgie.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/jeus.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/jeux.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/alchimie.hbs"}}
</div>
{{/if}}

View File

@@ -13,17 +13,17 @@
<li data-attribute="resistance" class="flexrow">
<span class="carac-label">Résistance</span>
<a class="resistance-moins"><i class="fa-solid fa-square-minus"></i></a>
<input type="text" name="system.etat.resistance.value" value="{{system.etat.resistance.value}}" data-dtype="Number" />
<input type="number" data-dtype="Number" name="system.etat.resistance.value" value="{{system.etat.resistance.value}}" min="0" max="{{system.etat.resistance.max}}"/>
/
<input type="text" name="system.etat.resistance.max" value="{{system.etat.resistance.max}}" data-dtype="Number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input type="number" data-dtype="Number" name="system.etat.resistance.max" value="{{system.etat.resistance.max}}" min="0" {{#unless @root.options.vueDetaillee}}disabled{{/unless}}/>
<a class="resistance-plus"><i class="fa-solid fa-square-plus"></i></a>
</li>
<li data-attribute="structure" class="flexrow">
<span class="carac-label">Structure</span>
<a class="structure-moins"><i class="fa-solid fa-square-minus"></i></a>
<input type="text" name="system.etat.structure.value" value="{{system.etat.structure.value}}" data-dtype="Number" />
<input type="number" data-dtype="Number" name="system.etat.structure.value" value="{{system.etat.structure.value}}" min="0" max="{{system.etat.structure.max}}"/>
/
<input type="text" name="system.etat.structure.max" value="{{system.etat.structure.max}}" data-dtype="Number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input type="number" data-dtype="Number" name="system.etat.structure.max" value="{{system.etat.structure.max}}" min="0" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<a class="structure-plus"><i class="fa-solid fa-square-plus"></i></a>
</li>
</ul>
@@ -61,23 +61,23 @@
</li>
<li class="caracteristique flexrow list-item">
<span class="carac-label">Vitesse</span>
<input class="caracteristique streched" type="text" name="system.vitesse" value="{{system.vitesse}}" data-dtype="String" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input class="caracteristique streched" type="text" data-dtype="String" name="system.vitesse" value="{{system.vitesse}}" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
</li>
<li class="caracteristique flexrow list-item">
<span class="carac-label">Bonus rames</span>
<input class="caracteristique streched" type="text" name="system.bonus" value="{{system.bonus}}" data-dtype="String" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input class="caracteristique streched" type="text" data-dtype="String" name="system.bonus" value="{{system.bonus}}" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
</li>
<li class="caracteristique flexrow list-item">
<span class="carac-label">Manoeuvrabilité</span>
<input class="caracteristique streched" type="text" name="system.manoeuvrabilite" value="{{system.manoeuvrabilite}}" data-dtype="String" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input class="caracteristique streched" type="text" data-dtype="String" name="system.manoeuvrabilite" value="{{system.manoeuvrabilite}}" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
</li>
<li class="caracteristique flexrow list-item">
<span class="carac-label">Equipage</span>
<input class="caracteristique streched" type="text" name="system.equipage" value="{{system.equipage}}" data-dtype="Number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input class="caracteristique streched" type="number" data-dtype="Number" name="system.equipage" value="{{system.equipage}}" min="0" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
</li>
<li class="caracteristique flexrow list-item">
<span class="carac-label">Capacité d'Encombrement</span>
<input class="caracteristique streched" type="text" name="system.capacite_encombrement" value="{{system.capacite_encombrement}}" data-dtype="Number" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<input class="caracteristique streched" type="number" data-dtype="Number" name="system.capacite_encombrement" value="{{system.capacite_encombrement}}" min="0" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
</li>
</ol>
</div>

View File

@@ -16,10 +16,12 @@
{{#if (or options.isGM (gt system.attributs.protection.value 0))}}
<li class="caracteristique flexrow list-item">
<label for="system.attributs.protection.value" >Protection naturelle</label>
<input class="derivee-value" type="number" {{#unless options.isGM}}disabled{{/unless}} name="system.attributs.protection.value" value="{{system.attributs.protection.value}}" data-dtype="number"/>
<input name="system.attributs.protection.value" class="derivee-value" type="number" data-dtype="Number"
value="{{system.attributs.protection.value}}" min="0" max="20" {{#unless options.isGM}}disabled{{/unless}}
/>
</li>
{{/if}}
<li class="caracteristique flexrow list-item" >
<label class="derivee-label">Malus armure</label>
<input class="derivee-value" type="number" disabled value="{{calc.malusArmure}}" data-dtype="number"/>
<input class="derivee-value" type="number" data-dtype="Number" disabled value="{{calc.malusArmure}}" />
</li>

View File

@@ -1,18 +1,22 @@
<li class="item item-blessure flexrow list-item blessure-active-{{lowercase system.label}}" data-item-id="{{id}}"
data-tooltip="Blessure {{system.label}}">
<span class="blessure-control">
<span class="blessure-control flexrow">
<img class="sheet-competence-img" src="{{img}}" />
{{system.label}}
{{#if (gt system.gravite 0)}}
{{system.label}}
{{/if}}
</span>
{{#if (gt system.gravite 6)}}
<span class="flexrow"></span>
<span class="flexrow"></span>
<span class="flexrow"></span>
{{else}}
<span class="flexrow">
<input type="checkbox" class="blessure-premierssoins-done" name="blessure.{{id}}.premierssoins.done" {{#if system.premierssoins.done}}checked{{/if}}/>
<input name="blessure.{{id}}.premierssoins.done" type="checkbox" class="blessure-premierssoins-done" {{#if system.premierssoins.done}}checked{{/if}}/>
{{#if system.premierssoins.done}}
{{#unless system.soinscomplets.done}}
<input type="text" class="blessure-premierssoins-bonus number-x2" name="blessure.{{id}}.premierssoins.bonus" data-dtype="number" value="{{system.premierssoins.bonus}}"/>
<input name="blessure.{{id}}.premierssoins.bonus" type="text" data-dtype="Number" class="blessure-premierssoins-bonus number-x2"
value="{{system.premierssoins.bonus}}" min="-6" max="2"/>
{{/unless}}
{{else}}
<label>{{system.premierssoins.tache}} / {{system.gravite}}</label>
@@ -20,18 +24,22 @@
</span>
<span class="flexrow">
{{#if system.premierssoins.done}}
<input type="checkbox" class="blessure-soinscomplets-done" name="blessure.{{id}}.system.soinscomplets.done" {{#if system.soinscomplets.done}}checked{{/if}}/>
{{#if system.soinscomplets.done}}
<input type="text" class="blessure-soinscomplets-bonus number-x2" name="blessure.{{id}}.system.soinscomplets.bonus" data-dtype="number" value="{{system.soinscomplets.bonus}}"/>
{{/if}}
{{else}}
<label>Difficulté {{system.difficulte}}</label>
<input name="blessure.{{id}}.system.soinscomplets.done" type="checkbox" class="blessure-soinscomplets-done" {{#if system.soinscomplets.done}}checked{{/if}}/>
{{#if system.soinscomplets.done}}
<input name="blessure.{{id}}.system.soinscomplets.bonus" data-dtype="Number" type="text" class="blessure-soinscomplets-bonus number-x2"
value="{{system.soinscomplets.bonus}}" min="0" max="3"/>
{{/if}}
{{/if}}
</span>
<span class="flexrow">
{{#unless system.soinscomplets.done}}{{system.difficulte}}{{/unless}}
</span>
{{/if}}
<span>
{{#if system.origine}}<span>Par {{system.origine}}</span>{{/if}}
{{#if (regle-optionnelle 'localisation-aleatoire')}}
{{#if system.localisation}}<span>{{system.localisation}}</span>{{/if}}
{{/if}}
</span>
{{>'systems/foundryvtt-reve-de-dragon/templates/actor/item-action-controls.hbs' item=this options=@root.options}}
</li>

Some files were not shown because too many files have changed in this diff Show More