Compare commits

...

202 Commits

Author SHA1 Message Date
1e53f2d9e8 Minot fix 2023-01-24 18:13:27 +01:00
792fcc1b39 Merge pull request 'v10' (#615) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #615
2023-01-24 15:22:36 +01:00
3d914a29c3 Fix: édition de carac créature 2023-01-24 01:43:10 +01:00
7fe7c1e9bd Fix: +dom pas affiché 2023-01-21 19:54:04 +01:00
d86b81fbbc Merge pull request 'v10.6.4' (#614) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #614
2023-01-21 16:57:37 +01:00
22da2807d8 Fix2: update de stress transformé 2023-01-21 00:17:36 +01:00
76e03b07ed v10.6.4 2023-01-20 23:53:03 +01:00
e4098c813b Les remèdes sont bien catégorisés 2023-01-20 23:53:03 +01:00
90aecd6cc0 Suppression compendium taches de soins
Avantageusement remplacé par les boutons de soins
2023-01-20 23:53:03 +01:00
254709f55b Recherche de potion par catégorie 2023-01-20 23:53:02 +01:00
064472dc11 Ajout de l'utilisation 'cuisine' 2023-01-20 23:52:37 +01:00
7a67cb7cea Prise en compte des milieux
Les filtrages par milieux prennent en compte la rareté/fréquence
du milieu
2023-01-20 23:52:09 +01:00
7f051e76be Tri par ordre alphabétique (tous compendiums)
Le tri étati fait séparément pas compendium, du coup, la présentation
des résultats n'était pas toujours pratique
2023-01-20 23:52:09 +01:00
38db7fb7c8 Cas où le stress peut être concaténé
Vu deux fois:
- passage de "29" à "290" en ajoutant 0
- valeur "00"
2023-01-20 23:52:08 +01:00
c448f32bb8 Merge pull request 'v10.6.3' (#613) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #613
2023-01-19 10:50:27 +01:00
e194514965 Taille de titre soins 2023-01-19 02:46:54 +01:00
a790c36618 Soins des blessures
Maintenant, les tâches peuvent être créées directement
dans l'onglet combat, à côté des blessures.

Les tâches des oins sont dans cet onglet
2023-01-19 02:42:25 +01:00
f221bb31eb Version 10.6.3 2023-01-19 01:50:02 +01:00
bdc2d8db3a Fix blocage du round
Les ActiveEffect n'ont pas de system, et causaint une exception
dans la méthode de passage de fin de round
2023-01-19 01:48:41 +01:00
782dc38268 Ajout de message si rencontre perdue
Ajout d'un message pour comprendre comment une rencontre peut
être perdue ( Issue #612)
2023-01-19 01:48:41 +01:00
024e355586 Fix: Affichage d'anciens objets temporels
Les objets temporels créés avant la 10.5.0 ne pouvaient pas être édités
2023-01-19 01:48:41 +01:00
37704558e0 Combat bloqué en cas d'init négative
Le code essayait de modifier le total d'un roll, qui n'est pas un
champ mais une methode get de la classe Roll
2023-01-19 01:48:41 +01:00
d816490839 Expérience sur jets de résistance
Une réussite particulière à un JR de difficulté négative fait
gagner un point d’expérience. On ne gagne qu’un seul point
quelle que soit la difficulté du JR, et cet unique point va
toujours en caractéristique RÊVE, jamais en Draconic.

Pour l'éthylisme, le jet de vie n'est pas un jet d'action, mais de
résistance, limitation à 1 point d'expérience pour lutter contre
l'alcoolisme optimisateur.
2023-01-19 01:48:41 +01:00
a4186da540 Merge pull request '10.6.2' (#611) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #611
2023-01-18 08:40:40 +01:00
b8b4a1ff14 Version 10.6.2 2023-01-18 02:01:53 +01:00
8925a4979d Ajout de fréquence aux équipements 2023-01-18 02:01:27 +01:00
851a208aee Généralisation des fréquences
Tout objet d'inventaire a une fréquence (en ville/village, ou autre
milieu)
2023-01-18 02:01:27 +01:00
4df04fe06f Configuration de la recherche 2023-01-18 00:12:34 +01:00
f4d074fa31 Paramétrage des compendiums de recherche 2023-01-17 21:51:49 +01:00
f7595a1bfe Taille Quantité dans commerces
Agrandissement pour mieux afficher jusqu'à 4 chiffres au cas
en cas d'objets en grande quantité, avec d'autres limités
2023-01-16 23:20:29 +01:00
86feb12811 Fix: /table milieu
régression causée par les changements de la fenêtre de recherche
2023-01-16 23:17:17 +01:00
2383094254 Increase relese 2023-01-15 23:37:06 +01:00
2a72f6ffea Merge pull request 'v10.6.1' (#610) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #610
2023-01-15 23:36:12 +01:00
3cc56c615c Amélioration "effacer les filtres" 2023-01-15 23:30:32 +01:00
948309bb4f Drag depuis la fenêtre de recherche 2023-01-15 23:21:19 +01:00
40fdff4057 Image du token pour les Commerces non liés 2023-01-15 21:40:18 +01:00
c972913a67 Amélioration des plantes comestibles&poisons 2023-01-15 15:55:35 +01:00
0d5c8e5304 Amélioration de recherches 2023-01-15 15:54:40 +01:00
0aba6d2f0a Fix: Typo monnaie "Dragon" 2023-01-15 12:42:53 +01:00
df3e181d7f Merge pull request 'Les classifications de Pralinor' (#609) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #609
2023-01-14 08:54:27 +01:00
8877939b9f Version 10.6.0 2023-01-14 01:56:47 +01:00
7334f2d8e6 Liens des plantes toxiques vers leur poisons 2023-01-14 01:56:47 +01:00
ceff3d1b50 Le retour du poisson acrobate 2023-01-14 01:56:47 +01:00
a4802d1113 Séparation ingrédients/plantes 2023-01-14 01:18:17 +01:00
162a6a04b8 Fenetre de recherche et tirage 2023-01-14 01:18:17 +01:00
0c36cf3c47 Fix ouverture de conteneurs 2023-01-13 01:53:25 +01:00
969e5f6b2d Rename: isAgiliteOuDerobee
Plutôt que isAgiliteOuDerivee qui pourrait inclure la Mêlée
2023-01-13 01:53:25 +01:00
306ecacc98 Les commerces ne gèrent pas d'encombrement 2023-01-13 01:53:25 +01:00
2dc86e6679 Fix: jet de volonté éthylisme
Dans certains cas, les jets d'éthylisme concaténaient
moral et éthylisme
2023-01-13 01:53:25 +01:00
484530d67d Fix: chronologie si le journal est supprimé 2023-01-13 01:53:25 +01:00
a08890dcb0 min-width calendrier pour titres courts
Pour certaines dates, les boutons lyre / vaisseau s'affichaient
sur 2 lignes
2023-01-12 20:53:35 +01:00
1956fdd755 Merge pull request 'Version 10.5.4 - le pense-bête d'Astrobazzarh' (#608) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #608
2023-01-10 23:11:49 +01:00
651356159a Version 10.5.4 - le pense-bête d'Astrobazzarh 2023-01-10 23:06:18 +01:00
ca304109d5 Cleanup 2023-01-10 23:06:18 +01:00
9c0d08cb6f Organiser la chronologie par date 2023-01-10 23:06:18 +01:00
f9fd8a1e24 Message si le journal est inaccessible 2023-01-10 23:06:18 +01:00
03f84eb3f7 Chronologie +/-
Quand le journal est sélectionné, la fenêtre de chronologie s'ouvre avec
les informations présaisies masquées.

Le bouton + permet de les afficher. Le bouton - de les masquer.
2023-01-10 23:06:18 +01:00
05e48b61ff Renommage showControlWhen
Vu que la méthode est publique
2023-01-10 22:17:30 +01:00
f20788d6d9 Merge pull request 'v10.5.3' (#607) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #607
2023-01-10 07:49:17 +01:00
009639dd5f Version 10.5.3 2023-01-09 23:23:29 +01:00
14542cf7cd Suppression template inutile
N'est plus utilisé, maintenant que les services sont des objets
dans les boutiques
2023-01-09 23:22:20 +01:00
f4f2db68e0 Table d'astrologie avec toutes les heures 2023-01-09 23:22:20 +01:00
7e75715d88 Merge pull request 'v10.5.2' (#606) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #606
2023-01-08 23:59:34 +01:00
d62d1c45bc Version 10.5.2 2023-01-08 23:36:53 +01:00
62914c343f Fix: à l'heure de => heure 2023-01-08 23:27:08 +01:00
9349e81580 Reformatage du calendrier 2023-01-08 23:21:14 +01:00
404539a004 Fix: ajout d'heure ajoute des minutes
Lors du passage à Lyre, on ajoutait 6 minutes
2023-01-08 23:20:54 +01:00
9eaeceafc4 Autofocus sur le texte pour la chronologie 2023-01-08 20:42:28 +01:00
1cd3bdef25 Amélioration titre du calendrier 2023-01-08 20:42:28 +01:00
fa356bd7f8 Fix calcul des heures de chance 2023-01-08 20:42:28 +01:00
771c8c9c8e Merge pull request 'Fix: migration automatique de calendrier quand l'heure est à Vaisseau+0 minutes' (#605) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #605
2023-01-08 18:44:42 +01:00
ca860a0243 Version 10.5.1 2023-01-08 16:37:38 +01:00
2e36221018 Fix de la migration quand l'heure est à 0 2023-01-08 16:36:54 +01:00
07e203513a Merge pull request '10.5.0' (#604) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #604
2023-01-08 09:49:45 +01:00
31960ce940 La périodicité est migrée 2023-01-08 01:03:03 +01:00
19118911b9 Version 10.5.0 2023-01-08 00:58:44 +01:00
652ae4cf51 Fix: enlever le bloc noir en pause 2023-01-08 00:57:50 +01:00
cf340f73f1 Migration maladies et poisons
Séparation de l'incubation / la périodicité
2023-01-08 00:57:05 +01:00
3782ed9055 Gestion d'update de timestamp 2023-01-07 23:58:33 +01:00
c3076fdbfc Gestion de la périodicité/temporalité 2023-01-07 23:28:30 +01:00
739fcbdf09 Calendrier/timestamp, suite
Correction autour des éditeurs/affichages
Migration de la date du monde dans les settings
2023-01-07 20:06:21 +01:00
19b3adc222 Cleanup logs 2023-01-07 20:06:21 +01:00
388629d36e Fix: édition des compétences de créatures 2023-01-07 20:06:21 +01:00
912b1d3df3 Gestion d'items temporels
- Séparation des timestamp / calendrier

Les poisons/maladies/souffles/queues/rencontres/signes peuvent
être temporaires.

- Ajout de champs pour stocker les timestamps de début et fin
- définition de la durée (selon les items)
- extraction des classes spécialisées des items
- initialisation des dates de début/fin des effets temporaires à
  l'ajout d'un item temporel
- préparation de la suppression automatique
- Fix de mauvaise présentations sur les dialog d'astrologie
  et d'édition du calendrier
2023-01-07 02:22:44 +01:00
11e4ad09d3 Ignore jsconfig.json and modules 2023-01-05 22:23:47 +01:00
8844f76b65 Les joueurs ne peuvent plus créer de signes
Les commandes /signe+ / /signe
2023-01-05 20:56:42 +01:00
ec24126e75 Cleanup: Suppression listes inutiles 2023-01-05 20:31:29 +01:00
b3c6845e68 Fix: la maladie bloque le calendrier
Le postItem d'une maladie bloquait le raffraîchissement du calendrier
2023-01-05 20:31:29 +01:00
cf8df182ba Merge pull request 'v10.4.9' (#603) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #603
2023-01-05 16:11:17 +01:00
30cc8419c5 Version 10.4.9 2023-01-05 14:44:38 +01:00
5ab6c5989e Fix: name unique pour tabulations 2023-01-05 14:36:01 +01:00
46df3d9f11 Fix: regression sur les ventes 2023-01-05 14:32:35 +01:00
6a6759087c Merge commerces fixes 2023-01-04 21:17:50 +01:00
a7ce1db7c5 Merge pull request 'Corrections mineurs commerces et ventes' (#602) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #602
2023-01-04 21:17:10 +01:00
a0efefad3f Suppression template inutile
Les herbes de soins sont mieux gérées
2023-01-04 00:25:23 +01:00
8e0825b6b9 Fix: vente depuis un commerce
La quantité proposée est limitée à la quantité disponible
ou illimité si le commerce est illimité
2023-01-03 23:46:08 +01:00
39d14c8496 Fix: boutons des commerces
- pas de vente des "étagères"

Les contenants groupes dans les commerces ne sont plus
montrables/vendables. Le MJ peut les éditer (pour changer le nom
et l'image, principalement), et c'est tout.

- le MJ peut acheter un objet de quantité 0 dans une boutique illimitée

le bouton n'était pas affiché...
2023-01-03 23:02:07 +01:00
89442ea6c6 Merge permissions fix 2023-01-03 16:12:23 +01:00
a66fe122c4 Merge pull request 'Le GR a toujours le droit de voir les acteurs' (#601) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #601
2023-01-03 16:11:34 +01:00
2e0abaa284 Les services sont inquantifiables
Ne pas décrémenter leur quantité, et leur quantite disponible est
undefined (ie: infinie)
2023-01-03 13:38:04 +01:00
5bddc548de Le GR a toujours le droit de voir
Change-Id: I3b58fddc3ce6765e6dcf59dc20d81cdadf381761
2023-01-03 13:26:44 +01:00
4acd0879b0 Merge pull request 'Les auberges sont des commerces' (#600) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #600
2023-01-03 10:43:25 +01:00
060943ee53 Version 10.4.6 2023-01-03 02:20:28 +01:00
80be0490eb Permettre d'éditer les conteneurs de regroupement 2023-01-03 02:20:16 +01:00
ceacee8e6c Les commerces peuvent appliquer un pourcentage 2023-01-03 02:08:27 +01:00
d5453c9b04 Ajout d'images de services 2023-01-03 01:37:07 +01:00
7ca3306c6f Fix: cacher quantite illimite dans les contenants 2023-01-03 01:19:38 +01:00
aa5d175027 Formatage de prix 2023-01-03 01:03:05 +01:00
d4ddc4e940 Services pour Commerces
Les services sont modifiés pour correspondre aux nouveaux commerces
2023-01-03 01:02:47 +01:00
87f12019ac Cleanup partials 2023-01-03 00:32:00 +01:00
128d7adf89 Migration des services en commerces 2023-01-03 00:32:00 +01:00
ee42bdcf83 Ajout d'un Actor commerce 2023-01-03 00:32:00 +01:00
5972db035d Permettre un arbre d'inventaire alternatif 2023-01-02 01:41:56 +01:00
92388df5c7 déplacement gestion equipement
Déplacement de la gestion d'équipement dans base-actor
2023-01-02 01:41:56 +01:00
274009d3fa Suppression de vente depuis un "service" 2023-01-02 01:40:48 +01:00
d7e5a09540 Permettre de regrouper des herbes
L'environnement est un tableau, dur à comparer
2023-01-02 01:40:14 +01:00
7f5b2e0abf Cleanup 2023-01-02 01:40:14 +01:00
fbd3aa7121 Extraction des settings dans une méthode dédiée 2023-01-02 01:40:14 +01:00
2ca601b5f8 onDrop depuis compendium async
Permettre de retrouver l'Item du compendium pour tester si un acteur
peut le recevoir
2023-01-02 01:40:13 +01:00
d77ecee9bd Fix: exaltation/dissolution 2022-12-31 18:57:59 +01:00
c2a3b5e246 Fix stress & achats 2022-12-31 11:45:54 +01:00
09bf28eed1 Update release + ajout Vincent 2022-12-30 09:36:06 +01:00
33c20dd2b7 Merge pull request 'Fixes préparatoire à actor échoppe' (#599) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #599
2022-12-30 09:35:01 +01:00
c933810b6b Fix: tab order milieux 2022-12-30 02:59:50 +01:00
df92a65f5d Fix: choix autocomplete sous le champ input 2022-12-30 02:55:11 +01:00
dbcab12f24 Rituels: Lecture et détection d'aura
Incorrectement déclarés comme sortilèges en oniros/narcos/thanatos
2022-12-30 02:38:03 +01:00
454193490d Fix astrologie joueur 2022-12-30 02:31:53 +01:00
c79298b60a Post to chat pour les acteurs 2022-12-30 02:31:53 +01:00
26808d7b49 Controle visibilite
Ne pas afficher les boutons non accessibles
2022-12-30 02:31:53 +01:00
2062ff0777 Support de plusieurs actors 2022-12-30 02:31:52 +01:00
6adeb790a0 Fix: pas d'actions pour les véhicules 2022-12-30 02:30:54 +01:00
2546b89b44 Fix: vente de service avec quantité 2022-12-30 02:30:49 +01:00
84f4a152a8 Fixed 2022-12-24 09:47:06 +01:00
d6f8698189 Fix repos 2022-12-23 23:18:40 +01:00
4f80c719c2 Fix services/boutique 2022-12-23 14:26:23 +01:00
7d8b5c9549 Merge pull request '10.4.0: Pour Noël, je voudrais plein de cadeaux' (#598) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #598
2022-12-23 09:50:26 +01:00
ec58317b35 Version 10.4.0 2022-12-23 02:18:43 +01:00
7c70e944b1 Ajout des "boutiques"
Une boutique est un Item service permettant de définir l'inventaire
en vente, et de le vendre facilement.

Les boutiques peuvent être accédées par les joueurs (avec le lien)
pour y faire leurs courses.
2022-12-23 02:17:37 +01:00
f397c82c6d Prepare sous-classes Item 2022-12-23 00:35:44 +01:00
81e3ceb4dc Lien vers Items monde/compendium 2022-12-23 00:35:44 +01:00
7bec249e8d Cleanup init & start
- classe SystemReveDeDragon pour l'init/start
- déplacement de la migration 1.5.34 dans les migrations
2022-12-23 00:25:11 +01:00
70b30b545b Affichage consistant prix unitaire/total 2022-12-22 00:04:49 +01:00
ed2eebf99d Renommage confirmation 2022-12-21 00:49:36 +01:00
4ba2c384d7 Alignement de prix/quantité/enc
- alignement à droite
- affichage des prix avec 2 décimales
2022-12-21 00:48:32 +01:00
7f27399f3c comptage de monde en async
Au cas où on n'a pas de connection internet (merci la coupure Orange)
2022-12-21 00:48:32 +01:00
61389e117b Séparation inventaire/monnaie 2022-12-20 01:13:51 +01:00
46cc245abf Fix edition minutes du calendrier 2022-12-20 01:13:51 +01:00
a372531849 Cleanup: html TMR multiligne 2022-12-20 01:12:52 +01:00
8a8323ac8d Cleanup: Suppression log 2022-12-20 01:12:52 +01:00
8e6d4fbb89 Fix: la valeur de l'inventaire hors fortune
La valeur de l'inventaire ne tient plus compte de la fortune
2022-12-20 01:12:51 +01:00
886307f24c Fix: affichage de la fortune avec arrondis
Affichage en sols + deniers
2022-12-20 01:12:51 +01:00
f40dbd5d7b Merge pull request '10.3.17 - petits fixes' (#597) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #597
2022-12-18 17:08:21 +01:00
66da7d5eb4 Version 10.3.17 2022-12-17 20:08:11 +01:00
b2d8c2439a Fix: affichage des descriptions
Pour les feuilles dérivées de RdDItemSheet, il faut appeler
super.getData() pour préparer les données à afficher dans le formulaire.
2022-12-17 20:08:11 +01:00
ceb4095c31 Fix: escaping dans les messages de rencontres 2022-12-17 20:08:11 +01:00
2af37cf98f Fix: l'ajout de queues fonctionne de nouveau 2022-12-17 20:08:11 +01:00
fd156960a7 Fix: les rencontres persistantes disparaissent
Après un échec, il était impossible de se débarasser d'une rencontre
persistante. Maintenant, les rencontres persistantes vaincues sont
bien supprimées.
2022-12-17 20:08:03 +01:00
80a904e533 Fix: Surencombrement toujours calculé
Sur de vieux personnages, le sur-encombrement stocké pouvait
être affiché (et incorrect).
2022-12-17 17:49:11 +01:00
c9dc847440 Migration des monnaies par nom
Vu que dans les migrations précédentes, le "cout" avait une valeur
(pas undefined), les migrations ont échoué
2022-12-17 17:26:39 +01:00
4e382d405e Merge pull request 'Ils sont bons mes Maquereaux, elles sont bien mes macros' (#596) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #596
2022-12-17 11:33:44 +01:00
6a12dc97f6 Version 10.3.16 2022-12-17 01:39:22 +01:00
e0b6957bc6 Ajout de macros RdD 2022-12-17 01:37:54 +01:00
944120b524 Fix commande /rdd sans compétence
On peut maintenant faire /rdd Vue -2
2022-12-17 00:55:13 +01:00
bb6bf3387e Malus fatigue indiqué sur la fatigue 2022-12-17 00:55:13 +01:00
467a4d53a4 Séparation des boutons Haut-rêve 2022-12-17 00:55:13 +01:00
d5635b27fe Suppression du test 2022-12-15 22:40:23 +01:00
3a33f7c4fc Fix actor sheet 2022-12-15 10:41:19 +01:00
d2e77dc61c Merge pull request 'Version 10.3.14' (#595) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #595
2022-12-15 07:36:04 +01:00
320bc471e3 Version 10.3.14
- Améliorations cosmétiques
- Affichage de la fortune
- Fix dissolution
- Fix des monnaies dans les compendiums
- Nettoyage des compendiums
2022-12-15 01:03:57 +01:00
090241f0f5 Alignement des champs d'environement 2022-12-15 01:00:28 +01:00
ddc37c6969 Ajustement champs description 2022-12-15 00:56:23 +01:00
6af6e41bc9 Reprise des attributs secondaires 2022-12-15 00:56:23 +01:00
0f7f609a2a Fix dissolution
La dissolution n'est que de 1 point
2022-12-15 00:56:23 +01:00
a58f701ca6 Centrage seuil de rêve 2022-12-15 00:56:23 +01:00
757b46080a Suppression compendiums inutilisés 2022-12-15 00:56:23 +01:00
df44cd66c7 Nettoyage compendiums
- correction des monnaies des acteurs par défaut
- re-export pour garder les données systeme
- suppression des champs "competencecreature[0]" incorrects
- suppression de champs data non utilisés
2022-12-15 00:56:23 +01:00
1040ec1be2 Auto reformat 2022-12-15 00:56:23 +01:00
92643d1c46 Autocomplete Main directrice
Permet de choisir rapidement dans les valeurs proposées
2022-12-15 00:56:22 +01:00
31c4aa32d9 Affichage de la fortune 2022-12-14 19:58:52 +01:00
f5431b58fb Merge pull request 'Non à la dévaluation' (#594) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #594
2022-12-13 22:28:42 +01:00
bd32e1039a Mettre en valeur l'argent sans valeur 2022-12-13 22:27:36 +01:00
6b8f0ed51e Version 10.3.13 2022-12-12 23:33:53 +01:00
10681b3f61 DialogItemSplit height fit-content 2022-12-12 23:32:35 +01:00
bbde3b73fe Gestion des monnaies de valeur 0
Ajout d'un message d'erreur quand on met la valeur d'une monnaie à 0.
Ajout d'une notification quand on détecte des monnaies de valeur 0

Pour les pièces en bois, les créer comme objets, si on ne veut pas de
messages d'avertissement.
2022-12-12 23:32:35 +01:00
57d52c1966 Inc release 2022-12-11 10:35:51 +01:00
e3a29cdab5 Merge pull request 'Fix taille du texte des boutons' (#593) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #593
2022-12-11 10:34:36 +01:00
10e4f14eb2 Fix taille du texte des boutons 2022-12-11 02:10:39 +01:00
04273dfcf1 Inc release 2022-12-10 18:14:52 +01:00
8c5c01114e Merge pull request 'Corrections esthétiques' (#592) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #592
2022-12-10 18:13:44 +01:00
19e6124330 Suppression scripts vides 2022-12-10 16:38:18 +01:00
1c908b50cb Dialog repos 2022-12-10 16:38:18 +01:00
969cedfc3d Fix espace avant boutons 2022-12-10 16:28:08 +01:00
830e66749d Lien des label vers les champs 2022-12-10 16:09:55 +01:00
df26e654ae Fix des tailles de polices 2022-12-10 16:09:35 +01:00
153bfe2e75 Simplification hbs pour la vue détaillée 2022-12-10 16:07:33 +01:00
f6d42875ae Stress et archétype
Déplacement du stress et de l'archétype avant les compétences

Les lignes d'archétypes totalement réparties disparaissent
2022-12-10 15:49:09 +01:00
450cb8e899 Largeur des compétences
Ajustement de la largeur des colonnes de compétenes en vue
simplifiée

Déplacement de l'en-tête dans la première ligne de la liste (avec les
boutons en vue détaillée)
2022-12-10 15:44:08 +01:00
0202938910 Ajout faune 2022-12-09 22:50:23 +01:00
512a056e59 Merge pull request 'Poissons, coquillages, crustacés' (#591) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #591
2022-12-09 22:49:29 +01:00
214377c66d Poissons, coquillages, crustacés
Ils sont frais, mes poissons!
2022-12-09 22:36:17 +01:00
752e534350 Ajout faune 2022-12-09 21:50:12 +01:00
c85a544cc9 Ajout faune + fix sur suppression milieux 2022-12-09 12:01:44 +01:00
233 changed files with 6305 additions and 4245 deletions

4
.gitignore vendored
View File

@ -3,3 +3,7 @@
todo.md
/.vscode
/ignored/
/node_modules/
/jsconfig.json
/package.json
/package-lock.json

BIN
icons/faune/Escargot.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
icons/faune/andurak.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
icons/faune/barbon.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
icons/faune/brocart.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
icons/faune/cancre.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
icons/faune/cancrelas.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
icons/faune/chamois.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/faune/colimace.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
icons/faune/coquille.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
icons/faune/fretin.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
icons/faune/ours.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/faune/padongre.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
icons/faune/sanglier.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/faune/soldieze.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
icons/faune/ver.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
icons/faune/wolf-head.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
icons/services/biere.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
icons/services/lit.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
icons/services/repas.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
icons/services/verre.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
icons/services/vin.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -3,6 +3,7 @@
"TypePersonnage": "Personnage",
"TypeCreature": "Créature",
"TypeEntite": "Entité de cauchemar",
"TypeCommerce": "Commerce",
"TypeVehicule": "Véhicule"
},
"ITEM": {
@ -18,7 +19,8 @@
"TypeRencontre": "Rencontre TMR",
"TypeMunition": "Munition",
"TypeMonnaie": "Monnaie",
"TypeHerbe": "Herbe ou plante",
"TypeHerbe": "Herbe",
"TypePlante": "Plante",
"TypeIngredient": "Ingrédient",
"TypeFaune": "Faune",
"TypeLivre": "Livre",
@ -27,6 +29,7 @@
"TypeArmure": "Armure",
"TypeConteneur": "Conteneur",
"TypeNourritureboisson": "Nourriture & boisson",
"TypeService": "Service",
"TypeChant": "Chant",
"TypeDanse": "Danse",
"TypeMusique": "Musique",

View File

@ -40,11 +40,4 @@ export class RdDActorCreatureSheet extends RdDActorSheet {
this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value));
});
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.object.update(formData);
}
}

View File

@ -3,20 +3,20 @@ import { RdDActorSheet } from "./actor-sheet.js";
export class RdDActorEntiteSheet extends RdDActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html",
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html",
width: 640,
height: 720,
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac"}],
dragDrop: [{dragSelector: ".item-list .item", dropSelector: undefined}]
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
});
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
@ -24,17 +24,17 @@ export class RdDActorEntiteSheet extends RdDActorSheet {
// On competence change
this.html.find('.creature-carac').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "carac_value", parseInt(event.target.value) );
} );
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value));
});
this.html.find('.creature-niveau').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "niveau", parseInt(event.target.value) );
} );
this.html.find('.creature-dommages').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "dommages", parseInt(event.target.value) );
} );
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "niveau", parseInt(event.target.value));
});
this.html.find('.creature-dommages').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value));
});
}
}

View File

@ -8,16 +8,18 @@ import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { DialogRepos } from "./dialog-repos.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js";
import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js";
import { RdDItem } from "./item.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDActorSheet extends ActorSheet {
export class RdDActorSheet extends RdDBaseActorSheet {
/** @override */
static get defaultOptions() {
@ -35,51 +37,38 @@ export class RdDActorSheet extends ActorSheet {
/* -------------------------------------------- */
async getData() {
let formData = await super.getData();
mergeObject(formData,
{
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
effects: this.actor.effects.map(e => foundry.utils.deepClone(e)),
limited: this.actor.limited,
owner: this.actor.isOwner,
biographie: await TextEditor.enrichHTML(this.actor.system.biographie, { async: true }),
notes: await TextEditor.enrichHTML(this.actor.system.notes, { async: true }),
});
mergeObject(formData.calc, {
surenc: this.actor.computeMalusSurEncombrement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.getMessageSurEncombrement(),
})
this.timerRecherche = undefined;
this.actor.computeEtatGeneral();
let formData = {
title: this.title,
id: this.actor.id,
type: this.actor.type,
img: this.actor.img,
name: this.actor.name,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
system: foundry.utils.deepClone(this.actor.system),
effects: this.actor.effects.map(e => foundry.utils.deepClone(e)),
limited: this.actor.limited,
options: this.options,
owner: this.actor.isOwner,
description: await TextEditor.enrichHTML(this.object.system.description, {async: true}),
biographie: await TextEditor.enrichHTML(this.object.system.biographie, {async: true}),
notes: await TextEditor.enrichHTML(this.object.system.notes, {async: true}),
notesmj: await TextEditor.enrichHTML(this.object.system.notesmj, {async: true}),
calc: {
encTotal: await this.actor.computeEncombrementTotalEtMalusArmure(),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.getMessageSurEncombrement(),
},
}
formData.options.isGM = game.user.isGM;
RdDUtility.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
if (formData.type == '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);
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);
formData.competences.forEach(item => {
item.system.isVisible = this.options.recherche
? RdDItemCompetence.nomContientTexte(item, this.options.recherche.text)
: (!this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(item));
? RdDItemCompetence.nomContientTexte(item, this.options.recherche.text)
: (!this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(item));
RdDItemCompetence.levelUp(item, formData.system.compteurs.experience.value);
});
@ -121,22 +110,14 @@ export class RdDActorSheet extends ActorSheet {
/* -------------------------------------------- */ /** @override */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
HtmlUtility._showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.item-split').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
RdDSheetUtility.splitItem(item, this.actor);
});
this.html.find('.item-edit').click(async event => RdDSheetUtility.getItem(event, this.actor)?.sheet.render(true))
this.html.find('.item-delete').click(async event => RdDUtility.confirmerSuppressionItem(this, RdDSheetUtility.getItem(event, this.actor)));
this.html.find('.item-vendre').click(async event => RdDSheetUtility.getItem(event, this.actor)?.proposerVente());
this.html.find('.item-montrer').click(async event => RdDSheetUtility.getItem(event, this.actor)?.postItem());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor));
this.html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const actorId = li.data("actor-id");
@ -170,14 +151,17 @@ export class RdDActorSheet extends ActorSheet {
this.html.find('.creer-tache').click(async event => {
this.createEmptyTache();
});
this.html.find('.creer-un-objet').click(async event => {
RdDUtility.selectObjetType(this);
this.html.find('.creer-tache-blessure-legere').click(async event => {
this.actor.createTacheBlessure('legere');
});
this.html.find('.creer-tache-blessure-grave').click(async event => {
this.actor.createTacheBlessure('grave');
});
this.html.find('.creer-tache-blessure-critique').click(async event => {
this.actor.createTacheBlessure('critique');
});
this.html.find('.creer-une-oeuvre').click(async event => {
RdDUtility.selectTypeOeuvre(this);
});
this.html.find('.nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs();
this.selectTypeOeuvreToCreate();
});
// Blessure control
@ -274,7 +258,7 @@ export class RdDActorSheet extends ActorSheet {
// Points de reve actuel
this.html.find('.ptreve-actuel a').click(async event => {
this.actor.rollCarac('reve-actuel');
this.actor.rollCarac('reve-actuel', true);
});
// Roll Weapon1
@ -308,7 +292,7 @@ export class RdDActorSheet extends ActorSheet {
});
this.html.find('.repos').click(async event => {
await DialogRepos.create(this.actor);
await this.actor.repos();
});
this.html.find('.delete-active-effect').click(async event => {
if (game.user.isGM) {
@ -321,10 +305,6 @@ export class RdDActorSheet extends ActorSheet {
await this.actor.removeEffects();
}
});
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
this.html.find('.carac-xp-augmenter').click(async event => {
let caracName = event.currentTarget.name.replace("augmenter.", "");
this.actor.updateCaracXPAuto(caracName);
@ -383,7 +363,7 @@ export class RdDActorSheet extends ActorSheet {
})
.keyup(async event => {
const nouvelleRecherche = this._optionRecherche(event.currentTarget);
if (this.options.recherche?.text != nouvelleRecherche?.text){
if (this.options.recherche?.text != nouvelleRecherche?.text) {
this.options.recherche = nouvelleRecherche;
if (this.timerRecherche) {
clearTimeout(this.timerRecherche);
@ -414,19 +394,12 @@ export class RdDActorSheet extends ActorSheet {
this.actor.setPointsDeSeuil(event.currentTarget.value);
});
this.html.find('#attribut-protection-edit').change(async event => {
this.actor.updateAttributeValue(event.currentTarget.attributes.name.value, parseInt(event.target.value));
});
// On stress change
this.html.find('.compteur-edit').change(async event => {
let fieldName = event.currentTarget.attributes.name.value;
this.actor.updateCompteurValue(fieldName, parseInt(event.target.value));
});
this.html.find('#ethylisme').change(async event => {
this.actor.setEthylisme(parseInt(event.target.value));
});
this.html.find('.stress-test').click(async event => {
this.actor.transformerStress();
});
@ -450,13 +423,6 @@ export class RdDActorSheet extends ActorSheet {
this.actor.jetEndurance();
});
this.html.find('.monnaie-plus').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), 1);
});
this.html.find('.monnaie-moins').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), -1);
});
this.html.find('.vie-plus').click(async event => {
this.actor.santeIncDec("vie", 1);
});
@ -495,29 +461,33 @@ export class RdDActorSheet extends ActorSheet {
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
const destItemId = this.html.find(event.target)?.closest('.item').attr('data-item-id')
const dropParams = RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor, dragData, this.objetVersConteneur)
if (dropParams) {
const callSuper = await this.actor.processDropItem(dropParams)
if (callSuper) {
await super._onDropItem(event, dragData)
}
async selectTypeOeuvreToCreate() {
let typeObjets = RdDItem.getTypesOeuvres();
let content = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
for (let typeName of typeObjets) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}
}
/* -------------------------------------------- */
async createItem(name, type) {
await this.actor.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
content += '</select>';
let dialog = new Dialog({
title: "Créer une oeuvre",
content: content,
buttons: {
create: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'oeuvre",
callback: () => this.actor.createItem($(".item-type").val())
}
}
});
dialog.render(true);
}
/* -------------------------------------------- */
async createEmptyTache() {
await this.createItem('Nouvelle tache', 'tache');
await this.actor.createItem('tache', 'Nouvelle tache');
}
_optionRecherche(target) {
if (!target.value?.length){
if (!target.value?.length) {
return undefined;
}
return {
@ -546,8 +516,8 @@ export class RdDActorSheet extends ActorSheet {
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
let bodyHeight = position.height - sheetHeader[0].clientHeight;
if (sheetTabs.length>0) {
bodyHeight -= sheetTabs[0].clientHeight;
if (sheetTabs.length > 0) {
bodyHeight -= sheetTabs[0].clientHeight;
}
sheetBody.css("height", bodyHeight);
return position;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,261 @@
import { RdDUtility } from "../rdd-utility.js";
import { Misc } from "../misc.js";
import { DialogSplitItem } from "../dialog-split-item.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { Monnaie } from "../item-monnaie.js";
import { RdDItem } from "../item.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDBaseActorSheet extends ActorSheet {
/** @override */
static get defaultOptions() {
RdDUtility.initAfficheContenu();
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false
});
}
/* -------------------------------------------- */
async getData() {
Monnaie.validerMonnaies(this.actor.itemTypes['monnaie']);
this.actor.recompute();
const userRightLevel = game.user.isGM ? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER : this.actor.getUserLevel(game.user)
const options = duplicate(this.options);
mergeObject(options, {
isGM: game.user.isGM,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
isLimited: userRightLevel >= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED,
isObserver: userRightLevel >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER,
isOwner: userRightLevel >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
owner: this.actor.isOwner,
});
let formData = {
title: this.title,
id: this.actor.id,
type: this.actor.type,
img: this.actor.img,
name: this.actor.name,
system: foundry.utils.deepClone(this.actor.system),
description: await TextEditor.enrichHTML(this.actor.system.description, { async: true }),
notesmj: await TextEditor.enrichHTML(this.actor.system.notesmj, { async: true }),
options: options,
}
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
formData.calc = {
fortune: this.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
encTotal: await this.actor.computeEncTotal(),
}
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
return formData;
}
toSolsDeniers(fortune) {
return {
sols: Math.floor(fortune),
deniers: Math.round(100 * (fortune - Math.floor(fortune)))
};
}
/* -------------------------------------------- */
static filterItemsPerTypeForSheet(formData, itemTypes) {
formData.recettescuisine = Misc.arrayOrEmpty(itemTypes['recettecuisine']);
formData.recettesAlchimiques = Misc.arrayOrEmpty(itemTypes['recettealchimique']);
formData.maladies = Misc.arrayOrEmpty(itemTypes['maladie']);
formData.poisons = Misc.arrayOrEmpty(itemTypes['poison']);
formData.possessions = Misc.arrayOrEmpty(itemTypes['possession']);
formData.maladiesPoisons = formData.maladies.concat(formData.poisons);
formData.competences = (itemTypes['competence'] ?? []).concat(itemTypes['competencecreature'] ?? []);
formData.sortsReserve = Misc.arrayOrEmpty(itemTypes['sortreserve']);
formData.sorts = Misc.arrayOrEmpty(itemTypes['sort']);
formData.rencontres = Misc.arrayOrEmpty(itemTypes['rencontre']);
formData.casestmr = Misc.arrayOrEmpty(itemTypes['casetmr']);
formData.signesdraconiques = Misc.arrayOrEmpty(itemTypes['signedraconique']);
formData.queues = Misc.arrayOrEmpty(itemTypes['queue']);
formData.souffles = Misc.arrayOrEmpty(itemTypes['souffle']);
formData.ombres = Misc.arrayOrEmpty(itemTypes['ombre']);
formData.tetes = Misc.arrayOrEmpty(itemTypes['tete']);
formData.taches = Misc.arrayOrEmpty(itemTypes['tache']);
formData.meditations = Misc.arrayOrEmpty(itemTypes['meditation']);
formData.chants = Misc.arrayOrEmpty(itemTypes['chant']);
formData.danses = Misc.arrayOrEmpty(itemTypes['danse']);
formData.musiques = Misc.arrayOrEmpty(itemTypes['musique']);
formData.oeuvres = Misc.arrayOrEmpty(itemTypes['oeuvre']);
formData.jeux = Misc.arrayOrEmpty(itemTypes['jeu']);
formData.services = Misc.arrayOrEmpty(itemTypes['service']);
formData.conteneurs = Misc.arrayOrEmpty(itemTypes['conteneur']);
formData.materiel = Misc.arrayOrEmpty(itemTypes['objet']);
formData.armes = Misc.arrayOrEmpty(itemTypes['arme']);
formData.armures = Misc.arrayOrEmpty(itemTypes['armure']);
formData.munitions = Misc.arrayOrEmpty(itemTypes['munition']);
formData.livres = Misc.arrayOrEmpty(itemTypes['livre']);
formData.potions = Misc.arrayOrEmpty(itemTypes['potion']);
formData.plantes = Misc.arrayOrEmpty(itemTypes['plante']);
formData.ingredients = Misc.arrayOrEmpty(itemTypes['ingredient']);
formData.faunes = Misc.arrayOrEmpty(itemTypes['faune']);
formData.herbes = Misc.arrayOrEmpty(itemTypes['herbe']);
formData.nourritureboissons = Misc.arrayOrEmpty(itemTypes['nourritureboisson']);
formData.gemmes = Misc.arrayOrEmpty(itemTypes['gemme']);
formData.monnaie = Misc.arrayOrEmpty(itemTypes['monnaie']).sort(Monnaie.triValeurEntiere());
formData.objets = RdDItem.getItemTypesInventaire('all')
.map(t => Misc.arrayOrEmpty(itemTypes[t]))
.reduce((a, b) => a.concat(b), [])
.sort(Misc.ascending(it => it.name));
}
/* -------------------------------------------- */ /** @override */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(this.getItemId(event));
this.render(true);
});
this.html.find('.item-edit').click(async event => this.getItem(event)?.sheet.render(true))
this.html.find('.item-montrer').click(async event => this.getItem(event)?.postItemToChat());
this.html.find('.actor-montrer').click(async event => this.actor.postActorToChat());
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.item-split').click(async event => {
const item = this.getItem(event);
RdDSheetUtility.splitItem(item, this.actor);
});
this.html.find('.item-delete').click(async event => RdDUtility.confirmActorItemDelete(this, this.getItem(event)));
this.html.find('.item-vendre').click(async event => this.vendre(this.getItem(event)));
this.html.find('.creer-un-objet').click(async event => {
this.selectObjetTypeToCreate();
});
this.html.find('.nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs();
});
this.html.find('.monnaie-plus').click(async event => {
this.actor.monnaieIncDec(this.getItemId(event), 1);
});
this.html.find('.monnaie-moins').click(async event => {
this.actor.monnaieIncDec(this.getItemId(event), -1);
});
}
getItemId(event) {
return RdDSheetUtility.getItemId(event);
}
getItem(event) {
return RdDSheetUtility.getItem(event, this.actor);
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
buttons.unshift({
class: "montrer",
icon: "fas fa-comment",
onclick: ev => this.actor.postActorToChat()
});
return buttons
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
const destItemId = this.html.find(event.target)?.closest('.item').attr('data-item-id')
const dropParams = await RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor, dragData, this.objetVersConteneur)
if (dropParams) {
const callSuper = await this.actor.processDropItem(dropParams)
if (callSuper) {
await super._onDropItem(event, dragData)
}
}
}
/* -------------------------------------------- */
async selectObjetTypeToCreate() {
let typeObjets = this.getTypesInventaire().sort(Misc.ascending(type => Misc.typeName('Item', type)));
let content = `<span class="competence-label">Selectionnez le type d'équipement</span><select class="item-type">`;
for (let typeName of typeObjets) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}
content += '</select>';
let d = new Dialog({
title: "Créer un équipement",
content: content,
buttons: {
create: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'objet",
callback: () => this.actor.createItem($(".item-type").val())
}
}
});
d.render(true);
}
getTypesInventaire() {
return RdDItem.getItemTypesInventaire();
}
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
let bodyHeight = position.height - sheetHeader[0].clientHeight;
if (sheetTabs.length > 0) {
bodyHeight -= sheetTabs[0].clientHeight;
}
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);
}
async _onSplitItem(item, split) {
if (split >= 1 && split < item.system.quantite) {
await item.diminuerQuantite(split);
const splitItem = duplicate(item);
splitItem.system.quantite = split;
await this.actor.createEmbeddedDocuments('Item', [splitItem])
}
}
vendre(item) {
item?.proposerVente(this.actor.getQuantiteDisponible(item));
}
}

627
module/actor/base-actor.js Normal file
View File

@ -0,0 +1,627 @@
import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js";
import { Monnaie } from "../item-monnaie.js";
import { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js";
import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
export class RdDBaseActor extends Actor {
static getDefaultImg(itemType) {
return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
}
/* -------------------------------------------- */
static init() {
Hooks.on("preUpdateItem", (item, change, options, id) => RdDBaseActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id));
Hooks.on("createItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onCreateItem(item, options, id));
Hooks.on("deleteItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onDeleteItem(item, options, id));
Hooks.on("updateActor", (actor, change, options, actorId) => actor.onUpdateActor(change, options, actorId));
}
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
case "msg_remote_actor_call":
return RdDBaseActor.onRemoteActorCall(sockmsg.data, sockmsg.userId);
case "msg_reset_nombre_astral":
console.log("RESET ASTRAL", game.user.character);
game.user.character.resetNombreAstral();
return;
}
}
static remoteActorCall(callData, userId = undefined) {
userId = userId ?? Misc.firstConnectedGMId();
if (userId == game.user.id) {
RdDBaseActor.onRemoteActorCall(callData, userId);
return false;
}
else {
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_remote_actor_call", data: callData, userId: userId });
return true;
}
}
static onRemoteActorCall(callData, userId) {
if (userId == game.user.id) {
const actor = game.actors.get(callData?.actorId);
if (Misc.isOwnerPlayerOrUniqueConnectedGM(actor)) { // Seul le joueur choisi effectue l'appel: le joueur courant si propriétaire de l'actor, ou le MJ sinon
const args = callData.args;
console.info(`RdDBaseActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDBaseActor.${callData.method}(`, ...args, ')');
actor[callData.method](...args);
}
}
}
static getParentActor(document) {
return document?.parent instanceof Actor ? document.parent : undefined
}
/**
* Cet methode surcharge Actor.create() pour ajouter si besoin des Items par défaut:
* compétences et monnaies.
*
* @param {Object} actorData template d'acteur auquel ajouter des informations.
* @param {Object} options optionspour customiser la création
*/
static async create(actorData, options) {
// import depuis un compendium
if (actorData instanceof Array) {
return super.create(actorData, options);
}
// Création d'un acteur avec des items (uniquement en cas de duplication): pas besoin d'ajouter d'items
if (actorData.items) {
return await super.create(actorData, options);
}
actorData.items = [];
if (actorData.type == "personnage") {
const competences = await SystemCompendiums.getCompetences(actorData.type);
actorData.items = actorData.items.concat(competences.map(i => i.toObject()))
.concat(Monnaie.monnaiesStandard());
}
else if (actorData.type == "commerce") {
actorData.items = actorData.items.concat(Monnaie.monnaiesStandard());
}
return super.create(actorData, options);
}
constructor(docData, context = {}) {
if (!context.rdd?.ready) {
mergeObject(context, { rdd: { ready: true } });
const ActorConstructor = game.system.rdd.actorClasses[docData.type];
if (ActorConstructor) {
if (!docData.img) {
docData.img = ActorConstructor.defaultIcon;
}
return new ActorConstructor(docData, context);
}
}
super(docData, context);
}
isCreatureEntite() { return this.type == 'creature' || this.type == 'entite'; }
isCreature() { return this.type == 'creature'; }
isEntite() { return this.type == 'entite'; }
isPersonnage() { return this.type == 'personnage'; }
isVehicule() { return this.type == 'vehicule'; }
getItem(id, type = undefined) {
const item = this.items.get(id);
if (type == undefined || (item?.type == type)) {
return item;
}
return undefined;
}
listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
filterItems(filter, type = undefined) { return this.listItems(type)?.filter(filter) ?? []; }
findItemLike(idOrName, type) {
return this.getItem(idOrName, type)
?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
}
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
recompute() { }
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) { }
async onDeleteItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { }
async onTimeChanging(oldTimestamp, newTimestamp) {
this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
}
/* -------------------------------------------- */
getFortune() {
return Monnaie.getFortune(this.itemTypes['monnaie']);
}
/* -------------------------------------------- */
async monnaieIncDec(id, value) {
let monnaie = this.getMonnaie(id);
if (monnaie) {
const quantite = Math.max(0, monnaie.system.quantite + value);
await this.updateEmbeddedDocuments('Item', [{ _id: monnaie.id, 'system.quantite': quantite }]);
}
}
computePrixTotalEquipement() {
return this.items.filter(it => it.isInventaire())
.filter(it => !it.isMonnaie())
.map(it => it.valeurTotale())
.reduce(Misc.sum(), 0);
}
async payerSols(depense) {
depense = Number(depense);
if (depense == 0) {
return;
}
let fortune = this.getFortune();
console.log("payer", game.user.character, depense, fortune);
let msg = "";
if (fortune >= depense) {
await Monnaie.optimiserFortune(this, fortune - depense);
msg = `Vous avez payé <strong>${depense} Sols</strong>, qui ont été soustraits de votre argent.`;
RdDAudio.PlayContextAudio("argent"); // Petit son
} else {
msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
}
let message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: msg
};
ChatMessage.create(message);
}
async depenserSols(sols) {
let reste = this.getFortune() - Number(sols);
if (reste >= 0) {
await Monnaie.optimiserFortune(this, reste);
}
return reste;
}
async ajouterSols(sols, fromActorId = undefined) {
sols = Number(sols);
if (sols == 0) {
return;
}
if (sols < 0) {
ui.notifications.error(`Impossible d'ajouter un gain de ${sols} <0`);
return;
}
if (fromActorId && !game.user.isGM) {
RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(),
actorId: this.id,
method: 'ajouterSols', args: [sols, fromActorId]
});
}
else {
const fromActor = game.actors.get(fromActorId)
await Monnaie.optimiserFortune(this, sols + this.getFortune());
RdDAudio.PlayContextAudio("argent"); // Petit son
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
});
}
}
/* -------------------------------------------- */
getQuantiteDisponible(item) {
return item?.isService() ? undefined : item?.getQuantite();
}
/* -------------------------------------------- */
async achatVente(achat) {
if (achat.vendeurId == achat.acheteurId) {
ui.notifications.info("Inutile de se vendre à soi-même");
return;
}
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
actorId: achat.vendeurId ?? achat.acheteurId,
method: 'achatVente',
args: [achat]
});
return;
}
const cout = Number(achat.prixTotal ?? 0);
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
const vente = achat.vente;
const quantite = (achat.choix.nombreLots ?? 1) * (vente.tailleLot);
const itemVendu = vendeur?.getItem(vente.item._id);
if (!this.verifierQuantite(vendeur, itemVendu, quantite)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
return
}
if (acheteur && !acheteur.verifierFortune(cout)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`);
return;
}
await this.decrementerVente(vendeur, itemVendu, quantite, cout);
if (acheteur) {
await acheteur.depenserSols(cout);
const createdItemId = await acheteur.creerQuantiteItem(vente.item, quantite);
await acheteur.consommerNourritureAchetee(achat, vente, createdItemId);
}
if (cout > 0) {
RdDAudio.PlayContextAudio("argent");
}
const chatAchatItem = duplicate(vente);
chatAchatItem.quantiteTotal = quantite;
ChatMessage.create({
user: achat.userId,
speaker: { alias: (acheteur ?? vendeur).name },
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem)
});
if (!vente.quantiteIllimite) {
if (vente.quantiteNbLots <= achat.choix.nombreLots) {
ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
}
else if (achat.chatMessageIdVente) {
vente["properties"] = itemVendu.getProprietes();
vente.quantiteNbLots -= achat.choix.nombreLots;
vente.jsondata = JSON.stringify(vente.item);
const messageVente = game.messages.get(achat.chatMessageIdVente);
messageVente.update({ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente) });
messageVente.render(true);
}
}
}
async decrementerVente(vendeur, itemVendu, quantite, cout) {
if (vendeur) {
await vendeur.ajouterSols(cout);
await vendeur.decrementerQuantiteItem(itemVendu, quantite);
}
}
verifierFortune(cout) {
return this.getFortune() >= cout;
}
verifierQuantite(vendeur, item, quantiteTotal) {
const disponible = vendeur?.getQuantiteDisponible(item);
return disponible == undefined || disponible >= quantiteTotal;
}
async consommerNourritureAchetee(achat, vente, createdItemId) {
if (achat.choix.consommer && vente.item.type == 'nourritureboisson' && createdItemId != undefined) {
achat.choix.doses = achat.choix.nombreLots;
await this.consommerNourritureboisson(createdItemId, achat.choix, vente.actingUserId);
}
}
async decrementerQuantiteItem(item, quantite, options = { supprimerSiZero: true }) {
if (item.isService()) {
return;
}
let resteQuantite = (item.system.quantite ?? 1) - quantite;
if (resteQuantite <= 0) {
if (options.supprimerSiZero) {
await this.deleteEmbeddedDocuments("Item", [item.id]);
}
else {
await this.updateEmbeddedDocuments("Item", [{ _id: item.id, 'system.quantite': 0 }]);
}
if (resteQuantite < 0) {
ui.notifications.warn(`La quantité de ${item.name} était insuffisante, l'objet a donc été supprimé`)
}
}
else if (resteQuantite > 0) {
await this.updateEmbeddedDocuments("Item", [{ _id: item.id, 'system.quantite': resteQuantite }]);
}
}
async creerQuantiteItem(item, quantite) {
if (this.canReceive(item)) {
const isItemEmpilable = "quantite" in item.system;
const baseItem = {
type: item.type,
img: item.img,
name: item.name,
system: mergeObject(item.system, { quantite: isItemEmpilable ? quantite : undefined })
};
const newItems = isItemEmpilable ? [baseItem] : Array.from({ length: quantite }, (_, i) => baseItem);
const items = await this.createEmbeddedDocuments("Item", newItems);
return items.length > 0 ? items[0].id : undefined;
}
}
/* -------------------------------------------- */
computeMalusSurEncombrement() {
return 0;
}
getEncombrementMax() {
return 0;
}
async computeEncTotal() {
if (!this.pack) {
this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0);
return this.encTotal;
}
return 0;
}
async createItem(type, name = undefined) {
if (!name) {
name = 'Nouveau ' + Misc.typeName('Item', type);
}
await this.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
}
canReceive(item) {
return false;
}
async processDropItem(params) {
const targetActorId = this.id;
const sourceActorId = params.sourceActorId;
const itemId = params.itemId;
const destId = params.destId;
const srcId = params.srcId;
if (sourceActorId && sourceActorId != targetActorId) {
console.log("Moving objects", sourceActorId, targetActorId, itemId);
this.moveItemsBetweenActors(itemId, sourceActorId);
return false;
}
let result = true;
const item = this.getItem(itemId);
if (item?.isInventaire('all') && sourceActorId == targetActorId) {
// rangement
if (srcId != destId && itemId != destId) { // déplacement de l'objet
const src = this.getItem(srcId);
const dest = this.getItem(destId);
const cible = this.getContenantOrParent(dest);
const [empilable, message] = item.isInventaireEmpilable(dest);
if (empilable) {
await dest.empiler(item)
result = false;
}
// changer de conteneur
else if (!cible || this.conteneurPeutContenir(cible, item)) {
await this.enleverDeConteneur(item, src, params.onEnleverConteneur);
await this.ajouterDansConteneur(item, cible, params.onAjouterDansConteneur);
if (message && !dest.isConteneur()) {
ui.notifications.info(cible
? `${message}<br>${item.name} a été déplacé dans: ${cible.name}`
: `${message}<br>${item.name} a été sorti du conteneur`);
}
}
}
}
await this.computeEncTotal();
return result;
}
getContenantOrParent(dest) {
if (!dest || dest.isConteneur()) {
return dest;
}
return this.getContenant(dest);
}
getContenant(item) {
return this.itemTypes['conteneur'].find(it => it.system.contenu.includes(item.id));
}
/* -------------------------------------------- */
conteneurPeutContenir(dest, item) {
if (!dest) {
return true;
}
if (!dest.isConteneur()) {
return false;
}
const destData = dest
if (this._isConteneurContenu(item, dest)) {
ui.notifications.warn(`Impossible de déplacer un conteneur parent (${item.name}) dans un de ses contenus ${destData.name} !`);
return false; // Loop detected !
}
// Calculer le total actuel des contenus
let encContenu = this.getRecursiveEnc(dest) - Number(destData.system.encombrement);
let newEnc = this.getRecursiveEnc(item); // Calculer le total actuel du nouvel objet
// Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
if (Number(destData.system.capacite) < encContenu + newEnc) {
ui.notifications.warn(
`Le conteneur ${dest.name} a une capacité de ${destData.system.capacite}, et contient déjà ${encContenu}.
Impossible d'y ranger: ${item.name} d'encombrement ${newEnc}!`);
return false;
}
return true;
}
/* -------------------------------------------- */
_isConteneurContenu(item, conteneur) {
if (item?.isConteneur()) { // Si c'est un conteneur, il faut vérifier qu'on ne le déplace pas vers un sous-conteneur lui appartenant
for (let id of item.system.contenu) {
let subObjet = this.getItem(id);
if (subObjet?.id == conteneur.id) {
return true; // Loop detected !
}
if (subObjet?.isConteneur()) {
return this._isConteneurContenu(subObjet, conteneur);
}
}
}
return false;
}
/* -------------------------------------------- */
getRecursiveEnc(objet) {
if (!objet) {
return 0;
}
const tplData = objet.system;
if (objet.type != 'conteneur') {
return Number(tplData.encombrement) * Number(tplData.quantite);
}
const encContenus = tplData.contenu.map(idContenu => this.getRecursiveEnc(this.getItem(idContenu)));
return encContenus.reduce(Misc.sum(), 0)
+ Number(tplData.encombrement) /* TODO? Number(tplData.quantite) -- on pourrait avoir plusieurs conteneurs...*/
}
/* -------------------------------------------- */
/** Ajoute un item dans un conteneur, sur la base
* de leurs ID */
async ajouterDansConteneur(item, conteneur, onAjouterDansConteneur) {
if (!conteneur) {
// TODO: afficher
item.estContenu = false;
}
else if (conteneur.isConteneur()) {
item.estContenu = true;
await this.updateEmbeddedDocuments('Item', [{
_id: conteneur.id,
'system.contenu': [...conteneur.system.contenu, item.id]
}]);
onAjouterDansConteneur(item.id, conteneur.id);
}
}
/* -------------------------------------------- */
/** Fonction de remise à plat de l'équipement (ie vide les champs 'contenu') */
async nettoyerConteneurs() {
RdDConfirm.confirmer({
settingConfirmer: "confirmation-vider",
content: `<p>Etes vous certain de vouloir vider tous les conteneurs ?</p>`,
title: 'Vider les conteneurs',
buttonLabel: 'Vider',
onAction: async () => {
const corrections = [];
for (let item of this.items) {
if (item.estContenu) {
item.estContenu = undefined;
}
if (item.type == 'conteneur' && item.system.contenu.length > 0) {
corrections.push({ _id: item.id, 'system.contenu': [] });
}
}
if (corrections.length > 0) {
await this.updateEmbeddedDocuments('Item', corrections);
}
}
});
}
/* -------------------------------------------- */
buildSubConteneurObjetList(conteneurId, deleteList) {
let conteneur = this.getItem(conteneurId);
if (conteneur?.type == 'conteneur') { // Si c'est un conteneur
for (let subId of conteneur.system.contenu) {
let subObj = this.getItem(subId);
if (subObj) {
if (subObj.type == 'conteneur') {
this.buildSubConteneurObjetList(subId, deleteList);
}
deleteList.push({ id: subId, conteneurId: conteneurId });
}
}
}
}
/* -------------------------------------------- */
async deleteAllConteneur(itemId, options) {
let list = [];
list.push({ id: itemId, conteneurId: undefined }); // Init list
this.buildSubConteneurObjetList(itemId, list);
await this.deleteEmbeddedDocuments('Item', list.map(it => it.id), options);
}
/* -------------------------------------------- */
/** Supprime un item d'un conteneur, sur la base
* de leurs ID */
async enleverDeConteneur(item, conteneur, onEnleverDeConteneur) {
if (conteneur?.isConteneur()) {
item.estContenu = false;
await this.updateEmbeddedDocuments('Item', [{
_id: conteneur.id,
'system.contenu': conteneur.system.contenu.filter(id => id != item.id)
}]);
onEnleverDeConteneur();
}
}
/* -------------------------------------------- */
async moveItemsBetweenActors(itemId, sourceActorId) {
let itemsList = []
let sourceActor = game.actors.get(sourceActorId);
itemsList.push({ id: itemId, conteneurId: undefined }); // Init list
sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list
const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id))
.map(it => duplicate(it))
.map(it => { it.system.contenu = []; return it; });
let newItems = await this.createEmbeddedDocuments('Item', itemsDataToCreate);
let itemMap = this._buildMapOldNewId(itemsList, newItems);
for (let item of itemsList) { // Second boucle pour traiter la remise en conteneurs
// gestion conteneur/contenu
if (item.conteneurId) { // l'Objet était dans un conteneur
let newConteneurId = itemMap[item.conteneurId]; // Get conteneur
let newConteneur = this.getItem(newConteneurId);
let newItemId = itemMap[item.id]; // Get newItem
console.log('New conteneur filling!', newConteneur, newItemId, item);
let contenu = duplicate(newConteneur.system.contenu);
contenu.push(newItemId);
await this.updateEmbeddedDocuments('Item', [{ _id: newConteneurId, 'system.contenu': contenu }]);
}
}
for (let item of itemsList) {
await sourceActor.deleteEmbeddedDocuments('Item', [item.id]);
}
}
_buildMapOldNewId(itemsList, newItems) {
let itemMap = {};
for (let i = 0; i < itemsList.length; i++) {
itemMap[itemsList[i].id] = newItems[i].id; // Pour garder le lien ancien / nouveau
}
return itemMap;
}
/* -------------------------------------------- */
async postActorToChat(modeOverride) {
let chatData = {
doctype: 'Actor',
id: this.id,
type: this.type,
img: this.img,
pack: this.pack,
name: this.name,
system: { description: this.system.description }
}
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.html', chatData)
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
}
}

View File

@ -0,0 +1,95 @@
import { DialogItemAchat } from "../dialog-item-achat.js";
import { RdDItem } from "../item.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDBaseActorSheet } from "./base-actor-sheet.js";
import { RdDCommerce } from "./commerce.js";
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDCommerceSheet extends RdDBaseActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-actor-sheet.html",
width: 600,
height: 720,
tabs: [],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
});
}
get title() {
if (this.actor.token && this.actor.token != this.actor.prototypeToken) {
return this.actor.token.name;
}
return super.title
}
async getData() {
const formData = await super.getData();
if (this.actor.token && this.actor.token != this.actor.prototypeToken) {
mergeObject(formData,
{
title: this.actor.token.name,
token: {
img: this.actor.token.texture.src
}
},
{ overwrite: true });
}
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
this.html.find('a.item-acheter').click(async event => await this.vente(this.getItem(event)));
if (!this.options.editable) return;
this.html.find('a.item-quantite-moins').click(async event => await this.getItem(event)?.quantiteIncDec(-1, { supprimerSiZero: false }));
this.html.find('a.item-quantite-plus').click(async event => await this.getItem(event)?.quantiteIncDec(1));
this.html.find('input.item-quantite').change(async event => {
const newQuantite = Math.max(0, Number.parseInt(this.html.find(event.currentTarget).val()));
await this.getItem(event)?.update({ "system.quantite": newQuantite });
})
this.html.find('input.item-cout').change(async event => {
const newCout = Math.max(0, Number(this.html.find(event.currentTarget).val()));
await this.getItem(event)?.update({ "system.cout": newCout });
})
}
getTypesInventaire() {
return RdDItem.getItemTypesInventaire('all');
}
async vente(item) {
const acheteur = RdDUtility.getSelectedActor();
if (!acheteur) {
ui.notifications.warn(`Pas d'acheteur sélectionné`);
return;
}
const disponible = this.actor.getQuantiteDisponible(item)
if (disponible == 0) {
ui.notifications.warn(`${this.name} n'a plus de ${item.name} en vente`);
return;
}
await DialogItemAchat.onAcheter({
item,
vendeur: this.actor,
acheteur,
quantiteIllimite: disponible == undefined,
nbLots: disponible ?? 1,
tailleLot: 1,
prixLot: item.calculerPrixCommercant()
});
}
}

53
module/actor/commerce.js Normal file
View File

@ -0,0 +1,53 @@
import { Misc } from "../misc.js";
import { RdDBaseActor } from "./base-actor.js";
export class RdDCommerce extends RdDBaseActor {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/services/commerce.webp";
}
prepareData() {
super.prepareData();
}
prepareDerivedData() {
super.prepareDerivedData();
}
canReceive(item) {
if (item.isInventaire('all')) {
return true;
}
return super.canReceive(item);
}
getQuantiteDisponible(item) {
return this.system.illimite || item.isService() ? undefined : item.getQuantite();
}
verifierFortune(cout) {
return this.system.illimite || super.verifierFortune(cout);
}
async depenserSols(cout) {
if (this.system.illimite) {
return
}
await super.depenserSols(cout)
}
async consommerNourritureAchetee(achat, vente, createdItemId) {
// ne pas consommer pour un commerce
}
async decrementerQuantiteItem(item, quantite) {
if (this.system.illimite) {
return;
}
await super.decrementerQuantiteItem(item, quantite, { supprimerSiZero: false });
}
calculerPrix(item) {
const pourcentage = this.system.pourcentage ?? 100;
return Misc.keepDecimals(Math.ceil(item.system.cout * pourcentage) / 100, 2);
}
}

View File

@ -1,5 +1,7 @@
import { SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
const LATEST_USED_JOURNAL_ID = "chronologie-dernier-journal";
@ -16,40 +18,20 @@ export class DialogChronologie extends Dialog {
});
}
static async create() {
const dateRdD = game.system.rdd.calendrier.getCalendrier();
const dialogData = {
auteur: game.user.name,
isGM: game.user.isGM,
information: "",
journalId: game.settings.get(SYSTEM_RDD, LATEST_USED_JOURNAL_ID),
journaux: game.journal.filter(it => it.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)),
dateRdD: dateRdD,
jourRdD: dateRdD.jour +1,
heureRdD: game.system.rdd.calendrier.getCurrentHeure(),
timestamp: game.system.rdd.calendrier.timestamp,
dateReel: DialogChronologie.getCurrentDateTime()
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-chronologie.html", dialogData);
const dialog = new DialogChronologie(html);
const dialog = new DialogChronologie(html, dialogData);
dialog.render(true);
}
constructor(html) {
const options = {
classes: ["DialogChronologie"],
width: 500,
height: 'fit-content',
'z-index': 99999
};
const conf = {
title: "Chronologie",
content: html,
buttons: {
ajout: { label: "Ajouter", callback: it => this.ajouter() },
}
};
super(conf, options);
}
static getCurrentDateTime() {
return new Date().toLocaleString("sv-SE", {
year: "numeric",
@ -60,18 +42,61 @@ export class DialogChronologie extends Dialog {
}).replace(" ", "T");
}
constructor(html, dialogData) {
const options = {
classes: ["DialogChronologie"],
width: 500,
height: 'fit-content',
'z-index': 99999
};
const timeData = dialogData.timestamp.toCalendrier()
const conf = {
title: `Chronologie - ${timeData.jourDuMois} ${timeData.mois.label} - Heure ${timeData.heure.label}`,
content: html,
buttons: {
}
};
super(conf, options);
this.dialogData = dialogData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
super.activateListeners(html);
const journalPrecedent = game.journal.get(this.dialogData.journalId);
this.showChronologiePreset(!(journalPrecedent?.canUserModify(game.user)))
this.html.find("a.chronologie-preset-show").click(event => this.showChronologiePreset(true));
this.html.find("a.chronologie-preset-hide").click(event => this.showChronologiePreset(false));
this.html.find("button.chronologie-ajouter").click(event => this.ajouter());
}
showChronologiePreset(showPreset) {
HtmlUtility.showControlWhen(this.html.find(".chronologie-preset-show"), !showPreset);
HtmlUtility.showControlWhen(this.html.find(".chronologie-preset-hide"), showPreset);
HtmlUtility.showControlWhen(this.html.find(".chronologie-preset"), showPreset);
}
async ajouter() {
await this.forceValidation();
const { journalId, journalEntry } = this.findJournal();
// ajouter à la page ou créer une page
this.addContentToJournal(journalEntry, await this.prepareChronologieEntry());
if (journalEntry?.canUserModify(game.user)) {
const journalParameters = this.extractJournalParameters();
this.storeLatestUsedJournalEntry(journalId);
const jour = journalParameters.dateRdD.jour;
const mois = journalParameters.dateRdD.mois.label;
const annee = journalParameters.dateRdD.annee;
const section = `${jour} ${mois} ${annee}`
const content = await this.prepareChronologieEntry(journalParameters);
// ajouter à la page ou créer une page
this.addContentToJournal(journalEntry, section, content);
this.storeLatestUsedJournalEntry(journalId);
this.close();
}
else {
const journal = this.html.find("form.rdddialogchrono select[name='journalId']").val();
ui.notifications.warn(`Le journal ${journal} n'est pas accessible`);
}
}
async forceValidation() {
@ -84,8 +109,8 @@ export class DialogChronologie extends Dialog {
return { journalId, journalEntry };
}
async prepareChronologieEntry() {
return await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chronologie-entry.html", this.extractJournalParameters());
async prepareChronologieEntry(journalParameters) {
return await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chronologie-entry.html", journalParameters);
}
extractJournalParameters() {
@ -93,28 +118,29 @@ export class DialogChronologie extends Dialog {
auteur: this.html.find("form.rdddialogchrono :input[name='auteur']").val(),
information: this.html.find("form.rdddialogchrono :input[name='information']").val(),
dateRdD: {
jour: this.html.find("form.rdddialogchrono :input[name='jourRdD']").val(),
moisRdD: this.html.find("form.rdddialogchrono :input[name='dateRdD.moisRdD.key']").val(),
annee: this.html.find("form.rdddialogchrono :input[name='dateRdD.annee']").val()
jour: this.html.find("form.rdddialogchrono :input[name='chronologie.jourDuMois']").val(),
mois: RdDTimestamp.definition(this.html.find("form.rdddialogchrono :input[name='chronologie.mois']").val()),
annee: this.html.find("form.rdddialogchrono :input[name='chronologie.annee']").val(),
heure: RdDTimestamp.definition(this.html.find("form.rdddialogchrono :input[name='chronologie.heure']").val()),
minute: this.html.find("form.rdddialogchrono :input[name='chronologie.minute']").val(),
},
heureRdD: this.html.find("form.rdddialogchrono :input[name='heureRdD']").val(),
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val().replace('T', ' ')
}
}
addContentToJournal(journalEntry, content) {
let page = journalEntry.pages.find(p => p.type == 'text' && Grammar.equalsInsensitive(p.name, 'Chronologie'));
addContentToJournal(journalEntry, section, content) {
let page = journalEntry.pages.find(p => p.type == 'text' && Grammar.equalsInsensitive(p.name, section));
if (page) {
page.update({ 'text.content': content + '\n' + page.text.content });
page.update({ 'text.content': page.text.content + '\n' + content });
}
else {
journalEntry.createEmbeddedDocuments('JournalEntryPage', [this.newPageChronologie(content)]);
journalEntry.createEmbeddedDocuments('JournalEntryPage', [this.newPageChronologie(section, content)]);
}
}
newPageChronologie(content) {
newPageChronologie(section, content) {
return new JournalEntryPage({
name: 'Chronologie',
name: section,
type: 'text',
title: { show: true, level: 1 },
text: { content: content, format: 1 }

View File

@ -1,6 +1,6 @@
import { ChatUtility } from "./chat-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemSigneDraconique } from "./item-signedraconique.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { TMRUtility } from "./tmr-utility.js";
export class DialogCreateSigneDraconique extends Dialog {
@ -98,7 +98,7 @@ export class DialogCreateSigneDraconique extends Dialog {
async setEphemere(ephemere) {
this.dialogData.signe.system.ephemere = ephemere;
HtmlUtility._showControlWhen(this.html.find(".signe-system-duree"), ephemere);
HtmlUtility.showControlWhen(this.html.find(".signe-system-duree"), ephemere);
}
async onSelectActor(event) {

View File

@ -1,14 +1,13 @@
import { Monnaie } from "./item-monnaie.js";
import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js";
export class DialogItemAchat extends Dialog {
static venteData(button) {
const vendeurId = button.attributes['data-vendeurId']?.value;
static preparerAchat(chatButton) {
const vendeurId = chatButton.attributes['data-vendeurId']?.value;
const vendeur = vendeurId ? game.actors.get(vendeurId) : undefined;
const acheteur = RdDUtility.getSelectedActor();
const json = button.attributes['data-jsondata']?.value;
const json = chatButton.attributes['data-jsondata']?.value;
if (!acheteur && !vendeur) {
ui.notifications.info("Pas d'acheteur ni de vendeur, aucun changement");
return undefined;
@ -18,46 +17,67 @@ export class DialogItemAchat extends Dialog {
return undefined;
}
const prixLot = Number(button.attributes['data-prixLot']?.value ?? 0);
return {
item: json ? JSON.parse(json) : undefined,
actingUserId: game.user.id,
vendeurId: vendeurId,
vendeur: vendeur,
acheteur: acheteur,
tailleLot: parseInt(button.attributes['data-tailleLot']?.value ?? 1),
quantiteIllimite: button.attributes['data-quantiteIllimite']?.value == 'true',
quantiteNbLots: parseInt(button.attributes['data-quantiteNbLots']?.value),
choix: {
nombreLots: 1,
seForcer: false,
supprimerSiZero: true
},
prixLot: prixLot,
prixTotal: prixLot,
isVente: prixLot > 0,
chatMessageIdVente: RdDUtility.findChatMessageId(button)
item: (json ? JSON.parse(json) : undefined),
vendeur,
acheteur,
nbLots: parseInt(chatButton.attributes['data-quantiteNbLots']?.value),
tailleLot: parseInt(chatButton.attributes['data-tailleLot']?.value ?? 1),
prixLot: Number(chatButton.attributes['data-prixLot']?.value ?? 0),
quantiteIllimite: chatButton.attributes['data-quantiteIllimite']?.value == 'true',
chatMessageIdVente: RdDUtility.findChatMessageId(chatButton),
};
}
static async onAcheter(venteData) {
static async onAcheter({ item, vendeur, acheteur, tailleLot, prixLot, nbLots, quantiteIllimite, chatMessageIdVente }) {
const venteData = {
item,
actingUserId: game.user.id,
vendeurId: vendeur?.id,
vendeur,
acheteur,
tailleLot,
quantiteIllimite,
quantiteNbLots: nbLots,
choix: { seForcer: false, supprimerSiZero: true },
prixLot,
isVente: prixLot > 0,
isConsommable: item.type == 'nourritureboisson' && acheteur?.isPersonnage(),
chatMessageIdVente
};
DialogItemAchat.changeNombreLots(venteData, 1);
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.html`, venteData);
new DialogItemAchat(html, venteData).render(true);
}
static changeNombreLots(venteData, nombreLots) {
venteData.choix.nombreLots = nombreLots;
venteData.prixTotal = (nombreLots * venteData.prixLot).toFixed(2);
if (venteData.isConsommable) {
const doses = nombreLots * venteData.tailleLot;
venteData.totalSust = Misc.keepDecimals(doses * (venteData.item.system.sust ?? 0), 2);
venteData.totalDesaltere = venteData.item.system.boisson
? Misc.keepDecimals(doses * (venteData.item.system.desaltere ?? 0), 2)
: 0;
}
}
constructor(html, venteData) {
const isConsommable = venteData.item.type == 'nourritureboisson' && venteData.acheteur?.isPersonnage();
let options = { classes: ["dialogachat"], width: 400, height: 'fit-content', 'z-index': 99999 };
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre";
const buttons = {};
if (isConsommable) {
if (venteData.isConsommable) {
buttons["consommer"] = { label: venteData.item.system.boisson ? "Boire" : "Manger", callback: it => this.onAchatConsommer() }
}
buttons[actionAchat] = { label: actionAchat, callback: it => { this.onAchat(); } };
buttons["decliner"] = { label: "Décliner", callback: it => { } };
const acheteur = venteData.acheteur?.name ?? 'Un acheteur';
const vendeur = venteData.vendeur?.name ?? 'Un vendeur';
let conf = {
title: venteData.acheteur ? venteData.acheteur.name + " - " + actionAchat : actionAchat,
title: `${acheteur} - ${actionAchat} à ${vendeur}`,
content: html,
default: actionAchat,
buttons: buttons
@ -98,13 +118,21 @@ export class DialogItemAchat extends Dialog {
}
setNombreLots(nombreLots) {
if (nombreLots > this.venteData.quantiteNbLots) {
ui.notifications.warn(`Seulement ${this.venteData.quantiteNbLots} lots disponibles, vous ne pouvez pas en prendre ${nombreLots}`)
if (!this.venteData.quantiteIllimite) {
if (!this.venteData.quantiteIllimite && nombreLots > this.venteData.quantiteNbLots) {
ui.notifications.warn(`Seulement ${this.venteData.quantiteNbLots} lots disponibles, vous ne pouvez pas en prendre ${nombreLots}`)
}
nombreLots = Math.min(nombreLots, this.venteData.quantiteNbLots);
}
this.venteData.choix.nombreLots = Math.min(nombreLots, this.venteData.quantiteNbLots);
this.venteData.prixTotal = (nombreLots * this.venteData.prixLot).toFixed(2);
this.html.find(".nombreLots").val(this.venteData.choix.nombreLots);
DialogItemAchat.changeNombreLots(this.venteData, nombreLots);
this.html.find(".nombreLots").val(nombreLots);
this.html.find(".prixTotal").text(this.venteData.prixTotal);
this.html.find("span.total-sust").text(this.venteData.totalSust);
this.html.find("span.total-desaltere").text(this.venteData.totalDesaltere);
}
}

View File

@ -2,21 +2,22 @@ import { HtmlUtility } from "./html-utility.js";
export class DialogItemVente extends Dialog {
static async display(item, callback) {
const quantite = item.isConteneur() ? 1 : item.system.quantite;
static async display({ item, callback, quantiteMax = undefined }) {
const quantite = quantiteMax ?? item.getQuantite() ?? 1;
const isOwned = item.parent;
const venteData = {
item: item,
alias: item.actor?.name ?? game.user.name,
vendeurId: item.actor?.id,
prixOrigine: item.system.cout,
prixUnitaire: item.system.cout,
prixLot: item.system.cout,
vendeurId: item.actor?.id ,
prixOrigine: item.calculerPrixCommercant(),
prixUnitaire: item.calculerPrixCommercant(),
prixLot: item.calculerPrixCommercant(),
tailleLot: 1,
quantiteNbLots: quantite,
quantiteMaxLots: quantite,
quantiteMax: quantite ,
quantiteIllimite: !item.isOwned,
isOwned: item.isOwned,
quantiteMax: quantite,
quantiteIllimite: item.isItemCommerce() ? quantiteMax == undefined : !isOwned,
isOwned: isOwned,
};
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.html`, venteData);
return new DialogItemVente(venteData, html, callback).render(true);
@ -40,8 +41,7 @@ export class DialogItemVente extends Dialog {
activateListeners(html) {
super.activateListeners(html);
this.html = html;
HtmlUtility._showControlWhen(this.html.find(".quantiteNbLots"), !this.venteData.quantiteIllimite)
this.setQuantiteIllimite(this.venteData.quantiteIllimite);
this.html.find(".tailleLot").change(event => this.setTailleLot(Number(event.currentTarget.value)));
this.html.find(".quantiteNbLots").change(event => this.setNbLots(Number(event.currentTarget.value)));
this.html.find(".quantiteIllimite").change(event => this.setQuantiteIllimite(event.currentTarget.checked));
@ -54,7 +54,7 @@ export class DialogItemVente extends Dialog {
await this.html.find(".quantiteIllimite").change();
await this.html.find(".prixLot").change();
this.callback(this.venteData);
}
}
/* -------------------------------------------- */
setPrixLot(prixLot) {
@ -68,13 +68,11 @@ export class DialogItemVente extends Dialog {
this.html.find(".prixLot").val(this.venteData.prixLot);
}
this.venteData.tailleLot = tailleLot;
if (this.venteData.isOwned) {
// recalculer le nombre de lots max
this.venteData.quantiteMaxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
this.venteData.quantiteNbLots = Math.min(this.venteData.quantiteMaxLots, this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
}
// recalculer le nombre de lots max
this.venteData.quantiteMaxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
this.venteData.quantiteNbLots = Math.min(this.venteData.quantiteMaxLots, this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
}
setNbLots(nbLots) {
@ -88,6 +86,6 @@ export class DialogItemVente extends Dialog {
setQuantiteIllimite(checked) {
this.venteData.quantiteIllimite = checked;
this.html.find(".label-quantiteIllimite").text(this.venteData.quantiteIllimite ? "Illimités" : "disponibles");
HtmlUtility._showControlWhen(this.html.find(".quantiteNbLots"), !this.venteData.quantiteIllimite)
HtmlUtility.showControlWhen(this.html.find(".quantiteNbLots"), !this.venteData.quantiteIllimite)
}
}

View File

@ -1,4 +1,3 @@
import { Misc } from "./misc.js";
export class DialogRepos extends Dialog {
@ -9,7 +8,7 @@ export class DialogRepos extends Dialog {
}
constructor(html, actor) {
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 500, height: 400, 'z-index': 99999 };
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 400, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: "Se reposer",
content: html,
@ -33,6 +32,7 @@ export class DialogRepos extends Dialog {
const selection = await this.html.find("[name='repos']:checked").val();
const nbHeures = Number.parseInt(await this.html.find("[name='nb-heures']").val());
const nbJours = Number.parseInt(await this.html.find("[name='nb-jours']").val());
console.log("ACTOR", this.actor)
switch (selection) {
case "sieste": {
await this.actor.dormir(nbHeures);

View File

@ -12,7 +12,7 @@ export class DialogSplitItem extends Dialog {
}
constructor(item, splitData, html, callback) {
let options = { classes: ["dialogsplit"], width: 300, height: 160, 'z-index': 99999 };
let options = { classes: ["dialogsplit"], width: 300, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: "Séparer en deux",
content: html,

View File

@ -1,223 +1,78 @@
import { SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { CompendiumTableHelpers, CompendiumTable } from "./settings/system-compendiums.js";
import { CompendiumTableHelpers, CompendiumTable, SystemCompendiums } from "./settings/system-compendiums.js";
const RARETES = [
{ name: 'Commune', frequence: 54, min: 27, max: 108 },
{ name: 'Frequente', frequence: 18, min: 9, max: 36 },
{ name: 'Rare', frequence: 6, min: 3, max: 12 },
{ name: 'Rarissime', frequence: 2, min: 1, max: 4 }]
const DEFAULT_RARETE = 1;
const SETTINGS_LISTE_MILIEUX = "liste-milieux";
const MILIEUX = [
"Collines",
"Cours d'eau",
"Déserts",
"Forêts",
"Marais",
"Maritimes",
"Montagnes",
"Plaines",
"Sous-sols"
]
const ITEM_ENVIRONNEMENT_TYPES = [
'herbe', 'ingredient', 'faune'
]
const COMPENDIUMS_RECHERCHE = 'compendiums-recherche';
export class Environnement {
static init() {
game.settings.register(SYSTEM_RDD, SETTINGS_LISTE_MILIEUX, {
name: "Liste des milieux proposés",
hint: "Liste des milieux proposés pour la faune&flore, séparés par des virgules",
game.settings.register(SYSTEM_RDD, COMPENDIUMS_RECHERCHE, {
name: COMPENDIUMS_RECHERCHE,
default: [
SystemCompendiums.getCompendium('faune-flore-mineraux'),
SystemCompendiums.getCompendium('meditations-et-ecrits'),
SystemCompendiums.getCompendium('equipement')
],
scope: "world",
config: true,
default: MILIEUX.reduce(Misc.joining(',')),
type: String
config: false,
type: Object
});
game.system.rdd.environnement = new Environnement();
Hooks.once('ready', () => game.system.rdd.environnement.onReady());
}
constructor() {
this.table = new CompendiumTable('faune-flore-mineraux', 'Item', ITEM_ENVIRONNEMENT_TYPES)
this.compendiums = [];
this.compendiumTables = [];
this.mapMilieux = {}
}
static getRarete(name = undefined) {
return RARETES.find(it => it.name == name) ?? RARETES[DEFAULT_RARETE];
}
static getFrequenceRarete(rarete, field = undefined) {
const selected = this.getRarete(rarete);
return selected[field];
async onReady() {
await this.$prepareCompendiums()
}
async milieux() {
return Object.values(await this.mapMilieux());
return Object.values(this.mapMilieux);
}
async mapMilieux() {
const compendiumItems = await this.getElements(it => 1, it => ITEM_ENVIRONNEMENT_TYPES.includes(it.type));
return Misc.indexLowercase(this.getMilieuxSettings().concat(Environnement.listMilieux(compendiumItems)));
async saveCompendiums(compendiumIds) {
game.settings.set(SYSTEM_RDD, COMPENDIUMS_RECHERCHE, compendiumIds);
await this.$prepareCompendiums();
}
static listMilieux(items) {
return Misc.concat(items.map(it => Environnement.$itemToMilieux(it).filter(m => m)));
async $prepareCompendiums() {
this.compendiums = game.settings.get(SYSTEM_RDD, COMPENDIUMS_RECHERCHE).filter(c => SystemCompendiums.getPack(c));
this.compendiumTables = this.compendiums.map(it => new CompendiumTable(it, 'Item'));
const compendiumItems = await this.getElements(it => 1, it => it.isInventaire());
const fromCompendiums = Misc.concat(compendiumItems.map(it => it.getMilieux().filter(m => m)));
this.mapMilieux = Misc.indexLowercase(fromCompendiums);
}
async autresMilieux(item) {
const mapMilieux = await this.mapMilieux();
const milieuxExistants = Environnement.$itemToMilieux(item).map(it => Grammar.toLowerCaseNoAccent(it));
return Object.keys(mapMilieux)
const milieuxExistants = item.getMilieux().map(it => Grammar.toLowerCaseNoAccent(it));
return Object.keys(this.mapMilieux)
.filter(it => !milieuxExistants.includes(it))
.map(it => mapMilieux[it]);
.map(it => this.mapMilieux[it]);
}
static $itemToMilieux(item) {
return item.system.environnement.map(env => env.milieu);
}
getMilieuxSettings() {
return game.settings.get(SYSTEM_RDD, SETTINGS_LISTE_MILIEUX).split(',').map(it => it.trim()).filter(it => it != '');
}
async findEnvironnementsLike(search) {
const milieux = await this.mapMilieux();
const searchLower = Grammar.toLowerCaseNoAccent(search);
const keys = Object.keys(milieux).filter(it => it.includes(searchLower));
if (keys.length > 1) {
const milieuExact = milieux[searchLower];
if (milieuExact) {
return [milieuExact];
}
}
return keys.map(k => milieux[k]);
}
async searchToChatMessage(milieux, typeName) {
const table = await this.buildEnvironnementTable(milieux);
await CompendiumTableHelpers.tableToChatMessage(table, 'Item', ITEM_ENVIRONNEMENT_TYPES, typeName);
return true
}
async getRandom(milieux, typeName) {
const table = await this.buildEnvironnementTable(milieux);
return await CompendiumTableHelpers.getRandom(table, 'Item', ITEM_ENVIRONNEMENT_TYPES, undefined, typeName);
}
async buildEnvironnementTable(milieux) {
const filterMilieux = item => item.system?.environnement.filter(env => milieux.includes(env.milieu));
const itemRareteEnMilieu = item => {
const raretes = filterMilieux(item);
const frequenceMax = Math.max(raretes.map(env => env.frequence));
return raretes.find(env => env.frequence == frequenceMax);
}
const itemFrequenceEnMilieu = item => itemRareteEnMilieu(item)?.frequence ?? 0;
const isPresentEnMilieu = item => itemFrequenceEnMilieu(item) > 0;
return await this.table.buildTable(itemFrequenceEnMilieu, isPresentEnMilieu);
}
async getElements(itemFrequence, filter) {
return await this.table.getContent(itemFrequence, filter);
const compendiumsElement = await Promise.all(
this.compendiumTables.map(async compTable => await compTable.getContent(itemFrequence, filter))
);
const elements = compendiumsElement.reduce((a, b) => a.concat(b));
elements.sort(Misc.ascending(it => it.name))
return elements;
}
async buildTable(itemFrequence, filter = it => true) {
if (!itemFrequence) {
itemFrequence = it => it.getFrequence()
}
const elements = await this.getElements(itemFrequence, filter);;
return CompendiumTableHelpers.buildTable(elements, itemFrequence);
}
}
export class EnvironmentSheetHelper {
static defaultOptions(defaultOptions) {
return mergeObject(defaultOptions, {
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "informations" }]
});
}
static setPosition(sheet, superPosition) {
const position = superPosition;
const sheetHeader = sheet.element.find(".sheet-header");
const sheetBody = sheet.element.find(".sheet-body");
sheetBody.css("height", position.height - sheetHeader[0].clientHeight)
return position;
}
/* -------------------------------------------- */
static async getData(sheet, formData) {
return mergeObject(formData, {
milieux: await game.system.rdd.environnement.autresMilieux(sheet.item)
});
}
static activateListeners(sheet) {
if (!sheet.options.editable) return;
sheet.html.find("input.input-selection-milieu").keypress(event => {
if (event.keyCode == '13') {
EnvironmentSheetHelper.onAddMilieu(sheet, event);
}
event.stopPropagation();
})
sheet.html.find("a.milieu-add").click(event => EnvironmentSheetHelper.onAddMilieu(sheet, event));
sheet.html.find("div.environnement-milieu a.milieu-delete").click(event => EnvironmentSheetHelper.onDeleteMilieu(sheet, event));
sheet.html.find("div.environnement-milieu select.environnement-rarete").change(event => EnvironmentSheetHelper.onChange(sheet, event,
updated => EnvironmentSheetHelper.$changeRarete(sheet, event, updated)));
sheet.html.find("div.environnement-milieu input[name='environnement-frequence']").change(event => EnvironmentSheetHelper.onChange(sheet, event,
updated => EnvironmentSheetHelper.$changeFrequence(sheet, event, updated)));
}
static $changeFrequence(sheet, event, updated) {
updated.frequence = Number(sheet.html.find(event.currentTarget).val());
}
static $changeRarete(sheet, event, updated) {
const name = sheet.html.find(event.currentTarget).val();
const rarete = Environnement.getRarete(name);
updated.rarete = rarete.name;
updated.frequence = rarete.frequence;
// updated.frequence = Math.min(
// Math.max(rarete.min, updated.frequence ?? rarete.frequence),
// rarete.max);
}
static async onAddMilieu(sheet, event) {
const milieu = sheet.html.find('input.input-selection-milieu').val();
if (!milieu) {
ui.notifications.warn(`Choisissez le milieu dans lequel se trouve le/la ${sheet.item.name}`);
return
}
const list = sheet.item.system.environnement;
const exists = list.find(it => it.milieu == milieu);
if (exists) {
ui.notifications.warn(`${sheet.item.name} a déjà une rareté ${exists.rarete} en ${milieu} (fréquence: ${exists.frequence})`);
return
}
const rarete = Environnement.getRarete();
const newList = [...list, { milieu, rarete: rarete.name, frequence: rarete.frequence }].sort(Misc.ascending(it => it.milieu))
await sheet.item.update({ 'system.environnement': newList })
}
static async onDeleteMilieu(sheet, event) {
const milieu = EnvironmentSheetHelper.$getEventMilieu(event);
if (milieu != undefined) {
const newList = sheet.item.system.environnement.filter(it => it.milieu != milieu)
.sort(Misc.ascending(it => it.milieu));
await sheet.item.update({ 'system.environnement': newList });
}
}
static async onChange(sheet, event, doMutation) {
const list = sheet.item.system.environnement;
const milieu = EnvironmentSheetHelper.$getEventMilieu(sheet, event);
const updated = list.find(it => it.milieu == milieu);
if (updated) {
doMutation(updated);
const newList = [...list.filter(it => it.milieu != milieu), updated]
.sort(Misc.ascending(it => it.milieu));
await sheet.item.update({ 'system.environnement': newList });
}
}
static $getEventMilieu(sheet, event) {
return sheet.html.find(event.currentTarget)?.parents("div.environnement-milieu").data("milieu");
}
}

View File

@ -1,5 +1,5 @@
export class HtmlUtility{
static _showControlWhen(jQuerySelector, condition) {
static showControlWhen(jQuerySelector, condition) {
if (condition) {
jQuerySelector.show();
}

View File

@ -1,5 +1,4 @@
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
const nomCategorieParade = {

View File

@ -8,18 +8,18 @@ const xp_par_niveau = [5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20,
const niveau_max = xp_par_niveau.length - 10;
/* -------------------------------------------- */
const limitesArchetypes = [
{ "niveau": 0, "nombreMax": 100, "nombre": 0 },
{ "niveau": 1, "nombreMax": 10, "nombre": 0 },
{ "niveau": 2, "nombreMax": 9, "nombre": 0 },
{ "niveau": 3, "nombreMax": 8, "nombre": 0 },
{ "niveau": 4, "nombreMax": 7, "nombre": 0 },
{ "niveau": 5, "nombreMax": 6, "nombre": 0 },
{ "niveau": 6, "nombreMax": 5, "nombre": 0 },
{ "niveau": 7, "nombreMax": 4, "nombre": 0 },
{ "niveau": 8, "nombreMax": 3, "nombre": 0 },
{ "niveau": 9, "nombreMax": 2, "nombre": 0 },
{ "niveau": 10, "nombreMax": 1, "nombre": 0 },
{ "niveau": 11, "nombreMax": 1, "nombre": 0 }
{ "niveau": 0, "nombreMax": 100, "reste": 100 },
{ "niveau": 1, "nombreMax": 10, "reste": 10 },
{ "niveau": 2, "nombreMax": 9, "reste": 9 },
{ "niveau": 3, "nombreMax": 8, "reste": 8 },
{ "niveau": 4, "nombreMax": 7, "reste": 7 },
{ "niveau": 5, "nombreMax": 6, "reste": 6 },
{ "niveau": 6, "nombreMax": 5, "reste": 5 },
{ "niveau": 7, "nombreMax": 4, "reste": 4 },
{ "niveau": 8, "nombreMax": 3, "reste": 3 },
{ "niveau": 9, "nombreMax": 2, "reste": 2 },
{ "niveau": 10, "nombreMax": 1, "reste": 1 },
{ "niveau": 11, "nombreMax": 1, "reste": 1 }
];
/* -------------------------------------------- */
@ -267,23 +267,20 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const archetype = RdDItemCompetence.getLimitesArchetypes();
const computed = duplicate(limitesArchetypes);
competences.map(it => Math.max(0, it.system.niveau_archetype))
.forEach(niveau => {
archetype[niveau] = archetype[niveau] ?? { "niveau": niveau, "nombreMax": 0, "nombre": 0 };
archetype[niveau].nombre = (archetype[niveau]?.nombre ?? 0) + 1;
.filter(n => n > 0)
.forEach(n => {
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0 };
computed[n].reste = computed[n].reste - 1;
});
return archetype;
return computed.filter(it => it.reste > 0 && it.niveau > 0);
}
/* -------------------------------------------- */
static getLimitesArchetypes() {
return duplicate(limitesArchetypes);
}
static triVisible(competences) {
return competences.filter(it => it.system.isVisible)
.sort((a, b) => RdDItemCompetence.compare(a,b))
.sort((a, b) => RdDItemCompetence.compare(a, b))
}
static $positionTri(comp) {
@ -308,7 +305,7 @@ export class RdDItemCompetence extends Item {
return 0;
}
static compare(a,b) {
static compare(a, b) {
const diff = RdDItemCompetence.$positionTri(a) - RdDItemCompetence.$positionTri(b);
return diff ? diff : a.name.localeCompare(b.name);
}

View File

@ -1,67 +0,0 @@
import { EnvironmentSheetHelper } from "./environnement.js";
import { RdDItemSheet } from "./item-sheet.js";
import { RdDUtility } from "./rdd-utility.js";
export class RdDFauneItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "faune" };
static get defaultOptions() {
return EnvironmentSheetHelper.defaultOptions(super.defaultOptions);
}
setPosition(options = {}) {
return EnvironmentSheetHelper.setPosition(this, super.setPosition(options));
}
async getData() {
const formData = await super.getData();
return await EnvironmentSheetHelper.getData(this, formData);
}
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
EnvironmentSheetHelper.activateListeners(this);
html.find("a.linked-actor-delete").click(event => this.onDeleteLinkedActor());
html.find("a.preparer-nourriture").click(event => this.preparerNourriture(event));
html.find("a.manger-nourriture").click(event => this.mangerNourriture(event));
}
async _onDropActor(event, dragData) {
console.log('faune:dropActor', event, dragData)
const linkedActor = fromUuidSync(dragData.uuid);
if (linkedActor?.pack) {
this.item.update({
'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.
<br>Choisissez une créature du compendium pour représenter un élément de faune générique`)
}
}
async onDeleteLinkedActor() {
this.item.update({
'system.actor.pack': '',
'system.actor.id': '',
'system.actor.name': ''
});
}
async preparerNourriture(event) {
if (this.actor) {
await this.actor.preparerNourriture(this.item);
}
}
async mangerNourriture(event) {
if (this.actor) {
await this.actor.mangerNourriture(this.item);
}
}
}

View File

@ -1,25 +0,0 @@
import { EnvironmentSheetHelper } from "./environnement.js";
import { RdDItemSheet } from "./item-sheet.js";
export class RdDHerbeItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "herbe" };
static get defaultOptions() {
return EnvironmentSheetHelper.defaultOptions(super.defaultOptions);
}
setPosition(options = {}) {
return EnvironmentSheetHelper.setPosition(this, super.setPosition(options));
}
async getData() {
const formData = await super.getData();
return await EnvironmentSheetHelper.getData(this, formData);
}
activateListeners(html) {
super.activateListeners(html);
EnvironmentSheetHelper.activateListeners(this);
}
}

View File

@ -1,25 +0,0 @@
import { EnvironmentSheetHelper } from "./environnement.js";
import { RdDItemSheet } from "./item-sheet.js";
export class RdDIngredientItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "ingredient" };
static get defaultOptions() {
return EnvironmentSheetHelper.defaultOptions(super.defaultOptions);
}
setPosition(options = {}) {
return EnvironmentSheetHelper.setPosition(this, super.setPosition(options));
}
async getData() {
const formData = await super.getData();
return await EnvironmentSheetHelper.getData(this, formData);
}
activateListeners(html) {
super.activateListeners(html);
EnvironmentSheetHelper.activateListeners(this);
}
}

View File

@ -2,27 +2,28 @@ import { Misc } from "./misc.js";
import { LOG_HEAD } from "./constants.js";
const MONNAIE_ETAIN = {
name: "Etain (1 denier)", type: 'monnaie',
name: "Denier (étain)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp",
system: { quantite: 0, cout: 0.01, encombrement: 0.001, description: "" }
};
const MONNAIE_BRONZE = {
name: "Bronze (10 deniers)", type: 'monnaie',
name: "Sou (bronze)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp",
system: { quantite: 0, cout: 0.10, encombrement: 0.002, description: "" }
};
const MONNAIE_ARGENT = {
name: "Argent (1 sol)", type: 'monnaie',
name: "Sol (argent)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp",
system: { quantite: 0, cout: 1, encombrement: 0.003, description: "" }
};
const MONNAIE_OR = {
name: "Or (10 sols)", type: 'monnaie',
name: "Dragon (or)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp",
system: { quantite: 0, cout: 10, encombrement: 0.004, description: "" }
};
const MONNAIES_STANDARD = [MONNAIE_ETAIN, MONNAIE_BRONZE, MONNAIE_ARGENT, MONNAIE_OR];
const VALEUR_DENIERS = sols => Math.max(Math.floor((sols ?? 0) * 100), 0);
export class Monnaie {
@ -40,15 +41,11 @@ export class Monnaie {
}
static deValeur(monnaie, valeur) {
return Monnaie.valEntiere(valeur) == Monnaie.valEntiere(monnaie.system.cout)
}
static valEntiere(sols) {
return Math.max(Math.floor((sols??0)*100), 0);
return VALEUR_DENIERS(valeur) == VALEUR_DENIERS(monnaie.system.cout)
}
static triValeurEntiere() {
return Misc.ascending(item => Monnaie.valEntiere(item.system.cout))
return Misc.ascending(item => VALEUR_DENIERS(item.system.cout))
}
static async creerMonnaiesStandard(actor) {
@ -65,29 +62,45 @@ export class Monnaie {
return deniers;
}
static getFortune(monnaies) {
return (monnaies??[])
.map(m => Number(m.system.cout) * Number(m.system.quantite))
.reduce(Misc.sum(), 0);
}
static async optimiserFortune(actor, fortune) {
let resteEnDeniers = Math.round(fortune*100);
let resteEnDeniers = Math.round(fortune * 100);
let monnaies = actor.itemTypes['monnaie'];
let updates = [];
let parValeur = Misc.classifyFirst(monnaies, it => Monnaie.valEntiere(it.system.cout));
for (let valeurDeniers of [1000, 100, 10, 1]) {
Monnaie.validerMonnaies(monnaies, actor);
let parValeur = Misc.classifyFirst(monnaies, it => VALEUR_DENIERS(it.system.cout));
for (let valeurDeniers of [1000, 100, 10, 1]) {
const itemPiece = parValeur[valeurDeniers];
if (itemPiece) {
const quantite = Math.floor(resteEnDeniers / valeurDeniers);
if (quantite != itemPiece.system.quantite) {
updates.push({ _id: parValeur[valeurDeniers].id, 'system.quantite': quantite });
}
resteEnDeniers -= quantite*valeurDeniers;
resteEnDeniers -= quantite * valeurDeniers;
}
}
console.log('Monnaie.optimiserFortune', actor.name, 'total', fortune, 'parValeur', parValeur, 'updates', updates, 'reste', resteEnDeniers);
if (updates.length > 0) {
await actor.updateEmbeddedDocuments('Item', updates);
}
if (resteEnDeniers > 0){
if (resteEnDeniers > 0) {
// créer le reste en deniers fortune en deniers
await Monnaie.creerMonnaiesDeniers(actor, resteEnDeniers);
}
}
static validerMonnaies(monnaies, actor = undefined) {
monnaies.filter(it => VALEUR_DENIERS(it.system.cout) == 0)
.map(it => `La monnaie ${it.name} de l'acteur ${actor?.name ?? 'sélectionné'} a une valeur de 0!`)
.forEach(message => {
ui.notifications.warn(message);
console.warn(message);
});
}
}

View File

@ -10,6 +10,7 @@ import { SYSTEM_RDD } from "./constants.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Misc } from "./misc.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
/**
* Extend the basic ItemSheet for RdD specific items
@ -60,13 +61,13 @@ export class RdDItemSheet extends ItemSheet {
buttons.unshift({
class: "vendre",
icon: "fas fa-comments-dollar",
onclick: ev => this.item.proposerVente()
onclick: ev => this.item.proposerVente(1)
});
}
buttons.unshift({
class: "montrer",
icon: "fas fa-comment",
onclick: ev => this.item.postItem()
onclick: ev => this.item.postItemToChat()
});
return buttons
}
@ -100,7 +101,7 @@ export class RdDItemSheet extends ItemSheet {
isSoins: false,
description: await TextEditor.enrichHTML(this.item.system.description, { async: true }),
descriptionmj: await TextEditor.enrichHTML(this.item.system.descriptionmj, { async: true }),
isComestible: this.item.isComestible()
isComestible: this.item.getUtilisationCuisine()
}
const competences = await SystemCompendiums.getCompetences(this.actor?.type);
@ -135,11 +136,7 @@ export class RdDItemSheet extends ItemSheet {
RdDGemme.calculDataDerivees(this.item);
}
if (this.item.type == 'potion') {
if (this.dateUpdated) {
formData.system.prdate = this.dateUpdated;
this.dateUpdated = undefined;
}
await RdDHerbes.updatePotionData(formData);
await RdDHerbes.addPotionFormData(formData);
}
if (formData.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) {
formData.isIngredientPotionBase = true;
@ -159,10 +156,8 @@ export class RdDItemSheet extends ItemSheet {
super.activateListeners(html);
this.html = html;
let itemSheetDialog = this;
HtmlUtility._showControlWhen(this.html.find(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs') || game.user.isGM || !this.item.isOwned);
HtmlUtility._showControlWhen(this.html.find(".item-magique"), this.item.isMagique());
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs') || game.user.isGM || !this.item.isOwned);
HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique());
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
@ -178,11 +173,21 @@ export class RdDItemSheet extends ItemSheet {
RdDUtility.checkThanatosXP(this.item.name);
}
});
this.html.find(".item-cout input[name='system.cout']").change(event => {
if (this.item.isMonnaie()) {
const value = event.currentTarget.value;
if (Number(value) == 0) {
ui.notifications.error(`${this.actor?.name ?? 'Monnaie'}: La monnaie ${this.item.name} a maintenant une valeur de 0, et ne peut plus être utilisée pour payer!`)
}
}
})
this.html.find('.enchanteDate').change((event) => {
let jour = Number(this.html.find('[name="splitDate.day"]').val());
let mois = this.html.find('[name="splitDate.month"]').val();
this.dateUpdated = game.system.rdd.calendrier.getIndexFromDate(jour, mois);
this.html.find('.date-enchantement').change((event) => {
const jour = Number(this.html.find('input.date-enchantement[name="enchantement.jour"]').val());
const mois = RdDTimestamp.definition(this.html.find('select.date-enchantement[name="enchantement.mois"]').val());
const indexDate = game.system.rdd.calendrier.getIndexFromDate(jour, mois.heure);
this.item.update({ 'system.prdate': indexDate });
console.warn(`Date d'enchantement modifiée ${jour}/${mois.heure}: ${indexDate}`)
});
this.html.find('.creer-tache-livre').click((event) => this._getEventActor(event).creerTacheDepuisLivre(this.item));
@ -201,12 +206,17 @@ export class RdDItemSheet extends ItemSheet {
}
});
this.html.find('.item-split').click(async event => RdDSheetUtility.splitItem(RdDSheetUtility.getItem(event, this.actor), this.actor, async () => itemSheetDialog.render(true)));
this.html.find('.item-split').click(async event => RdDSheetUtility.splitItem(RdDSheetUtility.getItem(event, this.actor), this.actor, async () => this.render(true)));
this.html.find('.item-edit').click(async event => RdDSheetUtility.getItem(event, this.actor)?.sheet.render(true));
this.html.find('.item-delete').click(async event => RdDUtility.confirmerSuppressionItem(this, RdDSheetUtility.getItem(event, this.actor)));
this.html.find('.item-delete').click(async event => RdDUtility.confirmActorItemDelete(this, RdDSheetUtility.getItem(event, this.actor)));
this.html.find('.item-vendre').click(async event => RdDSheetUtility.getItem(event, this.actor)?.proposerVente());
this.html.find('.item-montrer').click(async event => RdDSheetUtility.getItem(event, this.actor)?.postItem());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor, async () => itemSheetDialog.render(true)));
this.html.find('.item-montrer').click(async event => RdDSheetUtility.getItem(event, this.actor)?.postItemToChat());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor, async () => this.render(true)));
const updateItemTimestamp = (path, timestamp) => this.item.update({ [path]: duplicate(timestamp) })
RdDTimestamp.handleTimestampEditor(this.html, 'system.temporel.debut', updateItemTimestamp);
RdDTimestamp.handleTimestampEditor(this.html, 'system.temporel.fin', updateItemTimestamp);
}
_getEventActor(event) {
@ -215,6 +225,7 @@ export class RdDItemSheet extends ItemSheet {
return actor;
}
/* -------------------------------------------- */
async _onSelectCategorie(event) {
event.preventDefault();

View File

@ -1,4 +1,18 @@
export class RdDItemTache extends Item {
const BASE_TACHE_SOIN_BLESSURE = { type: "tache", img: 'systems/foundryvtt-reve-de-dragon/icons/competence_chirurgie.webp', system: { carac: "dexterite", competence: "Chirurgie", periodicite: "1 round", fatigue: 0, } }
const TACHES_SOIN_BLESSURE = {
'critique': { name: 'Blessure critique', system: { difficulte: -6, points_de_tache: 6 } },
'grave': { name: 'Blessure grave', system: { difficulte: -4, points_de_tache: 4 } },
'legere': { name: 'Blessure légère', system: { difficulte: -2, points_de_tache: 2 } },
}
export class RdDItemTache extends Item {
static prepareTacheSoin(gravite) {
const blessure = TACHES_SOIN_BLESSURE[gravite]
if (blessure) {
return mergeObject(duplicate(BASE_TACHE_SOIN_BLESSURE), blessure)
}
ui.notifications.warn(`Pas de tâche de soins pour une blessure ${gravite}`)
return undefined;
}
}

View File

@ -2,16 +2,20 @@ import { DialogItemVente } from "./dialog-item-vente.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDUtility } from "./rdd-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDRaretes } from "./item/raretes.js";
const typesObjetsInventaire = [
const typesInventaireMateriel = [
"arme",
"armure",
"conteneur",
"faune",
"gemme",
"herbe",
"plante",
"ingredient",
"faune",
"livre",
"monnaie",
"munition",
@ -19,11 +23,18 @@ const typesObjetsInventaire = [
"objet",
"potion",
]
const typesInventaire = {
materiel: typesInventaireMateriel,
all: ['service'].concat(typesInventaireMateriel),
}
const typesObjetsOeuvres = ["oeuvre", "recettecuisine", "musique", "chant", "danse", "jeu"]
const typesObjetsDraconiques = ["queue", "ombre", "souffle", "tete", "signedraconique", "sortreserve", "rencontre"]
const typesObjetsConnaissance = ["meditation", "recettealchimique", "sort"]
const typesObjetsEffet = ["possession", "poison", "maladie"]
const typesObjetsCompetence = ["competence", "competencecreature"]
const typesObjetsTemporels = ["poison", "maladie", "queue", "ombre", "souffle", "signedraconique", "rencontre"]
const typesEnvironnement = typesInventaireMateriel;
const encBrin = 0.00005; // un brin = 1 décigramme = 1/10g = 1/10000kg = 1/20000 enc
const encPepin = 0.0007; /* un pépin de gemme = 1/10 cm3 = 1/1000 l = 3.5/1000 kg = 7/2000 kg = 7/1000 enc
densité 3.5 (~2.3 à 4, parfois plus) -- https://www.juwelo.fr/guide-des-pierres/faits-et-chiffres/
@ -57,6 +68,7 @@ export const defaultItemImg = {
poison: "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/venin.webp",
oeuvre: "systems/foundryvtt-reve-de-dragon/icons/competence_comedie.webp",
nourritureboisson: "systems/foundryvtt-reve-de-dragon/icons/objets/provision_crue.webp",
service: "systems/foundryvtt-reve-de-dragon/icons/services/lit.webp",
signedraconique: "systems/foundryvtt-reve-de-dragon/icons/tmr/signe_draconique.webp",
gemme: "systems/foundryvtt-reve-de-dragon/icons/gemmes/almaze.webp",
possession: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
@ -69,11 +81,7 @@ export const defaultItemImg = {
export class RdDItem extends Item {
static getDefaultImg(itemType) {
return defaultItemImg[itemType];
}
static isItemInventaire(newLocal) {
return typesObjetsInventaire.includes(newLocal.type);
return game.system.rdd.itemClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
}
static isFieldInventaireModifiable(type, field) {
@ -92,6 +100,48 @@ export class RdDItem extends Item {
return true;
}
static async getCorrespondingItem(itemRef) {
if (itemRef.pack) {
return await SystemCompendiums.loadDocument(itemRef)
}
return game.items.get(itemRef.id ?? itemRef._id);
}
static getItemTypesInventaire(mode = 'materiel') {
return typesInventaire[mode ?? 'materiel']
}
static getItemTypesDraconiques() {
return typesObjetsDraconiques;
}
static getItemTypesEnvironnement() {
return typesEnvironnement;
}
static getTypesOeuvres() {
return typesObjetsOeuvres
}
constructor(docData, context = {}) {
if (!context.rdd?.ready) {
mergeObject(context, { rdd: { ready: true } });
const ItemConstructor = game.system.rdd.itemClasses[docData.type];
if (ItemConstructor) {
if (!docData.img) {
docData.img = ItemConstructor.defaultIcon;
}
return new ItemConstructor(docData, context);
}
}
if (!docData.img) {
docData.img = RdDItem.getDefaultImg(docData.type);
}
super(docData, context);
}
static get defaultIcon() {
return undefined;
}
getUniteQuantite() {
switch (this.type) {
case "monnaie": return "(Pièces)"
@ -107,47 +157,56 @@ export class RdDItem extends Item {
return '';
}
constructor(itemData, context) {
if (!itemData.img) {
itemData.img = RdDItem.getDefaultImg(itemData.type);
isCompetencePersonnage() { return this.type == 'competence' }
isCompetenceCreature() { return this.type == 'competencecreature' }
isConteneur() { return this.type == 'conteneur'; }
isMonnaie() { return this.type == 'monnaie'; }
isPotion() { return this.type == 'potion'; }
isNourritureBoisson() { return this.type == 'nourritureboisson'; }
isService() { return this.type == 'service'; }
isCompetence() { return typesObjetsCompetence.includes(this.type) }
isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
isDraconique() { return RdDItem.getItemTypesDraconiques().includes(this.type) }
isEffet() { return typesObjetsEffet.includes(this.type) }
isConnaissance() { return typesObjetsConnaissance.includes(this.type) }
isInventaire(mode = 'materiel') { return RdDItem.getItemTypesInventaire(mode).includes(this.type); }
isBoisson() { return this.isNourritureBoisson() && this.system.boisson; }
isAlcool() { return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise; }
isHerbeAPotion() { return this.type == 'herbe' && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
isPresentDansMilieux(milieux) {
return this.getEnvironnements(milieux).length > 0
}
getEnvironnements(milieux = undefined) {
const environnements = this.isInventaire() ? this.system.environnement : undefined;
if (milieux == undefined || !environnements) {
return environnements ?? [];
}
super(itemData, context);
return environnements.filter(env => milieux.includes(env.milieu))
}
static getItemTypesInventaire() {
return typesObjetsInventaire
getMilieux() {
return this.getEnvironnements().map(env => env.milieu);
}
static getTypesOeuvres() {
return typesObjetsOeuvres
getRaretes(milieux = undefined) {
if (this.isInventaire()) {
const raretes = this.getEnvironnements(milieux).map(env => RdDRaretes.byCode(env.rarete));
if (milieux == undefined && raretes.length == 0) {
return [RdDRaretes.rareteFrequente()];
}
return raretes;
}
return [RdDRaretes.rareteEgale()];
}
isCompetencePersonnage() {
return this.type == 'competence'
}
isCompetenceCreature() {
return this.type == 'competencecreature'
}
isCompetence() {
return typesObjetsCompetence.includes(this.type)
}
isInventaire() {
return RdDItem.isItemInventaire(this)
}
isOeuvre() {
return typesObjetsOeuvres.includes(this.type)
}
isDraconique() {
return typesObjetsDraconiques.includes(this.type)
}
isEffet() {
return typesObjetsEffet.includes(this.type)
}
isConnaissance() {
return typesObjetsConnaissance.includes(this.type)
}
isConteneur() {
return this.type == 'conteneur';
getFrequence(milieux = undefined) {
const frequences = this.getEnvironnements(milieux).map(it => it.frequence);
return frequences.length == 0 ? 0 : Math.max(...frequences);
}
getItemGroup() {
@ -160,43 +219,79 @@ export class RdDItem extends Item {
return "autres";
}
isConteneurNonVide() {
return this.isConteneur() && (this.system.contenu?.length ?? 0) > 0;
isConteneurNonVide() { return this.isConteneur() && (this.system.contenu?.length ?? 0) > 0; }
isConteneurVide() { return this.isConteneur() && (this.system.contenu?.length ?? 0) == 0; }
isVideOuNonConteneur() { return !this.isConteneur() || (this.system.contenu?.length ?? 0) == 0; }
isFinPeriode(oldTimestamp, newTimestamp) {
if (!this.isTemporel()) {
return false;
}
const finPeriode = new RdDTimestamp(this.system.temporel.fin);
return oldTimestamp.compare(finPeriode) < 0 && finPeriode.compare(newTimestamp) <= 0
}
isConteneurVide() {
return this.isConteneur() && (this.system.contenu?.length ?? 0) == 0;
async onCreateItemTemporel(actor) {
if (this.isTemporel()) {
const timestampDebut = game.system.rdd.calendrier.timestamp;
const timestampFin = await this.calculerFinPeriodeTemporel(timestampDebut);
await actor.updateEmbeddedDocuments('Item', [{
_id: this.id,
'system.temporel.debut': duplicate(timestampDebut),
'system.temporel.fin': duplicate(timestampFin),
}])
}
}
isVideOuNonConteneur() {
return !this.isConteneur() || (this.system.contenu?.length ?? 0) == 0;
async calculerFinPeriodeTemporel(timestampDebut) {
return timestampDebut;
}
isNourritureBoisson() {
return this.type == 'nourritureboisson';
async onFinPeriodeTemporel(oldTimestamp, newTimestamp) {
if (this.isTemporel() && this.actor) {
await this.onFinPeriode(oldTimestamp, newTimestamp);
}
}
isComestible() {
async onFinPeriode(oldTimestamp, newTimestamp) {
console.log(`${this.actor.name}: l'objet ${this.name} a expiré et été supprimé`);
await this.actor?.deleteEmbeddedDocuments('Item', [this.id]);
}
getUtilisation() {
switch (this.type) {
case 'nourritureboisson': return 'pret';
case 'herbe':
return this.system.categorie == 'Cuisine' && this.system.sust > 0 ? 'brut' : '';
case 'faune':
return this.system.sust > 0 ? 'brut' : '';
case 'potion':
switch (this.system.categorie) {
case 'Alchimie': case 'AlchimieEnchante': case 'AlchimieAutre': return 'alchimie'
case 'Cuisine': return 'cuisine'
case 'Remede': case 'Repos': case 'ReposEnchante': case 'Soin': case 'SoinEnchante': return 'soins'
}
return '';
case 'nourritureboisson': return 'cuisine';
case 'herbe': case 'faune': case 'ingredient': case 'plante':
switch (this.system.categorie) {
case 'Cuisine': return 'cuisine';
case 'Toxique': case 'Poison': return 'poison';
case 'Alchimie': return 'alchimie'
case 'Soin': case 'Repos': return 'soins'
}
return this.system.sust > 0 ? 'cuisine' : '';
}
return '';
}
isAlcool() {
return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise;
getUtilisationCuisine() {
if (this.getUtilisation() == 'cuisine') {
switch (this.type) {
case 'nourritureboisson':
return 'pret';
case 'herbe': case 'faune': case 'ingredient': case 'plante':
return 'brut';
}
}
return '';
}
isHerbeAPotion() {
return this.type == 'herbe' && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos');
}
isPotion() {
return this.type == 'potion';
}
isCristalAlchimique() {
return this.type == 'objet' && Grammar.toLowerCaseNoAccent(this.name) == 'cristal alchimique' && this.system.quantite > 0;
}
@ -205,16 +300,22 @@ export class RdDItem extends Item {
return this.system.magique
}
isItemCommerce() {
return this.parent?.type == 'commerce';
}
getQuantite() {
return Math.round(this.isConteneur() ? 1 : (this.system.quantite ?? 0))
return this.isService() ? undefined : Math.round(this.system.quantite ?? 0)
}
getEncTotal() {
return this.getEnc() * this.getQuantite();
return (this.isService() ? 0 : this.getQuantite()) * this.getEnc();
}
getEnc() {
switch (this.type) {
case 'service':
return 0;
case 'herbe':
return this.getEncHerbe();
case 'gemme':
@ -233,13 +334,21 @@ export class RdDItem extends Item {
}
valeurTotale() {
return this.getQuantite() * this.valeur()
return (this.isService() ? 1 : this.getQuantite()) * this.valeur()
}
valeur() {
return this.system.cout ?? 0
}
calculerPrixCommercant() {
if (this.isItemCommerce()) {
// appliquer le pourcentage
return this.parent.calculerPrix(this);
}
return this.system.cout;
}
prepareDerivedData() {
super.prepareDerivedData();
if (this.isInventaire()) {
@ -268,7 +377,7 @@ export class RdDItem extends Item {
}
if (this.actor?.isPersonnage()) {
const warn = options.warnIfNot;
if (this.isComestible() == 'brut') {
if (this.getUtilisationCuisine() == 'brut') {
return 'Utiliser';
}
switch (this.type) {
@ -281,7 +390,7 @@ export class RdDItem extends Item {
}
return undefined;
}
/* -------------------------------------------- */
async actionPrincipale(actor, onActionItem = async () => { }) {
if (!this.getActionPrincipale()) {
@ -317,7 +426,7 @@ export class RdDItem extends Item {
}
async onCreateDecoupeComestible(actor) {
if (actor && this.isComestible() == 'brut' && this.system.sust != 1) {
if (actor && this.getUtilisationCuisine() == 'brut' && this.system.sust != 1) {
if (this.system.sust < 1) {
await actor.updateEmbeddedDocuments('Item', [{
_id: this.id,
@ -338,7 +447,7 @@ export class RdDItem extends Item {
}
async empiler(item) {
if (this.isComestible() == 'brut') {
if (this.getUtilisationCuisine() == 'brut') {
const sust = this.system.sust + item.system.sust;
const encombrement = this.system.encombrement + item.system.encombrement;
await this.update({
@ -352,7 +461,7 @@ export class RdDItem extends Item {
await item.delete();
}
async quantiteIncDec(nombre, options = { diminuerQuantite: true, supprimerSiZero: false }) {
async quantiteIncDec(nombre, options = { supprimerSiZero: false }) {
const quantite = Number(this.system.quantite ?? -1);
if (quantite >= 0) {
const reste = Math.max(quantite + Number(nombre), 0);
@ -389,8 +498,8 @@ export class RdDItem extends Item {
return [false, `Impossible de regrouper ${this.name} avec ${other.name}`];
}
else {
const excludedProperties = ['quantite', 'cout', 'encTotal'];
if (this.isComestible()) {
const excludedProperties = ['quantite', 'cout', 'encTotal', 'environnement', 'contenu'];
if (this.getUtilisationCuisine()) {
excludedProperties.push('sust', 'encombrement');
}
let differences = Object.entries(this.system)
@ -407,54 +516,63 @@ export class RdDItem extends Item {
return [true, undefined];
}
async proposerVente() {
async proposerVente(quantiteMax = undefined) {
console.log(this);
if (this.isConteneurNonVide()) {
ui.notifications.warn(`Votre ${this.name} n'est pas vide, pas possible de le proposer`);
return;
}
await DialogItemVente.display(this, async (vente) => {
vente["properties"] = this.getProprietes();
if (vente.isOwned) {
if (vente.quantiteNbLots * vente.tailleLot > vente.quantiteMax) {
ui.notifications.warn(`Vous avez ${vente.quantiteMax} ${vente.item.name}, ce n'est pas suffisant pour vendre ${vente.quantiteNbLots} de ${vente.tailleLot}`)
return;
await DialogItemVente.display({
item: this,
quantiteMax,
callback: async (vente) => {
vente["properties"] = this.getProprietes();
if (vente.isOwned) {
if (vente.quantiteNbLots * vente.tailleLot > vente.quantiteMax) {
ui.notifications.warn(`Vous avez ${vente.quantiteMax} ${vente.item.name}, ce n'est pas suffisant pour vendre ${vente.quantiteNbLots} de ${vente.tailleLot}`)
return;
}
}
}
vente.jsondata = JSON.stringify(vente.item);
vente.jsondata = JSON.stringify(vente.item);
console.log(vente);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente);
ChatMessage.create(RdDUtility.chatDataSetup(html));
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente);
ChatMessage.create(RdDUtility.chatDataSetup(html));
}
});
}
/* -------------------------------------------- */
getProprietes() {
return this[`_${this.type}ChatData`]().filter(it => it != undefined);
if (this[`_${this.type}ChatData`]) {
return this[`_${this.type}ChatData`]().filter(it => it != undefined);
}
return [];
}
/* -------------------------------------------- */
async postItem(modeOverride) {
async postItemToChat(modeOverride) {
console.log(this);
let chatData = duplicate(this);
chatData["properties"] = this.getProprietes();
if (this.actor) {
chatData.actor = { id: this.actor.id };
let chatData = {
doctype: 'Item',
id: this.id,
type: this.type,
img: this.img,
pack: this.pack,
name: this.name,
actor: this.actor ? { id: this.actor.id } : undefined,
system: { description: this.system.description },
properties: this.getProprietes(),
}
// JSON object for easy creation
chatData.jsondata = JSON.stringify(
{
compendium: "postedItem",
payload: chatData,
});
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-item.html', chatData).then(html => {
renderTemplate(this.getChatItemTemplate(), chatData).then(html => {
let chatOptions = RdDUtility.chatDataSetup(html, modeOverride);
ChatMessage.create(chatOptions)
});
}
getChatItemTemplate() {
return 'systems/foundryvtt-reve-de-dragon/templates/post-item.html';
}
static propertyIfDefined(name, val, condition = true) {
return condition ? `<b>${name}</b>: ${val}` : undefined;
}
@ -703,5 +821,4 @@ export class RdDItem extends Item {
...this._inventaireTemplateChatData()
]
}
}

47
module/item/maladie.js Normal file
View File

@ -0,0 +1,47 @@
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDTimestamp } from "../rdd-timestamp.js";
export class RdDItemMaladie extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/maladie.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite);
}
async onFinPeriode(oldTimestamp, newTimestamp) {
await RdDItemMaladie.notifierMaladiePoison(this, oldTimestamp, newTimestamp)
}
static async notifierMaladiePoison(mal, oldTimestamp, newTimestamp) {
if (mal.actor) {
const souffrance = mal.system.identifie
? `de ${mal.name}`
: `d'un mal inconnu`
ChatMessage.create({ content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !` });
mal.postItemToChat('gmroll');
await RdDItemMaladie.prolongerPeriode(mal,oldTimestamp, newTimestamp);
}
}
static async prolongerPeriode(mal, oldTimestamp, newTimestamp) {
if (mal.actor) {
// TODO: déterminer le nombre de périodes écoulées
console.log(`${mal.actor.name}: le mal ${mal.name} a atteint la fin de sa période et été prolongé`);
const current = newTimestamp;
const finPeriode = new RdDTimestamp(mal.system.temporel.fin)
const periodeSuivante = (finPeriode.compare(current) > 0 ? finPeriode : current);
const timestampFin = await mal.calculerFinPeriodeTemporel(periodeSuivante);
await mal.actor.updateEmbeddedDocuments('Item', [{
_id: mal.id,
'system.temporel.fin': duplicate(timestampFin),
}])
}
}
}

11
module/item/ombre.js Normal file
View File

@ -0,0 +1,11 @@
import { RdDItem } from "../item.js";
export class RdDItemOmbre extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

17
module/item/poison.js Normal file
View File

@ -0,0 +1,17 @@
import { RdDItem } from "../item.js";
import { RdDItemMaladie } from "./maladie.js";
export class RdDItemPoison extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/venin.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite) ;
}
async onFinPeriode(oldTimestamp, newTimestamp) {
RdDItemMaladie.notifierMaladiePoison(this, oldTimestamp, newTimestamp)
}
}

13
module/item/queue.js Normal file
View File

@ -0,0 +1,13 @@
import { RdDItem } from "../item.js";
export class RdDItemQueue extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

31
module/item/raretes.js Normal file
View File

@ -0,0 +1,31 @@
const RARETE_COMMUNE = { code: 'Commune', label: 'Commune', frequence: 54, min: 27, max: 108 };
const RARETE_FREQUENTE = { code: 'Frequente', label: 'Fréquente', frequence: 18, min: 9, max: 36 };
const RARETE_RARE = { code: 'Rare', label: 'Rare', frequence: 6, min: 3, max: 12 };
const RARETE_RARISSIME = { code: 'Rarissime', label: 'Rarissime', frequence: 2, min: 1, max: 4 };
const RARETE_INEXISTANT = { code: 'Inexistant', label: 'Inexistant', frequence: 0, min: 0, max: 0 };
const RARETE_EGALE = { code: 'eqal', label: 'Egal', frequence: 1, min: 1, max: 1 };
const RARETES = [
RARETE_COMMUNE,
RARETE_FREQUENTE,
RARETE_RARE,
RARETE_RARISSIME,
RARETE_INEXISTANT,
]
export class RdDRaretes {
static rareteFrequente() { return RARETE_FREQUENTE; }
static rareteEgale() { return RARETE_EGALE; }
static raretes() { return RARETES; }
static byCode(code = undefined) {
return RARETES.find(it => it.code == code) ?? RARETE_FREQUENTE;
}
static getChamp(rarete, field = undefined) {
return RdDRaretes.byCode(rarete)[field ?? 'frequence'];
}
}

View File

@ -1,4 +1,5 @@
import { EffetsRencontre } from "./effets-rencontres.js";
import { EffetsRencontre } from "../effets-rencontres.js";
import { RdDItem } from "../item.js";
const tableEffets = [
{ code: "messager", resultat: "succes", description: "Envoie un message à (force) cases", method: EffetsRencontre.messager },
@ -36,7 +37,11 @@ const tableEffets = [
// { code: "epart-souffle", resultat: "echec", description: "Souffle de dragon sur échec particulier" },
];
export class RdDRencontre {
export class RdDRencontre extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp";
}
static getEffetsSucces() { return RdDRencontre.getEffets("succes"); }
static getEffetsEchec() { return RdDRencontre.getEffets("echec"); }
@ -68,4 +73,8 @@ export class RdDRencontre {
}
}
async calculerFinPeriodeTemporel(debut) {
return await debut.nouvelleHeure().addHeures(12);
}
}

17
module/item/service.js Normal file
View File

@ -0,0 +1,17 @@
import { RdDItem } from "../item.js";
export class RdDItemService extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/services/lit.webp";
}
isService() { return true; }
getProprietes() {
return [
RdDItem.propertyIfDefined('Qualité', this.system.qualite, this.system.qualite != 0),
RdDItem.propertyIfDefined('Moral', 'Situation heureuse', this.system.moral),
RdDItem.propertyIfDefined('Coût', `${this.calculerPrixCommercant()} sols`),
];
}
}

View File

@ -0,0 +1,120 @@
import { HtmlUtility } from "../html-utility.js";
import { RdDItemSheet } from "../item-sheet.js";
import { Misc } from "../misc.js";
import { RdDRaretes } from "./raretes.js";
const TYPE_ITEMS_NATURELS = ["faune", "herbe", "plante", "ingredient"];
export class RdDItemInventaireSheet extends RdDItemSheet {
static get defaultOptions() {
return mergeObject(RdDItemSheet.defaultOptions, {
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "informations" }]
});
}
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetBody = this.element.find(".sheet-body");
sheetBody.css("height", position.height - sheetHeader[0].clientHeight)
return position;
}
async getData() {
const formData = await super.getData();
return mergeObject(formData, {
milieux: await game.system.rdd.environnement.autresMilieux(this.item)
});
}
activateListeners(html) {
super.activateListeners(html);
HtmlUtility.showControlWhen(this.html.find("div.description-milieu"), TYPE_ITEMS_NATURELS.includes(this.item.type));
if (!this.options.editable) return;
this.html.find("a.preparer-nourriture").click(event => this.preparerNourriture(event));
this.html.find("a.manger-nourriture").click(event => this.mangerNourriture(event));
this.html.find("input.input-selection-milieu").keypress(event => {
if (event.keyCode == '13') {
this.onAddMilieu(event);
}
event.stopPropagation();
})
this.html.find("a.milieu-add").click(event => this.onAddMilieu(event));
this.html.find("div.environnement-milieu a.milieu-delete").click(event => this.onDeleteMilieu(event));
this.html.find("div.environnement-milieu select.environnement-rarete").change(event => this.onChange(event,
updated => this.$changeRarete(event, updated)));
this.html.find("div.environnement-milieu input[name='environnement-frequence']").change(event => this.onChange(event,
updated => this.$changeFrequence(event, updated)));
}
async preparerNourriture(event) {
if (this.actor && this.item.getUtilisationCuisine() == 'brut') {
await this.actor.preparerNourriture(this.item);
}
}
async mangerNourriture(event) {
if (this.actor && this.item.getUtilisation() == 'cuisine') {
await this.actor.mangerNourriture(this.item);
}
}
$changeFrequence(event, updated) {
updated.frequence = Number(this.html.find(event.currentTarget).val());
}
$changeRarete(event, updated) {
const code = this.html.find(event.currentTarget).val();
const rarete = RdDRaretes.byCode(code);
updated.rarete = rarete.code;
updated.frequence = rarete.frequence;
}
async onAddMilieu(event) {
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}`);
return
}
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 newList = [added, ...list].sort(Misc.ascending(it => it.milieu))
await this.item.update({ 'system.environnement': newList })
}
async onDeleteMilieu(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 });
}
}
async onChange(event, doMutation) {
const list = this.item.system.environnement;
const milieu = this.$getEventMilieu(event);
const updated = list.find(it => it.milieu == milieu);
if (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 });
}
}
$getEventMilieu(event) {
return this.html.find(event.currentTarget)?.parents("div.environnement-milieu").data("milieu");
}
}

View File

@ -1,8 +1,9 @@
import { RdDItemSheet } from "./item-sheet.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { RdDUtility } from "./rdd-utility.js";
import { RdDBaseActorSheet } from "../actor/base-actor-sheet.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDConteneurItemSheet extends RdDItemSheet {
export class RdDConteneurItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "conteneur" };
@ -27,7 +28,7 @@ export class RdDConteneurItemSheet extends RdDItemSheet {
/* -------------------------------------------- */
prepareConteneurData(formData) {
RdDUtility.filterEquipementParType(formData, this.actor.itemTypes);
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
@ -51,7 +52,7 @@ export class RdDConteneurItemSheet extends RdDItemSheet {
async _onDropItem(event, dragData) {
if (this.actor) {
const dropParams = RdDSheetUtility.prepareItemDropParameters(this.item.id, this.actor, dragData, this.objetVersConteneur);
const dropParams = await RdDSheetUtility.prepareItemDropParameters(this.item.id, this.actor, dragData, this.objetVersConteneur);
await this.actor.processDropItem(dropParams);
await this.render(true);
}

View File

@ -0,0 +1,38 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDFauneItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "faune" };
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
html.find("a.linked-actor-delete").click(event => this.onDeleteLinkedActor());
}
async _onDropActor(event, dragData) {
console.log('faune:dropActor', event, dragData)
const linkedActor = fromUuidSync(dragData.uuid);
if (linkedActor?.pack) {
this.item.update({
'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.
<br>Choisissez une créature du compendium pour représenter un élément de faune générique`)
}
}
async onDeleteLinkedActor() {
this.item.update({
'system.actor.pack': '',
'system.actor.id': '',
'system.actor.name': ''
});
}
}

View File

@ -0,0 +1,6 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDHerbeItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "herbe" };
}

View File

@ -0,0 +1,5 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDIngredientItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "ingredient" };
}

View File

@ -0,0 +1,7 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDPlanteItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "plante" };
}

View File

@ -1,5 +1,5 @@
import { RdDRencontre } from "./item-rencontre.js";
import { RdDItemSheet } from "./item-sheet.js";
import { RdDRencontre } from "./rencontre.js";
import { RdDItemSheet } from "../item-sheet.js";
export class RdDRencontreItemSheet extends RdDItemSheet {
@ -23,15 +23,8 @@ export class RdDRencontreItemSheet extends RdDItemSheet {
/* -------------------------------------------- */
async getData() {
const formData = duplicate(this.item);
const formData = await super.getData();
mergeObject(formData, {
title: formData.name,
isGM: game.user.isGM,
owner: this.actor?.isOwner,
isOwned: this.actor ? true : false,
actorId: this.actor?.id,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
effets: {
succes: {
liste: RdDRencontre.getEffetsSucces(),

View File

@ -0,0 +1,16 @@
import { RdDItemSheet } from "../item-sheet.js";
export class RdDServiceItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "service" };
async getData() {
const formData = await super.getData();
formData.disabled = formData.isGM || formData.isOwned ? '' : 'disabled';
return formData;
}
activateListeners(html) {
super.activateListeners(html);
}
}

View File

@ -1,6 +1,6 @@
import { RdDItemSheet } from "./item-sheet.js";
import { RdDItemSigneDraconique } from "./item-signedraconique.js";
import { TMRUtility } from "./tmr-utility.js";
import { RdDItemSheet } from "../item-sheet.js";
import { RdDItemSigneDraconique } from "./signedraconique.js";
import { TMRUtility } from "../tmr-utility.js";
/**
* Item sheet pour signes draconiques
@ -23,18 +23,9 @@ export class RdDSigneDraconiqueItemSheet extends RdDItemSheet {
/* -------------------------------------------- */
async getData() {
const formData = duplicate(this.item);
const formData = await super.getData();
this.tmrs = TMRUtility.buildSelectionTypesTMR(this.item.system.typesTMR);
mergeObject(formData, {
tmrs: this.tmrs,
title: formData.name,
isGM: game.user.isGM,
owner: this.actor?.isOwner,
isOwned: this.actor ? true : false,
actorId: this.actor?.id,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
});
formData.tmrs = this.tmrs;
return formData;
}

View File

@ -1,8 +1,9 @@
import { defaultItemImg } from "./item.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { TMRType, TMRUtility } from "./tmr-utility.js";
import { RdDItem, defaultItemImg } from "../item.js";
import { Misc } from "../misc.js";
import { RdDDice } from "../rdd-dice.js";
import { RdDRollTables } from "../rdd-rolltables.js";
import { RdDTimestamp } from "../rdd-timestamp.js";
import { TMRType, TMRUtility } from "../tmr-utility.js";
const tableSignesIndicatifs = [
{ rarete: "Très facile", difficulte: 0, xp: 6, nbCases: 14 },
@ -15,7 +16,17 @@ const tableSignesIndicatifs = [
const DIFFICULTE_LECTURE_SIGNE_MANQUE = +11;
export class RdDItemSigneDraconique {
export class RdDItemSigneDraconique extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/tmr/signe_draconique.webp";
}
async calculerFinPeriodeTemporel(debut) {
// TODO
return RdDTimestamp.formulesDuree().find(it => it.code == "").calcul(debut, this.actor);
}
static prepareSigneDraconiqueMeditation(meditation, rolled) {
return {
@ -96,6 +107,6 @@ export class RdDItemSigneDraconique {
static async randomSigneDescription() {
return await RdDRollTables.drawTextFromRollTable("Signes draconiques", false);
}
}
}

13
module/item/souffle.js Normal file
View File

@ -0,0 +1,13 @@
import { RdDItem } from "../item.js";
export class RdDItemSouffle extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/souffle_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

View File

@ -1,6 +1,10 @@
import { RdDBaseActor } from "./actor/base-actor.js";
import { LOG_HEAD, SYSTEM_RDD } from "./constants.js";
import { Environnement } from "./environnement.js";
import { Grammar } from "./grammar.js";
import { Monnaie } from "./item-monnaie.js";
import { RdDItem } from "./item.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
class Migration {
get code() { return "sample"; }
@ -29,6 +33,39 @@ class Migration {
}
class _1_5_34_migrationPngWebp {
get code() { return "migrationPngWebp"; }
get version() { return "1.5.34"; }
async migrate() {
const regexOldPngJpg = /(systems\/foundryvtt-reve-de-dragon\/icons\/.*)\.(png|jpg)/;
const replaceWithWebp = '$1.webp';
function convertImgToWebp(img) {
return img.replace(regexOldPngJpg, replaceWithWebp);
}
function prepareDocumentsImgUpdate(documents) {
return documents.filter(it => it.img && it.img.match(regexOldPngJpg))
.map(it => {
return { _id: it.id, img: convertImgToWebp(it.img) }
});
}
const itemsUpdates = prepareDocumentsImgUpdate(game.items);
const actorsUpdates = prepareDocumentsImgUpdate(game.actors);
//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);
});
}
}
class _10_0_16_MigrationSortsReserve extends Migration {
get code() { return "creation-item-sort-reserve"; }
get version() { return "10.0.16"; }
@ -236,7 +273,7 @@ class _10_3_0_Inventaire extends Migration {
}
_updatesMonnaies(items) {
return items
.filter(it => ['monnaie'].includes(it.type) && it.system.cout == undefined)
.filter(it => ['monnaie'].includes(it.type) && it.system.valeur_deniers != undefined)
.map(it => { return { _id: it.id, 'system.cout': it.system.valeur_deniers / 100, 'system.valeur_deniers': undefined } });
}
}
@ -251,10 +288,130 @@ class _10_3_0_FrequenceEnvironnement extends Migration {
}
_updatesFrequences(it) {
const rarete = RdDRaretes.byCode(it.system.rarete);
return {
_id: it.id,
'system.rarete': undefined,
'system.environnement': [{ milieu: it.system.milieu, rarete: it.system.rarete, frequence: Environnement.getFrequenceRarete(it.system.rarete, 'frequence') }]
'system.environnement': [{ milieu: it.system.milieu, rarete: rarete.code, frequence: rarete.frequence }]
}
}
}
class _10_3_17_Monnaies extends Migration {
constructor() {
super();
this.mapValeur = {
"Etain (1 denier)": { name: 'Denier (étain)', system: { cout: 0.01 } },
"Bronze (10 deniers)": { name: "Sou (bronze)", system: { cout: 0.1 } },
"Argent (1 sol)": { name: "Sol (argent)", system: { cout: 1 } },
"Or (10 sols)": { name: "Dragon (or)", system: { cout: 10 } }
};
}
get code() { return "migration-monnaies"; }
get version() { return "10.3.17"; }
async migrate() {
await this.applyItemsUpdates(items => this._updatesMonnaies(items));
}
_updatesMonnaies(items) {
return items
.filter(it => 'monnaie' == it.type)
.filter(it => this.mapValeur[it.name] != undefined)
.map(it => {
const correction = this.mapValeur[it.name];
return {
_id: it.id,
'name': correction.name,
'system.cout': correction.system.cout,
'system.valeur_deniers': undefined
}
});
}
}
class _10_4_6_ServicesEnCommerces extends Migration {
get code() { return "migration-service-acteurs"; }
get version() { return "10.4.6"; }
async migrate() {
const servicesToMigrate = game.items.filter(it => it.type == 'service');
servicesToMigrate.forEach(async service => {
const commerce = await this.convertServiceToCommerce(service);
await RdDBaseActor.create(commerce, { renderSheet: false });
await service.delete();
});
}
async convertServiceToCommerce(service) {
return {
name: service.name, img: service.img, type: 'commerce',
system: {
description: service.system.description,
notesmj: service.system.descriptionmj,
illimite: service.system.illimite
},
items: await this.transformInventaireCommerce(service)
}
}
async transformInventaireCommerce(service) {
const serviceItems = (service.system.items ?? []);
const commerceItems = await Promise.all(serviceItems.map(async (it) => { return await this.transformToItemBoutique(it); }));
return commerceItems.concat(Monnaie.monnaiesStandard());
}
async transformToItemBoutique(serviceRefItem) {
const item = await RdDItem.getCorrespondingItem(serviceRefItem);
const itemToCreate = {
name: item.name, img: item.img, type: item.type,
system: mergeObject({ cout: serviceRefItem.system.cout, quantite: serviceRefItem.system.quantite }, item.system, { overwrite: false })
};
return itemToCreate;
}
}
class _10_5_0_UpdatePeriodicite extends Migration {
get code() { return "migration-periodicite-poisons-maladies"; }
get version() { return "10.5.0"; }
async migrate() {
await this.applyItemsUpdates(items => this._updatePeriodicite(items));
}
_updatePeriodicite(items) {
return items.filter(it => ['poison', 'maladie'].includes(it.type))
.filter(it => it.system.periodicite != "")
.map(it => {
let [incubation, periodicite] = this.getPeriodicite(it);
const periode = periodicite.split(' ');
let unite = periode.length == 2
? RdDTimestamp.formulesPeriode().find(it => Grammar.includesLowerCaseNoAccent(periode[1], it.code))?.code
: undefined
if (unite && Number(periode[0])) {
return {
_id: it.id,
'system.periodicite': undefined,
'system.incubation': incubation,
'system.periode.nombre': Number.parseInt(periode[0]),
'system.periode.unite': unite
};
}
else {
return {
_id: it.id,
'system.periodicite': undefined,
'system.incubation': it.system.periodicite
};
}
}).filter(it => it != undefined);
}
getPeriodicite(it) {
let p = it.system.periodicite.split(/[\/\\]/);
switch (p.length) {
case 2: return [p[0].trim(), p[1].trim()];
case 1: return ["", it.system.periodicite.trim()];
default: return [it.system.periodicite.trim(), ""];
}
}
}
@ -262,6 +419,7 @@ class _10_3_0_FrequenceEnvironnement extends Migration {
export class Migrations {
static getMigrations() {
return [
new _1_5_34_migrationPngWebp(),
new _10_0_16_MigrationSortsReserve(),
new _10_0_17_MigrationCompetenceCreature(),
new _10_0_21_VehiculeStructureResistanceMax(),
@ -269,7 +427,10 @@ export class Migrations {
new _10_2_5_ArmesTirLancer(),
new _10_2_10_DesirLancinant_IdeeFixe(),
new _10_3_0_Inventaire(),
new _10_3_0_FrequenceEnvironnement()
new _10_3_0_FrequenceEnvironnement(),
new _10_3_17_Monnaies(),
new _10_4_6_ServicesEnCommerces(),
new _10_5_0_UpdatePeriodicite(),
];
}
@ -284,18 +445,12 @@ export class Migrations {
}
migrate() {
const currentVersion = game.settings.get(SYSTEM_RDD,"systemMigrationVersion");
//if (isNewerVersion(game.system.version, currentVersion)) {
if (true) { /* comment previous and uncomment here to test before upgrade */
const currentVersion = game.settings.get(SYSTEM_RDD, "systemMigrationVersion");
if (isNewerVersion(game.system.version, currentVersion)) {
//if (true) { /* comment previous and uncomment here to test before upgrade */
const migrations = Migrations.getMigrations().filter(m => isNewerVersion(m.version, currentVersion));
if (migrations.length > 0) {
migrations.sort((a, b) =>
isNewerVersion(a.version, b.version)
? 1
: isNewerVersion(b.version, a.version)
? -1
: 0
);
migrations.sort((a, b) => this.compareVersions(a, b));
migrations.forEach(async (m) => {
ui.notifications.info(
`Executing migration ${m.code}: version ${currentVersion} is lower than ${m.version}`
@ -321,4 +476,8 @@ export class Migrations {
console.log(LOG_HEAD + `No system version changed`);
}
}
compareVersions(a, b) {
return isNewerVersion(a.version, b.version) ? 1 : isNewerVersion(b.version, a.version) ? -1 : 0;
}
}

View File

@ -46,6 +46,9 @@ export class Misc {
: '';
}
static arrayOrEmpty(items) {
return items?.length ? items : [];
}
/**
* Converts the value to an integer, or to 0 if undefined/null/not representing integer
* @param {*} value value to convert to an integer using parseInt
@ -82,6 +85,7 @@ export class Misc {
list.forEach(it => addToObj(obj, it))
return obj;
}
static concat(lists) {
return lists.reduce((a, b) => a.concat(b), []);
}
@ -135,11 +139,11 @@ export class Misc {
}
static isRollModeHiddenToPlayer() {
switch (game.settings.get("core", "rollMode")) {
case CONST.DICE_ROLL_MODES.BLIND:
case CONST.DICE_ROLL_MODES.SELF: return true;
}
return false
switch (game.settings.get("core", "rollMode")) {
case CONST.DICE_ROLL_MODES.BLIND:
case CONST.DICE_ROLL_MODES.SELF: return true;
}
return false
}
static getActiveUser(id) {
@ -150,11 +154,11 @@ export class Misc {
return game.users.filter(u => u.isGM && u.active).sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
}
static isOwnerPlayer(actor, user=undefined) {
static isOwnerPlayer(actor, user = undefined) {
return actor.testUserPermission(user ?? game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)
}
static isOwnerPlayerOrUniqueConnectedGM(actor, user =undefined){
static isOwnerPlayerOrUniqueConnectedGM(actor, user = undefined) {
return Misc.isOwnerPlayer(actor, user) ?? Misc.isUniqueConnectedGM();
}

View File

@ -3,23 +3,28 @@
* Extend the base Dialog entity by defining a custom window to perform roll.
* @extends {Dialog}
*/
export class RdDAstrologieEditeur extends Dialog {
export class RdDAstrologieEditeur extends Dialog {
/* -------------------------------------------- */
constructor(html, calendrier, calendrierData) {
let myButtons = {
resetButton: { label: "Re-tirer les nombres astraux", callback: html => this.resetNombreAstraux() },
saveButton: { label: "Fermer", callback: html => this.fillData() }
};
resetButton: { label: "Re-tirer les nombres astraux", callback: html => this.resetNombreAstraux() },
saveButton: { label: "Fermer", callback: html => {} }
};
// Common conf
let dialogConf = { content: html, title: "Editeur d'Astrologie", buttons: myButtons, default: "saveButton" };
let dialogOptions = { classes: ["rdd-roll-dialog"], width: 600, height: 300, 'z-index': 99999 }
let dialogOptions = {
classes: ["rdd-roll-dialog"], width: 600,
height: 'fit-content',
'max-height': 800,
'z-index': 99999
}
super(dialogConf, dialogOptions)
this.calendrier = calendrier;
this.updateData( calendrierData );
this.updateData(calendrierData);
}
activateListeners(html) {
@ -28,7 +33,7 @@
}
/* -------------------------------------------- */
/* -------------------------------------------- */
async resetNombreAstraux() {
game.system.rdd.calendrier.resetNombreAstral();
await game.system.rdd.calendrier.rebuildListeNombreAstral();
@ -36,12 +41,8 @@
game.system.rdd.calendrier.showAstrologieEditor();
}
/* -------------------------------------------- */
fillData( ) {
}
/* -------------------------------------------- */
updateData( calendrierData ) {
updateData(calendrierData) {
this.calendrierData = duplicate(calendrierData);
}

View File

@ -10,7 +10,7 @@ import { SYSTEM_SOCKET_ID } from "./constants.js";
export class RdDAstrologieJoueur extends Dialog {
/* -------------------------------------------- */
static async create(actor, dialogConfig) {
static async create(actor) {
let dialogData = {
nombres: this.organizeNombres(actor),
@ -20,24 +20,22 @@ export class RdDAstrologieJoueur extends Dialog {
astrologie: RdDItemCompetence.findCompetence(actor.items, 'Astrologie')
}
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-astrologie-joueur.html', dialogData);
let options = { classes: ["rdd-roll-dialog"], width: 600, height: 500, 'z-index': 99999 };
if (dialogConfig.options) {
mergeObject(options, dialogConfig.options, { overwrite: true });
}
return new RdDAstrologieJoueur(html, actor, dialogData);
const options = { classes: ["rdd-roll-dialog"], width: 600, height: 'fit-content', 'z-index': 99999 };
const dialog = new RdDAstrologieJoueur(html, actor, dialogData, options);
dialog.render(true);
}
/* -------------------------------------------- */
constructor(html, actor, dialogData) {
constructor(html, actor, dialogData, dialogOptions) {
const dialogConf = {
title: "Nombres Astraux",
content: html,
default: "saveButton",
buttons: {
saveButton: { label: "Fermer", callback: html => this.quitDialog() }
saveButton: { label: "Fermer", callback: html => {} }
},
};
const dialogOptions = { classes: ["rdd-roll-dialog"], width: 600, height: 300, 'z-index': 99999 };
super(dialogConf, dialogOptions);
this.actor = actor;
@ -49,9 +47,7 @@ export class RdDAstrologieJoueur extends Dialog {
super.activateListeners(html);
this.html = html;
this.html.find(function () {
this.html.find("[name='diffConditions']").val(0);
});
this.html.find("[name='diffConditions']").val(0);
this.html.find('[name="jet-astrologie"]').click((event) => {
this.requestJetAstrologie();
@ -60,7 +56,7 @@ export class RdDAstrologieJoueur extends Dialog {
/* -------------------------------------------- */
static organizeNombres(actor) {
let itemNombres = actor.listItemsData('nombreastral');
let itemNombres = actor.listItems('nombreastral');
let itemFiltered = {};
for (let item of itemNombres) {
if (itemFiltered[item.system.jourindex]) {
@ -97,8 +93,4 @@ export class RdDAstrologieJoueur extends Dialog {
this.close();
}
/* -------------------------------------------- */
quitDialog() {
}
}

View File

@ -1,4 +1,4 @@
import { Misc } from "./misc.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
/**
* Extend the base Dialog entity by defining a custom window to perform roll.
@ -12,7 +12,7 @@ export class RdDCalendrierEditeur extends Dialog {
content: html,
title: "Editeur de date/heure",
buttons: {
save: { label: "Enregistrer", callback: html => this.fillData() }
save: { label: "Enregistrer", callback: html => this.saveCalendrier() }
},
default: "save"
};
@ -26,22 +26,37 @@ export class RdDCalendrierEditeur extends Dialog {
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("input[name='nomMois']").val(this.calendrierData.moisKey);
this.html.find("select[name='nomHeure']").val(this.calendrierData.heureKey);
this.html.find("select[name='jourMois']").val(this.calendrierData.jourMois);
this.html.find("select[name='minutesRelative']").val(calendrierData.minutesRelative);
this.html.find("select[name='annee']").val(this.calendrierData.annee);
this.html.find("input[name='calendar.annee']").val(this.calendrierData.annee);
this.html.find("select[name='calendar.mois']").val(this.calendrierData.mois.key);
this.html.find("select[name='calendar.heure']").val(this.calendrierData.heure.key);
RdDCalendrierEditeur.setLimited(this.html.find("input[name='calendar.jourDuMois']"), this.calendrierData.jourDuMois, 1, 28);
RdDCalendrierEditeur.setLimited(this.html.find("input[name='calendar.minute']"), this.calendrierData.minute, 0, 119);
}
static setLimited(input, init, min, max) {
input.val(init);
input.change(event => {
const val = Number.parseInt(input.val());
if (val < min) {
input.val(min);
}
if (val > max) {
input.val(max);
}
});
}
/* -------------------------------------------- */
fillData() {
this.calendrierData.annee = this.html.find("input[name='annee']").val();
this.calendrierData.moisKey = this.html.find("select[name='nomMois']").val();
this.calendrierData.heureKey = this.html.find("select[name='nomHeure']").val();
this.calendrierData.jourMois = this.html.find("select[name='jourMois']").val();
this.calendrierData.minutesRelative = this.html.find("select[name='minutesRelative']").val();
saveCalendrier() {
const annee = Number.parseInt(this.html.find("input[name='calendar.annee']").val());
const mois = this.html.find("select[name='calendar.mois']").val();
const jour = Number.parseInt(this.html.find("input[name='calendar.jourDuMois']").val());
const heure = this.html.find("select[name='calendar.heure']").val();
const minute = Number.parseInt(this.html.find("input[name='calendar.minute']").val());
this.calendrier.saveEditeur(this.calendrierData)
this.calendrier.setNewTimestamp(RdDTimestamp.timestamp(annee, mois, jour, heure, minute))
}
/* -------------------------------------------- */

View File

@ -1,44 +1,17 @@
/* -------------------------------------------- */
import { RdDCalendrierEditeur } from "./rdd-calendrier-editeur.js";
import { RdDAstrologieEditeur } from "./rdd-astrologie-editeur.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDUtility } from "./rdd-utility.js";
import { Grammar } from "./grammar.js";
import { RdDDice } from "./rdd-dice.js";
import { Misc } from "./misc.js";
import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { DialogChronologie } from "./dialog-chronologie.js";
import { RdDTimestamp, WORLD_TIMESTAMP_SETTING } from "./rdd-timestamp.js";
/* -------------------------------------------- */
const dossierIconesHeures = 'systems/foundryvtt-reve-de-dragon/icons/heures/'
const heuresList = ["vaisseau", "sirene", "faucon", "couronne", "dragon", "epees", "lyre", "serpent", "poissonacrobate", "araignee", "roseau", "chateaudormant"];
const heuresDef = {
"vaisseau": {key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "printemps", heure: 0, icon: 'hd01.svg' },
"sirene": { key: "sirene", label: "Sirène", lettreFont: 'i', saison: "printemps", heure: 1, icon: 'hd02.svg' },
"faucon": { key: "faucon", label: "Faucon", lettreFont: 'f', saison: "printemps", heure: 2, icon: 'hd03.svg' },
"couronne": { key: "couronne", label: "Couronne", lettreFont: '', saison: "ete", heure: 3, icon: 'hd04.svg' },
"dragon": { key: "dragon", label: "Dragon", lettreFont: 'd', saison: "ete", heure: 4, icon: 'hd05.svg' },
"epees": { key: "epees", label: "Epées", lettreFont: 'e', saison: "ete", heure: 5, icon: 'hd06.svg' },
"lyre": { key: "lyre", label: "Lyre", lettreFont: 'l', saison: "automne", heure: 6, icon: 'hd07.svg' },
"serpent": { key: "serpent", label: "Serpent", lettreFont: 's', saison: "automne", heure: 7, icon: 'hd08.svg' },
"poissonacrobate": { key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "automne", heure: 8, icon: 'hd09.svg' },
"araignee": { key: "araignee", label: "Araignée", lettreFont: 'a', saison: "hiver", heure: 9, icon: 'hd10.svg' },
"roseau": { key: "roseau", label: "Roseau", lettreFont: 'r', saison: "hiver", heure: 10, icon: 'hd11.svg' },
"chateaudormant": { key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "hiver", heure: 11, icon: 'hd12.svg' }
};
const saisonsDef = {
"printemps": { label: "Printemps" },
"ete": { label: "Eté" },
"automne": { label: "Automne" },
"hiver": { label: "Hiver" }
};
const RDD_MOIS_PAR_AN = 12;
export const RDD_JOUR_PAR_MOIS = 28;
const RDD_JOUR_PAR_MOIS = 28;
const RDD_HEURES_PAR_JOUR = 12;
const RDD_MINUTES_PAR_HEURES = 120;
const MAX_NOMBRE_ASTRAL = 12;
const JOURS_DU_MOIS = Array(RDD_JOUR_PAR_MOIS).fill().map((item, index) => 1 + index);
/* -------------------------------------------- */
export class RdDCalendrier extends Application {
@ -55,57 +28,6 @@ export class RdDCalendrier extends Application {
return { top: 200, left: 200 };
}
static getDefSigne(chiffre) {
chiffre = chiffre % RDD_MOIS_PAR_AN;
return Object.values(heuresDef).find(h => h.heure == chiffre);
}
static getSigneAs(key, value) {
const heure = (typeof value == 'string' || typeof value == 'number') && Number.isInteger(Number(value))
? Number(value)
: (typeof value == 'string') ? RdDCalendrier.getChiffreFromSigne(value)
: undefined
if (heure != undefined && ['key', 'label', 'lettreFont', 'saison', 'heure', 'icon'].includes(key)) {
return RdDCalendrier.getDefSigne(heure)[key]
}
if (heure != undefined && ['webp'].includes(key)) {
return RdDCalendrier.getDefSigne(heure)['icon'].replace('svg', 'webp');
}
console.error(`Appel à getSigneAs('${key}', ${value}) avec une clé/heure incorrects`);
return value;
}
static getChiffreFromSigne(signe) {
return heuresList.indexOf(signe);
}
static createCalendrierInitial() {
return {
heureRdD: 0,
minutesRelative: 0,
indexJour: 0,
annee: 0,
moisRdD: 0,
moisLabel: heuresDef["vaisseau"].label,
jour: 0
}
}
getCalendrier(index) {
index = index ?? this.getCurrentDayIndex();
const mois = Math.floor(index / RDD_JOUR_PAR_MOIS) % RDD_MOIS_PAR_AN;
return {
heureRdD: 0, // Index dans heuresList / heuresDef[x].heure
minutesRelative: 0,
indexJour: index,
annee: Math.floor(index / (RDD_JOUR_PAR_MOIS * RDD_MOIS_PAR_AN)),
moisRdD: RdDCalendrier.getDefSigne(mois).heure,
moisLabel: RdDCalendrier.getDefSigne(mois).label,
jour: (index % RDD_JOUR_PAR_MOIS) // Le calendrier stocke le jour en 0-27, mais en 1-28 à l'affichage
}
}
constructor() {
super();
// position
@ -114,19 +36,23 @@ export class RdDCalendrier extends Application {
this.calendrierPos = RdDCalendrier.createCalendrierPos();
game.settings.set(SYSTEM_RDD, "calendrier-pos", this.calendrierPos);
}
// Calendrier
this.calendrier = duplicate(game.settings.get(SYSTEM_RDD, "calendrier") ?? RdDCalendrier.createCalendrierInitial());
this.calendrier.annee = this.calendrier.annee ?? Math.floor((this.calendrier.moisRdD ?? 0) / RDD_MOIS_PAR_AN);
this.calendrier.moisRdD = (this.calendrier.moisRdD ?? 0) % RDD_MOIS_PAR_AN;
this.timestamp = RdDTimestamp.getWorldTime();
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
game.settings.set(SYSTEM_RDD, "calendrier", this.calendrier);
RdDTimestamp.setWorldTime(this.timestamp);
this.listeNombreAstral = this.getListeNombreAstral();
this.rebuildListeNombreAstral(HIDE_DICE); // Ensure always up-to-date
}
console.log('RdDCalendrier.constructor()', this.calendrier, this.calendrierPos, this.listeNombreAstral);
console.log('RdDCalendrier.constructor()', this.timestamp, this.timestamp.toCalendrier(), this.calendrierPos, this.listeNombreAstral);
Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
}
async onUpdateSetting(setting, update, options, id) {
if (setting.key == SYSTEM_RDD + '.' + WORLD_TIMESTAMP_SETTING) {
this.timestamp = RdDTimestamp.getWorldTime();
this.updateDisplay();
}
}
/* -------------------------------------------- */
@ -227,48 +153,50 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
getDateFromIndex(index) {
const dateRdD = this.getCalendrier(index);
return (dateRdD.jour + 1) + ' ' + dateRdD.moisLabel;
dateCourante() {
return this.timestamp.formatDate();
}
isAfterIndexDate(indexDate) {
// TODO: standardize
return indexDate < this.timestamp.indexDate;
}
/* -------------------------------------------- */
getDayMonthFromIndex(index = undefined) {
const dateRdD = this.getCalendrier(index);
return {
day: dateRdD.jour + 1,
month: heuresList[dateRdD.moisRdD]
}
}
heureCourante() { return RdDTimestamp.definition(this.timestamp.heure); }
/* -------------------------------------------- */
getCurrentHeure() {
return heuresList[this.calendrier.heureRdD];
getCurrentMinute() { return this.timestamp.indexMinute; }
getTimestampFinChateauDormant(nbJours = 0) {
return this.timestamp.nouveauJour().addJour(nbJours);
}
/* -------------------------------------------- */
getCurrentDayIndex() {
return (((this.calendrier.annee ?? 0) * RDD_MOIS_PAR_AN + (this.calendrier.moisRdD ?? 0)) * RDD_JOUR_PAR_MOIS) + (this.calendrier.jour ?? 0);
getTimestampFinHeure(nbHeures = 0) {
return this.timestamp.nouvelleHeure().addHeures(nbHeures);
}
/* -------------------------------------------- */
getIndexFromDate(jour, mois) {
return (heuresDef[mois].heure * RDD_JOUR_PAR_MOIS) + jour - 1;
const addYear = mois < this.timestamp.mois || (mois == this.timestamp.mois && jour < this.timestamp.jour)
const time = RdDTimestamp.timestamp(this.timestamp.annee + (addYear ? 1 : 0), mois, jour);
return time.indexDate;
}
/* -------------------------------------------- */
getJoursSuivants(num) {
getJoursSuivants(count) {
let jours = [];
let index = this.getCurrentDayIndex();
for (let i = 0; i < num; i++) {
jours[i] = { label: this.getDateFromIndex(index + i), index: index + i };
let indexDate = this.timestamp.indexDate;
for (let i = 0; i < count; i++, indexDate++) {
jours[i] = { label: RdDTimestamp.formatIndexDate(indexDate), index: indexDate };
}
return jours;
}
/* -------------------------------------------- */
async ajouterNombreAstral(index, showDice = SHOW_DICE) {
async ajouterNombreAstral(indexDate, showDice = SHOW_DICE) {
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, rollMode: "selfroll" });
const dateFuture = this.getDateFromIndex(index);
const dateFuture = RdDTimestamp.formatIndexDate(indexDate);
if (showDice != HIDE_DICE) {
ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients("GM"),
@ -278,20 +206,14 @@ export class RdDCalendrier extends Application {
return {
nombreAstral: nombreAstral,
valeursFausses: [],
index: index
index: indexDate
}
}
/* -------------------------------------------- */
getCurrentNombreAstral() {
let indexDate = this.getCurrentDayIndex();
return this.getNombreAstral(indexDate);
}
/* -------------------------------------------- */
resetNombreAstral() {
this.listeNombreAstral = [];
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", []);
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_reset_nombre_astral",
@ -300,19 +222,27 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
getNombreAstral(indexDate) {
/**
*
* @param {*} indexDate la date pour laquelle obtenir le nombre astral. Si undefined, on prend la date du jour
* @returns le nombre astral pour la date, ou pour la date du jour si la date n'est pas fournie.
* Si aucun nombre astral n'est trouvé, retourne 0 (cas où l'on demanderait un nombre astral en dehors des 12 jours courant et à venir)
*/
getNombreAstral(indexDate = undefined) {
if (indexDate == undefined) {
indexDate = this.timestamp.indexDate;
}
const listNombreAstral = this.getListeNombreAstral();
let astralData = listNombreAstral.find((nombreAstral, i) => nombreAstral.index == indexDate);
return astralData?.nombreAstral;
return astralData?.nombreAstral ?? 0;
}
/* -------------------------------------------- */
async rebuildListeNombreAstral(showDice = HIDE_DICE) {
if (Misc.isUniqueConnectedGM()) {
let jourCourant = this.getCurrentDayIndex();
let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
let dayIndex = jourCourant + i;
let dayIndex = this.timestamp.indexDate + i;
let na = this.listeNombreAstral.find(n => n.index == dayIndex);
if (na) {
newList[i] = na;
@ -320,11 +250,20 @@ export class RdDCalendrier extends Application {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
}
}
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", newList);
this.listeNombreAstral = newList;
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", newList);
}
}
/* -------------------------------------------- */
async setNewTimestamp(newTimestamp) {
game.actors.forEach(actor => actor.onTimeChanging(this.timestamp, newTimestamp));
RdDTimestamp.setWorldTime(newTimestamp);
this.timestamp = newTimestamp;
await this.rebuildListeNombreAstral();
this.updateDisplay();
}
/* -------------------------------------------- */
async onCalendarButton(ev) {
ev.preventDefault();
@ -339,101 +278,43 @@ export class RdDCalendrier extends Application {
this.updateDisplay();
}
/* -------------------------------------------- */
checkMaladie( periode) {
for (let actor of game.actors) {
if (actor.type == 'personnage') {
let maladies = actor.items.filter( item => (item.type == 'maladie' || (item.type == 'poison' && item.system.active) ) && item.system.periodicite.toLowerCase().includes(periode) );
for (let maladie of maladies) {
if ( maladie.system.identifie) {
ChatMessage.create({ content: `${actor.name} souffre de ${maladie.name} (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
} else {
ChatMessage.create({ content: `${actor.name} souffre d'un mal inconnu (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
}
let itemMaladie = actor.getObjet(maladie.id)
itemMaladie.postItem( 'gmroll');
}
}
}
}
/* -------------------------------------------- */
async incrementTime(minutes = 0) {
this.calendrier.minutesRelative += minutes;
this.checkMaladie("round");
this.checkMaladie("minute");
if (this.calendrier.minutesRelative >= RDD_MINUTES_PAR_HEURES) {
this.calendrier.minutesRelative -= RDD_MINUTES_PAR_HEURES;
this.calendrier.heureRdD += 1;
this.checkMaladie("heure");
}
if (this.calendrier.heureRdD >= RDD_HEURES_PAR_JOUR) {
this.calendrier.heureRdD -= RDD_HEURES_PAR_JOUR;
await this.incrementerJour();
this.checkMaladie("heure");
this.checkMaladie("jour");
}
game.settings.set(SYSTEM_RDD, "calendrier", duplicate(this.calendrier));
// Notification aux joueurs // TODO: replace with Hook on game settings update
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_sync_time",
data: duplicate(this.calendrier)
});
await this.setNewTimestamp(this.timestamp.addMinutes(minutes));
}
/* -------------------------------------------- */
async incrementerJour() {
const index = this.getCurrentDayIndex() + 1;
this.calendrier = this.getCalendrier(index);
await this.rebuildListeNombreAstral();
await this.setNewTimestamp(this.timestamp.nouveauJour());
}
/* -------------------------------------------- */
syncPlayerTime(calendrier) {
this.calendrier = duplicate(calendrier); // Local copy update
this.updateDisplay();
}
/* -------------------------------------------- */
async positionnerHeure(indexHeure) {
if (indexHeure <= this.calendrier.heureRdD) {
await this.incrementerJour();
}
this.calendrier.heureRdD = indexHeure;
this.calendrier.minutesRelative = 0;
game.settings.set(SYSTEM_RDD, "calendrier", duplicate(this.calendrier));
async positionnerHeure(heure) {
const indexDate = this.timestamp.indexDate;
const addDay = this.timestamp.heure < heure ? 0 : 1;
await this.setNewTimestamp(new RdDTimestamp({
indexDate: indexDate + addDay, indexHeure: 0
})
.addHeures(heure))
}
/* -------------------------------------------- */
fillCalendrierData(formData = {}) {
const mois = RdDCalendrier.getDefSigne(this.calendrier.moisRdD);
const heure = RdDCalendrier.getDefSigne(this.calendrier.heureRdD);
console.log('fillCalendrierData', this.calendrier, mois, heure);
formData.heureKey = heure.key;
formData.moisKey = mois.key;
formData.jourMois = this.calendrier.jour + 1;
formData.nomMois = mois.label; // heures et mois nommés identiques
formData.annee = this.calendrier.annee;
formData.iconMois = dossierIconesHeures + mois.icon;
formData.nomHeure = heure.label;
formData.iconHeure = dossierIconesHeures + heure.icon;
formData.nomSaison = saisonsDef[mois.saison].label;
formData.heureRdD = this.calendrier.heureRdD;
formData.minutesRelative = this.calendrier.minutesRelative;
mergeObject(formData, this.timestamp.toCalendrier());
formData.isGM = game.user.isGM;
return formData;
}
/* -------------------------------------------- */
getLectureAstrologieDifficulte(dateIndex) {
let indexNow = this.getCurrentDayIndex();
let indexNow = this.timestamp.indexDate;
let diffDay = dateIndex - indexNow;
return - Math.floor(diffDay / 2);
}
/* -------------------------------------------- */
async requestNombreAstral(request) {
const actor = game.actors.get(request.id);
if (Misc.isUniqueConnectedGM()) { // Only once
console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date);
@ -445,18 +326,26 @@ export class RdDCalendrier extends Application {
rollMode: "blindroll"
};
await RdDResolutionTable.rollData(rollData);
let nbAstral = this.getNombreAstral(request.date);
request.rolled = rollData.rolled;
request.isValid = true;
if (!request.rolled.isSuccess) {
request.isValid = false;
nbAstral = await RdDDice.rollTotal("1dhr" + nbAstral, { rollMode: "selfroll" });
// Mise à jour des nombres astraux du joueur
let astralData = this.listeNombreAstral.find((nombreAstral, i) => nombreAstral.index == request.date);
astralData.valeursFausses.push({ actorId: request.id, nombreAstral: nbAstral });
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
request.isValid = request.rolled.isSuccess;
request.nbAstral = this.getNombreAstral(request.date);
if (request.rolled.isSuccess) {
if (request.rolled.isPart) {
// Gestion expérience (si existante)
request.competence = actor.getCompetence("astrologie")
request.selectedCarac = actor.system.carac["vue"];
actor.appliquerAjoutExperience(request, 'hide');
}
}
request.nbAstral = nbAstral;
else {
request.nbAstral = await RdDDice.rollTotal("1dhr" + request.nbAstral, {
rollMode: "selfroll", showDice: HIDE_DICE
});
// Mise à jour des nombres astraux du joueur
this.addNbAstralIncorect(request.id, request.date, request.nbAstral);
}
if (Misc.getActiveUser(request.userId)?.isGM) {
RdDUtility.responseNombreAstral(request);
} else {
@ -468,57 +357,21 @@ export class RdDCalendrier extends Application {
}
}
/* -------------------------------------------- */
findHeure(heure) {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
let parHeureOuLabel = Object.values(heuresDef).filter(it => (it.heure + 1) == parseInt(heure) || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure);
if (parHeureOuLabel.length == 1) {
return parHeureOuLabel[0];
}
let parLabelPartiel = Object.values(heuresDef).filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
if (parLabelPartiel.length > 0) {
parLabelPartiel.sort(Misc.ascending(h => h.label.length));
return parLabelPartiel[0];
}
return undefined;
}
/* -------------------------------------------- */
getHeureNumber( hNum) {
let heure = Object.values(heuresDef).find(it => (it.heure) == hNum);
return heure
addNbAstralIncorect(actorId, date, nbAstral) {
let astralData = this.listeNombreAstral.find((nombreAstral, i) => nombreAstral.index == date);
astralData.valeursFausses.push({ actorId: actorId, nombreAstral: nbAstral });
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
}
/* -------------------------------------------- */
getHeuresChanceMalchance(heureNaissance) {
let heuresChancesMalchances = [];
let defHeure = this.findHeure(heureNaissance);
if (defHeure) {
let hn = defHeure.heure;
let chiffreAstral = this.getCurrentNombreAstral() ?? 0;
heuresChancesMalchances[0] = { value : "+4", heures: [this.getHeureNumber((hn + chiffreAstral) % RDD_HEURES_PAR_JOUR).label]};
heuresChancesMalchances[1] = { value : "+2", heures: [this.getHeureNumber((hn + chiffreAstral+4) % RDD_HEURES_PAR_JOUR).label,
this.getHeureNumber((hn + chiffreAstral + 8) % RDD_HEURES_PAR_JOUR).label ] };
heuresChancesMalchances[2] = { value : "-4", heures: [this.getHeureNumber((hn + chiffreAstral+6) % RDD_HEURES_PAR_JOUR).label]};
heuresChancesMalchances[3] = { value : "-2", heures: [this.getHeureNumber((hn + chiffreAstral+3) % RDD_HEURES_PAR_JOUR).label,
this.getHeureNumber((hn + chiffreAstral + 9) % RDD_HEURES_PAR_JOUR).label ]};
}
return heuresChancesMalchances;
static ecartHeureChance(heureNaissance, nombreAstral, heure) {
return (heureNaissance + nombreAstral - heure) % RDD_HEURES_PAR_JOUR;
}
/* -------------------------------------------- */
getAjustementAstrologique(heureNaissance, name = undefined) {
let defHeure = this.findHeure(heureNaissance);
let defHeure = RdDTimestamp.findHeure(heureNaissance);
if (defHeure) {
let hn = defHeure.heure;
let chiffreAstral = this.getCurrentNombreAstral() ?? 0;
let heureCourante = this.calendrier.heureRdD;
let ecartChance = (hn + chiffreAstral - heureCourante) % RDD_HEURES_PAR_JOUR;
switch (ecartChance) {
case 0: return 4;
case 4: case 8: return 2;
case 6: return -4;
case 3: case 9: return -2;
}
return RdDCalendrier.ajustementAstrologiqueHeure(defHeure.heure, this.getNombreAstral(), this.timestamp.heure);
}
else if (name) {
ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
@ -529,12 +382,20 @@ export class RdDCalendrier extends Application {
return 0;
}
static ajustementAstrologiqueHeure(hn, nbAstral, heure) {
switch (RdDCalendrier.ecartHeureChance(hn, nbAstral, heure)) {
case 0: return 4;
case 4: case 8: return 2;
case 6: return -4;
case 3: case 9: return -2;
}
return 0;
}
/* -------------------------------------------- */
getData() {
let formData = super.getData();
this.fillCalendrierData(formData);
this.setPos(this.calendrierPos);
return formData;
}
@ -563,50 +424,40 @@ export class RdDCalendrier extends Application {
updateDisplay() {
let calendrier = this.fillCalendrierData();
// Rebuild text du calendrier
let dateHTML = `${calendrier.jourMois} ${calendrier.nomMois} ${calendrier.annee} (${calendrier.nomSaison})`
let dateHTML = `${calendrier.jourDuMois} ${calendrier.mois.label} (${calendrier.mois.saison}) de l'année ${calendrier.annee}`
if (game.user.isGM) {
dateHTML = dateHTML + " - NA: " + (this.getCurrentNombreAstral() ?? "indéterminé");
dateHTML = dateHTML + "<br>Nombre Astral: " + (this.getNombreAstral() ?? "?");
}
for (let handle of document.getElementsByClassName("calendar-date-rdd")) {
handle.innerHTML = dateHTML;
}
for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
heure.innerHTML = calendrier.nomHeure;
heure.innerHTML = calendrier.heure.label;
}
for (const minute of document.getElementsByClassName("calendar-time-disp")) {
minute.innerHTML = `${calendrier.minutesRelative} minutes`;
minute.innerHTML = `${calendrier.minute} minutes`;
}
for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
heureImg.src = calendrier.iconHeure;
heureImg.src = calendrier.heure.icon;
}
}
/* -------------------------------------------- */
async saveEditeur(calendrierData) {
this.calendrier.minutesRelative = Number(calendrierData.minutesRelative);
this.calendrier.jour = Number(calendrierData.jourMois) - 1;
this.calendrier.moisRdD = RdDCalendrier.getChiffreFromSigne(calendrierData.moisKey);
this.calendrier.annee = Number(calendrierData.annee);
this.calendrier.heureRdD = RdDCalendrier.getChiffreFromSigne(calendrierData.heureKey);
game.settings.set(SYSTEM_RDD, "calendrier", duplicate(this.calendrier));
await this.rebuildListeNombreAstral();
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_sync_time",
data: duplicate(this.calendrier)
});
this.updateDisplay();
const newTimestamp = RdDTimestamp.timestamp(
Number.parseInt(calendrierData.annee),
calendrierData.mois.heure,
Number.parseInt(calendrierData.jourMois),
calendrierData.heure.heure,
Number.parseInt(calendrierData.minutes)
);
await this.setNewTimestamp(newTimestamp);
}
/* -------------------------------------------- */
async showCalendarEditor() {
let calendrierData = duplicate(this.fillCalendrierData());
let calendrierData = this.fillCalendrierData();
if (this.editeur == undefined) {
calendrierData.jourMoisOptions = RdDCalendrier.buildJoursMois();
calendrierData.heuresOptions = [0, 1];
calendrierData.minutesOptions = Array(RDD_MINUTES_PAR_HEURES).fill().map((item, index) => 0 + index);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData);
this.editeur = new RdDCalendrierEditeur(html, this, calendrierData)
}
@ -614,33 +465,38 @@ export class RdDCalendrier extends Application {
this.editeur.render(true);
}
static buildJoursMois() {
return Array(RDD_JOUR_PAR_MOIS).fill().map((item, index) => 1 + index);
}
static buildJoursMois() { return JOURS_DU_MOIS; }
/* -------------------------------------------- */
async showAstrologieEditor() {
let calendrierData = duplicate(this.fillCalendrierData());
let astrologieArray = [];
const calendrierData = duplicate(this.fillCalendrierData());
this.listeNombreAstral = this.listeNombreAstral || [];
for (let astralData of this.listeNombreAstral) {
astralData.humanDate = this.getDateFromIndex(astralData.index);
for (let vf of astralData.valeursFausses) {
calendrierData.astrologieData = this.listeNombreAstral.map(astro => {
const timestamp = new RdDTimestamp({ indexDate: astro.index });
astro.date = { mois: timestamp.mois, jour: timestamp.jour + 1 }
for (let vf of astro.valeursFausses) {
let actor = game.actors.get(vf.actorId);
vf.actorName = (actor) ? actor.name : "Inconnu";
}
astrologieArray.push(duplicate(astralData));
}
let heuresParActeur = {};
for (let actor of game.actors) {
let heureNaissance = actor.getHeureNaissance();
if ( heureNaissance) {
heuresParActeur[actor.name] = this.getHeuresChanceMalchance(heureNaissance);
}
}
//console.log("ASTRO", astrologieArray);
calendrierData.astrologieData = astrologieArray;
calendrierData.heuresParActeur = heuresParActeur;
return astro;
});
const nbAstral = this.getNombreAstral()
calendrierData.heures = Array.from(Array(RDD_HEURES_PAR_JOUR).keys());
calendrierData.ajustementsActeur = game.actors.filter(it => it.isPersonnage() && it.hasPlayerOwner).map(actor => {
return {
actor,
ajustements: calendrierData.heures.map(heure => {
const hn = RdDTimestamp.findHeure(actor.getHeureNaissance())?.heure;
return {
heure,
ajustement: RdDCalendrier.ajustementAstrologiqueHeure(hn, nbAstral, heure)
}
})
}
});
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-astrologie-template.html', calendrierData);
let astrologieEditeur = new RdDAstrologieEditeur(html, this, calendrierData)
astrologieEditeur.updateData(calendrierData);

View File

@ -39,9 +39,10 @@ const tableCaracDerivee = {
export class RdDCarac {
static isAgiliteOuDerivee(selectedCarac) {
static isAgiliteOuDerobee(selectedCarac) {
return selectedCarac?.label.match(/(Agilité|Dérobée)/);
}
static isVolonte(selectedCarac) {
return selectedCarac?.label == 'Volonté';
}

View File

@ -1,6 +1,5 @@
import { ChatUtility } from "./chat-utility.js";
import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { DialogSelectTarget } from "./dialog-select-target.js";
import { ENTITE_BLURETTE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { Grammar } from "./grammar.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
@ -111,10 +110,10 @@ export class RdDCombatManager extends Combat {
if (!roll.total) {
roll.evaluate({ async: false });
}
if (roll.total <= 0) roll.total = 0.00;
console.log("Compute init for", rollFormula, roll.total, combatant);
const total = Math.max(roll.total, 0.00);
console.log("Compute init for", rollFormula, roll, total, combatant);
let id = combatant._id || combatant.id;
await this.updateEmbeddedDocuments("Combatant", [{ _id: id, initiative: roll.total }]);
await this.updateEmbeddedDocuments("Combatant", [{ _id: id, initiative: total }]);
// Send a chat message
let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode");
@ -162,7 +161,6 @@ export class RdDCombatManager extends Combat {
if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) {
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)");
}
console.log(">>>>", arme)
if ((arme.system.unemain && arme.system.competence) ||
(arme.system.competence.toLowerCase().includes("corps à corps"))) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
@ -248,7 +246,7 @@ export class RdDCombatManager extends Combat {
}
if (actor.isCreatureEntite()) {
actions = actions.concat(RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']));
} else {
} else if (actor.isPersonnage()) {
// Recupération des items 'arme'
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
//.concat(RdDItemArme.empoignade())

View File

@ -14,6 +14,7 @@ import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { RdDUtility } from "./rdd-utility.js";
import { CompendiumTableHelpers } from "./settings/system-compendiums.js";
import { FenetreRechercheTirage } from "./tirage/fenetre-recherche-tirage.js";
import { TMRUtility } from "./tmr-utility.js";
const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
@ -23,6 +24,7 @@ export class RdDCommands {
static init() {
const rddCommands = new RdDCommands();
game.system.rdd.commands = rddCommands;
Hooks.on("chatMessage", (html, content, msg) => {
if (content[0] == '/') {
@ -35,7 +37,6 @@ export class RdDCommands {
return true;
});
game.system.rdd.commands = rddCommands;
}
constructor() {
@ -63,7 +64,6 @@ export class RdDCommands {
<br><strong>/table rencontre deso</strong> affiche la table des rencontres en Désolation
<br><strong>/table rencontre mauvaise</strong> affiche la table des mauvaises rencontres`
});
this.registerCommand({ path: ["/table", "milieu"], func: (content, msg, params) => this.tableMilieu(msg, params, 'liste'), descr: "Affiche la table des ressource naturelles pour un milieu donné" });
this.registerCommand({ path: ["/tirer", "comp"], func: (content, msg, params) => RdDRollTables.getCompetence('chat'), descr: "Tire une compétence au hasard" });
this.registerCommand({ path: ["/tirer", "queue"], func: (content, msg, params) => RdDRollTables.getQueue('chat'), descr: "Tire une Queue de Dragon" });
@ -75,7 +75,7 @@ export class RdDCommands {
this.registerCommand({ path: ["/tirer", "ideefixe"], func: (content, msg, params) => RdDRollTables.getIdeeFixe('chat'), descr: "Tire une Idée fixe" });
this.registerCommand({ path: ["/tirer", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant('chat'), descr: "Tire un Désir Lancinant" });
this.registerCommand({ path: ["/tirer", "rencontre"], func: (content, msg, params) => this.getRencontreTMR(params), descr: `Détermine une rencontre dans les TMR (synonyme de "/tmrr")` });
this.registerCommand({ path: ["/tirer", "milieu"], func: (content, msg, params) => this.tableMilieu(msg, params, 'chat'), descr: "Effectue un tirage dans la table desressource naturelles pour un milieu donné" });
this.registerCommand({ path: ["/tirage"], func: (content, msg, params) => this.tirage(), descr: "Ouvre la fenêtre de recherche et tirage" });
this.registerCommand({ path: ["/meteo"], func: (content, msg, params) => this.getMeteo(msg, params), descr: "Propose une météo marine" });
this.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.getName(msg, params), descr: "Génère un nom aléatoire" });
@ -206,16 +206,16 @@ export class RdDCommands {
let rollMode = game.settings.get("core", "rollMode");
if (["gmroll", "blindroll"].includes(rollMode)) {
msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
}
if (rollMode === "blindroll"){
}
if (rollMode === "blindroll") {
msg["blind"] = true;
}
}
msg["type"] = 0;
if (!this.commandsTable) {
this._registerCommands();
}
let command = commandLine[0].toLowerCase();
if (this._isCommandHandled(command)) {
let params = commandLine.slice(1);
@ -225,7 +225,7 @@ export class RdDCommands {
return false;
}
_isCommandHandled(command){
_isCommandHandled(command) {
return this.commandsTable[command] != undefined;
}
@ -254,10 +254,7 @@ export class RdDCommands {
}
/* -------------------------------------------- */
async help(msg) {
this.help(msg, undefined);
}
async help(msg, table) {
async help(msg, table = undefined) {
let commands = []
this._buildSubTableHelp(commands, table ?? this.commandsTable);
@ -331,7 +328,7 @@ export class RdDCommands {
diff = 0;
}
const caracName = params[0];
let competence = length > 1 ? actors[0].getCompetence(Misc.join(params.slice(1, length), ' ')) : undefined;
let competence = length > 1 ? actors[0].getCompetence(Misc.join(params.slice(1, length), ' ')) : { name: undefined };
if (competence) {
for (let actor of actors) {
await actor.rollCaracCompetence(caracName, competence.name, diff);
@ -398,32 +395,6 @@ export class RdDCommands {
return false;
}
async tableMilieu(msg, params, toChat) {
if (params && params.length > 0) {
const search = Misc.join(params, ' ');
const milieux = await game.system.rdd.environnement.findEnvironnementsLike(search);
if (milieux.length == 0) {
const tous = Object.values(await game.system.rdd.environnement.milieux());
return RdDCommands._chatAnswer(msg, `<strong>Aucun milieu correspondant à '${search}'.</strong>
<br>Milieux disponibles:
<br><ul class="chat-list"><li>${tous.reduce(Misc.joining('</li><li>'))}</li></ul>`);
}
if (milieux.length > 1) {
ui.notifications.warn(`<strong>Plusieurs milieux correspondent à '${search}'</strong>:
<br><ul class="chat-list"><li>${milieux.reduce(Misc.joining('</li><li>'))}</li></ul>`);
}
const tableName = `ressources en ${milieux.reduce(Misc.joining(', '))}`;
if (toChat == 'liste') {
return await game.system.rdd.environnement.searchToChatMessage(milieux, tableName);
}
else {
const row = await game.system.rdd.environnement.getRandom(milieux, tableName);
await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item');
return true;
}
}
return false;
}
/* -------------------------------------------- */
getCoutXpComp(msg, params) {
if (params && (params.length == 1 || params.length == 2)) {
@ -448,17 +419,27 @@ export class RdDCommands {
}
async creerSignesDraconiques() {
DialogCreateSigneDraconique.createSigneForActors();
if (game.user.isGM) {
DialogCreateSigneDraconique.createSigneForActors();
}
else {
ui.notifications.warn("Seul le MJ est autorisé à utiliser la commande /signe");
}
return true;
}
async supprimerSignesDraconiquesEphemeres() {
game.actors.forEach(actor => {
const ephemeres = actor.items.filter(item => item.type = 'signedraconique' && item.system.ephemere);
if (ephemeres.length > 0) {
actor.deleteEmbeddedDocuments("Item", ephemeres.map(item => item.id));
}
});
if (game.user.isGM) {
game.actors.forEach(actor => {
const ephemeres = actor.items.filter(item => item.type = 'signedraconique' && item.system.ephemere);
if (ephemeres.length > 0) {
actor.deleteEmbeddedDocuments("Item", ephemeres.map(item => item.id));
}
});
}
else {
ui.notifications.warn("Seul le MJ est autorisé à utiliser la commande /signe");
}
return true;
}
@ -501,5 +482,8 @@ export class RdDCommands {
return await RdDMeteo.getMeteo();
}
async tirage() {
FenetreRechercheTirage.create();
}
}

View File

@ -1,8 +1,8 @@
import { SYSTEM_RDD } from "./constants.js";
export class RddCompendiumOrganiser {
export class RdDCompendiumOrganiser {
static init() {
Hooks.on('renderCompendium', async (pack, html, compendiumData) => RddCompendiumOrganiser.onRenderCompendium(pack, html, compendiumData))
Hooks.on('renderCompendium', async (pack, html, compendiumData) => RdDCompendiumOrganiser.onRenderCompendium(pack, html, compendiumData))
}
static async onRenderCompendium(compendium, html, compendiumData) {
@ -10,14 +10,14 @@ export class RddCompendiumOrganiser {
const pack = compendium.collection
if (pack.metadata.system === SYSTEM_RDD) {
html.find('.directory-item').each((i, element) => {
RddCompendiumOrganiser.setEntityTypeName(pack, element);
RdDCompendiumOrganiser.setEntityTypeName(pack, element);
});
}
}
static async setEntityTypeName(pack, element) {
const label = RddCompendiumOrganiser.getEntityTypeLabel(await pack.getDocument(element.dataset.documentId));
RddCompendiumOrganiser.insertEntityType(element, label);
const label = RdDCompendiumOrganiser.getEntityTypeLabel(await pack.getDocument(element.dataset.documentId));
RdDCompendiumOrganiser.insertEntityType(element, label);
}
static insertEntityType(element, label) {

View File

@ -1,7 +1,6 @@
import { RdDUtility } from "./rdd-utility.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
import { Grammar } from "./grammar.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
/* -------------------------------------------- */
export class RdDHerbes extends Item {
@ -29,7 +28,7 @@ export class RdDHerbes extends Item {
}
/* -------------------------------------------- */
static async updatePotionData(formData) {
static async addPotionFormData(formData) {
formData.isSoins = formData.system.categorie.includes('Soin');
formData.isRepos = formData.system.categorie.includes('Repos');
if (formData.isSoins) {
@ -40,9 +39,8 @@ export class RdDHerbes extends Item {
}
formData.herbesSoins = RdDHerbes.buildHerbesList(this.herbesSoins, 12);
formData.herbesRepos = RdDHerbes.buildHerbesList(this.herbesRepos, 7);
formData.jourMoisOptions = RdDCalendrier.buildJoursMois();
formData.dateActuelle = game.system.rdd.calendrier.getDateFromIndex();
formData.splitDate = game.system.rdd.calendrier.getDayMonthFromIndex(formData.system.prdate);
formData.dateActuelle = game.system.rdd.calendrier.dateCourante();
formData.enchantement = RdDTimestamp.splitIndexDate(formData.system.prdate);
}
/* -------------------------------------------- */

View File

@ -1,4 +1,3 @@
import { Misc } from "./misc.js";
export class RdDHotbar {

View File

@ -1,345 +1,348 @@
/**
* RdD system
* Author: LeRatierBretonnien
* Software License: GNU GPLv3
*/
/* -------------------------------------------- */
/* -------------------------------------------- */
// Import Modules
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDActor } from "./actor.js";
import { RdDItemSheet } from "./item-sheet.js";
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDActorCreatureSheet } from "./actor-creature-sheet.js";
import { RdDActorVehiculeSheet } from "./actor-vehicule-sheet.js";
import { RdDActorEntiteSheet } from "./actor-entite-sheet.js";
import { Migrations } from './migrations.js';
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { DialogChronologie } from "./dialog-chronologie.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTokenHud } from "./rdd-token-hud.js";
import { RdDCommands } from "./rdd-commands.js";
import { RdDCombatManager, RdDCombat } from "./rdd-combat.js";
import { ChatUtility } from "./chat-utility.js";
import { StatusEffects } from "./settings/status-effects.js";
import { RddCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDItem } from "./item.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDPossession } from "./rdd-possession.js";
import { RdDSigneDraconiqueItemSheet } from "./item-signedraconique-sheet.js";
import { Misc } from "./misc.js";
import { Migrations } from './migrations.js';
import { DialogChronologie } from "./dialog-chronologie.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDRencontreItemSheet } from "./item-rencontre-sheet.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDHerbeItemSheet } from "./item-herbe-sheet.js";
import { Environnement } from "./environnement.js";
import { RdDIngredientItemSheet } from "./item-ingredient-sheet.js";
import { RdDFauneItemSheet } from "./item-faune-sheet.js";
import { RdDConteneurItemSheet } from "./item-conteneur-sheet.js";
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* -------------------------------------------- */
import { RdDActor } from "./actor.js";
import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDCommerce } from "./actor/commerce.js";
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDCommerceSheet } from "./actor/commerce-sheet.js";
import { RdDActorCreatureSheet } from "./actor-creature-sheet.js";
import { RdDActorVehiculeSheet } from "./actor-vehicule-sheet.js";
import { RdDActorEntiteSheet } from "./actor-entite-sheet.js";
import { RdDItem } from "./item.js";
import { RdDItemService } from "./item/service.js";
import { RdDItemMaladie } from "./item/maladie.js";
import { RdDItemPoison } from "./item/poison.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { RdDItemQueue } from "./item/queue.js";
import { RdDItemOmbre } from "./item/ombre.js";
import { RdDItemSouffle } from "./item/souffle.js";
import { RdDRencontre } from "./item/rencontre.js";
import { RdDItemSheet } from "./item-sheet.js";
import { RdDServiceItemSheet } from "./item/sheet-service.js";
import { RdDRencontreItemSheet } from "./item/sheet-rencontre.js";
import { RdDHerbeItemSheet } from "./item/sheet-herbe.js";
import { RdDPlanteItemSheet } from "./item/sheet-plante.js";
import { RdDIngredientItemSheet } from "./item/sheet-ingredient.js";
import { RdDFauneItemSheet } from "./item/sheet-faune.js";
import { RdDConteneurItemSheet } from "./item/sheet-conteneur.js";
import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js";
import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js";
/************************************************************************************/
Hooks.once("init", async function () {
console.log(`Initializing Reve de Dragon System`);
/**
* RdD system
* Author: LeRatierBretonnien
* Software License: GNU GPLv3
*/
export class SystemReveDeDragon {
// preload handlebars templates
RdDUtility.preloadHandlebarsTemplates();
// Create useful storage space
game.system.rdd = {
TMRUtility,
RdDUtility,
RdDHotbar,
RdDPossession,
static start() {
const system = new SystemReveDeDragon();
Hooks.once('init', async () => await system.onInit());
Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d));
}
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "accorder-entite-cauchemar", {
name: "Accorder le rêve aux entités",
hint: "A quel moment les personnages doivent accorder leur rêve aux entités de cauchemar",
scope: "world",
config: true,
type: String,
choices: { // If choices are defined, the resulting setting will be a select menu
"avant-attaque": "Avant l'attaque",
"avant-defense": "Avant la défense",
"avant-encaissement": "Avant l'encaissement",
},
default: "avant-encaissement"
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "calendrier", {
name: "calendrier",
scope: "world",
config: false,
default: RdDCalendrier.createCalendrierInitial(),
type: Object
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "migration-png-webp-1.5.34", {
name: "calendrier",
scope: "world",
config: false,
default: false,
type: Boolean
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "liste-nombre-astral", {
name: "liste-nombre-astral",
scope: "world",
config: false,
default: [],
type: Object
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "calendrier-pos", {
name: "calendrierPos",
scope: "client",
config: false,
default: RdDCalendrier.createCalendrierPos(),
type: Object
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "supprimer-dialogues-combat-chat", {
name: "Supprimer les dialogues de combat",
hint: "Si désactivée, tous les dialogues de combat sont conservés dans la conversation",
scope: "world",
config: true,
default: true,
type: Boolean
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "activer-sons-audio", {
name: "Activer les bruitages intégrés",
hint: "Si activé, certaines actions en jeu déclenchent un son d'ambiance",
scope: "world",
config: true,
default: true,
type: Boolean
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "appliquer-famine-soif", {
name: "Notifier de la famine et la soif pour",
hint: "Indique si les cas de famine et de soif seront indiqués durant Château Dormant",
scope: "world",
config: true,
type: String,
choices: {
"aucun": "ni la famine, ni la soif",
"famine": "seulement la famine",
"famine-soif": "la famine et la soif",
},
default: "aucun"
});
/* -------------------------------------------- */
// Set an initiative formula for the system
CONFIG.Combat.initiative = {
formula: "1+(1d6/10)",
decimals: 2
};
/* -------------------------------------------- */
game.socket.on(SYSTEM_SOCKET_ID, async (sockmsg) => {
console.log(">>>>> MSG RECV", sockmsg);
try {
RdDUtility.onSocketMessage(sockmsg);
RdDCombat.onSocketMessage(sockmsg);
ChatUtility.onSocketMessage(sockmsg);
RdDActor.onSocketMessage(sockmsg);
} catch (e) {
console.error('game.socket.on(SYSTEM_SOCKET_ID) Exception: ', sockmsg, ' => ', e)
constructor() {
this.RdDUtility = RdDUtility;
this.RdDHotbar = RdDHotbar;
this.itemClasses = {
service: RdDItemService,
maladie: RdDItemMaladie,
poison: RdDItemPoison,
queue: RdDItemQueue,
ombre: RdDItemOmbre,
souffle: RdDItemSouffle,
signedraconique: RdDItemSigneDraconique,
rencontre: RdDRencontre
}
this.actorClasses = {
creature: RdDActor,
entite: RdDActor,
personnage: RdDActor,
vehicule: RdDActor,
commerce: RdDCommerce,
}
});
/* -------------------------------------------- */
// Define custom Entity classes
CONFIG.Actor.documentClass = RdDActor;
CONFIG.Item.documentClass = RdDItem;
CONFIG.RDD = {
resolutionTable: RdDResolutionTable.resolutionTable,
carac_array: RdDUtility.getCaracArray(),
ajustementsConditions: RdDUtility.getAjustementsConditions(),
difficultesLibres: RdDUtility.getDifficultesLibres()
}
/* -------------------------------------------- */
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet(SYSTEM_RDD, RdDActorSheet, { types: ["personnage"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorCreatureSheet, { types: ["creature"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
/* Foundry VTT Initialization */
/* -------------------------------------------- */
async onInit() {
game.system.rdd = this;
RdDItemSheet.register(RdDSigneDraconiqueItemSheet);
RdDItemSheet.register(RdDRencontreItemSheet);
RdDItemSheet.register(RdDConteneurItemSheet);
RdDItemSheet.register(RdDHerbeItemSheet);
RdDItemSheet.register(RdDFauneItemSheet);
RdDItemSheet.register(RdDIngredientItemSheet);
console.log(`Initializing Reve de Dragon System`);
Items.registerSheet(SYSTEM_RDD, RdDItemSheet, {
types: [
"competence", "competencecreature",
"recettealchimique", "musique", "chant", "danse", "jeu", "recettecuisine", "oeuvre",
"objet", "arme", "armure", "livre", "potion", "munition",
"monnaie", "nourritureboisson", "gemme",
"meditation", "queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve",
"nombreastral", "tache", "maladie", "poison", "possession",
"tarot", "extraitpoetique"
], makeDefault: true
});
CONFIG.Combat.documentClass = RdDCombatManager;
// preload handlebars templates
RdDUtility.preloadHandlebarsTemplates();
// préparation des différents modules
SystemCompendiums.init();
DialogChronologie.init();
ReglesOptionelles.init();
RdDUtility.init();
RdDDice.init();
RdDCommands.init();
RdDCombat.init();
RdDCombatManager.init();
RdDTokenHud.init();
RdDActor.init();
RddCompendiumOrganiser.init();
EffetsDraconiques.init()
TMRUtility.init();
RdDHotbar.initDropbar();
RdDPossession.init();
TMRRencontres.init();
Environnement.init();
});
/* -------------------------------------------- */
this.initSystemSettings();
/* -------------------------------------------- */
function messageDeBienvenue() {
if (game.user.isGM) {
ChatUtility.removeChatMessageContaining('<div id="message-bienvenue-rdd">');
ChatMessage.create({
user: game.user.id,
content: `<div id="message-bienvenue-rdd"><span class="rdd-roll-part">Bienvenue dans le Rêve des Dragons !</span>
/* -------------------------------------------- */
// Set an initiative formula for the system
CONFIG.Combat.initiative = {
formula: "1+(1d6/10)",
decimals: 2
};
/* -------------------------------------------- */
game.socket.on(SYSTEM_SOCKET_ID, async (sockmsg) => {
console.log(">>>>> MSG RECV", sockmsg);
try {
RdDUtility.onSocketMessage(sockmsg);
RdDCombat.onSocketMessage(sockmsg);
ChatUtility.onSocketMessage(sockmsg);
RdDBaseActor.onSocketMessage(sockmsg);
} catch (e) {
console.error('game.socket.on(SYSTEM_SOCKET_ID) Exception: ', sockmsg, ' => ', e)
}
});
/* -------------------------------------------- */
// Define custom Entity classes
CONFIG.Actor.documentClass = RdDBaseActor;
CONFIG.Item.documentClass = RdDItem;
CONFIG.RDD = {
resolutionTable: RdDResolutionTable.resolutionTable,
carac_array: RdDUtility.getCaracArray(),
ajustementsConditions: RdDUtility.getAjustementsConditions(),
difficultesLibres: RdDUtility.getDifficultesLibres()
}
/* -------------------------------------------- */
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet(SYSTEM_RDD, RdDCommerceSheet, { types: ["commerce"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorSheet, { types: ["personnage"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorCreatureSheet, { types: ["creature"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
RdDItemSheet.register(RdDSigneDraconiqueItemSheet);
RdDItemSheet.register(RdDRencontreItemSheet);
RdDItemSheet.register(RdDConteneurItemSheet);
RdDItemSheet.register(RdDHerbeItemSheet);
RdDItemSheet.register(RdDFauneItemSheet);
RdDItemSheet.register(RdDPlanteItemSheet);
RdDItemSheet.register(RdDIngredientItemSheet);
RdDItemSheet.register(RdDServiceItemSheet);
Items.registerSheet(SYSTEM_RDD, RdDItemInventaireSheet, {
types: [
"objet", "arme", "armure", "livre", "potion", "munition",
"monnaie", "nourritureboisson", "gemme",
], makeDefault: true
});
Items.registerSheet(SYSTEM_RDD, RdDItemSheet, {
types: [
"competence", "competencecreature",
"recettealchimique", "musique", "chant", "danse", "jeu", "recettecuisine", "oeuvre",
"meditation", "queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve",
"nombreastral", "tache", "maladie", "poison", "possession",
"tarot", "extraitpoetique"
], makeDefault: true
});
CONFIG.Combat.documentClass = RdDCombatManager;
// préparation des différents modules
RdDTimestamp.init();
SystemCompendiums.init();
DialogChronologie.init();
ReglesOptionelles.init();
RdDUtility.init();
RdDDice.init();
RdDCommands.init();
RdDCombat.init();
RdDCombatManager.init();
RdDTokenHud.init();
RdDBaseActor.init();
RdDCompendiumOrganiser.init();
EffetsDraconiques.init()
TMRUtility.init();
RdDHotbar.initDropbar();
RdDPossession.init();
TMRRencontres.init();
Environnement.init();
Hooks.once('ready', () => this.onReady());
}
initSystemSettings() {
game.settings.register(SYSTEM_RDD, "accorder-entite-cauchemar", {
name: "Accorder le rêve aux entités",
hint: "A quel moment les personnages doivent accorder leur rêve aux entités de cauchemar",
scope: "world",
config: true,
type: String,
choices: {
"avant-attaque": "Avant l'attaque",
"avant-defense": "Avant la défense",
"avant-encaissement": "Avant l'encaissement",
},
default: "avant-encaissement"
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "liste-nombre-astral", {
name: "liste-nombre-astral",
scope: "world",
config: false,
default: [],
type: Object
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "calendrier-pos", {
name: "calendrierPos",
scope: "client",
config: false,
default: RdDCalendrier.createCalendrierPos(),
type: Object
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "supprimer-dialogues-combat-chat", {
name: "Supprimer les dialogues de combat",
hint: "Si désactivée, tous les dialogues de combat sont conservés dans la conversation",
scope: "world",
config: true,
default: true,
type: Boolean
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "activer-sons-audio", {
name: "Activer les bruitages intégrés",
hint: "Si activé, certaines actions en jeu déclenchent un son d'ambiance",
scope: "world",
config: true,
default: true,
type: Boolean
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "appliquer-famine-soif", {
name: "Notifier de la famine et la soif pour",
hint: "Indique si les cas de famine et de soif seront indiqués durant Château Dormant",
scope: "world",
config: true,
type: String,
choices: {
"aucun": "ni la famine, ni la soif",
"famine": "seulement la famine",
"famine-soif": "la famine et la soif",
},
default: "aucun"
});
}
async onReady() {
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* -------------------------------------------- */
// CSS patch for v9
if (game.version) {
let sidebar = document.getElementById("sidebar");
sidebar.style.width = "min-content";
}
if (Misc.isUniqueConnectedGM()) {
new Migrations().migrate();
}
StatusEffects.onReady();
RdDHerbes.initializeHerbes();
RdDDice.onReady();
/* -------------------------------------------- */
/* Affiche/Init le calendrier */
let calendrier = new RdDCalendrier();
let templatePath = "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html";
let templateData = {};
renderTemplate(templatePath, templateData).then(html => {
calendrier.render(true);
});
game.system.rdd.calendrier = calendrier; // Reference;
// Avertissement si joueur sans personnage
if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Vous n'êtes connecté à aucun personnage !");
ChatMessage.create({
content: "<b>ATTENTION</b> Le joueur " + game.user.name + " n'est connecté à aucun personnage !",
user: game.user.id
});
}
if (Misc.isUniqueConnectedGM()) {
this.messageDeBienvenue();
this.registerUsageCount(SYSTEM_RDD);
}
}
/* -------------------------------------------- */
messageDeBienvenue() {
if (game.user.isGM) {
ChatUtility.removeChatMessageContaining('<div id="message-bienvenue-rdd">');
ChatMessage.create({
user: game.user.id,
content: `<div id="message-bienvenue-rdd"><span class="rdd-roll-part">Bienvenue dans le Rêve des Dragons !</span>
<br>Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}
<br>La commande <code>/aide</code> dans le chat permet de voir les commandes spécifiques à Rêve de Dragon.</div>
` });
}
}
/* -------------------------------------------- */
// Register world usage statistics
function registerUsageCount(registerKey) {
if (game.user.isGM) {
game.settings.register("world", "world-key", {
name: "Unique world key",
scope: "world",
config: false,
default: "NONE",
type: String
});
let worldKey = game.settings.get("world", "world-key")
if (worldKey == undefined || worldKey == "") {
worldKey = randomID(32)
game.settings.set("world", "world-key", worldKey)
}
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
$.ajax(regURL)
/* -------------------------------------------- */
}
}
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* -------------------------------------------- */
Hooks.once("ready", async function () {
await migrationPngWebp_1_5_34()
if (Misc.isUniqueConnectedGM()) {
new Migrations().migrate();
}
StatusEffects.onReady();
RdDHerbes.initializeHerbes();
RdDDice.onReady();
/* -------------------------------------------- */
/* Affiche/Init le calendrier */
let calendrier = new RdDCalendrier();
let templatePath = "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html";
let templateData = {};
renderTemplate(templatePath, templateData).then(html => {
calendrier.render(true);
});
game.system.rdd.calendrier = calendrier; // Reference;
// Register world usage statistics
async registerUsageCount(registerKey) {
if (game.user.isGM) {
game.settings.register("world", "world-key", {
name: "Unique world key",
scope: "world",
config: false,
default: "NONE",
type: String
});
// Avertissement si joueur sans personnage
if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Vous n'êtes connecté à aucun personnage !");
ChatMessage.create({
content: "<b>ATTENTION</b> Le joueur " + game.user.name + " n'est connecté à aucun personnage !",
user: game.user.id
});
}
if (Misc.isUniqueConnectedGM()) {
messageDeBienvenue();
registerUsageCount(SYSTEM_RDD);
}
});
async function migrationPngWebp_1_5_34() {
if (!game.settings.get(SYSTEM_RDD, "migration-png-webp-1.5.34")) {
const regexOldPngJpg = /(systems\/foundryvtt-reve-de-dragon\/icons\/.*)\.(png|jpg)/;
const replaceWithWebp = '$1.webp';
function convertImgToWebp(img) {
return img.replace(regexOldPngJpg, replaceWithWebp);
}
function prepareDocumentsImgUpdate(documents) {
return documents.filter(it => it.img && it.img.match(regexOldPngJpg))
.map(it => {
return { _id: it.id, img: convertImgToWebp(it.img) }
});
}
const itemsUpdates = prepareDocumentsImgUpdate(game.items);
const actorsUpdates = prepareDocumentsImgUpdate(game.actors);
//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) });
let worldKey = game.settings.get("world", "world-key")
if (worldKey == undefined || worldKey == "") {
worldKey = randomID(32)
game.settings.set("world", "world-key", worldKey)
}
const actorItemsToUpdate = prepareDocumentsImgUpdate(actor.items);
actor.updateEmbeddedDocuments('Item', actorItemsToUpdate);
});
game.settings.set(SYSTEM_RDD, "migration-png-webp-1.5.34", true)
}
// CSS patch for v9
if (game.version) {
let sidebar = document.getElementById("sidebar");
sidebar.style.width = "min-content";
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
$.ajax(regURL)
/* -------------------------------------------- */
}
}
}
/* -------------------------------------------- */
/* Dice-so-nice ready */
/* -------------------------------------------- */
Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d));
SystemReveDeDragon.start();

View File

@ -1,4 +1,4 @@
import { RdDActor } from "./actor.js";
import { RdDBaseActor } from "./actor/base-actor.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
@ -20,7 +20,7 @@ export class RdDNameGen {
static async onCreerActeur(event) {
const button = event.currentTarget;
await RdDActor.create({
await RdDBaseActor.create({
name: button.attributes['data-nom'].value,
type: button.attributes['data-type'].value
},

View File

@ -115,17 +115,16 @@ export class RdDRollResolutionTable extends Dialog {
rollData.finalLevel = this._computeFinalLevel(rollData);
const htmlTable = await RdDResolutionTable.buildHTMLTable({
carac:rollData.caracValue,
level: rollData.finalLevel
carac: rollData.caracValue,
level: rollData.finalLevel,
maxCarac: 20,
maxLevel: 10
});
// Mise à jour valeurs
this.html.find("[name='carac']").val(rollData.caracValue);
this.html.find(".roll-param-resolution").text(rollData.selectedCarac.value + " / " + Misc.toSignedString(rollData.finalLevel));
this.html.find(".table-resolution").remove();
this.html.find(".table-proba-reussite").remove();
this.html.find("div.placeholder-resolution").append(htmlTable)
this.html.find("div.placeholder-resolution").empty().append(htmlTable)
}

View File

@ -276,11 +276,11 @@ export class RdDRoll extends Dialog {
const diffVariable = RdDItemSort.isDifficulteVariable(sort);
const coutVariable = RdDItemSort.isCoutVariable(sort);
HtmlUtility._showControlWhen(this.html.find(".div-sort-non-rituel"), !sort.system.isrituel);
HtmlUtility._showControlWhen(this.html.find(".div-sort-difficulte-var"), diffVariable);
HtmlUtility._showControlWhen(this.html.find(".div-sort-difficulte-fixe"), !diffVariable);
HtmlUtility._showControlWhen(this.html.find(".div-sort-ptreve-var"), coutVariable);
HtmlUtility._showControlWhen(this.html.find(".div-sort-ptreve-fixe"), !coutVariable);
HtmlUtility.showControlWhen(this.html.find(".div-sort-non-rituel"), !sort.system.isrituel);
HtmlUtility.showControlWhen(this.html.find(".div-sort-difficulte-var"), diffVariable);
HtmlUtility.showControlWhen(this.html.find(".div-sort-difficulte-fixe"), !diffVariable);
HtmlUtility.showControlWhen(this.html.find(".div-sort-ptreve-var"), coutVariable);
HtmlUtility.showControlWhen(this.html.find(".div-sort-ptreve-fixe"), !coutVariable);
}
async setSelectedSigneDraconique(signe) {
@ -311,11 +311,11 @@ export class RdDRoll extends Dialog {
const resolutionTable = await RdDResolutionTable.buildHTMLTable(RdDResolutionTable.subTable(rollData.caracValue, rollData.finalLevel))
const adjustements = await this.buildAjustements(rollData);
HtmlUtility._showControlWhen(this.html.find(".use-encTotal"), rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerivee(rollData.selectedCarac));
HtmlUtility._showControlWhen(this.html.find(".use-surenc"), rollData.ajustements.surenc.visible && RdDCarac.isActionPhysique(rollData.selectedCarac));
HtmlUtility._showControlWhen(this.html.find(".utilisation-moral"), rollData.use.appelAuMoral);
HtmlUtility._showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
HtmlUtility._showControlWhen(this.html.find(".divAppelAuMoral"), rollData.use.appelAuMoral);
HtmlUtility.showControlWhen(this.html.find(".use-encTotal"), rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac));
HtmlUtility.showControlWhen(this.html.find(".use-surenc"), rollData.ajustements.surenc.visible && RdDCarac.isActionPhysique(rollData.selectedCarac));
HtmlUtility.showControlWhen(this.html.find(".utilisation-moral"), rollData.use.appelAuMoral);
HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
HtmlUtility.showControlWhen(this.html.find(".divAppelAuMoral"), rollData.use.appelAuMoral);
// Mise à jour valeurs
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));

View File

@ -95,8 +95,9 @@ export class RdDRollTables {
const row = await table.getRandom(itemFrequence, filter);
if (row) {
await CompendiumTableHelpers.tableRowToChatMessage(row, type);
return row.document;
}
return row;
return undefined;
}
/* -------------------------------------------- */

View File

@ -1,4 +1,6 @@
import { DialogSplitItem } from "./dialog-split-item.js";
import { RdDItem } from "./item.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
export class RdDSheetUtility {
@ -19,8 +21,11 @@ export class RdDSheetUtility {
return $(event.currentTarget)?.parents(".item");
}
static prepareItemDropParameters(destItemId, actor, dragData, objetVersConteneur) {
const item = fromUuidSync(dragData.uuid)
static async prepareItemDropParameters(destItemId, actor, dragData, objetVersConteneur) {
let item = fromUuidSync(dragData.uuid);
if (item.pack && !item.system){
item = await RdDItem.getCorrespondingItem(item);
}
if (actor.canReceive(item)) {
return {
destId: destItemId,

309
module/rdd-timestamp.js Normal file
View File

@ -0,0 +1,309 @@
import { SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
export const WORLD_TIMESTAMP_SETTING = "calendrier";
const RDD_MOIS_PAR_AN = 12;
const RDD_JOURS_PAR_MOIS = 28;
const RDD_JOURS_PAR_AN = 336; //RDD_JOURS_PAR_MOIS * RDD_MOIS_PAR_AN;
const RDD_HEURES_PAR_JOUR = 12;
const RDD_MINUTES_PAR_HEURES = 120;
const RDD_MINUTES_PAR_JOUR = 1440; //RDD_HEURES_PAR_JOUR * RDD_MINUTES_PAR_HEURES;
const ROUNDS_PAR_MINUTE = 10;
const DEFINITION_HEURES = [
{ key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "Printemps" },
{ key: "sirene", label: "Sirène", lettreFont: 'i', saison: "Printemps" },
{ key: "faucon", label: "Faucon", lettreFont: 'f', saison: "Printemps" },
{ key: "couronne", label: "Couronne", lettreFont: '', saison: "Eté" },
{ key: "dragon", label: "Dragon", lettreFont: 'd', saison: "Eté" },
{ key: "epees", label: "Epées", lettreFont: 'e', saison: "Eté" },
{ key: "lyre", label: "Lyre", lettreFont: 'l', saison: "Automne" },
{ key: "serpent", label: "Serpent", lettreFont: 's', saison: "Automne" },
{ key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne" },
{ key: "araignee", label: "Araignée", lettreFont: 'a', saison: "Hiver" },
{ key: "roseau", label: "Roseau", lettreFont: 'r', saison: "Hiver" },
{ key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "Hiver" },
]
const FORMULES_DUREE = [
{ code: "", label: "", calcul: async (t, actor) => t.addJours(100 * RDD_JOURS_PAR_AN) },
{ code: "jour", label: "1 jour", calcul: async (t, actor) => t.nouveauJour().addJours(1) },
{ code: "1d7jours", label: "1d7 jour", calcul: async (t, actor) => t.nouveauJour().addJours(await RdDDice.rollTotal('1d7', { showDice: SHOW_DICE })) },
{ code: "1ddr", label: "Un dé draconique jours", calcul: async (t, actor) => t.nouveauJour().addJours(await RdDDice.rollTotal('1dr+7', { showDice: SHOW_DICE })) },
{ code: "hn", label: "Fin de l'Heure de Naissance", calcul: async (t, actor) => t.finHeure(actor.getHeureNaissance()) },
// { code: "1h", label: "Une heure", calcul: async (t, actor) => t.nouvelleHeure().addHeures(1) },
// { code: "12h", label: "12 heures", calcul: async (t, actor) => t.nouvelleHeure().addHeures(12) },
// { code: "chateaudormant", label: "Fin Chateau dormant", calcul: async (t, actor) => t.nouveauJour() },
// { code: "special", label: "Spéciale", calcul: async (t, actor) => t.addJours(100 * RDD_JOURS_PAR_AN) },
]
const FORMULES_PERIODE = [
{ code: 'round', label: "Rounds", calcul: async (t, nombre) => t.addMinutes(nombre / 10) },
{ code: 'minute', label: "Minutes", calcul: async (t, nombre) => t.addMinutes(nombre) },
{ code: 'heure', label: "Heures", calcul: async (t, nombre) => t.addHeures(nombre) },
{ code: 'jour', label: "Jours", calcul: async (t, nombre) => t.addJours(nombre) },
]
export class RdDTimestamp {
static iconeHeure(heure) {
return `systems/foundryvtt-reve-de-dragon/icons/heures/hd${heure < 9 ? '0' : ''}${heure + 1}.svg`;
}
static init() {
game.settings.register(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING, {
name: WORLD_TIMESTAMP_SETTING,
scope: "world",
config: false,
default: { indexDate: 0, indexMinute: 0 },
type: Object
});
for (let i = 0; i < DEFINITION_HEURES.length; i++) {
DEFINITION_HEURES[i].heure = i;
DEFINITION_HEURES[i].icon = RdDTimestamp.iconeHeure(i);
DEFINITION_HEURES[i].webp = DEFINITION_HEURES[i].icon.replace(".svg", ".webp");
}
}
/**
* @param signe
* @returns L'entrée de DEFINITION_HEURES correspondant au signe
*/
static definition(signe) {
if (signe == undefined) {
signe = 0;
}
if (Number.isInteger(signe)) {
return DEFINITION_HEURES[signe % RDD_HEURES_PAR_JOUR];
}
let definition = DEFINITION_HEURES.find(it => it.key == signe);
if (!definition) {
definition = Misc.findFirstLike(signe, DEFINITION_HEURES, { mapper: it => it.label, description: 'signe' });
}
return definition
}
static formulesDuree() {
return FORMULES_DUREE
}
static formulesPeriode() {
return FORMULES_PERIODE
}
static imgSigneHeure(heure) {
return RdDTimestamp.imgSigne(RdDTimestamp.definition(heure));
}
static imgSigne(signe) {
return signe == undefined ? '' : `<img class="img-signe-heure" src="${signe.webp}" alt="${signe.label}"/>`
}
static handleTimestampEditor(html, path, consumeTimestamp = async (path, timestamp) => { }) {
const fields = {
annee: html.find(`input[name="${path}.annee"]`),
mois: html.find(`select[name="${path}.mois"]`),
jourDuMois: html.find(`input[name="${path}.jourDuMois"]`),
heure: html.find(`select[name="${path}.heure"]`),
minute: html.find(`input[name="${path}.minute"]`)
};
async function onChangeTimestamp(fields, path) {
const annee = Number(fields.annee.val());
const mois = fields.mois.val();
const jour = Number(fields.jourDuMois.val());
const heure = fields.heure.val();
const minute = Number(fields.minute.val());
await consumeTimestamp(path, RdDTimestamp.timestamp(annee, mois, jour, heure, minute));
}
fields.annee.change(async (event) => await onChangeTimestamp(fields, path));
fields.mois.change(async (event) => await onChangeTimestamp(fields, path));
fields.jourDuMois.change(async (event) => await onChangeTimestamp(fields, path));
fields.heure.change(async (event) => await onChangeTimestamp(fields, path));
fields.minute.change(async (event) => await onChangeTimestamp(fields, path));
}
static findHeure(heure) {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
let parHeureOuLabel = DEFINITION_HEURES.filter(it => (it.heure) == parseInt(heure) % RDD_HEURES_PAR_JOUR || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure);
if (parHeureOuLabel.length == 1) {
return parHeureOuLabel[0];
}
let parLabelPartiel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
if (parLabelPartiel.length > 0) {
parLabelPartiel.sort(Misc.ascending(h => h.label.length));
return parLabelPartiel[0];
}
return undefined;
}
/**
* @param indexDate: la date (depuis le jour 0)
* @return la version formattée de la date
*/
static formatIndexDate(indexDate) {
return new RdDTimestamp({ indexDate }).formatDate()
}
static splitIndexDate(indexDate) {
const timestamp = new RdDTimestamp({ indexDate });
return {
jour: timestamp.jour + 1,
mois: RdDTimestamp.definition(timestamp.mois).key
}
}
static getWorldTime() {
let worldTime = game.settings.get(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING);
if (worldTime.indexJour != undefined && worldTime.heureRdD != undefined) {
// Migration
worldTime = {
indexDate: worldTime.indexJour,
indexMinute: worldTime.heureRdD * 120 + worldTime.minutesRelative
};
RdDTimestamp.setWorldTime(new RdDTimestamp(worldTime))
}
return new RdDTimestamp(worldTime);
}
static setWorldTime(timestamp) {
game.settings.set(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING, duplicate(timestamp));
}
/** construit un RdDTimestamp à partir de l'année/mois/jour/heure?/minute? */
static timestamp(annee, mois, jour, heure = 0, minute = 0) {
mois = this.definition(mois)?.heure
heure = this.definition(heure)?.heure
return new RdDTimestamp({
indexDate: (jour - 1) + (mois + annee * RDD_MOIS_PAR_AN) * RDD_JOURS_PAR_MOIS,
indexMinute: heure * RDD_MINUTES_PAR_HEURES + minute
})
}
/**
* Constructeur d'un timestamp.
* Selon les paramètres, l'objet construit se base su:
* - le timestamp
* - la date numérique + minute (dans la journée)
* @param indexDate: la date à utiliser pour ce timestamp
* @param indexMinute: la minute de la journée à utiliser pour ce timestamp
*
*/
constructor({ indexDate, indexMinute = undefined }) {
this.indexDate = indexDate
this.indexMinute = indexMinute ?? 0
}
/**
* Convertit le timestamp en une structure avec les informations utiles
* pour afficher la date et l'heure
*/
toCalendrier() {
return {
timestamp: this,
annee: this.annee,
mois: RdDTimestamp.definition(this.mois),
jour: this.jour,
jourDuMois: this.jour + 1,
heure: RdDTimestamp.definition(this.heure),
minute: this.minute
};
}
get annee() { return Math.floor(this.indexDate / RDD_JOURS_PAR_AN) }
get mois() { return Math.floor((this.indexDate % RDD_JOURS_PAR_AN) / RDD_JOURS_PAR_MOIS) }
get jour() { return (this.indexDate % RDD_JOURS_PAR_AN) % RDD_JOURS_PAR_MOIS }
get heure() { return Math.floor(this.indexMinute / RDD_MINUTES_PAR_HEURES) }
get minute() { return this.indexMinute % RDD_MINUTES_PAR_HEURES }
get round() { return ROUNDS_PAR_MINUTE * (this.indexMinute - Math.floor(this.indexMinute)) }
formatDate() {
const jour = this.jour + 1;
const mois = RdDTimestamp.definition(this.mois).label;
const annee = this.annee ?? '';
return `${jour} ${mois}` + (annee ? ' ' + annee : '');
}
nouveauJour() { return new RdDTimestamp({ indexDate: this.indexDate + 1, indexMinute: 0 }) }
nouvelleHeure() {
return this.heure >= RDD_HEURES_PAR_JOUR ? this.nouveauJour() : new RdDTimestamp({
indexDate: this.indexDate,
indexMinute: (this.heure + 1) * RDD_MINUTES_PAR_HEURES
})
}
addJours(jours) {
return jours == 0 ? this : new RdDTimestamp({
indexDate: this.indexDate + jours,
indexMinute: this.indexMinute
})
}
addHeures(heures) {
if (heures == 0) {
return this
}
const heure = this.heure + heures;
return new RdDTimestamp({
indexDate: this.indexDate + Math.floor(heure / RDD_HEURES_PAR_JOUR),
indexMinute: this.indexMinute + (heure % RDD_HEURES_PAR_JOUR) * RDD_MINUTES_PAR_HEURES
})
}
addMinutes(minutes) {
if (minutes == 0) {
return this;
}
const indexMinute = this.indexMinute + minutes;
const jours = Math.floor(indexMinute / RDD_MINUTES_PAR_JOUR)
return new RdDTimestamp({
indexDate: this.indexDate + jours,
indexMinute: indexMinute - (jours * RDD_MINUTES_PAR_JOUR)
})
}
addPeriode(nombre, unite) {
const formule = FORMULES_PERIODE.find(it => it.code == unite);
if (formule) {
return formule.calcul(this, nombre);
}
else {
ui.notifications.info(`Pas de période pour ${unite ?? 'Aucune uinité définie'}`)
}
return this;
}
finHeure(heure) {
return this.nouvelleHeure().addHeures((12 + heure - this.heure) % 12);
}
async appliquerDuree(duree, actor) {
const formule = FORMULES_DUREE.find(it => it.code == duree) ?? FORMULES_DUREE.find(it => it.code == "");
return await formule.calcul(this, actor);
}
compare(timestamp) {
let diff = this.indexDate - timestamp.indexDate
if (diff == 0) {
diff = this.indexMinute - timestamp.indexMinute
}
return diff < 0 ? -1 : diff > 0 ? 1 : 0;
}
difference(timestamp) {
const jours = this.indexDate - timestamp.indexDate;
const minutes = this.indexMinute - timestamp.indexMinute;
return {
jours: jours,
heures: Math.floor(minutes / RDD_MINUTES_PAR_HEURES),
minutes: minutes % RDD_MINUTES_PAR_HEURES
}
}
}

View File

@ -15,8 +15,9 @@ import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDDice } from "./rdd-dice.js";
import { STATUSES } from "./settings/status-effects.js";
import { RdDRencontre } from "./item-rencontre.js";
import { RdDRencontre } from "./item/rencontre.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
/* -------------------------------------------- */
@ -207,8 +208,8 @@ export class RdDTMRDialog extends Dialog {
return;
}
HtmlUtility._showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility._showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
// Roll Sort
this.html.find('.lancer-sort').click((event) => {
@ -245,7 +246,7 @@ export class RdDTMRDialog extends Dialog {
}
const coord = this._getActorCoord();
HtmlUtility._showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(coord));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(coord));
let ptsreve = document.getElementById("tmr-pointsreve-value");
ptsreve.innerHTML = this.actor.system.reve.reve.value;
@ -284,7 +285,12 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async onActionRencontre(action, tmr) {
async onActionRencontre(action, tmr, rencontre) {
if (!this.currentRencontre){
ui.notifications.warn("#612 Rencontre perdue, récupération en cours. Vous pouvez contacter l'équipe avec les logs pour aider à résoudre ce problème")
console.error("#612 Rencontre perdue", action, tmr, rencontre, this);
this.currentRencontre = rencontre;
}
switch (action) {
case 'derober':
await this.derober();
@ -405,6 +411,8 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _tentativeMaitrise(rencData) {
this.rencontreState = 'normal';
rencData.reve = this.actor.getReveActuel();
rencData.etat = this.actor.getEtatGeneral();
@ -450,7 +458,6 @@ export class RdDTMRDialog extends Dialog {
this._tentativeMaitrise(rencData);
this._deleteTmrMessages(rencData.actor, rencData.nbRounds);
}, 2000);
this.rencontreState == 'normal';
}
formatMessageRencontre(rencData, template) {
@ -460,14 +467,14 @@ export class RdDTMRDialog extends Dialog {
messageDuree = ` Au total, vous avez passé ${rencData.nbRounds} rounds à vous battre!`;
}
else {
messageDuree = ` Vous avez passé ${rencData.nbRounds} rounds à lutter!`;
messageDuree = ` Vous avez passé ${rencData.nbRounds} rounds à lutter!`;
}
}
try {
const compiled = Handlebars.compile(template);
return compiled(rencData) + messageDuree ;
return compiled(rencData) + messageDuree;
} catch (error) {
return template + messageDuree ;
return template + messageDuree;
}
}
@ -520,7 +527,7 @@ export class RdDTMRDialog extends Dialog {
}
this.currentRencontre = await this._jetDeRencontre(tmr);
if (this.currentRencontre) {
if (this.rencontresExistantes.find(it => it.id == this.currentRencontre.id)){
if (this.rencontresExistantes.find(it => it.id == this.currentRencontre.id)) {
// rencontre en attente suite à dérobade
await this.maitriserRencontre();
}
@ -550,7 +557,7 @@ export class RdDTMRDialog extends Dialog {
this.currentRencontre = present.clone({
'system.force': await RdDDice.rollTotal(present.system.formule),
'system.coord': tmr.coord
}, {save: false});
}, { save: false });
await EffetsDraconiques.presentCites.ouvrirLePresent(this.actor, presentCite);
this.removeToken(tmr, presentCite);
@ -587,6 +594,7 @@ export class RdDTMRDialog extends Dialog {
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
} else {
this._tellToUser(myRoll + ": Pas de rencontre en " + locTMR);
return undefined;
}
}
@ -839,7 +847,7 @@ export class RdDTMRDialog extends Dialog {
async processSortReserve(sortReserve) {
await this.actor.deleteEmbeddedDocuments('Item', [sortReserve.id]);
console.log("declencheSortEnReserve", sortReserve);
const heureCible = RdDCalendrier.getSigneAs('label', sortReserve.system.heurecible);
const heureCible = RdDTimestamp.definition(sortReserve.system.heurecible).label;
this._tellToUserAndGM(`Vous avez déclenché
${sortReserve.system.echectotal ? "<strong>l'échec total!</strong>" : "le sort"}
en réserve <strong>${sortReserve.name}</strong>

View File

@ -29,12 +29,13 @@ export class RdDTMRRencontreDialog extends Dialog {
this.toClose = false;
this.tmr = tmr;
this.tmrApp = tmrApp;
this.rencontre = rencontre;
this.tmrApp.minimize();
}
async onButtonAction(action) {
this.toClose = true;
this.tmrApp.onActionRencontre(action, this.tmr)
this.tmrApp.onActionRencontre(action, this.tmr, this.rencontre)
}
/* -------------------------------------------- */

View File

@ -111,6 +111,6 @@ export class RdDTokenHud {
static _toggleHudListActive(hud, list) {
hud.toggleClass('active');
HtmlUtility._showControlWhen(list, hud.hasClass('active'));
HtmlUtility.showControlWhen(list, hud.hasClass('active'));
}
}

View File

@ -8,14 +8,13 @@ import { DialogItemAchat } from "./dialog-item-achat.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDItem } from "./item.js";
import { Monnaie } from "./item-monnaie.js";
import { RdDPossession } from "./rdd-possession.js";
import { RdDNameGen } from "./rdd-namegen.js";
import { RdDConfirm } from "./rdd-confirm.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
import { Environnement } from "./environnement.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
/* -------------------------------------------- */
// This table starts at 0 -> niveau -10
@ -126,6 +125,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs-creature.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs-entitee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-effects.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-hautreve.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/vue-detaillee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-main.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-derivee.html',
@ -149,6 +149,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/jeux.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/alchimie.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/astrologie.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/chirurgie.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/non-haut-revant.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/haut-revant.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-queues.html',
@ -169,39 +170,18 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-animaux.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-suivants.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-vehicules.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.html',
//Items
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs',
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete.hbs',
'systems/foundryvtt-reve-de-dragon/templates/item/boutons-comestible.html',
'systems/foundryvtt-reve-de-dragon/templates/item/temporel.hbs',
'systems/foundryvtt-reve-de-dragon/templates/item/partial-inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/item/partial-environnement.html',
'systems/foundryvtt-reve-de-dragon/templates/item/partial-tab-environnement.html',
'systems/foundryvtt-reve-de-dragon/templates/header-item.html',
'systems/foundryvtt-reve-de-dragon/templates/item-competence-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-competencecreature-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-arme-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-armure-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-objet-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-conteneur-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-sort-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-herbe-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-ingredient-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-faune-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-livre-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-tache-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-potion-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-rencontre-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-queue-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-souffle-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-tarot-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-tete-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-ombre-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-monnaie-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-meditation-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-nourritureboisson-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-signedraconique-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-possession-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-extraitpoetique-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/header-item.html',
// partial enums
'systems/foundryvtt-reve-de-dragon/templates/enum-caracteristiques.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
@ -212,7 +192,6 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-vehicule.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-herbesoin-ingredient.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-heures.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-initpremierround.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-niveau-ethylisme.html',
@ -220,8 +199,13 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-draconic.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-type.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-periode.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-effet.html',
// Partials
'systems/foundryvtt-reve-de-dragon/templates/tirage/liste-resultats.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/timestamp.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/periodicite.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/enum-duree.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/compendium-link.hbs',
'systems/foundryvtt-reve-de-dragon/templates/partial-description-overflow.html',
'systems/foundryvtt-reve-de-dragon/templates/partial-description-sort.html',
@ -294,13 +278,20 @@ export class RdDUtility {
Handlebars.registerHelper('apostrophe', (article, str) => Grammar.apostrophe(article, str));
Handlebars.registerHelper('un', str => Grammar.articleIndetermine(str));
Handlebars.registerHelper('accord', (genre, ...args) => Grammar.accord(genre, args));
Handlebars.registerHelper('buildConteneur', (objet) => { return new Handlebars.SafeString(RdDUtility.buildConteneur(objet)); });
Handlebars.registerHelper('buildConteneur', (objet, templateItem, options) => { return new Handlebars.SafeString(RdDUtility.buildConteneur(objet, 1, templateItem, options)); });
Handlebars.registerHelper('buildContenu', (objet) => { return new Handlebars.SafeString(RdDUtility.buildContenu(objet, 1, true)); });
Handlebars.registerHelper('calculerPrixCommercant', item => item.calculerPrixCommercant());
Handlebars.registerHelper('caseTmr-label', coord => TMRUtility.getTMRLabel(coord));
Handlebars.registerHelper('caseTmr-type', coord => TMRUtility.getTMRType(coord));
Handlebars.registerHelper('typeTmr-name', type => TMRUtility.typeTmrName(type));
Handlebars.registerHelper('effetRencontre-name', coord => TMRUtility.typeTmrName(coord));
Handlebars.registerHelper('signeHeure', (key, heure) => RdDCalendrier.getSigneAs(key, heure));
Handlebars.registerHelper('timestamp-imgSigneHeure', (heure) => { return new Handlebars.SafeString(RdDTimestamp.imgSigneHeure(heure)) });
Handlebars.registerHelper('timestamp-imgSigne', (heure) => { return new Handlebars.SafeString(RdDTimestamp.imgSigne(heure)) });
Handlebars.registerHelper('timestamp-extract', timestamp => new RdDTimestamp(timestamp).toCalendrier());
Handlebars.registerHelper('timestamp-formulesDuree', () => RdDTimestamp.formulesDuree());
Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode());
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionelles.isUsing(option));
Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name)));
@ -308,68 +299,18 @@ export class RdDUtility {
Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name));
Handlebars.registerHelper('uniteQuantite', (itemId, actorId) => RdDUtility.getItem(itemId, actorId)?.getUniteQuantite());
Handlebars.registerHelper('isFieldInventaireModifiable', (type, field) => RdDItem.isFieldInventaireModifiable(type, field));
Handlebars.registerHelper('getFrequenceRarete', (rarete, field) => Environnement.getFrequenceRarete(rarete, field));
Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field));
return loadTemplates(templatePaths);
}
static getItem(itemId, actorId = undefined) {
return actorId ? game.actors.get(actorId)?.getObjet(itemId) : game.items.get(itemId);
return actorId ? game.actors.get(actorId)?.getItem(itemId) : game.items.get(itemId);
}
static linkCompendium(pack, id, name) {
return `@Compendium[${pack}.${id}]{${name}}`;
}
/* -------------------------------------------- */
static async creerObjet(actorSheet) {
let itemType = $(".item-type").val();
await actorSheet.createItem('Nouveau ' + itemType, itemType);
}
/* -------------------------------------------- */
static async selectObjetType(actorSheet) {
let typeObjets = RdDItem.getItemTypesInventaire();
let options = `<span class="competence-label">Selectionnez le type d'équipement</span><select class="item-type">`;
for (let typeName of typeObjets) {
options += `<option value="${typeName}">${typeName}</option>`
}
options += '</select>';
let d = new Dialog({
title: "Créer un équipement",
content: options,
buttons: {
one: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'objet",
callback: () => this.creerObjet(actorSheet)
}
}
});
d.render(true);
}
/* -------------------------------------------- */
static async selectTypeOeuvre(actorSheet) {
let typeObjets = RdDItem.getTypesOeuvres();
let options = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
for (let typeName of typeObjets) {
options += `<option value="${typeName}">${typeName}</option>`
}
options += '</select>';
let d = new Dialog({
title: "Créer un équipement",
content: options,
buttons: {
one: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'objet",
callback: () => this.creerObjet(actorSheet)
}
}
});
d.render(true);
}
/* -------------------------------------------- */
static buildListOptions(min, max) {
let options = ""
@ -409,68 +350,6 @@ export class RdDUtility {
return undefined;
}
/* -------------------------------------------- */
static filterItemsPerTypeForSheet(formData, itemTypes) {
RdDUtility.filterEquipementParType(formData, itemTypes);
formData.sorts = this.arrayOrEmpty(itemTypes['sort']);
formData.rencontres = this.arrayOrEmpty(itemTypes['rencontre']);
formData.casestmr = this.arrayOrEmpty(itemTypes['casetmr']);
formData.signesdraconiques = this.arrayOrEmpty(itemTypes['signedraconique']);
formData.queues = this.arrayOrEmpty(itemTypes['queue']);
formData.souffles = this.arrayOrEmpty(itemTypes['souffle']);
formData.ombres = this.arrayOrEmpty(itemTypes['ombre']);
formData.tetes = this.arrayOrEmpty(itemTypes['tete']);
formData.taches = this.arrayOrEmpty(itemTypes['tache']);
formData.meditations = this.arrayOrEmpty(itemTypes['meditation']);
formData.chants = this.arrayOrEmpty(itemTypes['chant']);
formData.danses = this.arrayOrEmpty(itemTypes['danse']);
formData.musiques = this.arrayOrEmpty(itemTypes['musique']);
formData.oeuvres = this.arrayOrEmpty(itemTypes['oeuvre']);
formData.jeux = this.arrayOrEmpty(itemTypes['jeu']);
formData.recettescuisine = this.arrayOrEmpty(itemTypes['recettecuisine']);
formData.recettesAlchimiques = this.arrayOrEmpty(itemTypes['recettealchimique']);
formData.maladies = this.arrayOrEmpty(itemTypes['maladie']);
formData.poisons = this.arrayOrEmpty(itemTypes['poison']);
formData.possessions = this.arrayOrEmpty(itemTypes['possession']);
formData.maladiesPoisons = formData.maladies.concat(formData.poisons);
formData.competences = (itemTypes['competence'] ?? []).concat(itemTypes['competencecreature'] ?? []);
formData.sortsReserve = this.arrayOrEmpty(itemTypes['sortreserve']);
}
static filterEquipementParType(formData, itemTypes) {
formData.conteneurs = this.arrayOrEmpty(itemTypes['conteneur']);
formData.materiel = this.arrayOrEmpty(itemTypes['objet']);
formData.armes = this.arrayOrEmpty(itemTypes['arme']);
formData.armures = this.arrayOrEmpty(itemTypes['armure']);
formData.munitions = this.arrayOrEmpty(itemTypes['munition']);
formData.livres = this.arrayOrEmpty(itemTypes['livre']);
formData.potions = this.arrayOrEmpty(itemTypes['potion']);
formData.ingredients = this.arrayOrEmpty(itemTypes['ingredient']);
formData.faunes = this.arrayOrEmpty(itemTypes['faune']);
formData.herbes = this.arrayOrEmpty(itemTypes['herbe']);
formData.monnaie = this.arrayOrEmpty(itemTypes['monnaie']).sort(Monnaie.triValeurEntiere());
formData.nourritureboissons = this.arrayOrEmpty(itemTypes['nourritureboisson']);
formData.gemmes = this.arrayOrEmpty(itemTypes['gemme']);
formData.objets = formData.conteneurs
.concat(formData.materiel)
.concat(formData.armes)
.concat(formData.armures)
.concat(formData.munitions)
.concat(formData.livres)
.concat(formData.potions)
.concat(formData.ingredients)
.concat(formData.herbes)
.concat(formData.faunes)
.concat(formData.monnaie)
.concat(formData.nourritureboissons)
.concat(formData.gemmes);
}
/* -------------------------------------------- */
static buildArbreDeConteneurs(conteneurs, objets) {
let objetVersConteneur = {};
@ -519,29 +398,34 @@ export class RdDUtility {
/** Construit la structure récursive des conteneurs, avec imbrication potentielle
*
*/
static buildConteneur(objet, profondeur) {
static buildConteneur(objet, profondeur, templateItem, options) {
if (!profondeur) profondeur = 1;
if (!templateItem) templateItem = 'actor/inventaire-item.html'
objet.niveau = profondeur;
const isConteneur = objet.type == 'conteneur';
const isOuvert = isConteneur && this.getAfficheContenu(objet._id);
const isVide = isConteneur && objet.system.contenu.length == 0;
const conteneur = Handlebars.partials['systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.html']({
const conteneur = Handlebars.partials[`systems/foundryvtt-reve-de-dragon/templates/${templateItem}`]({
item: objet,
vide: isVide,
ouvert: isOuvert
ouvert: isOuvert,
options: options
});
const contenu = isConteneur ? RdDUtility.buildContenu(objet, profondeur, isOuvert) : '';
const contenu = isConteneur ? RdDUtility.buildContenu(objet, profondeur, isOuvert, templateItem, options) : '';
return conteneur + contenu;
}
/* -------------------------------------------- */
static buildContenu(objet, profondeur, afficherContenu) {
static buildContenu(objet, profondeur, afficherContenu, templateItem, options) {
if (!profondeur) profondeur = 1;
if (!templateItem) templateItem = 'actor/inventaire-item.html'
objet.niveau = profondeur;
const display = afficherContenu ? 'item-display-show' : 'item-display-hide';
let strContenu = `<ul class='item-list alterne-list ${display} list-item-margin${Math.min(profondeur,6)}'>`;
let strContenu = `<ul class='item-list alterne-list ${display} list-item-margin${Math.min(profondeur, 6)}'>`;
for (let subItem of objet.subItems) {
strContenu += this.buildConteneur(subItem, profondeur + 1);
strContenu += this.buildConteneur(subItem, profondeur + 1, templateItem, options);
}
return strContenu + "</ul>";
}
@ -693,7 +577,7 @@ export class RdDUtility {
return await RdDUtility.prepareEncaissement(rollData, roll, armure);
}
/* -------------------------------------------- */
static async prepareEncaissement(rollData, roll, armure) {
const jetTotal = roll.total + rollData.dmg.total - armure;
@ -709,11 +593,11 @@ export class RdDUtility {
encaissement.endurance = await RdDUtility._evaluatePerte(encaissement.endurance, over20);
encaissement.penetration = rollData.arme?.system.penetration ?? 0;
encaissement.blessures = (
encaissement.critiques> 0 ? "Critique":
encaissement.graves> 0 ? "Grave":
encaissement.legeres> 0 ? "Légère":
encaissement.eraflures>0 ? "Contusions/Eraflures":
'Aucune'
encaissement.critiques > 0 ? "Critique" :
encaissement.graves > 0 ? "Grave" :
encaissement.legeres > 0 ? "Légère" :
encaissement.eraflures > 0 ? "Contusions/Eraflures" :
'Aucune'
);
return encaissement;
}
@ -767,8 +651,6 @@ export class RdDUtility {
switch (sockmsg.msg) {
case "msg_gm_chat_message":
return ChatUtility.handleGMChatMessage(sockmsg.data);
case "msg_sync_time":
return game.system.rdd.calendrier.syncPlayerTime(sockmsg.data);
case "msg_request_nombre_astral":
return game.system.rdd.calendrier.requestNombreAstral(sockmsg.data);
case "msg_response_nombre_astral":
@ -813,7 +695,7 @@ export class RdDUtility {
// gestion bouton tchat Acheter
html.on("click", '.button-acheter', event => {
const venteData = DialogItemAchat.venteData(event.currentTarget);
const venteData = DialogItemAchat.preparerAchat(event.currentTarget);
if (venteData) {
DialogItemAchat.onAcheter(venteData);
}
@ -829,6 +711,18 @@ export class RdDUtility {
ChatUtility.removeChatMessageId(RdDUtility.findChatMessageId(event.currentTarget));
}
});
html.on("click", '.rdd-world-content-link', async event => {
const htmlElement = html.find(event.currentTarget);
const id = htmlElement?.data("id");
const doctype = htmlElement?.data("doctype");
switch (doctype ?? 'Item') {
case 'Actor':
return game.actors.get(id)?.sheet.render(true);
case 'Item':
default:
return game.items.get(id)?.sheet.render(true);
}
});
}
static findChatMessageId(current) {
@ -904,7 +798,7 @@ export class RdDUtility {
if (p2[2] == 'd') deniers += Number(p2[1]);
if (p2[2] == 's') sols += Number(p2[1]);
let sommeAPayer = sols + deniers/100;
let sommeAPayer = sols + deniers / 100;
let msgPayer = `La somme de ${sols} Sols et ${deniers} Deniers est à payer<br>
<a class='payer-button chat-card-button' data-somme-a-payer='${sommeAPayer}'>Payer</a>`
ChatMessage.create({ content: msgPayer });
@ -946,7 +840,7 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async confirmerSuppressionItem(sheet, item, htmlToDelete) {
static async confirmActorItemDelete(sheet, item, htmlToDelete) {
const itemId = item.id;
const confirmationSuppression = {
settingConfirmer: "confirmation-supprimer-" + item.getItemGroup(),
@ -987,10 +881,10 @@ export class RdDUtility {
/* -------------------------------------------- */
static afficherHeuresChanceMalchance(heureNaissance) {
if (game.user.isGM) {
let heure = game.system.rdd.calendrier.findHeure(heureNaissance);
const heure = RdDTimestamp.findHeure(heureNaissance - 1);
if (heureNaissance && heure) {
let ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance);
const current = game.system.rdd.calendrier.findHeure(game.system.rdd.calendrier.getCurrentHeure());
const current = game.system.rdd.calendrier.heureCourante();
ChatMessage.create({
content: `A l'heure de <strong>${current.label}</strong>, le modificateur de Chance/Malchance est de <strong>${Misc.toSignedString(ajustement)}</strong> pour l'heure de naissance <strong>${heure.label}</strong>.`,
whisper: ChatMessage.getWhisperRecipients("GM")

View File

@ -56,14 +56,14 @@ export const referenceAjustements = {
getValue: (rollData, actor) => actor.getEtatGeneral({ ethylisme: rollData.forceAlcool != undefined })
},
malusArmure: {
isVisible: (rollData, actor) => RdDCarac.isAgiliteOuDerivee(rollData.selectedCarac),
isUsed: (rollData, actor) => RdDCarac.isAgiliteOuDerivee(rollData.selectedCarac),
isVisible: (rollData, actor) => RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac),
isUsed: (rollData, actor) => RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac),
getLabel: (rollData, actor) => 'Malus armure',
getValue: (rollData, actor) => actor.getMalusArmure()
},
encTotal: {
isVisible: (rollData, actor) => RdDCarac.isAgiliteOuDerivee(rollData.selectedCarac) && RdDItemCompetence.isMalusEncombrementTotal(rollData.competence),
isUsed: (rollData, actor) => !rollData.oeuvre && RdDCarac.isAgiliteOuDerivee(rollData.selectedCarac) && RdDItemCompetence.isMalusEncombrementTotal(rollData.competence) && rollData.use.encTotal,
isVisible: (rollData, actor) => RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac) && RdDItemCompetence.isMalusEncombrementTotal(rollData.competence),
isUsed: (rollData, actor) => !rollData.oeuvre && RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac) && RdDItemCompetence.isMalusEncombrementTotal(rollData.competence) && rollData.use.encTotal,
getLabel: (rollData, actor) => 'Encombrement total',
getValue: (rollData, actor) => -actor.getEncTotal()
},

View File

@ -52,12 +52,16 @@ export class SystemCompendiums extends FormApplication {
}
static getPack(compendium) {
const pack = game.packs.get(compendium);
if (pack) {
return pack;
}
return game.packs.get(SystemCompendiums.getCompendium(compendium)) ?? game.packs.get(SystemCompendiums._getDefaultCompendium(compendium));
}
static async getPackContent(compendium, docType) {
const pack = SystemCompendiums.getPack(compendium);
if (pack.metadata.type == docType) {
if (pack?.metadata.type == docType) {
return await pack.getDocuments();
}
return [];
@ -87,6 +91,11 @@ export class SystemCompendiums extends FormApplication {
return items;
}
static async loadDocument(document) {
const pack = game.packs.get(document.pack);
return await pack.getDocument(document.id ?? document._id);
}
static async getItems(compendium, itemType = undefined) {
const items = await SystemCompendiums.getPackContent(compendium, 'Item');
return (itemType ? items.filter(it => it.type == itemType) : items);
@ -172,7 +181,7 @@ export class SystemCompendiums extends FormApplication {
*/
export class CompendiumTable {
constructor(compendium, type, subTypes, sorting = undefined) {
constructor(compendium, type, subTypes = undefined, sorting = undefined) {
this.compendium = compendium;
this.type = type;
this.subTypes = subTypes;
@ -182,13 +191,13 @@ export class CompendiumTable {
async getContent(itemFrequence = it => it.system.frequence, filter = it => true) {
return await SystemCompendiums.getContent(this.compendium,
this.type,
it => this.subTypes.includes(it.type) && filter(it),
it => (!this.subTypes || this.subTypes.includes(it.type)) && itemFrequence(it) > 0 && filter(it),
itemFrequence,
this.sorting);
}
async buildTable(itemFrequence = it => it.system.frequence, filter = it => true) {
const elements = await this.getContent(filter, itemFrequence);
const elements = await this.getContent(itemFrequence, filter);
return CompendiumTableHelpers.buildTable(elements, itemFrequence);
}
@ -220,9 +229,23 @@ export class CompendiumTableHelpers {
});
}
static async getRandom(table, type, subTypes, forcedRoll = undefined, localisation = undefined) {
static concatTables(...tables) {
const rows = tables.reduce((a, b) => a.concat(b));
let max = 0;
const total = rows.map(it => it.frequence).reduce(Misc.sum(), 0);
return rows.map(row => {
const frequence = row.frequence;
row.min = max + 1;
row.max = max + frequence;
row.total = total
max += frequence;
return row;
})
}
static async getRandom(table, type, subTypes = ['objet'], forcedRoll = undefined, localisation = undefined) {
if (table.length == 0) {
ui.notifications.warn(`Aucun ${Misc.typeName(type, subTypes[0])} trouvé dans ${localisation ?? ' les compendiums'}`);
const typeName = Misc.typeName(type, subTypes[0]);
ui.notifications.warn(`Aucun ${typeName} trouvé dans ${localisation ?? ' les compendiums'}`);
return undefined;
}
return await CompendiumTableHelpers.selectRow(table, forcedRoll);
@ -255,7 +278,7 @@ export class CompendiumTableHelpers {
roll: row.roll,
document: row.document,
percentages,
typeName: Misc.typeName(type, row.document.type),
typeName: Misc.typeName(type, row.document?.type ?? 'objet'),
isGM: game.user.isGM,
});
const messageData = {

View File

@ -0,0 +1,328 @@
import { RdDItem } from '../item.js';
import { HtmlUtility } from '../html-utility.js';
import { Misc } from "../misc.js";
import { CompendiumTableHelpers } from '../settings/system-compendiums.js';
import { RdDRaretes } from '../item/raretes.js';
const FILTER_GROUPS = [
{ group: 'type', label: "Type d'objet" },
{ group: 'comestible', label: 'Alimentaire' },
{ group: 'utilisation', label: 'Utilisation' },
{ group: 'rarete', label: 'Rarete' },
{ group: 'qualite', label: 'Qualité' },
{ group: 'enc', label: 'Encombrement' },
{ group: 'prix', label: 'Prix' },
]
const FILTERS = [
{ group: 'comestible', code: 'comestible', label: 'Comestible', check: (item, milieux) => item.getUtilisation() == 'cuisine' },
{ group: 'comestible', code: 'pret', label: 'Préparé', check: (item, milieux) => item.getUtilisationCuisine() == 'pret' },
{ group: 'comestible', code: 'brut', label: 'A préparer', check: (item, milieux) => item.getUtilisationCuisine() == 'brut' },
{ group: 'comestible', code: 'boisson', label: 'Boisson', check: (item, milieux) => item.isBoisson() },
{ group: 'comestible', code: 'alcool', label: 'Alcool', check: (item, milieux) => item.isAlcool() },
{ group: 'comestible', code: 'immangeable', label: 'Immangeable', check: (item, milieux) => item.isInventaire() && item.getUtilisation() != 'cuisine' },
{ group: 'utilisation', code: 'alchimie', label: 'Alchimique', check: (item, milieux) => item.isInventaire() && item.getUtilisation() == 'alchimie' },
{ group: 'utilisation', code: 'soins', label: 'Médical', check: (item, milieux) => item.isInventaire() && item.getUtilisation() == 'soins' },
{ group: 'utilisation', code: 'poison', label: 'Toxique', check: (item, milieux) => item.isInventaire() && item.getUtilisation() == 'poison' },
{ group: 'utilisation', code: 'cuisine', label: 'Cuisine', check: (item, milieux) => item.isInventaire() && item.getUtilisation() == 'cuisine' },
{ group: 'utilisation', code: 'autres', label: 'Autres/inconnu', check: (item, milieux) => !item.isInventaire() || item.getUtilisation() == '' },
{ group: "qualite", code: "mauvaise", label: "Mauvaise (négative)", check: (item, milieux) => item.isInventaire() && item.system.qualite < 0 },
{ group: "qualite", code: "quelconque", label: "Quelconque (0)", check: (item, milieux) => item.isInventaire() && item.system.qualite == 0 },
{ group: "qualite", code: "correcte", label: "Correcte (1-3)", check: (item, milieux) => item.isInventaire() && 1 <= item.system.qualite && item.system.qualite <= 3 },
{ group: "qualite", code: "bonne", label: "Bonne (4-6)", check: (item, milieux) => item.isInventaire() && 4 <= item.system.qualite && item.system.qualite <= 6 },
{ group: "qualite", code: "excellente", label: "Excellente (7-9)", check: (item, milieux) => item.isInventaire() && 7 <= item.system.qualite && item.system.qualite <= 9 },
{ group: "qualite", code: "mythique", label: "Mythique (10+)", check: (item, milieux) => item.isInventaire() && 10 <= item.system.qualite },
{ group: "enc", code: "negligeable", label: "Négligeable (jusqu'à 0.1)", check: (item, milieux) => item.isInventaire() && item.system.encombrement <= 0.1 },
{ group: "enc", code: "leger", label: "Léger (0.1 à 0.5)", check: (item, milieux) => item.isInventaire() && 0.1 < item.system.encombrement && item.system.encombrement <= 0.5 },
{ group: "enc", code: "moyen", label: "Moyen (0.5 à 1.5)", check: (item, milieux) => item.isInventaire() && 0.5 < item.system.encombrement && item.system.encombrement <= 1.5 },
{ group: "enc", code: "lourd", label: "Lourd (1.5 à 3)", check: (item, milieux) => item.isInventaire() && 1.5 < item.system.encombrement && item.system.encombrement <= 3 },
{ group: "enc", code: "massif", label: "Massif (3 à 10)", check: (item, milieux) => item.isInventaire() && 3 < item.system.encombrement && item.system.encombrement <= 10 },
{ group: "enc", code: "anemort", label: "Un âne mort (plus de 10)", check: (item, milieux) => item.isInventaire() && 10 < item.system.encombrement },
{ group: "prix", code: "gratuit", label: "Gratuit", check: (item, milieux) => item.isInventaire() && item.system.cout == 0 },
{ group: "prix", code: "deniers", label: "Deniers (étain)", check: (item, milieux) => item.isInventaire() && 0 < item.system.cout && item.system.cout < 0.1 },
{ group: "prix", code: "bronze", label: "Sous (bronze)", check: (item, milieux) => item.isInventaire() && 0.1 <= item.system.cout && item.system.cout < 1 },
{ group: "prix", code: "sols", label: "Sols (argent)", check: (item, milieux) => item.isInventaire() && 1 <= item.system.cout && item.system.cout < 10 },
{ group: "prix", code: "dragons", label: "Dragons (or)", check: (item, milieux) => item.isInventaire() && 10 <= item.system.cout },
]
function $filterMilieux(milieux) {
return milieux.map(m => {
return {
code: m,
label: m,
check: (item, milieux) => item.isPresentDansMilieux(m)
}
})
}
function $filterRarete() {
return RdDRaretes.raretes()
.filter(it => it.frequence > 0)
.map(r => {
return {
group: 'rarete',
code: r.code,
label: r.label,
check: (item, milieux) => item.getRaretes(milieux).map(it => it.code).includes(r.code)
}
});
}
function $filterTypes() {
return RdDItem.getItemTypesInventaire().map(type => {
return {
group: 'type',
code: type,
label: Misc.typeName('Item', type),
check: (item, milieux) => item.type == type
};
});
}
function $getAllFilters() {
return FILTERS
.concat($filterTypes())
.concat($filterRarete());
}
function $addFilterToGroup(groups, filter) {
if (filter.group && filter.code && filter.label) {
let fg = groups.find(g => g.group == filter.group);
if (fg == undefined) {
groups.push({ group: filter.group, label: filter.group, filters: [filter] })
}
else if (fg.filters == undefined) {
fg.filters = [filter];
}
else {
fg.filters.push(filter);
}
}
else {
console.warn("Filtre incorrect, pas de groupe/code/label", filter);
}
}
function $loadFilters(parameters) {
$getAllFilters(parameters.milieux).forEach(f => $addFilterToGroup(parameters.filterGroups, f));
}
export class FenetreRechercheTirage extends Application {
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/tirage/fenetre-recherche-tirage.hbs",
title: `Recherches et tirages`,
width: 600,
height: 600,
popOut: true,
dragDrop: [{ dragSelector: "a.content-link" }],
resizable: true
});
}
static async create() {
const milieux = await game.system.rdd.environnement.milieux();
const parameters = {
milieux: milieux,
filterMilieux: $filterMilieux(milieux),
filterGroups: duplicate(FILTER_GROUPS).filter(it => it.group),
}
const options = {}
$loadFilters(parameters);
new FenetreRechercheTirage(parameters, options).render(true);
}
constructor(parameters, options) {
super(options);
this.parameters = parameters;
}
async getData() {
return mergeObject(await super.getData(), this.parameters)
}
_canDragStart() { return true; }
_onDragStart(event) { }
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
if (game.user.isGM) {
buttons.unshift({
class: "configurer",
label: "Configurer",
icon: "fas fa-cogs",
onclick: ev => this.configurer()
});
}
return buttons
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.showFilterGroup(this.html, false);
this.html.find("a.section-filters-toggle").click(event => {
const groupDiv = this.html.find(event.currentTarget)?.parents('div.section-filters-root').first();
const visible = groupDiv.find('div.section-filters-content').first().is(":visible");
this.showFilterGroup(groupDiv, !visible)
});
this.html.find("input:is(.activate-filter-group,.activate-filter-milieu)").change(event => this.changeListeFiltresActifs())
this.html.find("a.supprimer-filtres").click(async event => this.supprimerFiltres())
this.html.find("a.recherche-filtres").click(async event => await this.recherche())
this.html.find("a.tirage-filtres").click(async event => {
const table = await this.buildTable();
const row = await CompendiumTableHelpers.getRandom(table, 'Item')
await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item');
})
}
showFilterGroup(groupDiv, show) {
if (groupDiv) {
HtmlUtility.showControlWhen(groupDiv.find('div.section-filters-content'), show);
HtmlUtility.showControlWhen(groupDiv.find('i.section-filters-hide'), show);
HtmlUtility.showControlWhen(groupDiv.find('i.section-filters-show'), !show);
}
}
supprimerFiltres() {
this.html.find('input:is(.activate-filter-group,.activate-filter-milieu)').prop("checked", false);
this.html.find('div.liste-resultats').html('');
this.changeListeFiltresActifs();
}
async recherche() {
const table = await this.buildTable();
const htmlResultats = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/tirage/liste-resultats.hbs`, { resultats: table });
this.html.find('div.liste-resultats').html(htmlResultats);
this._dragDrop.forEach(dragDropHandler => dragDropHandler.bind(this.element[0]))
}
async buildTable() {
const milieux = this.getSelectedMilieux();
const filterItemMilieux = this.buildCheckedGroupFilter(milieux);
const filter = it => filterItemMilieux(it, milieux);
const itemFrequence = it => it.getFrequence(milieux);
return await game.system.rdd.environnement.buildTable(itemFrequence, filter)
}
buildMilieuxFilter(milieux) {
if (milieux) {
return this.buildOrFilter(this.parameters.filterMilieux.filter(it => milieux.includes(it.code)).map(f => f.check));
}
return (it, mi) => true;
}
buildCheckedGroupFilter(milieux) {
const filtersList = this.getGroupCheckedFilters()
.map(gf => this.buildOrFilter(gf.filters.map(f => f.check)));
if (milieux) {
filtersList.push(this.buildMilieuxFilter(milieux));
}
return this.buildAndFilter(filtersList)
}
buildAndFilter(filters) { return filters.reduce((f1, f2) => { return (it, mi) => f1(it, mi) && f2(it, mi); }); }
buildOrFilter(filters) { return filters.reduce((f1, f2) => { return (it, mi) => f1(it, mi) || f2(it, mi); }); }
changeListeFiltresActifs() {
const milieux = this.getSelectedMilieux();
const summariesList = this.getGroupCheckedFilters()
.map(gf => {
return gf.group.label + ': ' + gf.filters
.map(f => f.label)
.reduce(Misc.joining(', '))
});
if (milieux) {
summariesList.push('Milieux: ' + this.parameters.filterMilieux.filter(f => milieux.includes(f.code)).map(f => f.label).reduce(Misc.joining(', ')))
}
const fullText = summariesList.length == 0 ? "" : summariesList.reduce(Misc.joining(' - '));
this.html.find('span.liste-filtres-actifs').text(fullText);
}
getGroupCheckedFilters() {
const checkedGroupFilters = jQuery.map(this.html.find('input.activate-filter-group:checked'), it => this.html.find(it))
.map(element => {
return {
group: element.data('group'),
code: element.data('code')
};
}).filter(it => it.group);
const entries = Object.entries(Misc.classify(checkedGroupFilters, it => it.group));
return entries.map(([key, list]) => {
const group = this.parameters.filterGroups.find(g => g.group == key);
const filters = list.map(it => group.filters.find(f => it.code == f.code));
return { group, filters };
});
}
getSelectedMilieux() {
const milieux = jQuery.map(this.html.find('input.activate-filter-milieu:checked'), it => {
return this.html.find(it).data('code');
});
return milieux.length == 0 ? undefined : milieux
}
async configurer() {
FenetreRechercheConfiguration.create();
}
}
class FenetreRechercheConfiguration extends Dialog {
static async create() {
const configuration = {
compendiums: game.packs.filter(it => it.metadata.type == 'Item').map(it => it.metadata)
.map(it => mergeObject({ selected: game.system.rdd.environnement.compendiums.includes(it.id) }, it))
}
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/tirage/fenetre-recherche-configuration.hbs", configuration);
new FenetreRechercheConfiguration(html).render(true);
}
constructor(html) {
const options = {
classes: ["fenetre-recherche-configuration"],
width: 600,
height: 'fit-content',
'max-height': 600,
height: 'fit-content',
'z-index': 99999
};
const conf = {
title: 'Configuration de la recherche',
content: html,
buttons: {
"Sauvegarder": { label: "Sauvegarder", callback: async it => { await this.sauvegarder(); } }
}
};
super(conf, options)
}
activateListeners(html) {
this.html = html;
super.activateListeners(html);
this.html.find("button.configuration-save").click(event => this.sauvegarder())
}
async sauvegarder() {
const compendiumIds = jQuery.map(this.html.find("input.select-compendium:checked"), it => {
return this.html.find(it).data('id');
});
await game.system.rdd.environnement.saveCompendiums(compendiumIds);
this.close();
}
}

View File

@ -12,10 +12,10 @@ export class TMRRencontres {
static init() {
const tmrRencontre = new TMRRencontres();
game.system.rdd.rencontresTMR = tmrRencontre;
}
constructor(){
constructor() {
this.table = new CompendiumTable('rencontres', 'Item', 'rencontre', Misc.ascending(it => it.system.ordreTri));
}
@ -59,24 +59,16 @@ export class TMRRencontres {
return rencontre.clone({
'system.force': await RdDDice.rollTotal(rencontre.system.formule),
'system.coord': tmr?.coord,
'system.date': game.system.rdd.calendrier.getDateFromIndex(),
'system.heure': game.system.rdd.calendrier.getCurrentHeure()
'system.date': game.system.rdd.calendrier.dateCourante(),
'system.heure': game.system.rdd.calendrier.heureCourante().key
}, { save: false });
}
async calculRencontre(rencontre, tmr = undefined) {
if (rencontre.system.coord == "") {
rencontre.system.coord = tmr?.coord;
}
if (rencontre.system.force == 0) {
rencontre.system.force = await RdDDice.rollTotal(rencontre.system.formule);
}
if (rencontre.system.date == "") {
rencontre.system.date = game.system.rdd.calendrier.getDateFromIndex();
}
if (rencontre.system.heure == "") {
rencontre.system.heure = game.system.rdd.calendrier.getCurrentHeure();
}
rencontre.system.coord = rencontre.system.coord ?? tmr?.coord;
rencontre.system.force = rencontre.system.force ?? await RdDDice.rollTotal(rencontre.system.formule);
rencontre.system.date = rencontre.system.date ?? game.system.rdd.calendrier.dateCourante();
rencontre.system.heure = rencontre.system.heure ?? game.system.rdd.calendrier.heureCourante().key;
return rencontre;
}
@ -110,7 +102,7 @@ export class TMRRencontres {
/* -------------------------------------------- */
async $chatRolledRencontre(row, rencontre,tmr) {
async $chatRolledRencontre(row, rencontre, tmr) {
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll-rencontre.html',
{
roll: row.roll,

File diff suppressed because one or more lines are too long

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