Compare commits

...

271 Commits

Author SHA1 Message Date
3821b5bea1 Merge pull request 'v11.0.9 - Les Souvenirs de Khrachtchoum le Problémeux' (#657) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #657
2023-06-21 10:40:17 +02:00
2739c650d9 Version 11.0.9 - Les Souvenirs de Khrachtchoum 2023-06-20 23:43:59 +02:00
b101d58bee Correction typo "optionnelle" 2023-06-20 23:43:58 +02:00
0e3d721fc7 Nouvelle incarnation de l'archétype 2023-06-20 23:35:38 +02:00
e1e45a83be Correction titre des fenêtres Item 2023-06-20 23:35:38 +02:00
3b14e54829 Mode de saisie d'archétype 2023-06-20 23:35:38 +02:00
615a3f1315 Organisation des règles optionelles 2023-06-20 03:05:48 +02:00
ca72d088cc Merge pull request 'v11 - report v10.7.20' (#656) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #656
2023-06-19 15:36:53 +02:00
ac89b948ca Mise à jour du système
- lien vers le change log
- organisation des compendiums par défaut
2023-06-16 23:33:44 +02:00
f22c4d2ec8 Fin d'empoignade
En cours de round en atteignant 2 points d'empoignade, on peut
uniquement entraîner au sol.

En fin de round, si la victime est immobilisée, on peut projeter, ou
faire perdre de l'endurance
2023-06-15 02:03:49 +02:00
ffd80394d3 Restreindre les actions d'empoignade
Seul le propriétaire du défenseur peut effecuer les contres
d'empoignade ou tenter de se libérer.

Seul le propriétaire de l'empoigneur peut tenter d'empêcher
la libération du défenseur, de projeter au sol, ou de faire perdre
de l'endurance.
2023-06-15 02:03:43 +02:00
0959d1b18e Correction malus de taille empoignade 2023-06-15 02:03:37 +02:00
24e58abfcc Fix empoignade
- les items d'empoignade sont ajoutés par le MJ
- les caractéristiques du défenseur sont utilisées pour la défense
- la difficulté d'attaque est imposée au défenseur
- les particulières sont en finesse (p133)
2023-06-15 02:03:30 +02:00
7d292c009d Update v11 branch 2023-06-14 08:31:14 +02:00
4bf075786c Merge pull request 'v11 report v10.7.19' (#654) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #654
2023-06-14 08:28:49 +02:00
a2a04ade4c Migration des compendiums 2023-06-14 02:12:32 +02:00
2db6895c76 Gestion des difficultés de Possession
- gestion de la difficulté imposée sur la défense
- gestion des particulières en attaque considérées en finesse
- utilisation du rêve actuel pour les personnages
2023-06-14 02:12:28 +02:00
e46f35ef92 Catégories des compétences de créatures
Les créatures peuvent avoir des compétences d'armes (lancer,
mêlée, tir, armes naturelles), de parade, de possession, et générales.

Les initiatives sont cohérentes. Les possessions sont en phase 10
d'initiative.
2023-06-14 02:12:22 +02:00
9e050d3e12 Fix commerce achat MJ sans personnage
Correction sur les achats: l'objet acheté vient forcément soit d'un
personnage-vendeur, soit des Items globaux.

Ne pas essayer d'acheter autrement car on aurait des données d'item
non structurées, et donc ça ne fonctionnerait pas.
2023-06-14 02:12:15 +02:00
81492f2857 Merge pull request 'v11.0.6 - Report v10.7.18' (#652) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #652
2023-06-08 06:51:57 +02:00
959e751e48 Correction rendu changelog en ligne 2023-06-08 01:47:04 +02:00
b6a124b57d v11.0.6 2023-06-08 01:44:17 +02:00
db2ca945a8 Fix: la date des blessures ne marchait plus
la liste des types (pour aider à la saisie) est une idée mitigée

- ça évite les fautes d'orthographe dans les constantes de types
Mais:
- on peut oublier un type
- si le type n'est pas défini, il est undefined
  (donc risques de regressions)

Saisie de tous les types du template.
2023-06-08 01:35:06 +02:00
667d764db1 Cherry pick v10 gixes 2023-06-05 20:19:42 +02:00
4273f5f48f Fix: validation encaissement MJ 2023-06-05 20:18:05 +02:00
6c4a8eb70f Correction de liens
Quelques liens pointaient sur les icones du bazaar de forge-vtt
2023-06-05 20:17:40 +02:00
eb029e8d66 Fix affichage des "objets" dans les commerces
La confusion entre les Item "objets" et le champ formData.objets
rendait les Item "objets" indisponibles (par exemple, les vêtements)
2023-06-05 20:17:19 +02:00
9ee14b3b56 Add .db again? 2023-06-04 22:17:02 +02:00
3203fae111 Add .db again? 2023-06-04 20:53:15 +02:00
755301275b Add .db again? 2023-06-04 20:51:49 +02:00
f6d5dc9d7c Add .db again? 2023-06-04 20:49:55 +02:00
5e9b47af1f Add .db again? 2023-06-04 20:47:47 +02:00
18dfb36a78 v11 release 2023-05-29 13:47:08 +02:00
febccd508f v11 release 2023-05-29 13:40:38 +02:00
b147f8937b v11 release 2023-05-29 13:33:31 +02:00
55a93b317a v10/v11 compat 2023-05-29 08:50:13 +02:00
02a630152d v10/v11 compat 2023-05-29 08:50:08 +02:00
ad8ac0de77 v10/v11 compat 2023-05-29 08:42:16 +02:00
dceb6f026f v10/v11 compat 2023-05-29 08:41:59 +02:00
ad37ee1b8e v10/v11 compat 2023-05-29 08:38:22 +02:00
4d40e2fe59 Merge pull request 'cherry-pick changements v10' (#647) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #647
2023-05-29 07:47:58 +02:00
9d7e57d9fb Changelog v11 2023-05-28 22:11:16 +02:00
717029e572 Tri des listes d'items 2023-05-28 22:11:16 +02:00
d6f8976298 Utilisation de la dateReel du calendrier 2023-05-28 22:11:16 +02:00
92e93cbbea Refonte du journal d'expérience
Reprise du journal d'expérience pour:
- afficher ancienne/nouvelle valeur
- la valeur du changement
- si c'est manuel / automatique
- identifier les dépenses de stress
- identifier les augmentations de compétences
- les changements des compteurs
2023-05-28 22:11:16 +02:00
994eaad9a9 Ajout des acteurs accordés aux entités 2023-05-28 22:11:16 +02:00
cfed7e2afa v10/v11 compat 2023-05-25 21:16:59 +02:00
436f9e6fa4 v10/v11 compat 2023-05-25 21:06:03 +02:00
18a50197cd v10/v11 compat 2023-05-25 21:05:43 +02:00
b6910612f8 v10/v11 compat 2023-05-25 21:04:27 +02:00
4ba1ec8a2e v10/v11 compat 2023-05-25 21:04:02 +02:00
75a0f7c322 v10/v11 compat 2023-05-25 20:40:59 +02:00
e2a9f55740 Initial commit for v11 support 2023-05-25 20:39:53 +02:00
1e0788cde8 Merge pull request 'v11' (#645) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #645
2023-05-25 20:37:01 +02:00
c10195f753 Version 10.7.13 2023-05-25 20:23:54 +02:00
4d7317b964 Gestion de l'armure
Correction de la détérioration d'une armure variable

Séparation du code d'armure dans l'Item RdDArmureItem
2023-05-25 20:20:43 +02:00
7b1fa009bb Utilisation de constantes pour les types
(sans effet)
2023-05-12 22:56:59 +02:00
71bb8a14e1 Fix sieste 2023-04-28 08:35:26 +02:00
5444bc7fd8 Merge pull request 'Changelog et fix mineur' (#643) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #643
2023-04-28 08:33:59 +02:00
debaf1da34 Changelog v10.6 & v10.7
Récupéré des messages postés
2023-04-27 14:20:35 +02:00
69e2ab9000 Fix: sélection de sieste pour le repos
Quand le MJ gère les changements de jours, la fenêtyre de repos ne
proposait pas les options dormir/chateau dormant, mais utilisait
l'option "dormir" par défaut
2023-04-27 14:20:35 +02:00
24218ed062 Changelog 2023-04-22 20:34:30 +02:00
da4e18dc3c Gestion de l'empoignade 2023-04-22 07:55:26 +02:00
154a9b7a37 Gestion de l'empoignade 2023-04-21 23:09:40 +02:00
fb8189606b Merge pull request 'v10: pinaillages mineurs' (#642) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #642
2023-04-21 23:08:10 +02:00
cfbfad27bd cleanup Empoignade 2023-04-21 22:30:21 +02:00
f541849306 Centralisation du message empoignade
Déplacement dans le module empoignade du message et de
la vérification d'empoignade en cours.

Le message est donc centralisé (possible de le modifier une fois pour
toutes les utilisations)
2023-04-21 22:30:21 +02:00
d2d1891838 cleanup itemTypes
Utilisation de itemTypes plutôt que de méthode listItems ou de
filtrer les items par type.

Potentiellement, itemTypes peut être précalculé par Foundry
C'est aussi un peu plus lisibles (conditions du filter moins longues,
et le filtrage par type est mis en avant en premier)
2023-04-21 22:30:21 +02:00
5580b6d59c Debug: à la recherche de l'Item qui ne se vend pas 2023-04-21 21:38:21 +02:00
40c45c30de Equiper des vêtements, bijoux, ...
Permettre d'équiper les objets "de base" en plus des armes et armures
2023-04-21 21:38:21 +02:00
1b6c5256cc Gestion de l'empoignade 2023-04-21 18:18:41 +02:00
c0e6759164 Gestion de l'empoignade 2023-04-21 18:18:20 +02:00
a096590a07 Merge pull request 'Version 10.7.7 - Les bobos de Sémolosse' (#641) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #641
2023-04-15 07:42:39 +02:00
9883048fb5 Version 10.7.7
Les bobos de Sémolosse
2023-04-14 20:57:16 +02:00
7bec42c4cf Fix Mise à jour du texte de l'heure joueurs 2023-04-14 20:31:47 +02:00
6708dc0da9 Fix rituels Lecture/Détection aura Hypnos 2023-04-14 20:31:15 +02:00
96c2da0d05 Fix: thème astral par un joueur
La recherche des chjiffres astrologiques par les joueurs ne marchait
plus
2023-04-14 01:19:44 +02:00
2b19250e8b Log: debug cas d'échec achatVente 2023-04-07 01:14:50 +02:00
957eabcac7 Fix: sélection sous l'horloge 2023-04-07 01:14:50 +02:00
285937c201 Merge pull request 'Version 10.7.6 - L'origine des maux de Sémolosse' (#640) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #640
2023-03-31 07:22:28 +02:00
93965ce91a Version 10.7.6 2023-03-31 01:42:25 +02:00
18aba9adff Pas de bordure pour les boutons avance rapide 2023-03-31 01:32:52 +02:00
0a5662ff71 Couleurs jour nuit plus visibles 2023-03-31 01:32:24 +02:00
bf98e4eae2 Fix toggle horloge
Affiche/masque correctement l'horloge pour le MJ et les joueurs
2023-03-31 01:31:09 +02:00
e32a1d25f6 Ne pas fermer l'horloge sur escape
Surcharge de la méthode close pour empêcher la fermeture
2023-03-31 00:51:05 +02:00
327943c4aa Fix mise à jour sur action des herbes/potions
Par exemple, la mise à jour de quantité d'herbe ne se faisait pas
dans les liste d'équipement des feuilles de conteneurs, lors d'une
fabrication de potion dans les items
2023-03-31 00:47:25 +02:00
de56fa909a Ajout bouton Item diminuer quantité
Pour diminuer la quantité des objets multiples sans avoir à éditer
2023-03-30 02:56:06 +02:00
2aa62cffe9 Bonus de sorts en fleuve
Le bonus de sort en fleuve est identique pour toutes les cases fleuve.
2023-03-30 01:31:41 +02:00
c57f140c54 Ajout de l'origine d'une blessure
En combat, indication de l'origine des blessures
2023-03-30 00:19:18 +02:00
9cf14f8b75 Merge pull request 'Version 10.7.5 - La montre-gousset de Sémolosse' (#639) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #639
2023-03-29 23:06:31 +02:00
b8e7b21e14 Version 10.7.5
La montre-gousset de Sémolosse
2023-03-29 23:00:23 +02:00
c1d02d9fda Ajout d'une horloge analogique
Amélioration de la fenêtre calendrier:
* plus compacte
* horloge analogique
* normalement compatible pop-out
* minimisable (juste la barre de titre)
2023-03-29 23:00:23 +02:00
23af30a538 Merge pull request 'Version 10.7.4 - Les ligatures de Sémolosse' (#638) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #638
2023-03-24 23:16:49 +01:00
4e1b663dec Version 10.7.4
Les ligatures de Sémolosse
2023-03-24 21:37:32 +01:00
c9d98c57da Cleanup: templates non internes 2023-03-24 21:26:42 +01:00
0aef139cf8 Fix: boutons pour réserve en sécurité 2023-03-24 21:26:42 +01:00
9c85293714 Fix: bouton pour jet de vie sur critique 2023-03-24 21:26:42 +01:00
ab1c04ae17 Fix: La chirurgie dans les tâches
Re-déplacement des tâches de chirurgie, maintenant que les soins de
blessures sont facilités (par les tokens)
2023-03-24 21:26:42 +01:00
a585e0faba Fix: affichage bonus de cases
Les bonus de cases sont visibles pour les sorts attachés à un personnage
2023-03-24 21:26:42 +01:00
50c730ba72 Fix: queues non refoulables 2023-03-24 21:26:42 +01:00
928a60f092 Add flags 2023-03-23 17:03:40 +01:00
e5c2f52b0e Merge pull request 'Version 10.7.3 - Les tisanes de Sémolosse' (#637) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #637
2023-03-23 17:02:56 +01:00
f9391523f8 Version 10.7.3
Les tisanes de Sémolosse
2023-03-23 01:44:35 +01:00
55434762f4 Masquer bonus premiers soins si soins complets 2023-03-23 01:43:41 +01:00
5e6ffc7846 Fix: bonus d'herbe de potion non enchantée 2023-03-23 01:42:48 +01:00
3344e20936 Fix: consommation potion enchantée 2023-03-23 01:41:37 +01:00
5a66e4e741 Fix horloge aiguille des heures
L'aiguille des heures pointe sur l'heure au début de l'heure
draconique, comme pour une montre.

Début couronne, l'aiguille des heures et des minutes sont donc
toutes les deux en haut.
2023-03-21 22:00:24 +01:00
d329724d63 Merge pull request '10.7.2 - les maux de dents de Semolosse' (#636) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #636
2023-03-20 23:32:08 +01:00
8a5405c9f5 Version 10.7.2
les maux de dents de Semolosse
2023-03-20 23:29:46 +01:00
ea992aae46 Fix: récupération des blessures 2023-03-20 23:28:25 +01:00
0bfcfec58f Fix jet carac 2023-03-16 23:17:52 +01:00
88e00c59bc Fix actor 2023-03-16 23:12:08 +01:00
d314dc39a0 Fix actor 2023-03-16 23:10:17 +01:00
30bb803da2 Merge pull request 'v10.7.0 - l'os de Sémolosse' (#635) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #635
2023-03-16 23:09:10 +01:00
dd79e16ea5 Version 10.7.0 2023-03-15 01:29:55 +01:00
83e2d56fd4 Boutons soins&blessures 2023-03-15 01:29:48 +01:00
afc23dfa7b Fix: message sans jet d'endurance 2023-03-15 01:29:48 +01:00
3e189cbe5f Ajout des blessures sur encaissement 2023-03-15 01:29:48 +01:00
d0475e8677 Suppression anciennes blessures 2023-03-15 00:41:48 +01:00
a3694c1673 Cleanup 2023-03-15 00:41:47 +01:00
9e6d5856b1 Gestion des soins avancés
- raccourci soins HUD
- edition rapide des Item blessure
- gestion des taches de soins liées aux blessures
2023-03-15 00:41:47 +01:00
acc880b53f Preparation soins HUD 2023-03-15 00:41:47 +01:00
2598ae3489 Ajout item Blessure 2023-03-15 00:35:22 +01:00
4f5fb63751 Tri alphabétique des constantes 2023-03-15 00:35:21 +01:00
d739a7993a rollCaracCompetence avec dialog 2023-03-15 00:35:21 +01:00
41335cd433 Renommage méthode jet sans dialog 2023-03-15 00:35:21 +01:00
e470d76ea0 Méthode de fenêtre de jet partagée 2023-03-15 00:35:21 +01:00
ffccc819f1 Fix: merge d'options sur token 2023-03-15 00:35:21 +01:00
45bfc69b39 Ajout d'option pour informer si pas de cible
Pour le HUD, on peut vouloir utiliser la cible (soins)
2023-03-15 00:23:21 +01:00
79e9358072 Préparation Calendrier avant migration
Les migrations peuvent avoir besoin du temps courant.
Le calendrier est recréé après.
2023-03-15 00:23:21 +01:00
5cab418e62 Merge pull request 'v10.6.25 - fix erreur synchro astrologie/heure' (#634) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #634
2023-03-14 22:41:45 +01:00
7809f7c21a Version 10.6.25 2023-03-14 22:14:04 +01:00
1bac204071 Fix astrologie 2023-03-14 21:56:59 +01:00
3bf5beb67b Sync merge manuel 2023-03-09 23:49:37 +01:00
ac1da6e979 Update archetype/theme 2023-03-09 23:25:34 +01:00
67735197bc Merge pull request 'Version 10.6.21 - La théière de Pralinor' (#632) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #632
2023-03-08 23:17:45 +01:00
f81dc9a5f5 Version 10.6.21
La théière de Pralinor
2023-03-08 23:08:40 +01:00
a563233d6b Fix: les PNJs peuvent de nouveau dormir 2023-03-08 23:08:40 +01:00
0c11013694 Ajout Aiguille Horloge 2023-03-08 22:48:40 +01:00
77cf72a752 Intégration astrologie
Intégration du thème astral dans les fenêtres d'astrologie
2023-03-08 22:34:05 +01:00
42ed5da2d4 Création de tableaux d'entiers
ajout d'une méthode propre pour construire un tableau d'entiers
consécutifs
2023-03-08 02:00:38 +01:00
5fd3a43b2a Merge pull request 'Version 10.6.20 - Les Oracles de Pralinor: vous mangerez à Couronne' (#631) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #631
2023-03-07 13:32:21 +01:00
1bb710ce83 Version 10.6.20
Les Oracles de Pralinor: vous mangerez à Couronne
2023-03-07 11:13:56 +01:00
80579032ea Ajout de la fenêtre de thème astral
Accessible par macro/depuis les fenêtres d'astrologie
2023-03-07 10:46:31 +01:00
a810e20eca Merge pull request 'Version 10.6.19 - La cerise de Pralinor' (#630) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #630
2023-02-24 09:56:29 +01:00
c6f0aaeba0 Version 10.6.19 - La cerise de Pralinor 2023-02-24 00:50:06 +01:00
3c3be7409d Fix: problème de nombre d'utilisations 2023-02-24 00:38:14 +01:00
f97345e407 Fix: recherche avec droits limités 2023-02-24 00:33:49 +01:00
4cc50fc190 Ajout Haubert d'Oniros 2023-02-18 12:26:58 +01:00
26967fe1cd Merge pull request '10.6.18 - Les insomnies de Pralinor' (#629) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #629
2023-02-17 10:16:29 +01:00
b23dcaace5 Version 10.6.18
Les insomnies de Pralinor
2023-02-17 01:31:18 +01:00
045ee76013 Fix: transfert du moral à chateau dormant 2023-02-17 01:29:21 +01:00
7fed3844b5 Fix: calcul du nombre d'heures dormi
Le calcul était incorrect (5 au lieu de 4), du coup Chateau Dormant
ne se déclenchait pas.
2023-02-17 01:28:30 +01:00
b5571e6c7a Merge pull request 'v10.6.17 - Les désordres de Pralinor' (#628) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #628
2023-02-14 08:13:44 +01:00
b5eac8ccfa Version 10.6.17
Les désordres de Pralinor
2023-02-13 22:49:21 +01:00
673196c644 Fix: trier le contenu 2023-02-11 21:20:12 +01:00
9741028914 Fix: suppression doublons 2023-02-11 20:52:29 +01:00
10daab2bcd Merge pull request 'v10.6.16' (#627) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #627
2023-02-11 17:05:42 +01:00
99d2d38279 Version 10.6.16 2023-02-11 01:24:58 +01:00
e30825db6b Commerce "Liste d'équipement" 2023-02-11 01:24:58 +01:00
10ed4d9382 Corrections d'équipements
Séparation de:
- flèches et carreaux (qui deviennent des munitions)
- Luth et viole
- Burin, gouge, ciseau
- Lime, Râpe
- Alène, poinçon

Ajout de l'herbe à pipe
Ajout de prix à l'herbe de lune (qualité 3 par défaut)

Sang et Liquide deviennent de bêtes objets
2023-02-11 01:24:58 +01:00
2ffa0d8dc7 Fix: utiliser les compétences des humanoïdes
Utiliser les compétences de l'acteur empêchait l'édition des armes
(et objets liés à une compétence) stockées sur un animal, dans un
véhicule, ou dans un commerce
2023-02-11 01:24:58 +01:00
f229537206 Merge pull request 'v10.6.15' (#626) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #626
2023-02-10 21:37:33 +01:00
5c31c7bd7f Version 10.6.15 2023-02-10 21:30:30 +01:00
4cf428f630 Fix: messages et insomnie
Amélioration des messages de sommeil (nombre d'heure, seulement les
récupérations de rêve effectives, ...)

Les insomnies ne durent bien que 12h draconique à partir du prochain
chateau dormant (elles pouvaient durer 3 jours par erreur).
2023-02-10 02:01:55 +01:00
0c1b70f3f0 Fix: recherche dans arbre de conteneurs
On affiche bien tous les conteneurs intermédiaires
2023-02-10 02:01:55 +01:00
b7ea857bb4 Merge pull request '10.6.14' (#625) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #625
2023-02-09 07:22:14 +01:00
0238976ce8 Version 10.6.14 2023-02-08 21:17:20 +01:00
31fd91336a Correction affichage calendrier
Correction de l'affichage incorrect pour Chateau Dormant et
Poisson Acrobate

Nettoyage du css
2023-02-08 21:09:07 +01:00
c70b6c9d5f Dialog chateau-dormant
Pour permettre de pré-remplir les infos de chateau dormant
2023-02-08 21:09:07 +01:00
07d0d92f57 Suppression code commentaire 2023-02-08 00:24:05 +01:00
a2968697f4 Jet de moral neutre sans effet si moral à 0 2023-02-07 21:37:43 +01:00
fb7dbe6ea0 Merge pull request 'v10.6.13' (#624) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #624
2023-02-07 19:28:34 +01:00
59641bf5cf Version 10.6.13
Recherche dans les commerces
2023-02-07 18:08:13 +01:00
4ee4445836 Fix: message sur jet de moral neutre 2023-02-07 18:07:26 +01:00
1a346a21c3 Recherche dans les commerces 2023-02-07 18:07:26 +01:00
c6c0dd43fd Merge pull request 'Recherche dans l'inventaire' (#623) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #623
2023-02-05 23:00:20 +01:00
d69a07d4dd Version 10.6.12 2023-02-05 20:32:47 +01:00
56379a9234 Fix: bouton d'ajout d'objet 2023-02-05 20:30:19 +01:00
fbcc167272 Recherche dans l'inventaire
Par nom ou par type, et dans les contenants
2023-02-05 20:26:51 +01:00
703ab2579d Merge P3 2023-02-03 08:35:02 +01:00
6b7e881bf2 Merge pull request 'v10.6.11' (#621) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #621
2023-02-03 08:34:06 +01:00
dc3ceb1732 Déplacements des effets de rencontre
classe placée dans le dossier "tmr"
2023-02-03 02:23:46 +01:00
7a0132cf8d La griffe morbide donne un poison
Ajouter l'effet "poison: griffe morbide de thanatos" à la victime pour
agir "comme" un poison (sauf qu'un jet de vie jamais positif remplace
l'habituel jet de constitution).
2023-02-03 02:23:46 +01:00
ca1ea5a854 La récupération est bloquée par les maladies
Pas de récupération de vie ou de blessures possibles sous l'effet d'un
poison ou d'une maladie
2023-02-03 02:23:46 +01:00
9bdde92d61 Minot fixes 2023-02-02 08:56:00 +01:00
74313b7fd3 Merge pull request 'fixes' (#620) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #620
2023-02-02 08:55:24 +01:00
9a9399a581 Alchimie: températures et difficulté
- toujours afficher les difficultés des températures/consistances
- renommer "couleur" en "température" à l'affichage
- mise à jour des recettes du compendium
2023-02-02 01:24:42 +01:00
95a7ac73d0 Fix: description en mode edition 2023-02-02 01:21:06 +01:00
ab4c118d12 Fix: Sustentation consommée parfois concaténée 2023-02-02 00:53:32 +01:00
681358238d Merge branch 2023-02-01 10:11:33 +01:00
5511acc876 Merge pull request 'Alignement description en haut' (#619) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #619
2023-02-01 10:10:42 +01:00
2e144851d4 Alignement description en haut 2023-01-31 21:40:35 +01:00
9f24aee1f3 Merge pull request 'Recherche par nom' (#618) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #618
2023-01-31 07:32:09 +01:00
e605ab405a Version 10.6.8 2023-01-30 22:08:11 +01:00
86f69566a6 Recherche par nom
Dans la fenêtre de recherche, possibilité de chercher sur le nom
des objets en plus des autres critères
2023-01-30 22:08:11 +01:00
2561a658f2 Merge pull request 'v10.6.7' (#617) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #617
2023-01-28 20:50:52 +01:00
8a1d1fd253 Version 10.6.7 2023-01-28 18:08:47 +01:00
e42f384d7f Masquer le bouton si pas utilisable
Pour créer un nouvel objet, il faut être "owner"
2023-01-28 16:46:17 +01:00
d381191692 Monnaie pour gérer la fortune 2023-01-28 16:35:45 +01:00
53a7230f1d Fix: Actions dans un conteneur
Les actions dans un conteneur (ouvrir un des sous conteneurs par
exemple) n'étaient pas disponibles.
Ceci était lié à la structure des données contenant les droits
différente pour les items et les acteurs.

La gestion des droits était mélangées et faite de façons différentes.
Maintenant, les "options" dans les données du formulaire contiennent
les informations de droits d'accès et sont utilisées.
2023-01-28 16:34:09 +01:00
4d5651c2d3 Reprise de l'arbre d'inventaire
Soucis lors de l'affichage dans la fenêtre d'un conteneur
2023-01-28 15:43:31 +01:00
61474172a7 Fix: gestion de la fenêtre de vente
les nombre de lots ne marchaient plus (toujourts 1 de proposé)
2023-01-28 11:36:22 +01:00
083806612b Merge pull request 'v10.6.7' (#616) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #616
2023-01-25 10:26:26 +01:00
1c417d02a1 Version 10.6.6 2023-01-24 22:35:39 +01:00
ee9e7d2adb Messages compétences troncs en notifications
Lors d'une création de perso, ça spamme trop le tchat
2023-01-24 22:33:45 +01:00
fa3334273d Fix: ligne de carac vide à cause de la beauté
Une lighne de carac était ajouée lors de l'édition de la beauté.
2023-01-24 22:33:45 +01:00
05b73a41a5 Inversion: Taille puis poids
C'est dans cet ordre dans les scénarios
2023-01-24 22:33:45 +01:00
842243485a Corrections d'armes rudimentaires 2023-01-24 22:12:43 +01:00
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
229 changed files with 7617 additions and 4584 deletions

5
.gitignore vendored
View File

@ -1,5 +1,6 @@
.vscode/settings.json
.idea
.history
todo.md
/.vscode
/ignored/
@ -7,3 +8,7 @@ todo.md
/jsconfig.json
/package.json
/package-lock.json
/packs/*/
/packs/*/CURRENT
/packs/*/LOG
/packs/*/LOCK

View File

@ -1,10 +1,32 @@
==================================================================
v0.9.2 - 05/09/2020
# v11.0
Erreur de calcul sur points de vie
Gestion différente des compétences "troncs"
## v11.0.9 - Les Souvenirs de Khrachtchoum le Problémeux
- mode de saisie de l'archétype en vue détaillée
- création une nouvelle incarnation depuis l'archétype
- réorganisation de la fenêtre de sélection des règles optionnelles
- correction de l'affichage du type dans les fenêtres d'objets
==================================================================
v0.9.1 - 03/09/2020
## v11.0.8 - la poigne de Sémolosse
- lien vers le changelog
- organisation des compendiums du système
- correction de l'empoignade
- les items d'empoignade sont ajoutés par le MJ quand nécessaire
- seul le joueur propriétaire du personnage peut effectuer ses choix et actions d'empoignade
- les caractéristiques du défenseur sont utilisées pour la défense
- la difficulté d'attaque est imposée au défenseur
- les attaques particulières sont en finesse (p133)
- on peut entraîner au sol dès 2 points d'empoignade
- les actions liée à l'immobilisation sont proposées en fin de round
# v11.0.7
- les créatures ont maintenant le droit d'avoir des compétences de tir, lancer, mêlée, armes naturelles, parade.
- les créatures armées utilisent la bonne phase d'initiative
- correction des possessions
- la difficulté de la défense est imposée par l'attaque
- une attaque particulière de possession est en finesse
- le rêve actuel des personnages est bien utilisé
- correction des achats par le MJ sans acteur sélectionné
Cf branche v10 pour l'historique des versions 10
Initial official release

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
icons/sante/blessure.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
icons/sante/eraflure.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
icons/sante/mort.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -1,52 +1,56 @@
{
"ACTOR": {
"TypePersonnage": "Personnage",
"TypeCreature": "Créature",
"TypeEntite": "Entité de cauchemar",
"TypeCommerce": "Commerce",
"TypeVehicule": "Véhicule"
},
"ITEM": {
"TypeObjet": "Objet",
"TypeGemme": "Gemme",
"TypeCompetence": "Compétence",
"TypeCompetencecreature": "Compétence de créature",
"TypeMaladie": "Maladie",
"TypePoison": "Poison",
"TypeNombreastral": "Nombre astral",
"TypeTarot": "Carte de tarot",
"TypeCasetmr": "TMR spéciale",
"TypeRencontre": "Rencontre TMR",
"TypeMunition": "Munition",
"TypeMonnaie": "Monnaie",
"TypeHerbe": "Herbe ou plante",
"TypeIngredient": "Ingrédient",
"TypeFaune": "Faune",
"TypeLivre": "Livre",
"TypePotion": "Potion",
"TypeArme": "Arme",
"TypeArmure": "Armure",
"TypeConteneur": "Conteneur",
"TypeNourritureboisson": "Nourriture & boisson",
"TypeService": "Service",
"TypeChant": "Chant",
"TypeDanse": "Danse",
"TypeMusique": "Musique",
"TypeOeuvre": "Oeuvre",
"TypeTache": "Tâche",
"TypeJeu": "Jeu",
"TypeRecettealchimique": "Recette alchimique",
"TypeRecettecuisine": "Recette de cuisine",
"TypeSort": "Sort",
"TypeMeditation": "Méditation",
"TypeSignedraconique": "Signe draconique",
"TypeQueue": "Queue de Dragon",
"TypeOmbre": "Ombre de Thanatos",
"TypeSouffle": "Souffle de Dragon",
"TypeTete": "Tête de Dragon",
"TypePossession": "Possession",
"TypeSortreserve": "Sort en réserve",
"TypeExtraitpoetique": "Extrait poetique"
"TYPES": {
"Actor": {
"Personnage": "Personnage",
"Creature": "Créature",
"Entite": "Entité de cauchemar",
"Commerce": "Commerce",
"Vehicule": "Véhicule"
},
"Item": {
"Arme": "Arme",
"Armure": "Armure",
"Blessure": "Blessure",
"Casetmr": "TMR spéciale",
"Chant": "Chant",
"Competence": "Compétence",
"Competencecreature": "Compétence de créature",
"Conteneur": "Conteneur",
"Danse": "Danse",
"Extraitpoetique": "Extrait poetique",
"Faune": "Faune",
"Gemme": "Gemme",
"Herbe": "Herbe",
"Ingredient": "Ingrédient",
"Jeu": "Jeu",
"Livre": "Livre",
"Maladie": "Maladie",
"Meditation": "Méditation",
"Monnaie": "Monnaie",
"Munition": "Munition",
"Musique": "Musique",
"Nombreastral": "Nombre astral",
"Nourritureboisson": "Nourriture & boisson",
"Objet": "Objet",
"Oeuvre": "Oeuvre",
"Ombre": "Ombre de Thanatos",
"Plante": "Plante",
"Poison": "Poison",
"Possession": "Possession",
"Potion": "Potion",
"Queue": "Queue de Dragon",
"Recettealchimique": "Recette alchimique",
"Recettecuisine": "Recette de cuisine",
"Rencontre": "Rencontre TMR",
"Service": "Service",
"Signedraconique": "Signe draconique",
"Sort": "Sort",
"Sortreserve": "Sort en réserve",
"Souffle": "Souffle de Dragon",
"Tache": "Tâche",
"Tarot": "Carte de tarot",
"Tete": "Tête de Dragon"
}
},
"EFFECT": {
"StatusStunned": "Sonné",

View File

@ -1,4 +1,6 @@
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { RdDUtility } from "./rdd-utility.js";
export class RdDActorEntiteSheet extends RdDActorSheet {
@ -13,6 +15,12 @@ export class RdDActorEntiteSheet extends RdDActorSheet {
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
});
}
async getData() {
let formData = await super.getData();
formData.resonances = this.actor.system.sante.resonnance.actors.map(actorId => game.actors.get(actorId))
.map(actor => { return { id: actor.id, name: actor.name, img: actor.img } })
return formData
}
/* -------------------------------------------- */
/** @override */
@ -35,6 +43,23 @@ export class RdDActorEntiteSheet extends RdDActorSheet {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value));
});
this.html.find('.resonance-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const actorId = li.data("actor-id");
if (actorId) {
const actorResonance = game.actors.get(actorId);
RdDUtility.confirmerSuppressionSubacteur(this, actorResonance, li, () => {
console.log('Delete : ', actorId);
this.removeSubacteur(actorId);
RdDUtility.slideOnDelete(this, li);
});
}
});
}
async removeSubacteur(actorId) {
let newResonances = this.actor.system.sante.resonnance.actors.filter(id => id != actorId);
await this.actor.update({ 'system.sante.resonnance.actors': newResonances }, { renderSheet: false });
}
}

View File

@ -7,12 +7,14 @@ import { Misc } from "./misc.js";
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 { ReglesOptionnelles } from "./settings/regles-optionnelles.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";
import { RdDItemBlessure } from "./item/blessure.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
/* -------------------------------------------- */
/**
@ -31,7 +33,8 @@ export class RdDActorSheet extends RdDBaseActorSheet {
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false
vueDetaillee: false,
vueArchetype: false,
});
}
@ -66,9 +69,9 @@ export class RdDActorSheet extends RdDBaseActorSheet {
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));
item.system.isHidden = this.options.recherche
? !item.isNomLike(this.options.recherche.text)
: (this.options.showCompNiveauBase && RdDItemCompetence.isNiveauBase(item));
RdDItemCompetence.levelUp(item, formData.system.compteurs.experience.value);
});
@ -82,6 +85,7 @@ export class RdDActorSheet extends RdDBaseActorSheet {
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac);
formData.esquives = this.actor.getCompetences("Esquive");
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
formData.empoignades = this.actor.getEmpoignades();
this.armesList = formData.combat;
@ -111,19 +115,26 @@ export class RdDActorSheet extends RdDBaseActorSheet {
activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor));
this.html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.actionPrincipale(this.actor, async () => this.render())
});
this.html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const actorId = li.data("actor-id");
if (actorId) {
const subActor = game.actors.get(actorId);
RdDUtility.confirmerSuppressionSubacteur(this, subActor, li);
RdDUtility.confirmerSuppressionSubacteur(this, subActor, li, () => {
console.log('Delete : ', subActor.id);
this.actor.removeSubacteur(subActor.id);
RdDUtility.slideOnDelete(this, li);
});
}
});
this.html.find('.experiencelog-delete').click(async event => {
@ -136,6 +147,13 @@ export class RdDActorSheet extends RdDBaseActorSheet {
const key = Number(li.data("key") ?? -1);
await this.actor.deleteExperienceLog(0, key + 1);
});
this.html.find("input.derivee-value[name='system.compteurs.stress.value']").change(async event => {
this.actor.updateCompteurValue("stress", parseInt(event.target.value));
});
this.html.find("input.derivee-value[name='system.compteurs.experience.value']").change(async event => {
this.actor.updateCompteurValue("experience", parseInt(event.target.value));
});
this.html.find('.encaisser-direct').click(async event => {
this.actor.encaisser();
})
@ -151,33 +169,31 @@ export class RdDActorSheet extends RdDBaseActorSheet {
this.html.find('.creer-tache').click(async event => {
this.createEmptyTache();
});
this.html.find('.creer-tache-blessure-legere').click(async event => RdDItemBlessure.createTacheSoinBlessure(this.actor, 2));
this.html.find('.creer-tache-blessure-grave').click(async event => RdDItemBlessure.createTacheSoinBlessure(this.actor, 4));
this.html.find('.creer-tache-blessure-critique').click(async event => RdDItemBlessure.createTacheSoinBlessure(this.actor, 6));
this.html.find('.creer-blessure-legere').click(async event => RdDItemBlessure.createBlessure(this.actor, 2));
this.html.find('.creer-blessure-grave').click(async event => RdDItemBlessure.createBlessure(this.actor, 4));
this.html.find('.creer-blessure-critique').click(async event => RdDItemBlessure.createBlessure(this.actor, 6));
this.html.find('.creer-une-oeuvre').click(async event => {
this.selectTypeOeuvreToCreate();
});
// Blessure control
this.html.find('.blessure-control').click(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
let btype = tr.data("blessure-type");
let index = tr.data('blessure-index');
let active = this.html.find(event.currentTarget).data('blessure-active');
//console.log(btype, index, active);
await this.actor.manageBlessureFromSheet(btype, index, active);
this.html.find('.blessure-premierssoins-done').change(async event => {
const blessure = this.getBlessure(event);
await blessure?.setSoinsBlessure({ premierssoins: { done: event.currentTarget.checked } });
});
// Blessure data
this.html.find('.blessure-soins').change(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
let btype = tr.data('blessure-type');
let index = tr.data('blessure-index');
let psoins = tr.find('.blessure-premiers_soins').val();
let pcomplets = tr.find('.blessure-soins_complets').val();
let jours = tr.find('.blessure-jours').val();
let loc = tr.find('.blessure-localisation').val();
let psdone = tr.find('.blessure-psdone:checked').val();
let scdone = tr.find('.blessure-scdone:checked').val();
console.log(btype, index, psoins, pcomplets, jours, loc, psdone, scdone);
await this.actor.setDataBlessureFromSheet(btype, index, psoins, pcomplets, jours, loc, psdone, scdone);
this.html.find('.blessure-soinscomplets-done').change(async event => {
const blessure = this.getBlessure(event);
await blessure?.setSoinsBlessure({ soinscomplets: { done: event.currentTarget.checked } })
});
this.html.find('.blessure-premierssoins-bonus').change(async event => {
const blessure = this.getBlessure(event);
await blessure?.setSoinsBlessure({ premierssoins: { bonus: Number(event.currentTarget.value) } })
});
this.html.find('.blessure-soinscomplets-bonus').change(async event => {
const blessure = this.getBlessure(event);
await blessure?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } })
});
// Equip Inventory Item
@ -249,9 +265,14 @@ export class RdDActorSheet extends RdDBaseActorSheet {
// Points de reve actuel
this.html.find('.ptreve-actuel a').click(async event => {
this.actor.rollCarac('reve-actuel');
this.actor.rollCarac('reve-actuel', true);
});
// Suite empoignade
this.html.find('.empoignade-label a').click(async event => {
let emp = RdDSheetUtility.getItem(event, this.actor)
RdDEmpoignade.onAttaqueEmpoignadeFromItem(emp)
});
// Roll Weapon1
this.html.find('.arme-label a').click(async event => {
let arme = this._getEventArmeCombat(event);
@ -333,41 +354,25 @@ export class RdDActorSheet extends RdDBaseActorSheet {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value));
});
this.html.find('.toggle-archetype').click(async event => {
this.options.vueArchetype = !this.options.vueArchetype;
this.render(true);
});
// On competence archetype change
this.html.find('.competence-archetype').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceArchetype(compName, parseInt(event.target.value));
});
}
this.html.find('.nouvelle-incarnation').click(async event => {
this.actor.nouvelleIncarnation();
});
this.html.find('.show-hide-competences').click(async event => {
this.options.showCompNiveauBase = !this.options.showCompNiveauBase;
this.render(true);
});
this.html.find('.recherche')
.each((index, field) => {
if (this.options.recherche) {
field.focus();
field.setSelectionRange(this.options.recherche.start, this.options.recherche.end);
}
})
.keyup(async event => {
const nouvelleRecherche = this._optionRecherche(event.currentTarget);
if (this.options.recherche?.text != nouvelleRecherche?.text) {
this.options.recherche = nouvelleRecherche;
if (this.timerRecherche) {
clearTimeout(this.timerRecherche);
}
this.timerRecherche = setTimeout(() => {
this.timerRecherche = undefined;
this.render(true);
}, 500);
}
})
.change(async event =>
this.options.recherche = this._optionRecherche(event.currentTarget)
);
this.html.find('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
@ -440,6 +445,12 @@ export class RdDActorSheet extends RdDBaseActorSheet {
});
}
getBlessure(event) {
const itemId = this.html.find(event.currentTarget).parents(".item-blessure").data('item-id');
const blessure = this.actor.getItem(itemId, 'blessure');
return blessure;
}
isCompetenceAffichable(competence) {
return !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence);
}
@ -453,9 +464,9 @@ export class RdDActorSheet extends RdDBaseActorSheet {
/* -------------------------------------------- */
async selectTypeOeuvreToCreate() {
let typeObjets = RdDItem.getTypesOeuvres();
let types = RdDItem.getTypesOeuvres();
let content = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
for (let typeName of typeObjets) {
for (let typeName of types) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}
content += '</select>';
@ -478,17 +489,6 @@ export class RdDActorSheet extends RdDBaseActorSheet {
await this.actor.createItem('tache', 'Nouvelle tache');
}
_optionRecherche(target) {
if (!target.value?.length) {
return undefined;
}
return {
text: target.value,
start: target.selectionStart,
end: target.selectionEnd,
};
}
_getEventArmeCombat(event) {
const li = this.html.find(event.currentTarget)?.parents(".item");
let armeName = li.data("arme-name");

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,8 @@ 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";
import { RdDItem, TYPES } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
/* -------------------------------------------- */
/**
@ -31,52 +32,56 @@ export class RdDBaseActorSheet extends ActorSheet {
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),
system: 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,
options: RdDSheetUtility.mergeDocumentRights(this.options, this.actor, this.isEditable)
}
this.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
formData.calc = {
fortune: this.toSolsDeniers(this.actor.getFortune()),
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
encTotal: await this.actor.computeEncTotal(),
}
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == TYPES.competencecreature)
.forEach(it => it.isdommages = RdDItemCompetenceCreature.isDommages(it))
return formData;
}
toSolsDeniers(fortune) {
return {
sols: Math.floor(fortune),
deniers: Math.round(100 * (fortune - Math.floor(fortune)))
};
_appliquerRechercheObjets(conteneurs, inventaires) {
if (this.options.recherche?.text) {
const recherche = this.options.recherche;
const allVisible = inventaires.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id);
let addVisible = conteneurs.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id)
do {
allVisible.push(...addVisible)
const parentsIds = conteneurs.filter(it => it.system.contenu.find(id => allVisible.includes(id))).map(it => it.id)
addVisible = parentsIds.filter(id => !allVisible.includes(id))
}
while (addVisible.length > 0)
inventaires.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
conteneurs.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
}
else {
inventaires.forEach(it => it.system.isHidden = false)
conteneurs.forEach(it => it.system.isHidden = false)
}
}
/* -------------------------------------------- */
filterItemsPerTypeForSheet(formData, itemTypes) {
static filterItemsPerTypeForSheet(formData, itemTypes) {
formData.blessures = Misc.arrayOrEmpty(itemTypes['blessure']);
formData.recettescuisine = Misc.arrayOrEmpty(itemTypes['recettecuisine']);
formData.recettesAlchimiques = Misc.arrayOrEmpty(itemTypes['recettealchimique']);
formData.maladies = Misc.arrayOrEmpty(itemTypes['maladie']);
@ -110,14 +115,16 @@ export class RdDBaseActorSheet extends ActorSheet {
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.monnaies = Misc.arrayOrEmpty(itemTypes['monnaie']).sort(Monnaie.triValeurEntiere());
formData.objets = Misc.arrayOrEmpty(itemTypes['objet'])
formData.objets = RdDItem.getItemTypesInventaire('all')
formData.inventaires = RdDItem.getItemTypesInventaire('all')
.map(t => Misc.arrayOrEmpty(itemTypes[t]))
.reduce((a, b) => a.concat(b), [])
.sort(Misc.ascending(it => it.name));
@ -135,7 +142,13 @@ export class RdDBaseActorSheet extends ActorSheet {
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());
this.html.find('.recherche')
.each((index, field) => {
this._rechercheSelectArea(field);
})
.keyup(async event => this._rechercherKeyup(event))
.change(async event => this._rechercherKeyup(event));
this.html.find('.recherche').prop("disabled", false);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
@ -143,6 +156,8 @@ export class RdDBaseActorSheet extends ActorSheet {
const item = this.getItem(event);
RdDSheetUtility.splitItem(item, this.actor);
});
this.html.find('.item-quantite-plus').click(async event => this.actor.itemQuantiteIncDec(this.getItemId(event), 1));
this.html.find('.item-quantite-moins').click(async event => this.actor.itemQuantiteIncDec(this.getItemId(event), -1));
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)));
@ -152,12 +167,28 @@ export class RdDBaseActorSheet extends ActorSheet {
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);
});
}
_rechercherKeyup(event) {
const currentTarget = event.currentTarget;
const nouvelleRecherche = this._optionRecherche(currentTarget);
if (this.options.recherche?.text != nouvelleRecherche?.text) {
this.options.recherche = nouvelleRecherche;
if (this.timerRecherche) {
clearTimeout(this.timerRecherche);
}
this.timerRecherche = setTimeout(() => {
this.timerRecherche = undefined;
this.render(true);
}, 500);
}
}
_rechercheSelectArea(field) {
if (this.options.recherche) {
field.focus();
field.setSelectionRange(this.options.recherche.start, this.options.recherche.end);
}
}
getItemId(event) {
@ -168,6 +199,16 @@ export class RdDBaseActorSheet extends ActorSheet {
return RdDSheetUtility.getItem(event, this.actor);
}
_optionRecherche(target) {
if (!target.value?.length) {
return undefined;
}
return {
text: target.value,
start: target.selectionStart,
end: target.selectionEnd,
};
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
@ -193,9 +234,9 @@ export class RdDBaseActorSheet extends ActorSheet {
/* -------------------------------------------- */
async selectObjetTypeToCreate() {
let typeObjets = this.getTypesInventaire().sort(Misc.ascending(type => Misc.typeName('Item', type)));
let types = 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) {
for (let typeName of types) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}
content += '</select>';

View File

@ -3,8 +3,10 @@ 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 { RdDConfirm } from "../rdd-confirm.js";
import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { APP_ASTROLOGIE_REFRESH } from "../sommeil/app-astrologie.js";
export class RdDBaseActor extends Actor {
@ -20,14 +22,16 @@ export class RdDBaseActor extends Actor {
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();
game.user.character.resetNombresAstraux();
game.system.rdd.calendrier.notifyChangeNombresAstraux();
return;
case "msg_refresh_nombre_astral":
Hooks.callAll(APP_ASTROLOGIE_REFRESH);
return;
}
}
@ -106,7 +110,6 @@ export class RdDBaseActor extends Actor {
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)) {
@ -116,7 +119,7 @@ export class RdDBaseActor extends Actor {
}
listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
filterItems(filter, type = undefined) { return this.listItems(type)?.filter(filter) ?? []; }
filterItems(filter, type = undefined) { return type ? this.itemTypes[type]?.filter(filter) ?? [] : []; }
findItemLike(idOrName, type) {
return this.getItem(idOrName, type)
?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
@ -141,17 +144,29 @@ export class RdDBaseActor extends Actor {
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
}
async creerObjetParMJ(object){
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
actorId: this.id,
method: 'creerObjetParMJ',
args: [object]
});
return;
}
await this.createEmbeddedDocuments('Item', [object])
}
/* -------------------------------------------- */
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 }]);
async itemQuantiteIncDec(id, value) {
let item = this.getItem(id);
if (item && item.isInventaire()) {
const quantite = Math.max(0, item.system.quantite + value);
await this.updateEmbeddedDocuments('Item', [{ _id: item.id, 'system.quantite': quantite }]);
}
}
@ -241,14 +256,16 @@ export class RdDBaseActor extends Actor {
});
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)) {
const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
if (!itemVendu) {
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !`: `Impossible de retrouver: ${achat.vente.item.name} !`);
return;
}
if (vendeur && !this.verifierQuantite(itemVendu, quantite)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
return
}
@ -259,13 +276,13 @@ export class RdDBaseActor extends Actor {
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);
const createdItemId = await acheteur.creerQuantiteItem(itemVendu, quantite);
await acheteur.consommerNourritureAchetee(achat, achat.vente, createdItemId);
}
if (cout > 0) {
RdDAudio.PlayContextAudio("argent");
}
const chatAchatItem = duplicate(vente);
const chatAchatItem = duplicate(achat.vente);
chatAchatItem.quantiteTotal = quantite;
ChatMessage.create({
user: achat.userId,
@ -274,16 +291,16 @@ export class RdDBaseActor extends Actor {
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem)
});
if (!vente.quantiteIllimite) {
if (vente.quantiteNbLots <= achat.choix.nombreLots) {
if (!achat.vente.quantiteIllimite) {
if (achat.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);
achat.vente.properties = itemVendu.getProprietes();
achat.vente.quantiteNbLots -= achat.choix.nombreLots;
achat.vente.jsondata = JSON.stringify(achat.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.update({ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', achat.vente) });
messageVente.render(true);
}
}
@ -300,9 +317,9 @@ export class RdDBaseActor extends Actor {
return this.getFortune() >= cout;
}
verifierQuantite(vendeur, item, quantiteTotal) {
const disponible = vendeur?.getQuantiteDisponible(item);
return disponible == undefined || disponible >= quantiteTotal;
verifierQuantite(item, quantiteDemande) {
const disponible = item?.getQuantite();
return disponible == undefined || disponible >= quantiteDemande;
}
async consommerNourritureAchetee(achat, vente, createdItemId) {
@ -350,6 +367,14 @@ export class RdDBaseActor extends Actor {
}
/* -------------------------------------------- */
computeMalusSurEncombrement() {
return 0;
}
getEncombrementMax() {
return 0;
}
async computeEncTotal() {
if (!this.pack) {
this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0);

View File

@ -22,17 +22,38 @@ export class RdDCommerceSheet extends RdDBaseActorSheet {
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-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()));
@ -48,7 +69,7 @@ export class RdDCommerceSheet extends RdDBaseActorSheet {
return RdDItem.getItemTypesInventaire('all');
}
async vente(item) {
const acheteur = RdDUtility.getSelectedActor();
if (!acheteur) {

View File

@ -22,7 +22,7 @@ export class RdDCommerce extends RdDBaseActor {
}
getQuantiteDisponible(item) {
return this.system.illimite || item.isService() ? undefined : item.getQuantite();
return (this.system.illimite || item?.isService()) ? undefined : item.getQuantite();
}
verifierFortune(cout) {

View File

@ -0,0 +1,39 @@
export const XP_TOPIC = {
XP: { code: 'xp', label: 'xp' },
XPSORT: { code: 'xpsort', label: 'xp sort' },
NIVEAU: { code: 'niveau', label: 'Niveau' },
XPCARAC: { code: 'xpcarac', label: 'xp carac' },
CARAC: { code: 'carac', label: 'Carac' },
STRESS: { code: 'stress', label: 'Stress' },
TRANSFORM: { code: 'xps', label: 'Transformé' },
}
export class ExperienceLog {
static async add(actor, topic, from, to, raison, manuel = false) {
if (!actor.hasPlayerOwner || !actor.isPersonnage()) {
return
}
if (from == to) {
return
}
const newXpLog = {
mode: topic?.code ?? topic,
raison: (manuel ? '(manuel) ' : '') + raison,
from: from,
to: to,
valeur: to - from,
daterdd: game.system.rdd.calendrier.dateCourante(),
datereel: game.system.rdd.calendrier.dateReel().replace('T', ' ')
};
console.log('ExperienceLog.add', newXpLog)
const newExperienceLog = (actor.system.experiencelog ?? []).concat([newXpLog]);
await actor.update({ [`system.experiencelog`]: newExperienceLog });
}
static labelTopic(topic) {
const xpt = Object.values(XP_TOPIC).find(it => it.code == topic);
return xpt?.label ?? xpt?.code ?? topic;
}
}

View File

@ -78,11 +78,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static async createChatWithRollMode(name, chatOptions) {
return await ChatUtility.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions);
}
/* -------------------------------------------- */
static async createChatMessage(name, rollMode, chatOptions) {
let rollMode = game.settings.get("core", "rollMode")
switch (rollMode) {
case "blindroll": // GM only
if (!game.user.isGM) {

View File

@ -1,6 +1,7 @@
import { SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
const LATEST_USED_JOURNAL_ID = "chronologie-dernier-journal";
@ -24,52 +25,68 @@ export class DialogChronologie extends Dialog {
journalId: game.settings.get(SYSTEM_RDD, LATEST_USED_JOURNAL_ID),
journaux: game.journal.filter(it => it.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)),
timestamp: game.system.rdd.calendrier.timestamp,
dateReel: DialogChronologie.getCurrentDateTime()
dateReel: game.system.rdd.calendrier.dateReel()
};
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) {
constructor(html, dialogData) {
const options = {
classes: ["DialogChronologie"],
width: 500,
height: 'fit-content',
'z-index': 99999
};
const timeData = dialogData.timestamp.toCalendrier()
const conf = {
title: "Chronologie",
title: `Chronologie - ${timeData.jourDuMois} ${timeData.mois.label} - Heure ${timeData.heure.label}`,
content: html,
buttons: {
ajout: { label: "Ajouter", callback: it => this.ajouter() },
}
};
super(conf, options);
}
static getCurrentDateTime() {
return new Date().toLocaleString("sv-SE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit"
}).replace(" ", "T");
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() {
@ -82,8 +99,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() {
@ -97,23 +114,23 @@ export class DialogChronologie extends Dialog {
heure: RdDTimestamp.definition(this.html.find("form.rdddialogchrono :input[name='chronologie.heure']").val()),
minute: this.html.find("form.rdddialogchrono :input[name='chronologie.minute']").val(),
},
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val().replace('T', ' ')
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val()
}
}
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/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

@ -5,7 +5,7 @@ import { RdDUtility } from "./rdd-utility.js";
export class DialogFabriquerPotion extends Dialog {
/* -------------------------------------------- */
static async create(actor, item, dialogConfig) {
static async create(actor, item, onActionItem) {
const min = DialogFabriquerPotion.nombreBrinsMinimum(item);
if (item.system.quantite < min) {
ui.notifications.warn(`Vous avez ${item.system.quantite} brins de ${item.name}, il en faut au moins ${min} pour faire une potion!`);
@ -13,12 +13,10 @@ export class DialogFabriquerPotion extends Dialog {
}
let potionData = DialogFabriquerPotion.prepareData(actor, item);
const html = await renderTemplate(dialogConfig.html, potionData);
const html = await renderTemplate( 'systems/foundryvtt-reve-de-dragon/templates/dialog-fabriquer-potion-base.html', potionData);
let options = { classes: ["dialogfabriquerpotion"], width: 600, height: 160, 'z-index': 99999 };
mergeObject(options, dialogConfig.options ?? {}, { overwrite: true })
new DialogFabriquerPotion(actor, potionData, html, options).render(true);
new DialogFabriquerPotion(actor, potionData, onActionItem, html, options).render(true);
}
/* -------------------------------------------- */
@ -34,14 +32,14 @@ export class DialogFabriquerPotion extends Dialog {
}
/* -------------------------------------------- */
constructor(actor, potionData, html, options) {
constructor(actor, potionData, onActionItem, html, options) {
const conf = {
title: `Fabriquer une potion de ${potionData.system.categorie}`,
content: html,
default: 'fabriquer',
buttons: {
'fabriquer': {
label: potionData.buttonName, callback: it => this.onFabriquer(html)
label: potionData.buttonName, callback: it => this.onFabriquer()
}
}
};
@ -50,6 +48,7 @@ export class DialogFabriquerPotion extends Dialog {
this.actor = actor;
this.potionData = potionData;
this.onActionItem = onActionItem;
}
/* -------------------------------------------- */
@ -64,10 +63,11 @@ export class DialogFabriquerPotion extends Dialog {
}
/* -------------------------------------------- */
async onFabriquer(html) {
async onFabriquer() {
await this.html.find("[name='nbBrins']").change();
this.actor.fabriquerPotion(this.potionData);
await this.actor.fabriquerPotion(this.potionData);
this.close();
await this.onActionItem()
}
static nombreBrinsMinimum(herbeData) {

View File

@ -18,7 +18,7 @@ export class DialogItemAchat extends Dialog {
}
return {
item: (json ? JSON.parse(json) : undefined),
item: JSON.parse(json),
vendeur,
acheteur,
nbLots: parseInt(chatButton.attributes['data-quantiteNbLots']?.value),
@ -34,7 +34,6 @@ export class DialogItemAchat extends Dialog {
const venteData = {
item,
actingUserId: game.user.id,
vendeurId: vendeur?.id,
vendeur,
acheteur,
tailleLot,

View File

@ -8,7 +8,7 @@ export class DialogItemVente extends Dialog {
const venteData = {
item: item,
alias: item.actor?.name ?? game.user.name,
vendeurId: item.actor?.id ,
vendeurId: item.actor?.id,
prixOrigine: item.calculerPrixCommercant(),
prixUnitaire: item.calculerPrixCommercant(),
prixLot: item.calculerPrixCommercant(),
@ -40,52 +40,63 @@ export class DialogItemVente extends Dialog {
activateListeners(html) {
super.activateListeners(html);
this.html = html;
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));
this.html.find(".prixLot").change(event => this.setPrixLot(Number(event.currentTarget.value)));
this.setQuantiteIllimite(this.venteData.quantiteIllimite);
}
async onProposer(it) {
await this.html.find(".tailleLot").change();
await this.html.find(".quantiteNbLots").change();
await this.html.find(".quantiteIllimite").change();
await this.html.find(".prixLot").change();
this.updateVente(this.getChoixVente());
this.callback(this.venteData);
}
updateVente(update) {
mergeObject(this.venteData, update);
}
getChoixVente() {
return {
quantiteNbLots: Number(this.html.find(".quantiteNbLots").val()),
tailleLot: Number(this.html.find(".tailleLot").val()),
quantiteIllimite: this.html.find(".quantiteIllimite").is(':checked'),
prixLot: Number(this.html.find(".prixLot").val())
};
}
/* -------------------------------------------- */
setPrixLot(prixLot) {
this.venteData.prixLot = prixLot;
}
setTailleLot(tailleLot) {
// recalculer le prix du lot
if (tailleLot != this.venteData.tailleLot) {
this.venteData.prixLot = (tailleLot * this.venteData.prixOrigine).toFixed(2);
this.html.find(".prixLot").val(this.venteData.prixLot);
}
this.venteData.tailleLot = tailleLot;
// 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);
const maxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
this.updateVente({
tailleLot,
quantiteNbLots: Math.min(maxLots, this.venteData.quantiteNbLots),
quantiteMaxLots: maxLots,
prixLot: (tailleLot * this.venteData.prixOrigine).toFixed(2)
});
this.html.find(".prixLot").val(this.venteData.prixLot);
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
}
setNbLots(nbLots) {
if (this.venteData.isOwned) {
nbLots = Math.max(0, Math.min(nbLots, this.venteData.quantiteMaxLots));
}
this.venteData.quantiteNbLots = nbLots;
this.updateVente({
quantiteNbLots: this.venteData.isOwned ? Math.max(0, Math.min(nbLots, this.venteData.quantiteMaxLots)) : nbLots
})
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
}
setQuantiteIllimite(checked) {
this.venteData.quantiteIllimite = checked;
this.updateVente({ 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,57 +0,0 @@
export class DialogRepos extends Dialog {
static async create(actor) {
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-repos.html", actor);
const dialog = new DialogRepos(html, actor);
dialog.render(true);
}
constructor(html, actor) {
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 400, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: "Se reposer",
content: html,
default: "repos",
buttons: {
"repos": { label: "Se reposer", callback: async it => { this.repos(); } }
}
};
super(conf, options);
this.actor = actor;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
}
/* -------------------------------------------- */
async repos() {
await this.html.find("[name='nb-heures']").change();
await this.html.find("[name='nb-jours']").change();
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);
return;
}
case "nuit": {
let heuresDormies = await this.actor.dormir(nbHeures);
if (heuresDormies == nbHeures) {
await this.actor.dormirChateauDormant();
}
return;
}
case "chateau-dormant":
await this.actor.dormirChateauDormant();
return;
case "gris-reve": {
await this.actor.grisReve(nbJours);
return;
}
}
}
}

View File

@ -7,7 +7,7 @@ import { RdDUtility } from "./rdd-utility.js";
*/
export class DialogValidationEncaissement extends Dialog {
static async validerEncaissement(actor, rollData, armure, show, onEncaisser) {
static async validerEncaissement(actor, rollData, armure, show, attackerId, onEncaisser) {
let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: HIDE_DICE });
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.html', {
actor: actor,
@ -15,15 +15,15 @@ export class DialogValidationEncaissement extends Dialog {
encaissement: encaissement,
show: show
});
const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, show, onEncaisser);
const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, show, attackerId, onEncaisser);
dialog.render(true);
}
/* -------------------------------------------- */
constructor(html, actor, rollData, armure, encaissement, show, onEncaisser) {
constructor(html, actor, rollData, armure, encaissement, show, attackerId, onEncaisser) {
// Common conf
let buttons = {
"valider": { label: "Valider", callback: html => this.validerEncaissement() },
"valider": { label: "Valider", callback: html => this.onValider() },
"annuler": { label: "Annuler", callback: html => { } },
};
@ -48,6 +48,7 @@ export class DialogValidationEncaissement extends Dialog {
this.armure = armure;
this.encaissement = encaissement;
this.show = show;
this.attackerId = attackerId;
this.onEncaisser = onEncaisser;
this.forceDiceResult = {total: encaissement.roll.result };
}
@ -64,8 +65,8 @@ export class DialogValidationEncaissement extends Dialog {
});
}
async validerEncaissement() {
async onValider() {
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
this.onEncaisser(this.encaissement, this.show)
this.onEncaisser(this.encaissement, this.show, this.attackerId)
}
}

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(sheet, 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,5 @@
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
import { Misc } from "./misc.js";
import { TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.js";
const nomCategorieParade = {
@ -20,7 +20,7 @@ const nomCategorieParade = {
export class RdDItemArme extends Item {
static isArme(item) {
return (item.type == 'competencecreature' && item.system.iscombat) || item.type == 'arme';
return RdDItemCompetenceCreature.getCategorieAttaque(item) || item.type == 'arme';
}
/* -------------------------------------------- */
@ -28,7 +28,7 @@ export class RdDItemArme extends Item {
switch (arme ? arme.type : '') {
case 'arme': return arme;
case 'competencecreature':
return RdDItemCompetenceCreature.armeNaturelle(arme);
return RdDItemCompetenceCreature.armeCreature(arme);
}
return RdDItemArme.mainsNues();
}
@ -166,7 +166,7 @@ export class RdDItemArme extends Item {
let corpsACorps = competences.find(it => it.name == 'Corps à corps') ?? { system: { niveau: -6 } };
let init = RdDCombatManager.calculInitiative(corpsACorps.system.niveau, carac['melee'].value);
armes.push(RdDItemArme.mainsNues({ niveau: corpsACorps.system.niveau, initiative: init }));
//armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init }));
armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init }));
}
static corpsACorps(mainsNuesActor) {

View File

@ -8,22 +8,22 @@ 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, "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 }
{ niveau: 0, nombreMax: 100 },
{ niveau: 1, nombreMax: 10 },
{ niveau: 2, nombreMax: 9 },
{ niveau: 3, nombreMax: 8 },
{ niveau: 4, nombreMax: 7 },
{ niveau: 5, nombreMax: 6 },
{ niveau: 6, nombreMax: 5 },
{ niveau: 7, nombreMax: 4 },
{ niveau: 8, nombreMax: 3 },
{ niveau: 9, nombreMax: 2 },
{ niveau: 10, nombreMax: 1 },
{ niveau: 11, nombreMax: 1 },
];
/* -------------------------------------------- */
const categorieCompetences = {
const categoriesCompetences = {
"generale": { base: -4, label: "Générales" },
"particuliere": { base: -8, label: "Particulières" },
"specialisee": { base: -11, label: "Spécialisées" },
@ -49,16 +49,16 @@ const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static getCategorieCompetences() {
return categorieCompetences;
}
/* -------------------------------------------- */
static getNiveauBase(category) {
return categorieCompetences[category].base;
static getCategories() {
return categoriesCompetences;
}
/* -------------------------------------------- */
static getLabelCategorie(category) {
return categorieCompetences[category].label;
return categoriesCompetences[category].label;
}
/* -------------------------------------------- */
static getNiveauBase(category, categories = categoriesCompetences) {
return categories[category]?.base ?? 0;
}
/* -------------------------------------------- */
@ -190,18 +190,9 @@ export class RdDItemCompetence extends Item {
}
}
/* -------------------------------------------- */
static isVisible(item) {
return Number(item.system.niveau) != RdDItemCompetence.getNiveauBase(item.system.categorie);
}
static nomContientTexte(item, texte) {
return Grammar.toLowerCaseNoAccent(item.name).includes(Grammar.toLowerCaseNoAccent(texte))
}
/* -------------------------------------------- */
static isNiveauBase(item) {
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie);
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.getCategories());
}
/* -------------------------------------------- */
@ -268,18 +259,22 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const computed = duplicate(limitesArchetypes);
computed.forEach(it => { it.nombre = 0; it.reste = it.nombreMax; });
competences.map(it => Math.max(0, it.system.niveau_archetype))
.filter(n => n > 0)
.forEach(n => {
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0 };
computed[n].reste = computed[n].reste - 1;
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0, nombre: 0 };
computed[n].reste--;
computed[n].nombre++;
});
return computed.filter(it => it.reste > 0 && it.niveau > 0);
return computed.filter(it => it.niveau > 0);
}
/* -------------------------------------------- */
static triVisible(competences) {
return competences.filter(it => it.system.isVisible)
return competences.filter(it => !it.system.isHidden)
.sort((a, b) => RdDItemCompetence.compare(a, b))
}

View File

@ -1,51 +1,97 @@
import { RdDItem, TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.js";
const categories = {
"generale": { base: 0, label: "Générale" },
"naturelle": { base: 0, label: "Arme naturelle" },
"melee": { base: 0, label: "Mêlée" },
"parade": { base: 0, label: "Parade" },
"tir": { base: 0, label: "Tir" },
"lancer": { base: 0, label: "Lancer" },
"possession": { base: 0, label: "Possession" },
}
/* -------------------------------------------- */
export class RdDItemCompetenceCreature extends Item {
/* -------------------------------------------- */
static setRollDataCreature(rollData) {
rollData.competence = rollData.competence
rollData.carac = { "carac_creature": { label: rollData.competence.name, value: rollData.competence.system.carac_value } }
rollData.competence.system.defaut_carac = "carac_creature"
rollData.competence.system.categorie = "creature"
rollData.selectedCarac = rollData.carac.carac_creature
if (rollData.competence.system.iscombat) {
rollData.arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence);
}
static getCategories() {
return categories;
}
/* -------------------------------------------- */
static armeNaturelle(competencecreature) {
if (RdDItemCompetenceCreature.isCompetenceAttaque(competencecreature)) {
// si c'est un Item compétence: cloner pour ne pas modifier lma compétence
let arme = (competencecreature instanceof Item) ? competencecreature.clone(): competencecreature;
mergeObject(arme.system,
static setRollDataCreature(rollData) {
rollData.carac = { "carac_creature": { label: rollData.competence.name, value: rollData.competence.system.carac_value } }
rollData.competence.system.defaut_carac = "carac_creature"
rollData.selectedCarac = rollData.carac.carac_creature
rollData.arme = RdDItemCompetenceCreature.armeCreature(rollData.competence);
}
/* -------------------------------------------- */
static armeCreature(item) {
const categorieAttaque = RdDItemCompetenceCreature.getCategorieAttaque(item)
if (categorieAttaque != undefined) {
// si c'est un Item compétence: cloner pour ne pas modifier la compétence
let arme = item.clone();
mergeObject(arme,
{
competence: arme.name,
initiative: RdDCombatManager.calculInitiative(competencecreature.system.niveau, competencecreature.system.carac_value),
niveau: competencecreature.system.niveau,
equipe: true,
resistance: 100,
dommagesReels: arme.system.dommages,
penetration: 0,
force: 0,
rapide: true,
cac: competencecreature.system.isnaturelle ? "naturelle" : "",
action: 'attaque'
action: item.isCompetencePossession() ? 'possession' : 'attaque',
system: {
competence: arme.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: item.system.niveau,
initiative: RdDCombatManager.calculInitiative(item.system.niveau, item.system.carac_value),
equipe: true,
resistance: 100,
dommagesReels: arme.system.dommages,
penetration: 0,
force: 0,
rapide: true,
}
});
return arme;
}
console.error("RdDItemCompetenceCreature.toActionArme(", competencecreature, ") : impossible de transformer l'Item en arme");
return undefined;
}
/* -------------------------------------------- */
static isCompetenceAttaque(item) {
return item.type == 'competencecreature' && item.system.iscombat;
static getCategorieAttaque(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
case "possession":
return item.system.categorie
}
}
return undefined
}
static isDommages(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
return true
}
}
return false
}
static isParade(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "naturelle":
case "parade":
return true
}
}
return false
}
/* -------------------------------------------- */
static isCompetenceParade(item) {
return item.type == 'competencecreature' && item.system.categorie_parade !== "";

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

@ -17,7 +17,7 @@ const MONNAIE_ARGENT = {
system: { quantite: 0, cout: 1, encombrement: 0.003, description: "" }
};
const MONNAIE_OR = {
name: "Dreagon (or)", 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: "" }
};
@ -62,6 +62,13 @@ export class Monnaie {
return deniers;
}
static toSolsDeniers(fortune) {
return {
sols: Math.floor(fortune),
deniers: Math.round(100 * (fortune - Math.floor(fortune)))
};
}
static getFortune(monnaies) {
return (monnaies??[])
.map(m => Number(m.system.cout) * Number(m.system.quantite))

View File

@ -5,12 +5,14 @@ import { RdDItemCompetence } from "./item-competence.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDGemme } from "./rdd-gemme.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
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";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { TYPES } from "./item.js";
/**
* Extend the basic ItemSheet for RdD specific items
@ -86,33 +88,32 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */
async getData() {
let formData = {
id: this.item.id,
title: this.item.name,
id: this.item.id,
type: this.item.type,
img: this.item.img,
name: this.item.name,
system: this.item.system,
isGM: game.user.isGM,
actorId: this.actor?.id,
isOwned: this.actor ? true : false,
owner: this.item.isOwner,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
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(),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable)
}
if (this.item.type == TYPES.competencecreature) {
formData.isparade = RdDItemCompetenceCreature.isParade(this.item)
formData.isdommages = RdDItemCompetenceCreature.isDommages(this.item)
}
const competences = await SystemCompendiums.getCompetences(this.actor?.type);
formData.categorieCompetences = RdDItemCompetence.getCategorieCompetences()
const competences = await SystemCompendiums.getCompetences('personnage');
formData.categories = this.item.getCategories()
if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') {
formData.caracList = duplicate(game.system.model.Actor.personnage.carac)
formData.caracList["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve)
formData.competences = competences;
}
if (this.item.type == 'arme') {
formData.competences = competences.filter(it => RdDItemCompetence.isCompetenceArme(it));
formData.competences = competences.filter(it => RdDItemCompetence.isCompetenceArme(it))
}
if (['sort', 'sortreserve'].includes(this.item.type)) {
formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it));
@ -138,12 +139,12 @@ export class RdDItemSheet extends ItemSheet {
if (this.item.type == 'potion') {
await RdDHerbes.addPotionFormData(formData);
}
if (formData.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) {
if (formData.options.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) {
formData.isIngredientPotionBase = true;
}
if (this.item.type == 'sortreserve') {
const sortId = this.item.system.sortid;
formData.sort = formData.isOwned ? this.item.actor.items.get(sortId) : game.items.get(sortId);
formData.sort = formData.options.isOwned ? this.item.actor.items.get(sortId) : game.items.get(sortId);
}
formData.bonusCaseList = RdDItemSort.getBonusCaseList(formData, true);
@ -156,8 +157,10 @@ export class RdDItemSheet extends ItemSheet {
super.activateListeners(html);
this.html = html;
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"), ReglesOptionnelles.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;
@ -191,7 +194,7 @@ export class RdDItemSheet extends ItemSheet {
});
this.html.find('.creer-tache-livre').click((event) => this._getEventActor(event).creerTacheDepuisLivre(this.item));
this.html.find('.consommer-potion').click((event) => this._getEventActor(event).consommerPotion(this.item));
this.html.find('.consommer-potion').click((event) => this._getEventActor(event).consommerPotion(this.item, this.getActionRenderItem()));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).dialogFabriquerPotion(this.item));
this.html.find('.alchimie-tache a').click((event) => {
@ -206,12 +209,23 @@ export class RdDItemSheet extends ItemSheet {
}
});
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.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)?.postItemToChat());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor, async () => this.render(true)));
if (this.actor) {
this.html.find('.item-split').click(async event => RdDSheetUtility.splitItem(RdDSheetUtility.getItem(event, this.actor), this.actor, this.getActionRenderItem()));
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.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)?.postItemToChat());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor, this.getActionRenderItem()));
this.html.find('.item-quantite-plus').click(async event => {
await this.actor.itemQuantiteIncDec(RdDSheetUtility.getItemId(event), 1)
this.render();
});
this.html.find('.item-quantite-moins').click(async event => {
await this.actor.itemQuantiteIncDec(RdDSheetUtility.getItemId(event), -1)
this.render();
});
}
const updateItemTimestamp = (path, timestamp) => this.item.update({ [path]: duplicate(timestamp) })
@ -219,6 +233,16 @@ export class RdDItemSheet extends ItemSheet {
RdDTimestamp.handleTimestampEditor(this.html, 'system.temporel.fin', updateItemTimestamp);
}
getActionRenderItem() {
return async () => {
let item = this.item;
while (item) {
await item.sheet?.render()
item = this.actor.getContenant(item)
}
}
}
_getEventActor(event) {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
@ -231,7 +255,8 @@ export class RdDItemSheet extends ItemSheet {
event.preventDefault();
if (this.item.isCompetence()) {
let level = RdDItemCompetence.getNiveauBase(event.currentTarget.value);
const categorie = event.currentTarget.value;
const level = RdDItemCompetence.getNiveauBase(categorie, this.item.getCategories());
this.item.system.base = level;
this.html.find('[name="system.base"]').val(level);
}
@ -242,9 +267,8 @@ export class RdDItemSheet extends ItemSheet {
_updateObject(event, formData) {
if (this.item.type == 'sort') {
// Données de bonus de cases ?
formData['system.bonuscase'] = RdDItemSort.buildBonusCaseStringFromFormData(formData.bonusValue, formData.caseValue);
formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue);
}
return this.item.update(formData);
}

View File

@ -1,4 +1,3 @@
/* -------------------------------------------- */
import { Misc } from "./misc.js";
import { TMRUtility } from "./tmr-utility.js";
@ -14,9 +13,9 @@ export class RdDItemSort extends Item {
static isCoutVariable(sort) {
return sort && (sort.system.ptreve.toLowerCase() == "variable" || sort.system.ptreve.indexOf("+") >= 0);
}
/* -------------------------------------------- */
static setCoutReveReel(sort){
static setCoutReveReel(sort) {
if (sort) {
sort.system.ptreve_reel = this.isCoutVariable(sort) ? 1 : sort.system.ptreve;
}
@ -25,94 +24,91 @@ export class RdDItemSort extends Item {
/* -------------------------------------------- */
static getDifficulte(sort, variable) {
if (sort && !RdDItemSort.isDifficulteVariable(sort)) {
return Misc.toInt(sort.system.difficulte);
return Misc.toInt(sort.system.difficulte);
}
return variable;
}
/* -------------------------------------------- */
static buildBonusCaseList( caseBonusString, newCase ) {
if (caseBonusString == undefined) {
return [];
}
let bonusCaseList = [];
let bonusCaseArray = caseBonusString == undefined ? [] : caseBonusString.split(',');
for( let bonusCase of bonusCaseArray) {
let bonusSplit = bonusCase.split(':');
bonusCaseList.push( { case: bonusSplit[0], bonus: bonusSplit[1] } );
}
if ( newCase )
bonusCaseList.push( {case: "Nouvelle", bonus: 0} );
return bonusCaseList;
static buildBonusCaseList(bonuscase, newCase) {
const list = RdDItemSort._bonuscaseStringToList(bonuscase)
if (newCase) {
return list.concat({ case: "Nouvelle", bonus: 0 });
}
/* -------------------------------------------- */
return list;
}
/**
* Retourne une liste de bonus/case pour un item-sheet
* @param {} item
*/
static getBonusCaseList( item, newCase = false ) {
static getBonusCaseList(item, newCase = false) {
// Gestion spéciale case bonus
if ( item.type == 'sort') {
return this.buildBonusCaseList(item.system.bonuscase, newCase );
if (item.type == 'sort') {
return RdDItemSort.buildBonusCaseList(item.system.bonuscase, newCase);
}
return undefined;
}
/* -------------------------------------------- */
/** Met à jour les données de formulaire
* si static des bonus de cases sont présents
* */
static buildBonusCaseStringFromFormData( bonuses, cases ) {
if ( bonuses ) {
let list = [];
let caseCheck = {};
for (let i=0; i<bonuses.length; i++) {
let coord = cases[i]?.toUpperCase() || 'A1';
let bonus = bonuses[i] || 0;
if ( TMRUtility.verifyTMRCoord( coord ) && bonus > 0 && caseCheck[coord] == undefined ) {
static buildBonuscaseFromArrays(bonuses, coords) {
if (bonuses) {
const list = [];
const caseCheck = {};
for (let i = 0; i < bonuses.length && i < coords.length; i++) {
const coord = coords[i] == 'Fleuve' ? 'Fleuve' : (coords[i]?.toUpperCase() ?? 'A1');
const bonus = bonuses[i] || 0;
if (TMRUtility.verifyTMRCoord(coord) && bonus > 0 && caseCheck[coord] == undefined) {
caseCheck[coord] = bonus;
list.push( coord+":"+bonus );
list.push({ case: coord, bonus: bonus });
}
}
return list.toString();
return RdDItemSort._bonuscaseListToString(list);
}
return undefined;
}
/* -------------------------------------------- */
static incrementBonusCase( actor, sort, coord ) {
let bonusCaseList = this.buildBonusCaseList(sort.system.bonuscase, false);
//console.log("ITEMSORT", sort, bonusCaseList);
let found = false;
let StringList = [];
for( let bc of bonusCaseList) {
if (bc.case == coord) { // Case existante
found = true;
bc.bonus = Number(bc.bonus) + 1;
}
StringList.push( bc.case+':'+bc.bonus );
}
if ( !found) { //Nouvelle case, bonus de 1
StringList.push(coord+':1');
}
// Sauvegarde/update
let bonuscase = StringList.toString();
//console.log("Bonus cae :", bonuscase);
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }] );
}
/* -------------------------------------------- */
static getCaseBonus( sort, coord) {
let bonusCaseList = this.buildBonusCaseList(sort.system.bonuscase, false);
for( let bc of bonusCaseList) {
if (bc.case == coord) { // Case existante
return Number(bc.bonus);
}
static incrementBonusCase(actor, sort, coord) {
if (TMRUtility.getTMR(coord).type == "fleuve") {
coord = 'Fleuve';
}
return 0;
const list = RdDItemSort.buildBonusCaseList(sort.system.bonuscase, false);
const bonus = Number(list.find(it => it.case == coord)?.bonus ?? 0);
const modified = { case: coord, bonus: bonus + 1 };
const bonuscase = RdDItemSort._bonuscaseListToString(
list.filter(it => it.case != coord).concat(modified)
);
// Sauvegarde/update
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }]);
}
/* -------------------------------------------- */
static getCaseBonus(sort, coord) {
const isFleuve = TMRUtility.getTMR(coord).type == "fleuve";
let bc = RdDItemSort.buildBonusCaseList(sort.system.bonuscase, false)
.filter(it => it.case == coord || (isFleuve && it.case == 'Fleuve'))
.find(it => true)
return Number(bc?.bonus ?? 0);
}
static _bonuscaseListToString(list) {
return list.map(it => `${it.case}:${it.bonus}`)
.sort(Misc.ascending())
.join(',');
}
static _bonuscaseStringToList(bonuscase) {
return (bonuscase ?? '').split(',').map(it => {
const b = it.split(':');
return { case: b[0], bonus: b[1] };
});
}
}

View File

@ -1,4 +0,0 @@
export class RdDItemTache extends Item {
}

View File

@ -2,36 +2,88 @@ 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 { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDUtility } from "./rdd-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDRaretes } from "./item/raretes.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
export const TYPES = {
competence: 'competence',
competencecreature: 'competencecreature',
empoignade: 'empoignade',
possession: 'possession',
blessure: 'blessure',
maladie: 'maladie',
poison: 'poison',
arme: 'arme',
armure: 'armure',
conteneur: 'conteneur',
objet: 'objet',
monnaie: 'monnaie',
gemme: 'gemme',
munition: 'munition',
nourritureboisson: 'nourritureboisson',
herbe: 'herbe',
plante: 'plante',
ingredient: 'ingredient',
faune: 'faune',
livre: 'livre',
potion: 'potion',
service: 'service',
musique: 'musique',
danse: 'danse',
chant: 'chant',
jeu: 'jeu',
recettecuisine: 'recettecuisine',
oeuvre: 'oeuvre',
recettealchimique: 'recettealchimique',
tache: 'tache',
sort: 'sort',
sortreserve: 'sortreserve',
rencontre: 'rencontre',
queue: 'queue',
ombre: 'ombre',
souffle: 'souffle',
tete: 'tete',
casetmr: 'casetmr',
meditation: 'meditation',
signedraconique: 'signedraconique',
tarot: 'tarot',
nombreastral: 'nombreastral',
extraitpoetique: 'extraitpoetique',
}
const typesInventaireMateriel = [
"arme",
"armure",
"conteneur",
"faune",
"gemme",
"herbe",
"ingredient",
"livre",
"monnaie",
"munition",
"nourritureboisson",
"objet",
"potion",
TYPES.arme,
TYPES.armure,
TYPES.conteneur,
TYPES.faune,
TYPES.gemme,
TYPES.herbe,
TYPES.plante,
TYPES.ingredient,
TYPES.livre,
TYPES.monnaie,
TYPES.munition,
TYPES.nourritureboisson,
TYPES.objet,
TYPES.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 typesObjetsOeuvres = [TYPES.oeuvre, TYPES.recettecuisine, TYPES.musique, TYPES.chant, TYPES.danse, TYPES.jeu]
const typesObjetsDraconiques = [TYPES.queue, TYPES.ombre, TYPES.souffle, TYPES.tete, TYPES.signedraconique, TYPES.sortreserve, TYPES.rencontre]
const typesObjetsConnaissance = [TYPES.meditation, TYPES.recettealchimique, TYPES.sort]
const typesObjetsEffet = [TYPES.possession, TYPES.poison, TYPES.maladie, TYPES.blessure]
const typesObjetsCompetence = [TYPES.competence, TYPES.competencecreature]
const typesObjetsTemporels = [TYPES.blessure, TYPES.poison, TYPES.maladie, TYPES.queue, TYPES.ombre, TYPES.souffle, TYPES.signedraconique, TYPES.rencontre]
const typesObjetsEquipable = [TYPES.arme, TYPES.armure, TYPES.objet];
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/
@ -72,11 +124,16 @@ export const defaultItemImg = {
sortreserve: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp",
extraitpoetique: "systems/foundryvtt-reve-de-dragon/icons/competence_ecriture.webp",
tarot: "systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp",
empoignade: "systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp"
}
/* -------------------------------------------- */
export class RdDItem extends Item {
static get defaultIcon() {
return undefined;
}
static getDefaultImg(itemType) {
return game.system.rdd.itemClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
}
@ -84,12 +141,12 @@ export class RdDItem extends Item {
static isFieldInventaireModifiable(type, field) {
switch (field) {
case 'quantite':
if (['conteneur'].includes(type)) {
if ([TYPES.conteneur].includes(type)) {
return false;
}
break;
case 'cout':
if (['monnaie'].includes(type)) {
if ([TYPES.monnaie].includes(type)) {
return game.user.isGM;
}
break;
@ -107,6 +164,12 @@ export class RdDItem extends Item {
static getItemTypesInventaire(mode = 'materiel') {
return typesInventaire[mode ?? 'materiel']
}
static getItemTypesDraconiques() {
return typesObjetsDraconiques;
}
static getItemTypesEnvironnement() {
return typesEnvironnement;
}
static getTypesOeuvres() {
return typesObjetsOeuvres
@ -129,43 +192,86 @@ export class RdDItem extends Item {
super(docData, context);
}
static get defaultIcon() {
return undefined;
}
getUniteQuantite() {
switch (this.type) {
case "monnaie": return "(Pièces)"
case "herbe":
case TYPES.monnaie: return "(Pièces)"
case TYPES.herbe:
switch (this.system.categorie) {
case 'Alchimie': case 'Repos': case 'Soin':
return "(Brins)"
case 'Cuisine': return '';
}
return '';
case "ingredient": return "(Pépins ou Brins)"
case TYPES.ingredient: return "(Pépins ou Brins)"
}
return '';
}
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'; }
isEquipable() {
return typesObjetsEquipable.includes(this.type)
}
isCompetencePersonnage() { return this.type == TYPES.competence }
isCompetenceCreature() { return this.type == TYPES.competencecreature }
isConteneur() { return this.type == TYPES.conteneur; }
isMonnaie() { return this.type == TYPES.monnaie; }
isPotion() { return this.type == TYPES.potion; }
isNourritureBoisson() { return this.type == TYPES.nourritureboisson; }
isService() { return this.type == TYPES.service; }
isCompetence() { return typesObjetsCompetence.includes(this.type) }
isCompetencePossession() { return TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
isDraconique() { return typesObjetsDraconiques.includes(this.type) }
isDraconique() { return RdDItem.getItemTypesDraconiques().includes(this.type) }
isQueueDragon() { return [TYPES.queue, TYPES.ombre].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'); }
isHerbeAPotion() { return this.type == TYPES.herbe && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
isBlessure() { return this.type == TYPES.blessure }
isPresentDansMilieux(milieux) {
return this.getEnvironnements(milieux).length > 0
}
getCategories() {
switch (this.type) {
case TYPES.competence: return RdDItemCompetence.getCategories()
case TYPES.competencecreature: return RdDItemCompetenceCreature.getCategories()
}
return {}
}
getEnvironnements(milieux = undefined) {
const environnements = this.isInventaire() ? this.system.environnement : undefined;
if (milieux == undefined || !environnements) {
return environnements ?? [];
}
return environnements.filter(env => milieux.includes(env.milieu))
}
getMilieux() {
return this.getEnvironnements().map(env => env.milieu);
}
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()];
}
getFrequence(milieux = undefined) {
const frequences = this.getEnvironnements(milieux).map(it => it.frequence);
return frequences.length == 0 ? 0 : Math.max(...frequences);
}
getItemGroup() {
if (this.isInventaire()) return "equipement";
@ -216,19 +322,42 @@ export class RdDItem extends Item {
await this.actor?.deleteEmbeddedDocuments('Item', [this.id]);
}
isComestible() {
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 TYPES.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 TYPES.nourritureboisson: return 'cuisine';
case TYPES.herbe: case TYPES.faune: case TYPES.ingredient: case TYPES.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 '';
}
getUtilisationCuisine() {
if (this.getUtilisation() == 'cuisine') {
switch (this.type) {
case TYPES.nourritureboisson:
return 'pret';
case TYPES.herbe: case TYPES.faune: case TYPES.ingredient: case TYPES.plante:
return 'brut';
}
}
return '';
}
isCristalAlchimique() {
return this.type == 'objet' && Grammar.toLowerCaseNoAccent(this.name) == 'cristal alchimique' && this.system.quantite > 0;
return this.type == TYPES.objet && Grammar.includesLowerCaseNoAccent(this.name, 'cristal alchimique') && this.system.quantite > 0;
}
isMagique() {
@ -239,21 +368,28 @@ export class RdDItem extends Item {
return this.parent?.type == 'commerce';
}
isNomLike(texte) {
return Grammar.includesLowerCaseNoAccent(this.name, texte)
}
isNomTypeLike(texte) {
return this.isNomLike(texte) || Grammar.includesLowerCaseNoAccent(Misc.typeName(this.type, 'Item'), texte)
}
getQuantite() {
return this.isService() ? undefined : Math.round(this.system.quantite ?? 0)
}
getEncTotal() {
return (this.isService() ? 0 : this.getQuantite()) * this.getEnc();
return (this.getQuantite() ?? 0) * this.getEnc();
}
getEnc() {
switch (this.type) {
case 'service':
case TYPES.service:
return 0;
case 'herbe':
case TYPES.herbe:
return this.getEncHerbe();
case 'gemme':
case TYPES.gemme:
return encPepin * this.system.taille;
}
return Math.max(this.system.encombrement ?? 0, 0);
@ -293,6 +429,7 @@ export class RdDItem extends Item {
}
this.system.actionPrincipale = this.getActionPrincipale({ warnIfNot: false });
}
this.equipable = this.isEquipable();
}
prepareDataPotion() {
@ -308,19 +445,19 @@ export class RdDItem extends Item {
getActionPrincipale(options = { warnIfNot: true }) {
switch (this.type) {
case 'conteneur': return 'Ouvrir';
case TYPES.conteneur: return 'Ouvrir';
}
if (this.actor?.isPersonnage()) {
const warn = options.warnIfNot;
if (this.isComestible() == 'brut') {
if (this.getUtilisationCuisine() == 'brut') {
return 'Utiliser';
}
switch (this.type) {
case 'nourritureboisson': return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
case 'potion': return this._actionOrWarnQuantiteZero('Boire', warn);
case 'livre': return this._actionOrWarnQuantiteZero('Lire', warn);
case 'herbe': return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
case 'queue': case 'ombre': return this.system.refoulement > 0 ? 'Refouler' : undefined;
case TYPES.nourritureboisson: return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
case TYPES.potion: return this._actionOrWarnQuantiteZero('Boire', warn);
case TYPES.livre: return this._actionOrWarnQuantiteZero('Lire', warn);
case TYPES.herbe: return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
case TYPES.queue: case TYPES.ombre: return this.system.refoulement > 0 ? 'Refouler' : undefined;
}
}
return undefined;
@ -335,11 +472,11 @@ export class RdDItem extends Item {
return;
}
switch (this.type) {
case 'potion': return await actor.consommerPotion(this, onActionItem);
case 'livre': return await actor.actionLire(this);
case 'conteneur': return await this.sheet.render(true);
case 'herbe': return await actor.actionHerbe(this);
case 'queue': case 'ombre': return await actor.actionRefoulement(this);
case TYPES.potion: return await actor.consommerPotion(this, onActionItem);
case TYPES.livre: return await actor.actionLire(this);
case TYPES.conteneur: return await this.sheet.render(true);
case TYPES.herbe: return await actor.actionHerbe(this, onActionItem);
case TYPES.queue: case TYPES.ombre: return await actor.actionRefoulement(this);
}
}
@ -361,7 +498,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,
@ -382,7 +519,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({
@ -433,8 +570,8 @@ export class RdDItem extends Item {
return [false, `Impossible de regrouper ${this.name} avec ${other.name}`];
}
else {
const excludedProperties = ['quantite', 'cout', 'encTotal', 'environnement'];
if (this.isComestible()) {
const excludedProperties = ['quantite', 'cout', 'encTotal', 'environnement', 'contenu'];
if (this.getUtilisationCuisine()) {
excludedProperties.push('sust', 'encombrement');
}
let differences = Object.entries(this.system)

51
module/item/armure.js Normal file
View File

@ -0,0 +1,51 @@
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
export class RdDItemArmure extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/armes_armures/armure_plaques.webp";
}
deteriorerArmure(dmg) {
if (!ReglesOptionnelles.isUsing('deteriorationArmure') || this.system.protection == '0') {
return;
}
let deterioration = (this.system.deterioration ?? 0) + dmg;
let protection = this.system.protection;
if (deterioration >= 10) {
deterioration -= 10;
protection = this.calculProtectionDeterioree();
ChatMessage.create({ content: `Votre armure ${this.name} s'est détériorée, elle protège maintenant de ${protection}` });
}
this.update({
system: {
deterioration: deterioration,
protection: protection
}
});
}
calculProtectionDeterioree() {
const protectionCourante = this.system.protection;
let res = /(\d+)?d(\d+)(\-\d+)?/.exec(protectionCourante);
if (res) {
let protection = Misc.toInt(res[2]);
let malus = Misc.toInt(res[3]) - 1;
if (protection + malus <= 0) {
return 0;
} else {
return `1d${protection}${malus}`;
}
}
else if (/\d+/.exec(protectionCourante)) {
return `1d${protectionCourante}`;
}
else {
ui.notifications.warn(`La valeur d'armure de votre ${this.name} est incorrecte`);
return undefined;
}
}
}

199
module/item/blessure.js Normal file
View File

@ -0,0 +1,199 @@
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDTimestamp } from "../time/rdd-timestamp.js";
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 = {
6: { name: 'Blessure critique', system: { difficulte: -6, points_de_tache: 6 } },
4: { name: 'Blessure grave', system: { difficulte: -4, points_de_tache: 4 } },
2: { name: 'Blessure légère', system: { difficulte: -2, points_de_tache: 2 } },
}
const definitionsBlessures = [
{ type: "contusion", gravite: 0, label: 'Contusion/éraflure', max: 100, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/eraflure.webp" },
{ type: "legere", gravite: 2, label: 'Légère', max: 5, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp" },
{ type: "grave", gravite: 4, label: 'Grave', max: 2, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp" },
{ type: "critique", gravite: 6, label: 'Critique', max: 1, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp" },
{ type: "mort", gravite: 8, label: 'Mort', max: 1, icon: "systems/foundryvtt-reve-de-dragon/icons/sante/mort.webp" }
]
export class RdDItemBlessure extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp";
}
prepareDerivedData() {
super.prepareDerivedData();
this.system.label = this.getLabelGravite()
}
static prepareTacheSoin(gravite) {
const tache = TACHES_SOIN_BLESSURE[gravite]
if (!tache) {
ui.notifications.warn(`Pas de tâche de soins pour une blessure ${gravite}`)
return undefined;
}
return mergeObject(duplicate(BASE_TACHE_SOIN_BLESSURE), tache)
}
static async createBlessure(actor, gravite, localisation = '', attacker) {
const definition = RdDItemBlessure.getDefinition(gravite)
const blessure = {
name: definition.label,
type: 'blessure',
img: definition.icon,
system: {
gravite: gravite,
difficulte: - gravite,
localisation: localisation,
origine: attacker?.name ?? ""
}
}
const blessures = await actor.createEmbeddedDocuments('Item', [blessure])
return blessures[0]
}
static async createTacheSoinBlessure(actor, gravite) {
const tache = RdDItemBlessure.prepareTacheSoin(gravite)
if (tache) {
const taches = await actor.createEmbeddedDocuments('Item', [tache], { renderSheet: false });
return taches[0];
}
return undefined
}
async updateTacheSoinBlessure(tache) {
if (tache) {
await tache.update({
system: {
itemId: this.id,
difficulte: Math.min(this.system.difficulte, tache.system.difficulte),
points_de_tache_courant: Math.max(0, this.system.premierssoins.tache)
}
});
}
}
async setSoinsBlessure(systemUpdate = {}) {
systemUpdate = mergeObject(systemUpdate, this.system, { overwrite: false }),
systemUpdate.soinscomplets.done = systemUpdate.premierssoins.done && systemUpdate.soinscomplets.done
await this.update({
img: this.getImgSoins(systemUpdate.gravite, systemUpdate.soinscomplets.done),
system: systemUpdate
});
}
async recuperationBlessure({ actor, timestamp, message, isMaladeEmpoisonne, blessures }) {
if (this.parent != actor || actor == undefined) {
return
}
if (new RdDTimestamp(this.system.temporel.fin).compare(timestamp) > 0) {
// attente periode
return
}
if (this.system.gravite > 0) {
const update = { system: { premierssoins: { bonus: 0 }, soinscomplets: { bonus: 0 } } }
const gravite = this.system.gravite;
const graviteMoindre = gravite - 2;
const moindres = blessures.filter(it => it.system.gravite == graviteMoindre, 'blessures').length
const label = this.getLabelGravite();
let rolled = await actor.jetRecuperationConstitution(this.system.soinscomplets.bonus, message);
if (rolled.isETotal) {
message.content += ` -- une blessure ${label} s'infecte (temps de guérison augmenté de ${gravite} jours, perte de vie)`;
await actor.santeIncDec("vie", -1);
mergeObject(update, {
system: { fin: { indexDate: timestamp.addJours(gravite).indexDate } }
});
}
else {
if (!isMaladeEmpoisonne && rolled.isSuccess && this.peutRetrograder(graviteMoindre, moindres)) {
message.content += ` -- une blessure ${label} cicatrise`;
mergeObject(update, {
system: { gravite: graviteMoindre, fin: { indexDate: timestamp.addJours(graviteMoindre).indexDate } }
});
}
else {
message.content += ` -- une blessure ${label} reste stable`;
}
}
await this.update(update);
}
}
peutRetrograder(graviteMoindre, moindres) {
return moindres < RdDItemBlessure.getDefinition(graviteMoindre).max
}
async calculerFinPeriodeTemporel(debut) {
return await debut.nouveauJour().addJours(this.system.gravite);
}
async onFinPeriode(oldTimestamp, newTimestamp) {
if (this.system.gravite <= 0) {
await super.onFinPeriode(oldTimestamp, newTimestamp)
}
}
getImgSoins(gravite, soins) {
let img = 'blessure'
if (gravite > 6) {
img = 'mort'
}
if (gravite <= 0) {
img = 'eraflure'
}
return `systems/foundryvtt-reve-de-dragon/icons/sante/${soins ? 'blessure-soins' : img}.webp`
}
getLabelGravite() {
return RdDItemBlessure.getDefinition(this.system.gravite).label
}
static getDefinition(gravite) {
return definitionsBlessures.sort(Misc.ascending(it => it.gravite))
.find(it => it.gravite >= gravite);
}
static maxBlessures(gravite) {
return RdDItemBlessure.getDefinition(gravite).max
}
isContusion() {
return this.system.gravite <= 0
}
isLegere() {
return this.system.gravite > 0 && this.system.gravite <= 2
}
isGrave() {
return this.system.gravite > 2 && this.system.gravite <= 4
}
isCritique() {
return this.system.gravite > 4 && this.system.gravite <= 6
}
isMort() {
return this.system.gravite > 6
}
getProprietes() {
return [
RdDItem.propertyIfDefined('Causée par', this.system.origine, this.system.origine),
`<b>Heure et Date</b>: ${new RdDTimestamp(this.system.temporel.debut).formatDateHeure()}`,
RdDItem.propertyIfDefined('Blessé', this.parent?.name, this.parent),
`<b>Localisation</b>: ${this.system.localisation}`,
`<b>Gravité</b>: ${RdDItemBlessure.getDefinition(this.system.gravite).label}`,
`<b>Difficulté des soins</b>: ${this.system.difficulte}`,
(this.system.soinscomplets.done ?
`<b>Bonus soins complets</b>: ${this.system.soinscomplets.bonus}` :
(this.system.premierssoins.done ?
`<b>Bonus premiers soins</b>: ${this.system.premierssoins.bonus}` :
`<b>Points de tâche</b>: ${this.system.premierssoins.tache}`
)
),
];
}
}

View File

@ -1,6 +1,6 @@
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDTimestamp } from "../rdd-timestamp.js";
import { RdDTimestamp } from "../time/rdd-timestamp.js";
export class RdDItemMaladie extends RdDItem {

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,4 @@
import { EffetsRencontre } from "../effets-rencontres.js";
import { EffetsRencontre } from "../tmr/effets-rencontres.js";
import { RdDItem } from "../item.js";
const tableEffets = [

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

@ -0,0 +1,29 @@
import { RdDItemSheet } from "../item-sheet.js";
export class RdDBlessureItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "blessure" };
async getData() {
const formData = await super.getData();
formData.disabled = formData.options.isGM || formData.options.isOwned ? '' : 'disabled';
return formData;
}
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
this.html.find('[name="premierssoins-done"]').change(async event => {
await this.item.setSoinsBlessure({ premierssoins: { done: event.currentTarget.checked } });
});
this.html.find('[name="soinscomplets-done"]').change(async event => {
await this.item.setSoinsBlessure({ soinscomplets: { done: event.currentTarget.checked } })
});
this.html.find('[name="system-gravite"]').change(async event => {
const gravite = Number(event.currentTarget.value)
await this.item.setSoinsBlessure({ gravite: gravite, difficulte: - gravite })
});
}
}

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,9 +28,8 @@ export class RdDConteneurItemSheet extends RdDItemSheet {
/* -------------------------------------------- */
prepareConteneurData(formData) {
RdDUtility.filterEquipementParType(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
}
@ -43,7 +43,8 @@ export class RdDConteneurItemSheet extends RdDItemSheet {
const dragData = {
actorId: this.actor.id,
type: "Item",
data: item.system
data: item.system,
uuid: item.uuid
};
event.dataTransfer.setData("text/plain", JSON.stringify(dragData));
@ -51,7 +52,8 @@ export class RdDConteneurItemSheet extends RdDItemSheet {
async _onDropItem(event, dragData) {
if (this.actor) {
const dropParams = await RdDSheetUtility.prepareItemDropParameters(this.item.id, this.actor, dragData, this.objetVersConteneur);
const destItemId = this.html.find(event.target)?.closest('.item').attr('data-item-id') ?? this.item.id
const dropParams = await RdDSheetUtility.prepareItemDropParameters(destItemId, 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/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 {

View File

@ -1,4 +1,4 @@
import { RdDItemSheet } from "./item-sheet.js";
import { RdDItemSheet } from "../item-sheet.js";
export class RdDServiceItemSheet extends RdDItemSheet {
@ -6,7 +6,7 @@ export class RdDServiceItemSheet extends RdDItemSheet {
async getData() {
const formData = await super.getData();
formData.disabled = formData.isGM || formData.isOwned ? '' : 'disabled';
formData.disabled = formData.options.isGM || formData.options.isOwned ? '' : 'disabled';
return formData;
}

View File

@ -1,6 +1,6 @@
import { RdDItemSheet } from "./item-sheet.js";
import { RdDItemSigneDraconique } from "./item/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

View File

@ -2,7 +2,7 @@ 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 { RdDTimestamp } from "../time/rdd-timestamp.js";
import { TMRType, TMRUtility } from "../tmr-utility.js";
const tableSignesIndicatifs = [

View File

@ -1,10 +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 { RdDItem, TYPES } from "./item.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
class Migration {
get code() { return "sample"; }
@ -13,7 +13,7 @@ class Migration {
async applyItemsUpdates(computeUpdates) {
await game.actors.forEach(async (actor) => {
const actorItemUpdates = computeUpdates(actor.items);
const actorItemUpdates = computeUpdates(actor.items).filter(it => it != undefined);
if (actorItemUpdates.length > 0) {
console.log(
this.code,
@ -24,7 +24,7 @@ class Migration {
}
});
const itemUpdates = computeUpdates(game.items);
const itemUpdates = computeUpdates(game.items).filter(it => it != undefined);
if (itemUpdates.length > 0) {
console.log(this.code, "Applying updates on items", itemUpdates);
await Item.updateDocuments(itemUpdates);
@ -65,7 +65,6 @@ class _1_5_34_migrationPngWebp {
}
}
class _10_0_16_MigrationSortsReserve extends Migration {
get code() { return "creation-item-sort-reserve"; }
get version() { return "10.0.16"; }
@ -288,10 +287,11 @@ 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 }]
}
}
}
@ -369,6 +369,7 @@ class _10_4_6_ServicesEnCommerces extends Migration {
return itemToCreate;
}
}
class _10_5_0_UpdatePeriodicite extends Migration {
get code() { return "migration-periodicite-poisons-maladies"; }
get version() { return "10.5.0"; }
@ -415,6 +416,104 @@ class _10_5_0_UpdatePeriodicite extends Migration {
}
}
class _10_7_0_MigrationBlessures extends Migration {
get code() { return "migration-blessures"; }
get version() { return "10.7.0"; }
async migrate() {
const timestamp = game.system.rdd.calendrier.getTimestamp()
await Promise.all(game.actors.filter(it => it.isPersonnage() || it.isCreature())
.map(async (actor) => {
const legeres = actor.system.blessures?.legeres.liste.filter(it => it.active).map(it => this.creerBlessure(2, 'légère', it, timestamp)) ?? [];
const graves = actor.system.blessures?.graves.liste.filter(it => it.active).map(it => this.creerBlessure(4, 'grave', it, timestamp)) ?? [];
const critiques = actor.system.blessures?.critiques.liste.filter(it => it.active).map(it => this.creerBlessure(6, 'critique', it, timestamp));
const blessures = legeres.concat(graves).concat(critiques);
if (blessures.length > 0) {
await actor.createEmbeddedDocuments("Item", blessures);
}
await actor.update({
'system.blessures.legeres.liste': [],
'system.blessures.graves.liste': [],
'system.blessures.critiques.liste': []
})
}));
}
creerBlessure(gravite, graviteTexte, blessure, timestamp) {
const dateBlessure = timestamp.addJours(-blessure.jours);
const datePremiereRecup = dateBlessure.addJours(gravite);
return {
name: `Blessure ${graviteTexte}`,
type: 'blessure',
img: `systems/foundryvtt-reve-de-dragon/icons/sante/blessure${blessure.psdone ? '-soins' : ''}.webp`,
system: {
gravite: gravite,
difficulte: -gravite,
debut: { indexDate: dateBlessure.indexDate, indexMinute: 0 },
fin: { indexDate: datePremiereRecup.indexDate, indexMinute: 0 },
premierssoins: { done: blessure.psdone, bonus: blessure.premiers_soins },
soinscomplets: { done: blessure.scdone, bonus: blessure.soins_complets },
localisation: blessure.localisation
}
}
}
}
class _10_7_19_CategorieCompetenceCreature extends Migration {
get code() { return "categorie-competence-creature"; }
get version() { return "10.7.19"; }
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => TYPES.competencecreature == it.type)
.map(it => this.migrateCompetenceCreature(it))
);
}
migrateCompetenceCreature(it) {
const categorie = this.getCategorie(it)
if (categorie == it.system.categorie) {
return undefined
}
return { _id: it.id, 'system.categorie': categorie }
}
getCategorie(it) {
if (it.system.ispossession) {
return 'possession'
}
switch (it.system.categorie) {
case "melee":
if (it.system.isnaturelle) {
return 'naturelle'
}
return 'melee'
case "particuliere": case "specialisee": case "connaissance":
return "generale"
default:
return it.system.categorie
}
}
}
class _10_7_19_PossessionsEntiteVictime extends Migration {
get code() { return "possessions-entite-victime"; }
get version() { return "10.7.19"; }
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => TYPES.possession == it.type)
.map(it => this.migratePossession(it))
);
}
migratePossession(it) {
return { _id: it.id,
'system.entite.actorid': it.system.possesseurid,
'system.victime.actorid': it.system.possedeid
}
}
}
export class Migrations {
static getMigrations() {
return [
@ -430,6 +529,9 @@ export class Migrations {
new _10_3_17_Monnaies(),
new _10_4_6_ServicesEnCommerces(),
new _10_5_0_UpdatePeriodicite(),
new _10_7_0_MigrationBlessures(),
new _10_7_19_CategorieCompetenceCreature(),
new _10_7_19_PossessionsEntiteVictime(),
];
}
@ -446,7 +548,7 @@ 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 */
// 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) => this.compareVersions(a, b));

View File

@ -42,7 +42,7 @@ export class Misc {
}
static typeName(type, subType) {
return subType ? game.i18n.localize(`${type.toUpperCase()}.Type${Misc.upperFirst(subType)}`)
return subType ? game.i18n.localize(`TYPES.${type}.${Misc.upperFirst(subType)}`)
: '';
}
@ -85,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), []);
}
@ -118,6 +119,17 @@ export class Misc {
}
}
/**
* @returns an array of incremental integers (including from / excluding to).
* if max<min, the array is decrementing integers
*/
static intArray(from, to) {
if (from > to) {
return Array.from(Array(from - to).keys()).map(i => from - i)
}
return Array.from(Array(to - from).keys()).map(i => from + i)
}
static distinct(array) {
return [...new Set(array)];
}
@ -227,4 +239,15 @@ export class Misc {
}
return subset;
}
static cssRotation(angle) {
const rotation = `rotate(${angle}deg)`;
return {
'transform': rotation,
'-ms-transform': rotation,
'-moz-transform': rotation,
'-webkit-transform': rotation,
'-o-transform': rotation
};
}
}

View File

@ -8,13 +8,11 @@ export class RdDAlchimie {
/* -------------------------------------------- */
static processManipulation(recette, actorId = undefined) {
//console.log("CALLED", recette, recette.isOwned, actorId );
let manip = recette.system.manipulation;
let matchArray = manip.match(matchOperations);
if (matchArray) {
for (let matchStr of matchArray) {
let result = matchStr.match(matchOperationTerms);
//console.log("RESULT ", result);
if (result[1] && result[2]) {
let commande = Misc.upperFirst(result[1]);
let replacement = this[`_alchimie${commande}`](recette, result[2], actorId);
@ -27,20 +25,19 @@ export class RdDAlchimie {
/* -------------------------------------------- */
static _alchimieCouleur(recette, couleurs, actorId) {
if (actorId) {
return `<span class="alchimie-tache"><a data-recette-id="${recette._id}" data-actor-id="${actorId}" data-alchimie-tache="couleur" data-alchimie-data="${couleurs}">couleur ${couleurs}</a></span>`;
} else {
return `<span class="alchimie-tache">couleur ${couleurs} </span>`;
}
return RdDAlchimie._alchimieLink(recette, couleurs, actorId, 'couleur', 'Température');
}
/* -------------------------------------------- */
static _alchimieConsistance(recette, consistances, actorId) {
if (actorId) {
return `<span class="alchimie-tache"><a data-recette-id="${recette._id}" data-actor-id="${actorId}" data-alchimie-tache="consistance" data-alchimie-data="${consistances}">consistance ${consistances}</a></span>`;
} else {
return `<span class="alchimie-tache">consistance ${consistances} </span>`;
}
return RdDAlchimie._alchimieLink(recette, consistances, actorId, 'consistance', 'Consistance');
}
static _alchimieLink(recette, termes, actorId, tacheAlchimie, labelTache) {
const difficulte = RdDAlchimie.getDifficulte(termes);
const link = actorId ? ` <a data-recette-id="${recette._id}" data-actor-id="${actorId}" data-alchimie-tache="${tacheAlchimie}" data-alchimie-data="${termes}">` : '';
const endLink = actorId ? '</a>' : '';
return `<span class="alchimie-tache">${link}${labelTache} ${termes} (${difficulte})${endLink}</span>`;
}
/* -------------------------------------------- */

View File

@ -1,53 +0,0 @@
/**
* Extend the base Dialog entity by defining a custom window to perform roll.
* @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() }
};
// Common conf
let dialogConf = { content: html, title: "Editeur d'Astrologie", buttons: myButtons, default: "saveButton" };
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);
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
}
/* -------------------------------------------- */
async resetNombreAstraux() {
game.system.rdd.calendrier.resetNombreAstral();
await game.system.rdd.calendrier.rebuildListeNombreAstral();
game.system.rdd.calendrier.showAstrologieEditor();
}
/* -------------------------------------------- */
fillData() {
}
/* -------------------------------------------- */
updateData(calendrierData) {
this.calendrierData = duplicate(calendrierData);
}
}

View File

@ -1,100 +0,0 @@
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
import { SYSTEM_SOCKET_ID } from "./constants.js";
/**
* Extend the base Dialog entity by defining a custom window to perform roll.
* @extends {Dialog}
*/
export class RdDAstrologieJoueur extends Dialog {
/* -------------------------------------------- */
static async create(actor) {
let dialogData = {
nombres: this.organizeNombres(actor),
dates: game.system.rdd.calendrier.getJoursSuivants(10),
etat: actor.getEtatGeneral(),
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
astrologie: RdDItemCompetence.findCompetence(actor.items, 'Astrologie')
}
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-astrologie-joueur.html', 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, dialogOptions) {
const dialogConf = {
title: "Nombres Astraux",
content: html,
default: "saveButton",
buttons: {
saveButton: { label: "Fermer", callback: html => this.quitDialog() }
},
};
super(dialogConf, dialogOptions);
this.actor = actor;
this.dataNombreAstral = duplicate(dialogData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("[name='diffConditions']").val(0);
this.html.find('[name="jet-astrologie"]').click((event) => {
this.requestJetAstrologie();
});
}
/* -------------------------------------------- */
static organizeNombres(actor) {
let itemNombres = actor.listItems('nombreastral');
let itemFiltered = {};
for (let item of itemNombres) {
if (itemFiltered[item.system.jourindex]) {
itemFiltered[item.system.jourindex].listValues.push(item.system.value);
} else {
itemFiltered[item.system.jourindex] = {
listValues: [item.system.value],
jourlabel: item.system.jourlabel
}
}
}
return itemFiltered;
}
/* -------------------------------------------- */
requestJetAstrologie() {
let socketData = {
id: this.actor.id,
carac_vue: this.actor.system.carac['vue'].value,
etat: this.dataNombreAstral.etat,
astrologie: this.dataNombreAstral.astrologie,
conditions: this.html.find('[name="diffConditions"]').val(),
date: this.html.find('[name="joursAstrologie"]').val(),
userId: game.user.id
}
if (Misc.isUniqueConnectedGM()) {
game.system.rdd.calendrier.requestNombreAstral(socketData);
} else {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_request_nombre_astral",
data: socketData
});
}
this.close();
}
/* -------------------------------------------- */
quitDialog() {
}
}

View File

@ -1,4 +1,5 @@
import { RdDCarac } from "./rdd-carac.js";
import { RdDPossession } from "./rdd-possession.js";
const conditionsTactiques = [
{ type: '', descr: '', dmg: 0, attaque: 0, parade: 0, esquive: true },
@ -24,11 +25,17 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static isDefenseAttaqueFinesse(rollData) {
if (rollData.isEmpoignade && rollData.rolled?.isPart) {
return true
}
if (RdDPossession.isDefensePossession(rollData)) {
return RdDPossession.isPossessionFinesse(rollData)
}
return rollData.attackerRoll?.particuliere == 'finesse';
}
/* -------------------------------------------- */
static dmg(rollData, dmgActor, isCauchemar = false) {
static dmg(rollData, dmgActor, isEntiteIncarnee = false) {
let dmg = { total: 0 };
if (rollData.arme && rollData.arme.name.toLowerCase() == "esquive") {
// Specific case management
@ -41,7 +48,7 @@ export class RdDBonus {
dmg.dmgSurprise = RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used);
dmg.dmgActor = rollData.selectedCarac ? RdDBonus._dmgPerso(dmgActor, rollData.selectedCarac.label, dmg.dmgArme) : 0;
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere;
dmg.mortalite = RdDBonus._calculMortalite(rollData, isCauchemar)
dmg.mortalite = RdDBonus._calculMortalite(rollData, isEntiteIncarnee)
}
return dmg;
}
@ -62,11 +69,8 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static _calculMortalite(rollData, isCauchemar) {
if (isCauchemar) {
return "cauchemar";
}
return isCauchemar ? "cauchemar"
static _calculMortalite(rollData, isEntiteIncarnee) {
return isEntiteIncarnee ? "entiteincarnee"
: rollData.dmg?.mortalite
?? rollData.arme?.system.mortalite
?? "mortel";
@ -74,7 +78,7 @@ export class RdDBonus {
/* -------------------------------------------- */
static _dmgArme(rollData) {
if ( rollData.arme) {
if (rollData.arme) {
let dmgBase = rollData.arme.system.dommagesReels ?? Number(rollData.arme.system.dommages ?? 0);
//Le bonus dégats magiques ne peut pas faire dépasser le bonus de l'arme (cf p.278)
return dmgBase + Math.min(dmgBase, rollData.arme.system.magique ? rollData.arme.system.ecaille_efficacite : 0);

View File

@ -1,505 +0,0 @@
import { RdDCalendrierEditeur } from "./rdd-calendrier-editeur.js";
import { RdDAstrologieEditeur } from "./rdd-astrologie-editeur.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDUtility } from "./rdd-utility.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 RDD_JOUR_PAR_MOIS = 28;
const RDD_HEURES_PAR_JOUR = 12;
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 {
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html",
popOut: false,
resizable: false
});
}
static createCalendrierPos() {
return { top: 200, left: 200 };
}
constructor() {
super();
// position
this.calendrierPos = duplicate(game.settings.get(SYSTEM_RDD, "calendrier-pos"));
if (this.calendrierPos == undefined || this.calendrierPos.top == undefined) {
this.calendrierPos = RdDCalendrier.createCalendrierPos();
game.settings.set(SYSTEM_RDD, "calendrier-pos", this.calendrierPos);
}
// Calendrier
this.timestamp = RdDTimestamp.getWorldTime();
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp);
this.listeNombreAstral = this.getListeNombreAstral();
this.rebuildListeNombreAstral(HIDE_DICE); // Ensure always up-to-date
}
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();
}
}
/* -------------------------------------------- */
/** @override */
async activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.updateDisplay();
this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
this.html.find('.calendar-btn-edit').click(ev => {
ev.preventDefault();
this.showCalendarEditor();
});
this.html.find('.astrologie-btn-edit').click(ev => {
ev.preventDefault();
this.showAstrologieEditor();
});
this.html.find('#calendar-move-handle').mousedown(ev => {
ev.preventDefault();
ev = ev || window.event;
let isRightMB = false;
if ("which" in ev) { // Gecko (Firefox), WebKit (Safari/Chrome) & Opera
isRightMB = ev.which == 3;
} else if ("button" in ev) { // IE, Opera
isRightMB = ev.button == 2;
}
if (!isRightMB) {
dragElement(document.getElementById("calendar-time-container"));
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
function dragElement(elmnt) {
elmnt.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.bottom = undefined
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
elmnt.onmousedown = undefined;
document.onmouseup = undefined;
document.onmousemove = undefined;
let xPos = (elmnt.offsetLeft - pos1) > window.innerWidth ? window.innerWidth - 200 : (elmnt.offsetLeft - pos1);
let yPos = (elmnt.offsetTop - pos2) > window.innerHeight - 20 ? window.innerHeight - 100 : (elmnt.offsetTop - pos2)
xPos = xPos < 0 ? 0 : xPos;
yPos = yPos < 0 ? 0 : yPos;
if (xPos != (elmnt.offsetLeft - pos1) || yPos != (elmnt.offsetTop - pos2)) {
elmnt.style.top = (yPos) + "px";
elmnt.style.left = (xPos) + "px";
}
game.system.rdd.calendrier.calendrierPos.top = yPos;
game.system.rdd.calendrier.calendrierPos.left = xPos;
if (game.user.isGM) {
game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
}
}
} else if (isRightMB) {
game.system.rdd.calendrier.calendrierPos.top = 200;
game.system.rdd.calendrier.calendrierPos.left = 200;
if (game.user.isGM) {
game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
this.setPos(game.system.rdd.calendrier.calendrierPos);
}
});
}
/* -------------------------------------------- */
getListeNombreAstral() {
return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? [];
}
/* -------------------------------------------- */
dateCourante() {
return this.timestamp.formatDate();
}
isAfterIndexDate(indexDate) {
// TODO: standardize
return indexDate < this.timestamp.indexDate;
}
/* -------------------------------------------- */
heureCourante() { return RdDTimestamp.definition(this.timestamp.heure); }
/* -------------------------------------------- */
getCurrentMinute() { return this.timestamp.indexMinute; }
getTimestampFinChateauDormant(nbJours = 0) {
return this.timestamp.nouveauJour().addJour(nbJours);
}
getTimestampFinHeure(nbHeures = 0) {
return this.timestamp.nouvelleHeure().addHeures(nbHeures);
}
/* -------------------------------------------- */
getIndexFromDate(jour, mois) {
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(count) {
let jours = [];
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(indexDate, showDice = SHOW_DICE) {
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, rollMode: "selfroll" });
const dateFuture = RdDTimestamp.formatIndexDate(indexDate);
if (showDice != HIDE_DICE) {
ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients("GM"),
content: `Le chiffre astrologique du ${dateFuture} sera le ${nombreAstral}`
});
}
return {
nombreAstral: nombreAstral,
valeursFausses: [],
index: indexDate
}
}
/* -------------------------------------------- */
getCurrentNombreAstral() {
return this.getNombreAstral(this.timestamp.indexDate);
}
/* -------------------------------------------- */
resetNombreAstral() {
this.listeNombreAstral = [];
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_reset_nombre_astral",
data: {}
});
}
/* -------------------------------------------- */
getNombreAstral(indexDate) {
const listNombreAstral = this.getListeNombreAstral();
let astralData = listNombreAstral.find((nombreAstral, i) => nombreAstral.index == indexDate);
return astralData?.nombreAstral;
}
/* -------------------------------------------- */
async rebuildListeNombreAstral(showDice = HIDE_DICE) {
if (Misc.isUniqueConnectedGM()) {
let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
let dayIndex = this.timestamp.indexDate + i;
let na = this.listeNombreAstral.find(n => n.index == dayIndex);
if (na) {
newList[i] = na;
} else {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
}
}
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();
const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
if (calendarAvance) {
await this.incrementTime(Number(calendarAvance.value));
}
else if (calendarSet) {
this.positionnerHeure(Number(calendarSet.value));
}
this.updateDisplay();
}
/* -------------------------------------------- */
async incrementTime(minutes = 0) {
await this.setNewTimestamp(this.timestamp.addMinutes(minutes));
}
/* -------------------------------------------- */
async incrementerJour() {
await this.setNewTimestamp(this.timestamp.nouveauJour());
}
/* -------------------------------------------- */
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 = {}) {
mergeObject(formData, this.timestamp.toCalendrier());
formData.isGM = game.user.isGM;
return formData;
}
/* -------------------------------------------- */
getLectureAstrologieDifficulte(dateIndex) {
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);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
let rollData = {
caracValue: request.carac_vue,
finalLevel: niveau,
showDice: HIDE_DICE,
rollMode: "blindroll"
};
await RdDResolutionTable.rollData(rollData);
request.rolled = rollData.rolled;
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');
}
}
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 {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_response_nombre_astral",
data: request
});
}
}
}
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);
}
getHeureChance(heure) {
return heure + (this.getCurrentNombreAstral() ?? 0);
}
/* -------------------------------------------- */
getHeuresChanceMalchance(heureNaissance) {
let defHeure = RdDTimestamp.findHeure(heureNaissance);
if (defHeure) {
const signe = h => h % RDD_HEURES_PAR_JOUR;
const chance = this.getHeureChance(defHeure.heure);
return [
{ ajustement: "+4", heures: [signe(chance)] },
{ ajustement: "+2", heures: [signe(chance + 4), signe(chance + 8)] },
{ ajustement: "-4", heures: [signe(chance + 6)] },
{ ajustement: "-2", heures: [signe(chance + 3), signe(chance + 9)] }
];
}
return [];
}
/* -------------------------------------------- */
getAjustementAstrologique(heureNaissance, name = undefined) {
let defHeure = RdDTimestamp.findHeure(heureNaissance);
if (defHeure) {
const chance = this.getHeureChance(defHeure.heure);
const ecartChance = (chance - this.timestamp.heure) % 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;
}
}
else if (name) {
ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
}
else {
ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
}
return 0;
}
/* -------------------------------------------- */
getData() {
let formData = super.getData();
this.fillCalendrierData(formData);
this.setPos(this.calendrierPos);
return formData;
}
/* -------------------------------------------- */
setPos(pos) {
return new Promise(resolve => {
function check() {
let elmnt = document.getElementById("calendar-time-container");
if (elmnt) {
elmnt.style.bottom = undefined;
let xPos = (pos.left) > window.innerWidth ? window.innerWidth - 200 : pos.left;
let yPos = (pos.top) > window.innerHeight - 20 ? window.innerHeight - 100 : pos.top;
elmnt.style.top = (yPos) + "px";
elmnt.style.left = (xPos) + "px";
resolve();
} else {
setTimeout(check, 30);
}
}
check();
});
}
/* -------------------------------------------- */
updateDisplay() {
let calendrier = this.fillCalendrierData();
// Rebuild text du calendrier
let dateHTML = `${calendrier.jourDuMois} ${calendrier.mois.label} (${calendrier.mois.saison}) de l'année ${calendrier.annee}`
if (game.user.isGM) {
dateHTML = dateHTML + "<br>Nombre Astral: " + (this.getCurrentNombreAstral() ?? "?");
}
for (let handle of document.getElementsByClassName("calendar-date-rdd")) {
handle.innerHTML = dateHTML;
}
for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
heure.innerHTML = calendrier.heure.label;
}
for (const minute of document.getElementsByClassName("calendar-time-disp")) {
minute.innerHTML = `${calendrier.minute} minutes`;
}
for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
heureImg.src = calendrier.heure.icon;
}
}
/* -------------------------------------------- */
async saveEditeur(calendrierData) {
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 = this.fillCalendrierData();
if (this.editeur == undefined) {
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData);
this.editeur = new RdDCalendrierEditeur(html, this, calendrierData)
}
this.editeur.updateData(calendrierData);
this.editeur.render(true);
}
static buildJoursMois() { return JOURS_DU_MOIS; }
/* -------------------------------------------- */
async showAstrologieEditor() {
const calendrierData = duplicate(this.fillCalendrierData());
this.listeNombreAstral = this.listeNombreAstral || [];
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";
}
return astro;
});
calendrierData.heuresParActeur = {};
game.actors.filter(it => it.isPersonnage() && it.hasPlayerOwner).forEach(actor => {
let heureNaissance = actor.getHeureNaissance();
if (heureNaissance) {
calendrierData.heuresParActeur[actor.name] = this.getHeuresChanceMalchance(heureNaissance);
}
})
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);
astrologieEditeur.render(true);
}
}

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é';
}
@ -57,15 +58,6 @@ export class RdDCarac {
selectedCarac?.label.match(/(Apparence|Force|Agilité|Dextérité|Vue|Ouïe|Odorat-Goût|Empathie|Dérobée|Mêlée|Tir|Lancer)/);
}
static isIgnoreEtatGeneral(rollData) {
const selectedCarac = rollData.selectedCarac;
return !selectedCarac ||
rollData.ethylisme ||
RdDCarac.isChance(selectedCarac) ||
(RdDCarac.isReve(selectedCarac) && !rollData.competence);
}
static computeTotal(carac, beaute = undefined) {
const total = Object.values(carac ?? {}).filter(c => !c.derivee)
.map(it => parseInt(it.value))

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";
@ -10,9 +9,10 @@ import { RdDBonus } from "./rdd-bonus.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { STATUSES } from "./settings/status-effects.js";
import { Targets } from "./targets.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
/* -------------------------------------------- */
const premierRoundInit = [
@ -40,35 +40,32 @@ export class RdDCombatManager extends Combat {
static init() {
/* -------------------------------------------- */
Hooks.on("getCombatTrackerEntryContext", (html, options) => {
RdDCombatManager.pushInitiativeOptions(html, options);
});
Hooks.on("preDeleteCombat", (combat, html, id) => {
combat.onPreDeleteCombat()
});
}
/* -------------------------------------------- */
cleanItemUse() {
for (let turn of this.turns) {
turn.actor.resetItemUse()
}
Hooks.on("getCombatTrackerEntryContext", (html, options) => { RdDCombatManager.pushInitiativeOptions(html, options); });
Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) });
Hooks.on("preDeleteCombat", (combat, html, id) => { combat.onPreDeleteCombat() });
}
/* -------------------------------------------- */
async nextRound() {
this.cleanItemUse();
await this.finDeRound();
return await super.nextRound();
}
/* -------------------------------------------- */
async onPreDeleteCombat() {
await this.finDeRound({ terminer: true });
if (Misc.isUniqueConnectedGM()) {
await this.finDeRound({ terminer: true });
ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
.forEach(it => it.delete());
RdDEmpoignade.deleteAllEmpoignades()
}
}
/* -------------------------------------------- */
async finDeRound(options = { terminer: false }) {
this.turns.forEach(turn => turn.actor.resetItemUse());
for (let combatant of this.combatants) {
if (combatant.actor) {
await combatant.actor.finDeRound(options);
@ -90,19 +87,32 @@ export class RdDCombatManager extends Combat {
let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, 0);
if (!formula) {
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
const competence = combatant.actor.items.find(it => it.system.iscombat)
const competence = combatant.actor.items.find(it => RdDItemCompetenceCreature.getCategorieAttaque(it))
if (competence) {
rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, 0);
}
} else {
const armeCombat = combatant.actor.itemTypes['arme'].find(it => it.system.equipe)
const compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.system.competence;
let compName = "Corps à corps"
if (armeCombat) {
if (armeCombat.system.competence != "") {
compName = armeCombat.system.competence
}
if (armeCombat.system.lancer != "") {
compName = armeCombat.system.lancer
}
if (armeCombat.system.tir != "") {
compName = armeCombat.system.tir
}
}
const competence = RdDItemCompetence.findCompetence(combatant.actor.items, compName);
if (competence) {
if (competence && competence.system.defaut_carac) {
const carac = combatant.actor.system.carac[competence.system.defaut_carac].value;
const niveau = competence.system.niveau;
const bonusEcaille = (armeCombat?.system.magique) ? armeCombat.system.ecaille_efficacite : 0;
rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, bonusEcaille);
} else {
ui.notifications.warn(`Votre arme ${armeCombat.name} n'a pas de compétence renseignée`);
}
}
}
@ -111,10 +121,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");
@ -145,9 +155,8 @@ export class RdDCombatManager extends Combat {
}
/* -------------------------------------------- */
static calculInitiative(niveau, caracValue, bonusEcaille = 0) {
let base = niveau + Math.floor(caracValue / 2);
base += bonusEcaille;
static calculInitiative(niveau, caracValue, bonus = 0) {
let base = niveau + Math.floor(caracValue / 2) + bonus;
return "1d6" + (base >= 0 ? "+" : "") + base;
}
@ -221,15 +230,15 @@ export class RdDCombatManager extends Combat {
}
static listActionsCreature(competences) {
return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(it => RdDItemCompetenceCreature.armeNaturelle(it));
return competences.map(it => RdDItemCompetenceCreature.armeCreature(it))
.filter(it => it != undefined);
}
static listActionsPossessions(actor) {
return RdDCombatManager._indexActions(actor.getPossessions().map(p => {
return {
name: p.name,
action: 'conjurer',
action: 'possession',
system: {
competence: p.name,
possessionid: p.system.possessionid,
@ -246,15 +255,15 @@ export class RdDCombatManager extends Combat {
return actions;
}
if (actor.isCreatureEntite()) {
actions = actions.concat(RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']));
actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']);
} else if (actor.isPersonnage()) {
// Recupération des items 'arme'
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
//.concat(RdDItemArme.empoignade())
.concat(RdDItemArme.empoignade())
.concat(RdDItemArme.mainsNues());
const competences = actor.itemTypes['competence'];
actions = actions.concat(RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac));
actions = RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac);
if (actor.system.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } });
@ -342,7 +351,7 @@ export class RdDCombatManager extends Combat {
} else if (combatant.actor.getSurprise() == "demi") {
initOffset = 0;
initInfo = "Demi Surprise"
} else if (action.action == 'conjurer') {
} else if (action.action == 'possession') {
initOffset = 10;
caracForInit = combatant.actor.getReveActuel();
initInfo = "Possession"
@ -421,18 +430,11 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
export class RdDCombat {
static init() {
Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) });
Hooks.on("preDeleteCombat", (combat, options, userId) => { RdDCombat.onPreDeleteCombat(combat, options, userId); });
}
/* -------------------------------------------- */
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
case "msg_encaisser":
return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense":
return RdDCombat.onMsgDefense(sockmsg.data);
case "msg_encaisser": return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense": return RdDCombat.onMsgDefense(sockmsg.data);
}
}
@ -443,16 +445,6 @@ export class RdDCombat {
}
}
/* -------------------------------------------- */
static onPreDeleteCombat(combat, options, userId) {
if (Misc.isUniqueConnectedGM()) {
combat.cleanItemUse();
ChatUtility.removeChatMessageContaining(`<div data-combatid="${combat.id}" data-combatmessage="actor-turn-summary">`)
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
.forEach(it => it.delete());
}
}
/* -------------------------------------------- */
static combatNouveauTour(combat) {
if (Misc.isUniqueConnectedGM()) {
@ -757,29 +749,14 @@ export class RdDCombat {
/* -------------------------------------------- */
async attaque(competence, arme) {
// const nonIncarnee = this.defender.isEntite([ENTITE_NONINCARNE])
// const blurette = this.defender.isEntite([ENTITE_BLURETTE])
// if (nonIncarnee || blurette) {
// ChatMessage.create( {
// content: `<strong>La cible est ${nonIncarnee ? 'non incarnée' : 'une blurette'}.
// Il est impossible de l'atteindre.`,
// whisper: ChatMessage.getWhisperRecipients("GM")})
// }
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return;
}
if (arme.system.cac == 'empoignade' && this.attacker.isCombatTouche()) {
ChatMessage.create({
alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-actor-perte-empoignade.html', {
attacker: this.attacker,
competence: competence
})
});
if (arme.system.cac == 'empoignade') {
RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender)
return;
}
RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
let rollData = this._prepareAttaque(competence, arme);
console.log("RdDCombat.attaque >>>", rollData);
@ -813,7 +790,7 @@ export class RdDCombat {
passeArme: randomID(16),
mortalite: arme?.system.mortalite,
coupsNonMortels: false,
competence: competence,
competence: competence.clone(),
surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
targetToken: Targets.extractTokenData(this.target),
@ -971,9 +948,8 @@ export class RdDCombat {
/* -------------------------------------------- */
_filterArmesParade(defender, competence) {
let items = defender.items.filter(it => RdDItemArme.isArmeUtilisable(it) || RdDItemCompetenceCreature.isCompetenceParade(it))
for (let item of items) {
item.system.nbUsage = defender.getItemUse(item.id); // Ajout du # d'utilisation ce round
}
items.forEach(item => item.system.nbUsage = defender.getItemUse(item.id)); // Ajout du # d'utilisation ce round
switch (competence.system.categorie) {
case 'tir':
case 'lancer':
@ -1069,10 +1045,10 @@ export class RdDCombat {
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
competence: this.defender.getCompetence(competenceParade),
competence: this.defender.getCompetence(competenceParade).clone(),
arme: armeParade,
surprise: this.defender.getSurprise(true),
needParadeSignificative: ReglesOptionelles.isUsing('categorieParade') && RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade),
needParadeSignificative: ReglesOptionnelles.isUsing('categorieParade') && RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade),
needResist: RdDItemArme.needArmeResist(attackerRoll.arme, armeParade),
carac: this.defender.system.carac,
show: {}
@ -1150,7 +1126,7 @@ export class RdDCombat {
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
competence: competence,
competence: competence.clone(),
surprise: this.defender.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
carac: this.defender.system.carac,
@ -1190,7 +1166,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async computeDeteriorationArme(defenderRoll) {
if (!ReglesOptionelles.isUsing('resistanceArmeParade')) {
if (!ReglesOptionnelles.isUsing('resistanceArmeParade')) {
return;
}
const attackerRoll = defenderRoll.attackerRoll;
@ -1239,7 +1215,7 @@ export class RdDCombat {
}
}
// Si l'arme de parade n'est pas un bouclier, jet de désarmement (p.132)
if (ReglesOptionelles.isUsing('defenseurDesarme') && resistance > 0 && RdDItemArme.getCategorieParade(defenderRoll.arme) != 'boucliers') {
if (ReglesOptionnelles.isUsing('defenseurDesarme') && resistance > 0 && RdDItemArme.getCategorieParade(defenderRoll.arme) != 'boucliers') {
let desarme = await RdDResolutionTable.rollData({
caracValue: this.defender.getForce(),
finalLevel: Misc.toInt(defenderRoll.competence.system.niveau) - dmg,
@ -1254,7 +1230,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async computeRecul(defenderRoll) { // Calcul du recul (p. 132)
const attackerRoll = defenderRoll.attackerRoll;
if (ReglesOptionelles.isUsing('recul') && this._isForceOuCharge(attackerRoll)) {
if (ReglesOptionnelles.isUsing('recul') && this._isForceOuCharge(attackerRoll)) {
const impact = this._computeImpactRecul(attackerRoll);
const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact });
if (rollRecul.rolled.isSuccess) {
@ -1328,13 +1304,8 @@ export class RdDCombat {
blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(),
actorId: actor.id,
isGrave: false,
isCritique: false
}
if (actor.countBlessuresNonSoigneeByName("critiques") > 0) { // Pour éviter le cumul grave + critique
formData.isCritique = true;
} else if (actor.countBlessuresNonSoigneeByName("graves") > 0) {
formData.isGrave = true;
isGrave: actor.countBlessures(it => it.isGrave()) > 0,
isCritique: actor.countBlessures(it => it.isCritique()) > 0
}
ChatUtility.createChatWithRollMode(actor.name, {

View File

@ -2,7 +2,8 @@
import { DialogChronologie } from "./dialog-chronologie.js";
import { DialogCreateSigneDraconique } from "./dialog-create-signedraconique.js";
import { DialogStress } from "./dialog-stress.js";
import { DialogChateauDormant } from "./sommeil/dialog-chateau-dormant.js";
import { DialogStress } from "./sommeil/dialog-stress.js";
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
import { RdDCarac } from "./rdd-carac.js";
@ -13,7 +14,7 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js";
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,8 +75,9 @@ 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: ["/sommeil"], func: (content, msg, params) => this.sommeil(msg, params), descr: "Prépare le passage de journée pour chateau dormant" });
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" });
@ -254,10 +255,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);
@ -334,7 +332,7 @@ export class RdDCommands {
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);
await actor.doRollCaracCompetence(caracName, competence.name, diff);
}
}
return;
@ -398,32 +396,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)) {
@ -491,13 +463,14 @@ export class RdDCommands {
let name = params[params.length - 1];
if (name == undefined) {
for (let actor of game.actors) {
actor.distribuerStress('stress', stress, motif);
// TODO: ne plus stresser les entités de cauchemar!
await actor.distribuerStress('stress', stress, motif);
}
} else {
//console.log(stressValue, nomJoueur);
let actor = Misc.findActor(name, game.actors.filter(it => it.hasPlayerOwner)) ?? Misc.findPlayer(name)?.character
if (actor) {
actor.distribuerStress('stress', stress, motif);
await actor.distribuerStress('stress', stress, motif);
}
else {
ui.notifications.warn(`Pas de personnage ou de joueur correspondant à ${name}!`);
@ -511,5 +484,11 @@ export class RdDCommands {
return await RdDMeteo.getMeteo();
}
async tirage() {
FenetreRechercheTirage.create();
}
async sommeil() {
DialogChateauDormant.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,10 +1,10 @@
import { Grammar } from "./grammar.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
export class RdDConfirm {
/* -------------------------------------------- */
static confirmer(options, autresActions) {
options.bypass = options.bypass || !(options.settingConfirmer == undefined || ReglesOptionelles.isUsing(options.settingConfirmer));
options.bypass = options.bypass || !(options.settingConfirmer == undefined || ReglesOptionnelles.isUsing(options.settingConfirmer));
if (options.bypass) {
options.onAction();
}
@ -47,7 +47,7 @@ export class RdDConfirm {
icon: '<i class="fas fa-user-check"></i>',
label: options.buttonLabel + "<br>et ne plus demander",
callback: () => {
ReglesOptionelles.set(options.settingConfirmer, false);
ReglesOptionnelles.set(options.settingConfirmer, false);
options.onAction();
}
}

436
module/rdd-empoignade.js Normal file
View File

@ -0,0 +1,436 @@
/* -------------------------------------------- */
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ChatUtility } from "./chat-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { TYPES } from "./item.js";
/* -------------------------------------------- */
/* -------------------------------------------- */
export class RdDEmpoignade {
/* -------------------------------------------- */
static init() {
}
/* -------------------------------------------- */
static registerChatCallbacks(html) {
html.on("click", '.defense-empoignade-cac', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Corps à corps", "melee")
});
html.on("click", '.defense-empoignade-esquive', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Esquive", "derobee")
});
html.on("click", '.empoignade-poursuivre', event => {
let attackerId = event.currentTarget.attributes['data-attackerId'].value
let defenderId = event.currentTarget.attributes['data-defenderId'].value
RdDEmpoignade.onAttaqueEmpoignadeValidee(game.actors.get(attackerId), game.actors.get(defenderId))
});
html.on("click", '.empoignade-entrainer-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.entrainerAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("click", '.empoignade-projeter-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.projeterAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("change", '.empoignade-perte-endurance', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
if (event.currentTarget.value && event.currentTarget.value != "none") {
RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value)
ChatUtility.removeChatMessageId(chatMessage.id)
}
});
}
/* -------------------------------------------- */
static checkEmpoignadeEnCours(actor) {
// TODO: autoriser la perception? la comédie/séduction?
if (RdDEmpoignade.isEmpoignadeEnCours(actor)) {
ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.")
return true;
}
return false;
}
/* -------------------------------------------- */
static $storeRollEmpoignade(msg, rollData) {
RdDEmpoignade.$reduceActorToIds(rollData);
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData);
}
static $reduceActorToIds(rollData) {
rollData.attacker = { id: rollData.attacker.id };
rollData.defender = { id: rollData.defender.id };
}
/* -------------------------------------------- */
static $readRollEmpoignade(msg) {
const rollData = ChatUtility.getMessageData(msg, 'empoignade-roll-data');
RdDEmpoignade.$replaceIdsWithActors(rollData);
return rollData
}
static $replaceIdsWithActors(rollData) {
rollData.attacker = game.actors.get(rollData.attacker.id);
rollData.defender = game.actors.get(rollData.defender.id);
}
/* -------------------------------------------- */
static isEmpoignadeEnCours(actor) {
return actor.itemTypes[TYPES.empoignade].find(it => it.system.pointsemp > 0)
}
/* -------------------------------------------- */
static getEmpoignadeById(actor, id) {
let emp = actor.itemTypes[TYPES.empoignade].find(it => it.system.empoignadeid == id)
return emp && duplicate(emp) || undefined;
}
/* -------------------------------------------- */
static getEmpoignade(attacker, defender) {
let emp = attacker.itemTypes[TYPES.empoignade].find(it =>
(it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
(it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
)
if (emp) {
return duplicate(emp);
}
return undefined;
}
/* -------------------------------------------- */
static getMalusTaille(emp, attacker, defender) {
// Si pas empoigné, alors 0
if (emp.system.pointsemp == 0) {
return 0
}
// p135: Malus de -1 par point de taille de différence de taille au delà de 1 (donc -2 pour une différence de 3, ...)
const diffTaille = attacker.system.carac.taille.value - defender.system.carac.taille.value;
const diffTailleAbs = Math.abs(diffTaille)
const signDiff = diffTaille > 0 ? 1 : -1
if (diffTailleAbs > 2) {
return signDiff * (diffTailleAbs - 1)
}
return 0
}
static isActionAutorisee(mode, attacker, defender) {
const acting = RdDEmpoignade.isActionDefenseur(mode) ? defender : attacker;
if (acting.getUserLevel(game.user) < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
ui.notifications.warn(`Vous n'êtes pas autorisé à choisir l'action de ${acting.name}`)
return false;
}
return true
}
static isActionDefenseur(mode) {
switch (mode) {
case "liberer":
case "contrer-empoigner":
return true;
}
return false;
}
/* -------------------------------------------- */
static async onAttaqueEmpoignade(attacker, defender) {
if (!RdDEmpoignade.isActionAutorisee("empoigner", attacker, defender)) {
return
}
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const isNouvelle = empoignade == undefined;
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
//console.log("W.", empoignade, defender.hasArmeeMeleeEquipee())
if ((isNouvelle || empoignade.system.pointsemp == 0) && defender.hasArmeeMeleeEquipee()) {
ChatUtility.createChatWithRollMode(attacker.name, {
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.html`, { attacker: attacker, defender: defender })
})
} else {
await this.onAttaqueEmpoignadeValidee(attacker, defender)
}
}
/* -------------------------------------------- */
static async onAttaqueEmpoignadeValidee(attacker, defender) {
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const isNouvelle = empoignade == undefined;
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
let mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer"
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
return
}
let rollData = {
mode, empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps").clone(),
selectedCarac: attacker.system.carac.melee,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
if (empoignade.system.pointsemp >= 2) {
if (!empoignade.system.ausol) {
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
} else {
await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle);
}
}
/* -------------------------------------------- */
static async onAttaqueEmpoignadeFromItem(empoignade) {
let attacker = game.actors.get(empoignade.system.empoigneurid)
let defender = game.actors.get(empoignade.system.empoigneid)
await this.onAttaqueEmpoignadeValidee(attacker, defender)
}
static async onImmobilisation(attacker, defender, empoignade) {
const rollData = {
mode: "immobilise",
empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps").clone()
}
const msg = await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(attacker.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.html`, rollData)
})
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async $rollAttaqueEmpoignade(attacker, rollData, isNouvelle = false) {
const dialog = await RdDRoll.create(attacker, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
name: 'jet-empoignade',
label: 'Empoigner',
callbacks: [{ action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, isNouvelle) },]
});
dialog.render(true);
}
/* -------------------------------------------- */
static async $onRollEmpoignade(rollData, isNouvelle = false) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (rollData.rolled.isSuccess && isNouvelle) {
const objectEmpoignade = rollData.empoignade.toObject();
// Creer l'empoignade sur attaquant/defenseur
attacker.creerObjetParMJ(objectEmpoignade);
defender.creerObjetParMJ(objectEmpoignade);
}
rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
if (rollData.rolled.isPart) {
rollData.particuliere = "finesse";
}
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-resultat.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async onDefenseEmpoignade(attackerRoll, mode, competenceName = "Corps à corps", carac = "melee") {
let attacker = game.actors.get(attackerRoll.attacker.id)
let defender = game.actors.get(attackerRoll.defender.id)
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
if (!empoignade) {
ui.notifications.warn("Une erreur s'est produite : Aucune empoignade trouvée !!")
return
}
empoignade = duplicate(empoignade)
let defenderRoll = {
mode, attacker, defender, empoignade, attackerRoll,
diffLibre: attackerRoll.diffLibre,
attaqueParticuliere: attackerRoll.particuliere,
competence: defender.getCompetence(competenceName).clone(),
surprise: defender.getSurprise(true),
carac: defender.system.carac,
selectedCarac: defender.system.carac[carac],
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, defender, attacker),
show: {}
};
await RdDEmpoignade.$rollDefenseEmpoignade(defender, defenderRoll);
}
/* -------------------------------------------- */
static async $rollDefenseEmpoignade(defender, defenderRoll) {
const dialog = await RdDRoll.create(defender, defenderRoll,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-empoignade.html' },
{
name: 'empoignade',
label: 'Contrer',
callbacks: [
{ action: async (r) => await RdDEmpoignade.$onRollContrerLiberer(r) }
]
}
);
dialog.render(true);
}
/* -------------------------------------------- */
static async $onRollContrerLiberer(rollData) {
let empoignade = rollData.empoignade
if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) {
empoignade.system.pointsemp++
RdDEmpoignade.$updateEtatEmpoignade(empoignade)
}
if (rollData.mode == "contrer-liberer" && !rollData.rolled.isSuccess) {
empoignade.system.pointsemp--
RdDEmpoignade.$updateEtatEmpoignade(empoignade)
}
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html')
if (empoignade.system.pointsemp >= 2) {
let msg = await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-empoignade-entrainer.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
}
/* -------------------------------------------- */
static async $updateEtatEmpoignade(empoignade) {
console.log("UPDATE Empoignade", empoignade)
let defender = game.actors.get(empoignade.system.empoigneid)
let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
let update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol }
await defender.updateEmbeddedDocuments('Item', [update])
let attacker = game.actors.get(empoignade.system.empoigneurid)
emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol }
await attacker.updateEmbeddedDocuments('Item', [update])
}
/* -------------------------------------------- */
static async $deleteEmpoignade(empoignade) {
console.log("DELETE Empoignade", empoignade)
let defender = game.actors.get(empoignade.system.empoigneid)
let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
await defender.deleteEmbeddedDocuments('Item', [emp._id])
}
/* -------------------------------------------- */
static async entrainerAuSol(rollData) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
empoignade.system.ausol = true
await this.$updateEtatEmpoignade(empoignade)
await attacker.setEffect(STATUSES.StatusProne, true);
await defender.setEffect(STATUSES.StatusProne, true);
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async projeterAuSol(rollData) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
await defender.setEffect(STATUSES.StatusProne, true);
await this.$deleteEmpoignade(empoignade)
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async perteEndurance(rollData, perteMode) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
//console.log("Perte d'endurance :!!!", perteMode)
let endValue = defender.system.sante.endurance.value
if (perteMode == "end0") {
await defender.santeIncDec("endurance", -endValue);
}
if (perteMode == "end1") {
await defender.santeIncDec("endurance", -(endValue - 1));
}
if (perteMode == "endmoitie") {
await defender.santeIncDec("endurance", -Math.floor(endValue / 2));
}
if (perteMode == "endquart") {
await defender.santeIncDec("endurance", -(3 * Math.floor(endValue / 4)));
}
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async deleteAllEmpoignades() {
for (let actor of game.actors) {
let empIds = actor.itemTypes["empoignade"].map(it => it.id)
await actor.deleteEmbeddedDocuments('Item', empIds)
}
}
/* -------------------------------------------- */
static async deleteLinkedEmpoignade(actorId, empoignade) {
let actorDeleteId = (actorId == empoignade.system.empoigneurid) ? empoignade.system.empoigneid : empoignade.system.empoigneurid
let actor = game.actors.get(actorDeleteId)
let emp = this.getEmpoignadeById(actor, empoignade.system.empoignadeid)
if (emp) {
await actor.deleteEmbeddedDocuments('Item', [emp._id])
}
}
/* -------------------------------------------- */
static async createEmpoignade(attacker, defender) {
return await Item.create({
name: "Empoignade en cours de " + attacker.name + ' sur ' + defender.name,
type: 'empoignade',
img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
system: { description: "", empoignadeid: randomID(16), compteempoigne: 0, empoigneurid: attacker.id, empoigneid: defender.id, ptsemp: 0, empoigneurname: attacker.name, empoignename: defender.name }
},
{
temporary: true
})
}
}

View File

@ -1,6 +1,6 @@
import { Grammar } from "./grammar.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
/* -------------------------------------------- */
export class RdDHerbes extends Item {

View File

@ -1,54 +1,64 @@
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDActor } from "./actor.js";
import { RdDItem } from "./item.js";
import { Migrations } from './migrations.js';
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDCalendrier } from "./time/rdd-calendrier.js";
import { RdDTimestamp } from "./time/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 { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDPossession } from "./rdd-possession.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 { Environnement } from "./environnement.js";
import { RdDItemService } from "./item/item-service.js";
import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDCommerceSheet } from "./actor/commerce-sheet.js";
import { RdDCommerce } from "./actor/commerce.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDItemMaladie } from "./item/maladie.js";
import { RdDItemPoison } from "./item/poison.js";
import { RdDItemSigneDraconique } from "./item/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/item-rencontre.js";
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 { RdDItemSheet } from "./item-sheet.js";
import { RdDServiceItemSheet } from "./item-service-sheet.js";
import { RdDHerbeItemSheet } from "./item-herbe-sheet.js";
import { RdDRencontreItemSheet } from "./item-rencontre-sheet.js";
import { RdDIngredientItemSheet } from "./item-ingredient-sheet.js";
import { RdDFauneItemSheet } from "./item-faune-sheet.js";
import { RdDConteneurItemSheet } from "./item-conteneur-sheet.js";
import { RdDSigneDraconiqueItemSheet } from "./item-signedraconique-sheet.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.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 { RdDBlessureItemSheet } from "./item/sheet-blessure.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";
import { AppAstrologie } from "./sommeil/app-astrologie.js";
import { RdDItemArmure } from "./item/armure.js";
/**
* RdD system
@ -67,21 +77,23 @@ export class SystemReveDeDragon {
this.RdDUtility = RdDUtility;
this.RdDHotbar = RdDHotbar;
this.itemClasses = {
service: RdDItemService,
armure: RdDItemArmure,
blessure: RdDItemBlessure,
maladie: RdDItemMaladie,
ombre: RdDItemOmbre,
poison: RdDItemPoison,
queue: RdDItemQueue,
ombre: RdDItemOmbre,
souffle: RdDItemSouffle,
rencontre: RdDRencontre,
service: RdDItemService,
signedraconique: RdDItemSigneDraconique,
rencontre: RdDRencontre
souffle: RdDItemSouffle,
}
this.actorClasses = {
commerce: RdDCommerce,
creature: RdDActor,
entite: RdDActor,
personnage: RdDActor,
vehicule: RdDActor,
commerce: RdDCommerce,
}
}
@ -90,6 +102,8 @@ export class SystemReveDeDragon {
/* -------------------------------------------- */
async onInit() {
game.system.rdd = this;
this.AppAstrologie = AppAstrologie;
console.log(`Initializing Reve de Dragon System`);
@ -145,35 +159,41 @@ export class SystemReveDeDragon {
RdDItemSheet.register(RdDConteneurItemSheet);
RdDItemSheet.register(RdDHerbeItemSheet);
RdDItemSheet.register(RdDFauneItemSheet);
RdDItemSheet.register(RdDPlanteItemSheet);
RdDItemSheet.register(RdDIngredientItemSheet);
RdDItemSheet.register(RdDServiceItemSheet);
RdDItemSheet.register(RdDBlessureItemSheet);
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",
"objet", "arme", "armure", "livre", "potion", "munition",
"monnaie", "nourritureboisson", "gemme",
"meditation", "queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve",
"nombreastral", "tache", "maladie", "poison", "possession",
"tarot", "extraitpoetique"
"tarot", "extraitpoetique", "empoignade"
], makeDefault: true
});
CONFIG.Combat.documentClass = RdDCombatManager;
// préparation des différents modules
RdDTimestamp.init();
RdDCalendrier.init();
SystemCompendiums.init();
DialogChronologie.init();
ReglesOptionelles.init();
ReglesOptionnelles.init();
RdDUtility.init();
RdDDice.init();
RdDCommands.init();
RdDCombat.init();
RdDCombatManager.init();
RdDTokenHud.init();
RdDBaseActor.init();
RddCompendiumOrganiser.init();
RdDCompendiumOrganiser.init();
EffetsDraconiques.init()
TMRUtility.init();
RdDHotbar.initDropbar();
@ -199,25 +219,6 @@ export class SystemReveDeDragon {
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",
@ -263,9 +264,11 @@ export class SystemReveDeDragon {
let sidebar = document.getElementById("sidebar");
sidebar.style.width = "min-content";
}
game.system.rdd.calendrier = new RdDCalendrier();
if (Misc.isUniqueConnectedGM()) {
new Migrations().migrate();
this.messageDeBienvenue();
this.registerUsageCount(SYSTEM_RDD);
}
StatusEffects.onReady();
@ -273,14 +276,7 @@ export class SystemReveDeDragon {
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;
game.system.rdd.calendrier.display();
// Avertissement si joueur sans personnage
if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Vous n'êtes connecté à aucun personnage !");
@ -289,14 +285,10 @@ export class SystemReveDeDragon {
user: game.user.id
});
}
if (Misc.isUniqueConnectedGM()) {
this.messageDeBienvenue();
this.registerUsageCount(SYSTEM_RDD);
}
}
/* -------------------------------------------- */
messageDeBienvenue() {
messageDeBienvenue() {
if (game.user.isGM) {
ChatUtility.removeChatMessageContaining('<div id="message-bienvenue-rdd">');
ChatMessage.create({

View File

@ -1,9 +1,9 @@
/* -------------------------------------------- */
import { RdDCombat } from "./rdd-combat.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { Targets } from "./targets.js";
import { TYPES } from "./item.js";
/* -------------------------------------------- */
/* On part du principe qu'une entité démarre tjs
@ -20,9 +20,9 @@ export class RdDPossession {
/* -------------------------------------------- */
static searchPossessionFromEntite(attacker, defender) {
let poss = attacker.items.find(poss => poss.type == 'possession' && poss.system.possedeid == defender.id);
let poss = attacker.items.find(poss => poss.type == TYPES.possession && poss.system.victime.actorid == defender.id);
if (!poss) {
poss = defender.items.find(poss => poss.type == 'possession' && poss.system.possedeid == defender.id);
poss = defender.items.find(poss => poss.type == TYPES.possession && poss.system.victime.actorid == defender.id);
}
return poss && duplicate(poss) || undefined;
}
@ -31,39 +31,40 @@ export class RdDPossession {
static async onAttaquePossession(target, attacker, competence, suitePossession = undefined) {
const defender = target.actor;
const fromEntite = RdDPossession.searchPossessionFromEntite(attacker, defender);
const isNouvelle = !suitePossession && ! fromEntite;
const isNouvelle = !suitePossession && !fromEntite;
const possession = (suitePossession ?? fromEntite ?? (await RdDPossession.createPossession(attacker, defender)));
RdDPossession.$updateEtatPossession(possession)
let rollData = {
mode: "possession",
mode: "attaque",
isECNIDefender: false,
competence: competence,
competence: competence.clone(),
possession: possession,
attacker: attacker,
defender: defender,
targetToken: Targets.extractTokenData(target)
};
if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
RdDPossession.selectCompetenceDraconicOuPossession(rollData, attacker)
await RdDPossession.$rollAttaquePossession(attacker, rollData, isNouvelle);
}
/* -------------------------------------------- */
static async onConjurerPossession(attacker, competence, possession) {
static async onConjurerPossession(attacker, possession) {
possession = duplicate(possession);
RdDPossession.$updateEtatPossession(possession)
const defender = game.actors.get(possession.system.entite.actorid);
let rollData = {
mode: "possession",
mode: "attaque",
isECNIDefender: true,
competence: competence,
possession: possession,
attacker: attacker,
defender: game.actors.get(possession.system.possesseurid)
defender: defender,
};
RdDPossession.selectCompetenceDraconicOuPossession(rollData, attacker)
await RdDPossession.$rollAttaquePossession(attacker, rollData);
}
@ -71,7 +72,7 @@ export class RdDPossession {
static async onDefensePossession(attackerId, defenderId, possessionId) {
let attacker = game.actors.get(attackerId)
let possession = attacker?.getPossession(possessionId)
defenderId = defenderId ?? possession?.system.possesseurid ?? undefined
defenderId = defenderId ?? possession?.system.entite.actorid ?? undefined
let defender = game.actors.get(defenderId)
possession = possession ?? defender?.getPossession(possessionId) ?? undefined;
@ -82,19 +83,29 @@ export class RdDPossession {
possession = duplicate(possession)
// Update for draconic roll
let rollData = {
mode: "conjuration",
mode: "defense",
isECNIDefender: defender.type == "entite",
possession: possession,
attacker: attacker,
defender: defender,
competence: defender.getDraconicOuPossession(),
selectedCarac: defender.system.carac.reve,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } }
}
rollData.competence.system.defaut_carac = 'reve-actuel'
RdDPossession.selectCompetenceDraconicOuPossession(rollData, defender)
rollData.diffLibre = RdDPossession.getInfoAttaque(rollData).diffLibre
await RdDPossession.$rollDefensePossession(defender, rollData);
}
static selectCompetenceDraconicOuPossession(rollData, rollingActor) {
rollData.competence = rollingActor.getDraconicOuPossession();
if (rollingActor.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
else {
rollData.selectedCarac = rollingActor.system.carac.reve
rollData.forceCarac = { 'reve-actuel': { label: "Rêve Actuel", value: rollingActor.getReveActuel() } }
rollData.competence.system.defaut_carac = 'reve-actuel'
}
}
/* -------------------------------------------- */
static async $rollAttaquePossession(attacker, rollData, isNouvelle = false) {
@ -104,21 +115,22 @@ export class RdDPossession {
name: 'jet-possession',
label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession',
callbacks: [
{ condition: r => (r.rolled.isSuccess), action: async (r) => await RdDPossession.$onRollPossession(r, true, isNouvelle) },
{ condition: r => (r.rolled.isEchec), action: async (r) => await RdDPossession.$onRollPossession(r, false, isNouvelle) },
{ action: async (r) => await RdDPossession.$onRollPossession(r, isNouvelle) },
]
});
dialog.render(true);
dialog.render(true);
}
/* -------------------------------------------- */
static async $onRollPossession(rollData, isSuccess, isNouvelle = false) {
rollData.possession.isSuccess = isSuccess;
RdDPossession.$updateEtatPossession(rollData.possession);
static async $onRollPossession(rollData, isNouvelle = false) {
rollData.possession.isSuccess = rollData.rolled.isSuccess;
RdDPossession.$updateEtatPossession(rollData.possession, rollData);
if (isNouvelle) {
// Creer la possession sur le defenseur
rollData.defender.createEmbeddedDocuments('Item', [rollData.possession.toObject()])
await rollData.defender.createEmbeddedDocuments('Item', [rollData.possession.toObject()])
}
const possession = (rollData.isECNIDefender ? rollData.attacker : rollData.defender).getPossession(rollData.possession.system.possessionid)
RdDPossession.storePossessionAttaque(possession, rollData)
await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html');
}
@ -127,35 +139,42 @@ export class RdDPossession {
const dialog = await RdDRoll.create(defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html' },
{
name: 'conjurer',
name: 'possession',
label: 'Conjurer une Possession',
callbacks: [
{ action: async (r) => await RdDPossession.$onRollConjuration(r) }
]
}
);
]
}
);
dialog.render(true);
}
}
/* -------------------------------------------- */
static async $onRollConjuration(rollData) {
let actor = game.actors.get(rollData.possession.system.possedeid)
let victime = game.actors.get(rollData.possession.system.victime.actorid)
let compteur = rollData.possession.system.compteur
if (!rollData.rolled.isSuccess) {
if (rollData.isECNIDefender) {
rollData.possession.system.compteur--
compteur--
} else {
rollData.possession.system.compteur++
compteur++
}
let update = { _id: rollData.possession._id, "system.compteur": rollData.possession.system.compteur }
await actor.updateEmbeddedDocuments('Item', [update])
}
const possession = victime.getPossession(rollData.possession.system.possessionid)
await possession.update({
system: {
compteur: compteur,
entite: { diffLibre: 0, finesse: false },
victime: { diffLibre: 0, finesse: false }
}
})
rollData.possession = possession
RdDPossession.$updateEtatPossession(rollData.possession)
await RdDResolutionTable.displayRollData(rollData,rollData.defender, 'chat-resultat-possession.html')
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.html')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
// conjuration
actor.deleteEmbeddedDocuments("Item", [rollData.possession._id])
victime.deleteEmbeddedDocuments("Item", [rollData.possession._id])
}
}
@ -180,13 +199,43 @@ export class RdDPossession {
}
}
/* -------------------------------------------- */
static isPossessionFinesse(rollData) {
return RdDPossession.getInfoAttaque(rollData).finesse
}
/* -------------------------------------------- */
static getInfoAttaque(rollData) {
return rollData.possession.system[rollData.isECNIDefender ? 'victime' : 'entite'];
}
/* -------------------------------------------- */
static isDefensePossession(rollData) {
return rollData.possession && rollData.mode == "defense"
}
/* -------------------------------------------- */
static storePossessionAttaque(possession, rollData = undefined) {
const attaquant = rollData?.isECNIDefender ? 'victime' : 'entite'
possession.update({
[`system.${attaquant}`]: {
diffLibre: rollData?.diffLibre ?? 0,
finesse: rollData?.rolled.isPart ?? false
}
})
}
/* -------------------------------------------- */
static async createPossession(attacker, defender) {
return await Item.create({
name: "Possession en cours de " + attacker.name, type: 'possession',
img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
system: { description: "", typepossession: attacker.name, possede: false, possessionid: randomID(16), possesseurid: attacker.id, possedeid: defender.id, date: 0, compteur: 0 }
},
name: "Possession en cours de " + attacker.name, type: 'possession',
img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
system: {
description: "", typepossession: attacker.name,
possede: false,
possessionid: randomID(16),
entite: { actorid: attacker.id },
victime: { actorid: defender.id },
compteur: 0
}
},
{
temporary: true
})

View File

@ -1,7 +1,7 @@
import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
/**
* difficultés au delà de -10
@ -91,13 +91,17 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
return await ChatUtility.createChatWithRollMode(actor?.userName ?? game.user.name, {
content: await RdDResolutionTable.buildRollDataHtml(rollData, actor, template)
return await ChatUtility.createChatWithRollMode(RdDResolutionTable.actorChatName(actor), {
content: await RdDResolutionTable.buildRollDataHtml(rollData, template)
});
}
static actorChatName(actor) {
return actor?.userName ?? game.user.name;
}
/* -------------------------------------------- */
static async buildRollDataHtml(rollData, actor, template = 'chat-resultat-general.html') {
static async buildRollDataHtml(rollData, template = 'chat-resultat-general.html') {
rollData.show = rollData.show || {};
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
}
@ -122,7 +126,7 @@ export class RdDResolutionTable {
rolled.bonus = rollData.bonus;
rolled.factorHtml = Misc.getFractionHtml(rollData.diviseurSignificative);
if (ReglesOptionelles.isUsing("afficher-colonnes-reussite")) {
if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) {
rolled.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll);
rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
}
@ -195,17 +199,6 @@ export class RdDResolutionTable {
return Math.max(Math.floor(carac * (diff + 10) / 2), 1);
}
/* -------------------------------------------- */
static isAjustementAstrologique(rollData) {
if (rollData.selectedCarac?.label.toLowerCase().includes('chance')) {
return true;
}
if (rollData.selectedSort?.system.isrituel) {
return true;
}
return false;
}
/* -------------------------------------------- */
static isEchec(rollData) {
switch (rollData.surprise) {
@ -294,12 +287,9 @@ export class RdDResolutionTable {
carac: carac,
difficulte: level,
min: minLevel,
rows: RdDResolutionTable.incrementalArray(minCarac, maxCarac),
cols: RdDResolutionTable.incrementalArray(minLevel, maxLevel)
rows: Misc.intArray(minCarac, maxCarac+1),
cols: Misc.intArray(minLevel, maxLevel+1)
});
}
static incrementalArray(min, max) {
return Array.from(Array(max-min+1).keys()).map(i=>i+min)
}
}

View File

@ -30,9 +30,9 @@ export class RdDEncaisser extends Dialog {
};
}
else if (actor.isEntite([ENTITE_BLURETTE, ENTITE_INCARNE])) {
dialogConf.default = "cauchemar"
dialogConf.default = "entiteincarnee"
dialogConf.buttons = {
"cauchemar": { label: "Cauchemar", callback: html => this.performEncaisser("cauchemar") }
"entiteincarnee": { label: "Entité incarnée", callback: html => this.performEncaisser("entiteincarnee") }
}
}
@ -70,7 +70,6 @@ export class RdDEncaisser extends Dialog {
total: Number(this.modifier),
ajustement: Number(this.modifier),
encaisserSpecial: this.encaisserSpecial,
loc: { result: 0, label: "" },
mortalite: mortalite
}
});

View File

@ -6,7 +6,7 @@ import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
/**
* Extend the base Dialog entity to select roll parameters
@ -85,7 +85,7 @@ export class RdDRoll extends Dialog {
if (RdDBonus.isDefenseAttaqueFinesse(rollData)) {
facteurSign *= 2;
}
if (!ReglesOptionelles.isUsing('tripleSignificative')) {
if (!ReglesOptionnelles.isUsing('tripleSignificative')) {
facteurSign = Math.min(facteurSign, 4);
}
return facteurSign;
@ -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(".divAppelAuMoral"), rollData.use.appelAuMoral);
HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
// Mise à jour valeurs
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));

View File

@ -1,9 +1,25 @@
import { DialogSplitItem } from "./dialog-split-item.js";
import { RdDItem } from "./item.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
export class RdDSheetUtility {
static mergeDocumentRights(options, document, editable) {
const userRightLevel = game.user.isGM
? CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
: document.getUserLevel(game.user);
let newOptions = {
isGM: game.user.isGM,
isOwned: document.parent ? true : false,
editable: editable,
cssClass: editable ? "editable" : "locked",
isLimited: userRightLevel >= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED,
isObserver: userRightLevel >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER,
isOwner: userRightLevel >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER
}
mergeObject(options, newOptions);
return options;
}
static getItem(event, actor) {
return actor.items.get(RdDSheetUtility.getItemId(event))
}
@ -23,7 +39,7 @@ export class RdDSheetUtility {
static async prepareItemDropParameters(destItemId, actor, dragData, objetVersConteneur) {
let item = fromUuidSync(dragData.uuid);
if (item.pack && !item.system){
if (item.pack && !item.system) {
item = await RdDItem.getCorrespondingItem(item);
}
if (actor.canReceive(item)) {

View File

@ -12,12 +12,11 @@ import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDDice } from "./rdd-dice.js";
import { STATUSES } from "./settings/status-effects.js";
import { RdDRencontre } from "./item/item-rencontre.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDRencontre } from "./item/rencontre.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
/* -------------------------------------------- */
@ -56,7 +55,7 @@ export class RdDTMRDialog extends Dialog {
this.actor = actor;
this.actor.tmrApp = this; // reference this app in the actor structure
this.viewOnly = tmrData.mode == "visu"
this.fatigueParCase = this.viewOnly || !ReglesOptionelles.isUsing("appliquer-fatigue") ? 0 : this.actor.getTMRFatigue();
this.fatigueParCase = this.viewOnly || !ReglesOptionnelles.isUsing("appliquer-fatigue") ? 0 : this.actor.getTMRFatigue();
this.cumulFatigue = 0;
this.loadRencontres();
this.loadCasesSpeciales();
@ -208,8 +207,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"), ReglesOptionnelles.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) => {
@ -228,7 +227,7 @@ export class RdDTMRDialog extends Dialog {
// Gestion du cout de montée en points de rêve
let reveCout = ((this.tmrdata.isRapide && !EffetsDraconiques.isDeplacementAccelere(this.actor)) ? -2 : -1) - this.actor.countMonteeLaborieuse();
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
await this.actor.reveActuelIncDec(reveCout);
@ -246,7 +245,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;
@ -264,7 +263,7 @@ export class RdDTMRDialog extends Dialog {
let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = this.actor.system.reve.refoulement.value;
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
let fatigueItem = document.getElementById("tmr-fatigue-table");
fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.actor.system.sante.fatigue.value, this.actor.system.sante.endurance.max).html() + "</table>";
}
@ -285,7 +284,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();
@ -363,7 +367,7 @@ export class RdDTMRDialog extends Dialog {
return true;
}
const resteAvantInconscience = this.actor.getFatigueMax() - this.actor.getFatigueActuelle() - this.cumulFatigue;
if (ReglesOptionelles.isUsing("appliquer-fatigue") && resteAvantInconscience <= 0) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && resteAvantInconscience <= 0) {
this._tellToGM("Vous vous écroulez de fatigue : vous quittez les Terres médianes !");
this.quitterLesTMRInconscient();
return true;
@ -447,7 +451,7 @@ export class RdDTMRDialog extends Dialog {
setTimeout(() => {
// TODO: remplacer par une boucle while(this.currentRencontre) ?
rencData.nbRounds++;
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
this._tentativeMaitrise(rencData);
@ -589,6 +593,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;
}
}
@ -799,19 +804,21 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async declencheSortEnReserve(coord) {
let sorts = this.getSortsReserve(coord);
const sorts = this.getSortsReserve(coord);
if (sorts.length > 0) {
if (EffetsDraconiques.isSortReserveImpossible(this.actor)) {
ui.notifications.error("Une queue ou un souffle vous empèche de déclencher de sort!");
return;
}
if (!EffetsDraconiques.isUrgenceDraconique(this.actor) &&
(EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord))) {
let msg = "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête <strong>Reserve en Sécurité</strong> ou <strong>Réserve Exensible</strong>, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher : <ul>";
for (let sort of sorts) {
msg += `<li><a class="chat-card-button declencher-sort-reserve" data-actor-id="${this.actor.id}" data-tmr-coord="${coord}" data-sort-id='${sort.id}">${sort.name}</a></li>`;
}
msg += "</ol>";
const reserveSecurite = EffetsDraconiques.isReserveEnSecurite(this.actor);
const reserveExtensible = this.isReserveExtensible(coord);
if (!EffetsDraconiques.isUrgenceDraconique(this.actor) && (reserveSecurite || reserveExtensible)) {
const msg = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-demande-declencher-sort.hbs`, {
actor: this.actor,
sorts: sorts,
coord: coord,
tete: { reserveSecurite: reserveSecurite, reserveExtensible: reserveExtensible }
})
ChatMessage.create({
content: msg,
whisper: ChatMessage.getWhisperRecipients(game.user.name)
@ -997,7 +1004,7 @@ export class RdDTMRDialog extends Dialog {
await this.actor.updateCoordTMR(tmr.coord);
this.forceDemiRevePositionView();
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
this.updateValuesDisplay();
@ -1045,6 +1052,7 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
static _computeEventOddq(origEvent) {
console.log("EVENT", origEvent)
let canvasRect = origEvent.target.getBoundingClientRect();
let x = origEvent.clientX - canvasRect.left;
let y = origEvent.clientY - canvasRect.top;

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

@ -2,6 +2,7 @@
import { HtmlUtility } from "./html-utility.js";
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { Targets } from "./targets.js";
/* -------------------------------------------- */
export class RdDTokenHud {
@ -18,29 +19,39 @@ export class RdDTokenHud {
}
/* -------------------------------------------- */
static async addExtensionHud(app, html, tokenId) {
static async addExtensionHud(app, html, tokenId, isCombat) {
let token = canvas.tokens.get(tokenId);
let actor = token.actor;
let combatant = game.combat.combatants.find(c => c.tokenId == tokenId);
if (! (combatant?.actor) ) {
app.hasExtension = true;
// soins
await RdDTokenHud.addExtensionHudSoins(html, actor);
if (isCombat) {
let combatant = game.combat.combatants.find(c => c.tokenId == tokenId);
if (!(combatant?.actor)) {
ui.notifications.warn(`Le combatant ${token.name} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return;
}
let actions = RdDCombatManager.listActionsCombat(combatant);
// initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions);
// combat
await RdDTokenHud.addExtensionHudCombat(html, combatant, actions);
}
app.hasExtension = true;
let actionsCombat = RdDCombatManager.listActionsCombat(combatant);
}
static async addExtensionHudInit(html, combatant, actions) {
const hudData = {
combatant: combatant,
actions: actionsCombat,
combatant, actions,
commandes: [
{ name: "Autre action", command: 'autre' },
{ name: 'Initiative +1', command: 'inc', value: 0.01 },
{ name: 'Initiative +1', command: 'inc', value: 0.01 },
{ name: 'Initiative -1', command: 'dec', value: -0.01 }]
};
const controlIconCombat = html.find('.control-icon[data-action=combat]');
// initiative
await RdDTokenHud._configureSubMenu(controlIconCombat,
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-init.html',
hudData,
@ -51,48 +62,70 @@ export class RdDTokenHud {
RdDTokenHud._initiativeCommand(initCommand, combatantId);
} else {
let index = event.currentTarget.attributes['data-action-index'].value;
let action = actionsCombat[index];
let action = hudData.actions[index];
RdDCombatManager.rollInitiativeAction(combatantId, action);
}
}
});
}
static async addExtensionHudCombat(html, combatant, actions) {
const hudData = { combatant, actions, commandes: [] };
const controlIconTarget = html.find('.control-icon[data-action=target]');
// combat
await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData,
(event) => {
const actionIndex = event.currentTarget.attributes['data-action-index']?.value;
const action = actionsCombat[actionIndex];
if (action.action == 'conjurer') {
actor.conjurerPossession(actor.getPossession(action.system.possessionid));
const action = hudData.actions[actionIndex];
const possession = action.action == 'possession' ? combatant.actor.getPossession(action.system.possessionid) : undefined;
if (possession) {
combatant.actor.conjurerPossession(possession);
}
else {
actor.rollArme(action);
combatant.actor.rollArme(action);
}
});
}
static async addExtensionHudSoins(html, sourceActor) {
const target = Targets.getTarget({ warn: false });
if (target?.actor) {
const hudSoins = { blessures: target.actor.blessuresASoigner() ?? [] };
if (hudSoins.blessures.length > 0) {
// soins
const controlIconTarget = html.find('.control-icon[data-action=combat]');
await RdDTokenHud._configureSubMenu(controlIconTarget,
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-soins.hbs',
hudSoins,
(event) => {
const blessureId = event.currentTarget.attributes['data-blessure-id']?.value;
sourceActor.rollSoins(target.actor, blessureId)
});
}
}
}
static _initiativeCommand(initCommand, combatantId) {
switch (initCommand) {
case 'inc': return RdDCombatManager.incDecInit(combatantId, 0.01);
case 'dec': return RdDCombatManager.incDecInit(combatantId, -0.01);
case 'autre': return RdDCombatManager.rollInitiativeAction(combatantId,
case 'autre': return RdDCombatManager.rollInitiativeAction(combatantId,
{ name: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } });
}
}
/* -------------------------------------------- */
static async addTokenHudExtensions(app, html, tokenId) {
const controlIconCombat = html.find('.control-icon[data-action=combat]');
controlIconCombat.click(event => {
if (event.currentTarget.className.includes('active')) {
RdDTokenHud.removeExtensionHud(app, html, tokenId);
} else {
setTimeout(function () { RdDTokenHud.addExtensionHud(app, html, tokenId) }, 200);
}
});
const controlIconCombat = html.find('.control-icon[data-action=combat]');
if (controlIconCombat.length > 0) {
controlIconCombat.click(event => {
if (event.currentTarget.className.includes('active')) {
RdDTokenHud.removeExtensionHud(app, html, tokenId);
} else {
setTimeout(() => RdDTokenHud.addExtensionHud(app, html, tokenId), 200);
}
});
if (controlIconCombat.length>0 && controlIconCombat[0].className.includes('active')) {
RdDTokenHud.addExtensionHud(app, html, tokenId);
const isCombat = controlIconCombat[0].className.includes('active');
RdDTokenHud.addExtensionHud(app, html, tokenId, isCombat);
}
}
@ -100,9 +133,9 @@ export class RdDTokenHud {
static async _configureSubMenu(insertionPoint, template, hudData, onMenuItem) {
const hud = $(await renderTemplate(template, hudData));
const list = hud.find('div.rdd-hud-list');
RdDTokenHud._toggleHudListActive(hud, list);
hud.find('img.rdd-hud-togglebutton').click(event => RdDTokenHud._toggleHudListActive(hud, list));
list.find('.rdd-hud-menu').click(onMenuItem);
@ -111,6 +144,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

@ -5,24 +5,25 @@ import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js";
import { TMRUtility } from "./tmr-utility.js";
import { DialogItemAchat } from "./dialog-item-achat.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.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 { Environnement } from "./environnement.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
import { ExperienceLog } from "./actor/experience-log.js";
/* -------------------------------------------- */
// This table starts at 0 -> niveau -10
const carac_array = ["taille", "apparence", "constitution", "force", "agilite", "dexterite", "vue", "ouie", "odoratgout", "volonte", "intellect", "empathie", "reve", "chance", "melee", "tir", "lancer", "derobee"];
const difficultesLibres = [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10];
const ajustementsConditions = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6, +7, +8, +9, +10];
const ajustementsEncaissement = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, +13, +14, +15, +16, +17, +18, +19, +20, +21, +22, +23, +24, +25];
const difficultesLibres = Misc.intArray(0, -11);
const ajustementsConditions = Misc.intArray(-10, 11);
const ajustementsEncaissement = Misc.intArray(-10, 26);
/* -------------------------------------------- */
function _buildAllSegmentsFatigue(max) {
@ -67,38 +68,31 @@ const fatigueMarche = {
"tresdifficile": { "4": 4, "6": 6 }
}
/* -------------------------------------------- */
const definitionsBlessures = [
{ type: "legere", facteur: 2 },
{ type: "grave", facteur: 4 },
{ type: "critique", facteur: 6 }
]
/* -------------------------------------------- */
const nomEthylisme = ["Emeché", "Gris", "Pinté", "Pas frais", "Ivre", "Bu", "Complètement fait", "Ivre mort"];
/* -------------------------------------------- */
const definitionsEncaissement = {
"mortel": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", eraflures: 0, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", eraflures: 0, legeres: 1, graves: 0, critiques: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "2", eraflures: 0, legeres: 0, graves: 1, critiques: 0 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "4 + @over20", eraflures: 0, legeres: 0, graves: 0, critiques: 1 },
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1},
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0},
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 2},
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "2", gravite: 4},
{ minimum: 20, maximum: undefined, endurance: "100", vie: "4 + @over20", gravite: 6},
],
"non-mortel": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", eraflures: 0, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", eraflures: 0, legeres: 1, graves: 0, critiques: 0 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "0", eraflures: 0, legeres: 1, graves: 0, critiques: 0 },
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1},
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 2 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "0", gravite: 2 },
],
"cauchemar": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", eraflures: 0, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
{ minimum: 20, maximum: undefined, endurance: "3d6 + @over20", vie: "0", eraflures: 1, legeres: 0, graves: 0, critiques: 0 },
"entiteincarnee": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1},
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0},
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 0 },
{ minimum: 20, maximum: undefined, endurance: "3d6 + @over20", vie: "0", gravite: 0 },
]
};
@ -127,6 +121,7 @@ export class RdDUtility {
'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/archetype.hbs',
'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',
@ -140,9 +135,10 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/xp-competences.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/combat.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/blessures.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/blessure.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/blessure.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/maladies-poisons.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/possessions.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/resonances.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/taches.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/taches.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.html',
@ -150,6 +146,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',
@ -186,7 +183,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/enum-caracteristiques.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-aspect-tarot.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categories.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-ingredient.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html',
@ -202,6 +199,8 @@ export class RdDUtility {
'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-recherche.hbs',
'systems/foundryvtt-reve-de-dragon/templates/time/horloge.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',
@ -233,10 +232,10 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-meditation.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-alchimie.html',
'systems/foundryvtt-reve-de-dragon/templates/dialog-astrologie-joueur.html',
// Calendrier
'systems/foundryvtt-reve-de-dragon/templates/calendar-template.html',
'systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html',
'systems/foundryvtt-reve-de-dragon/templates/sommeil/sommeil-actor-moral.hbs',
'systems/foundryvtt-reve-de-dragon/templates/sommeil/astrologie-gardien.hbs',
'systems/foundryvtt-reve-de-dragon/templates/sommeil/astrologie-joueur.hbs',
'systems/foundryvtt-reve-de-dragon/templates/sommeil/astrologie-theme.hbs',
// HUD
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-init.html',
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html',
@ -245,20 +244,6 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/chat-description.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-appelchance.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-attaque.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-parade.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-esquive.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-general.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-tache.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-sort.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-alchimie.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-resultat-possession.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html',
@ -272,13 +257,15 @@ export class RdDUtility {
Handlebars.registerHelper('computeResolutionChances', (row, col) => RdDResolutionTable.computeChances(row, col));
Handlebars.registerHelper('upperFirst', str => Misc.upperFirst(str ?? 'Null'));
Handlebars.registerHelper('lowerFirst', str => Misc.lowerFirst(str ?? 'Null'));
Handlebars.registerHelper('upper', str => str?.toUpperCase() ?? 'NULL');
Handlebars.registerHelper('upper', str => str?.toUpperCase() ?? '');
Handlebars.registerHelper('lowercase', str => str?.toLowerCase() ?? '');
Handlebars.registerHelper('le', str => Grammar.articleDetermine(str));
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, 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('buildLigneInventaire', (item, options) => { return new Handlebars.SafeString(RdDUtility.buildLigneInventaire(item, options)); });
Handlebars.registerHelper('buildInventaireConteneur', (actorId, itemId, options) => { return new Handlebars.SafeString(RdDUtility.buildInventaireConteneur(actorId, itemId, options)); });
Handlebars.registerHelper('buildContenuConteneur', (item, options) => { return new Handlebars.SafeString(RdDUtility.buildContenuConteneur(item, options)); });
Handlebars.registerHelper('calculerPrixCommercant', item => item.calculerPrixCommercant());
Handlebars.registerHelper('caseTmr-label', coord => TMRUtility.getTMRLabel(coord));
Handlebars.registerHelper('caseTmr-type', coord => TMRUtility.getTMRType(coord));
@ -292,13 +279,16 @@ export class RdDUtility {
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('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option));
Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name)));
Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences));
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));
Handlebars.registerHelper('experienceLog-topic', topic => ExperienceLog.labelTopic(topic));
return loadTemplates(templatePaths);
}
@ -350,13 +340,13 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static buildArbreDeConteneurs(conteneurs, objets) {
static buildArbreDeConteneurs(conteneurs, inventaires) {
let objetVersConteneur = {};
// Attribution des objets aux conteneurs
for (let conteneur of conteneurs) {
conteneur.subItems = [];
for (let id of conteneur.system.contenu ?? []) {
let objet = objets.find(objet => (id == objet._id));
let objet = inventaires.find(objet => (id == objet._id));
if (objet) {
objet.estContenu = true; // Permet de filtrer ce qui est porté dans le template
objetVersConteneur[id] = conteneur._id;
@ -365,20 +355,20 @@ export class RdDUtility {
}
}
for (let conteneur of conteneurs) {
conteneur.system.encTotal = RdDUtility.calculEncContenu(conteneur, objets);
conteneur.system.encTotal = RdDUtility.calculEncContenu(conteneur, inventaires);
}
return objetVersConteneur;
}
/* -------------------------------------------- */
static calculEncContenu(conteneur, objets) {
static calculEncContenu(conteneur, inventaires) {
const contenus = (conteneur.system.contenu ?? []).filter(id => id != undefined)
.map(id => objets.find(it => (id == it.id)))
.map(id => inventaires.find(it => (id == it.id)))
.filter(it => it);
let enc = Number(conteneur.system.encombrement ?? 0) * Number(conteneur.system.quantite ?? 1);
for (let contenu of contenus) {
if (contenu.type == 'conteneur') {
enc += RdDUtility.calculEncContenu(contenu, objets);
enc += RdDUtility.calculEncContenu(contenu, inventaires);
}
else {
enc += Number(contenu.system.encombrement ?? 0) * Number(contenu.system.quantite ?? 1)
@ -393,40 +383,64 @@ export class RdDUtility {
return conteneurs.filter((conteneur, index, arr) => !conteneur.estContenu);
}
/* -------------------------------------------- */
/** Construit la structure récursive des conteneurs, avec imbrication potentielle
*
*/
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/${templateItem}`]({
item: objet,
vide: isVide,
ouvert: isOuvert,
options: options
});
const contenu = isConteneur ? RdDUtility.buildContenu(objet, profondeur, isOuvert, templateItem, options) : '';
return conteneur + contenu;
static prepareOptionsArbreInventaire(item, optionsArbre) {
if (!optionsArbre.profondeur) {
optionsArbre.profondeur = 1
};
if (!optionsArbre.templateItem) {
optionsArbre.templateItem = item.parent?.type == 'commerce'
? "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.html"
: "systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.html";
}
item.niveau = optionsArbre.profondeur;
}
/* -------------------------------------------- */
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)}'>`;
for (let subItem of objet.subItems) {
strContenu += this.buildConteneur(subItem, profondeur + 1, templateItem, options);
/**
* Construit la structure récursive des conteneurs, avec imbrication potentielle
*/
static buildLigneInventaire(item, options = {}, optionsArbre = { ouvert: false, profondeur: 1 }) {
RdDUtility.prepareOptionsArbreInventaire(item, optionsArbre);
const isConteneur = item.type == 'conteneur';
const inventaire = {
item: item,
vide: isConteneur && item.system.contenu.length == 0,
ouvert: isConteneur && RdDUtility.getAfficheContenu(item._id),
options: options
};
optionsArbre.ouvert = inventaire.ouvert
const ligneObjet = Handlebars.partials[optionsArbre.templateItem](inventaire);
if (isConteneur) {
return ligneObjet + RdDUtility.buildContenuConteneur(item, options, optionsArbre);
}
return strContenu + "</ul>";
return ligneObjet;
}
static buildInventaireConteneur(actorId, itemId, options) {
const actor = game.actors.get(actorId)
const item = actor?.items.get(itemId)
if (item) {
return RdDUtility.buildContenuConteneur(item, options, { ouvert: true, profondeur: 1 });
}
return '';
}
/* -------------------------------------------- */
static buildContenuConteneur(conteneur, options = {}, optionsArbre = {}) {
RdDUtility.prepareOptionsArbreInventaire(conteneur, optionsArbre);
const display = optionsArbre.ouvert ? 'item-display-show' : 'item-display-hide';
const profondeur = optionsArbre.profondeur;
optionsArbre.profondeur++;
const lignesContenu = conteneur.subItems.sort(Misc.ascending(it => it.name))
.map(contenu => this.buildLigneInventaire(contenu, options, optionsArbre));
return `<ul class='item-list alterne-list ${display} list-item-margin${Math.min(profondeur, 6)}'>`
+ lignesContenu.reduce(Misc.joining(''), '')
+ "</ul>";
}
/* -------------------------------------------- */
@ -443,10 +457,6 @@ export class RdDUtility {
return ajustementsEncaissement;
}
static getDefinitionsBlessures() {
return definitionsBlessures;
}
/* -------------------------------------------- */
static getSegmentsFatigue(maxEnd) {
maxEnd = Math.max(maxEnd, 1);
@ -469,7 +479,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static calculFatigueHtml(fatigue, endurance) {
return ReglesOptionelles.isUsing("appliquer-fatigue") ? {
return ReglesOptionnelles.isUsing("appliquer-fatigue") ? {
malus: RdDUtility.calculMalusFatigue(fatigue, endurance),
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(fatigue, endurance).html() + "</table>"
} : { malus: 0, html: '' };
@ -545,14 +555,14 @@ export class RdDUtility {
let formula = "2d10";
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre')) {
if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "min" + valeurMin;
}
}
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-ajout-malus-libre')) {
if (ReglesOptionnelles.isUsing('degat-ajout-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "+" + valeurMin;
@ -562,7 +572,7 @@ export class RdDUtility {
let roll = await RdDDice.roll(formula, options);
// 1 dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre-simple')) {
if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre-simple')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
if (roll.terms[0].results[0].result < valeurMin) {
@ -587,17 +597,10 @@ export class RdDUtility {
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;';
encaissement.roll = roll;
encaissement.armure = armure;
encaissement.penetration = rollData.arme?.system.penetration ?? 0;
encaissement.total = jetTotal;
encaissement.vie = await RdDUtility._evaluatePerte(encaissement.vie, over20);
encaissement.endurance = await RdDUtility._evaluatePerte(encaissement.endurance, over20);
encaissement.penetration = rollData.arme?.system.penetration ?? 0;
encaissement.blessures = (
encaissement.critiques > 0 ? "Critique" :
encaissement.graves > 0 ? "Grave" :
encaissement.legeres > 0 ? "Légère" :
encaissement.eraflures > 0 ? "Contusions/Eraflures" :
'Aucune'
);
return encaissement;
}
@ -622,7 +625,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static currentFatigueMalus(value, max) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
max = Math.max(1, Math.min(max, 60));
value = Math.min(max * 2, Math.max(0, value));
@ -666,6 +669,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static async chatListeners(html) {
RdDCombat.registerChatCallbacks(html);
RdDEmpoignade.registerChatCallbacks(html);
// Gestion spécifique message passeurs
html.on("click", '.tmr-passeur-coord a', event => {
@ -824,17 +828,13 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static confirmerSuppressionSubacteur(sheet, subActor, htmlToDelete) {
static confirmerSuppressionSubacteur(sheet, subActor, htmlToDelete, onSuppression = ()=>{}) {
RdDConfirm.confirmer({
settingConfirmer: "confirmation-supprimer-lien-acteur",
content: `<p>Etes vous certain de vouloir supprimer le lien vers ${subActor.name} ?</p>`,
title: 'Confirmer la suppression',
buttonLabel: 'Supprimer le lien',
onAction: () => {
console.log('Delete : ', subActor.id);
sheet.actor.removeSubacteur(subActor.id);
RdDUtility.slideOnDelete(sheet, htmlToDelete);
}
onAction: onSuppression
})
}

View File

@ -5,8 +5,9 @@ import { RdDItemSort } from "./item-sort.js";
import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDPossession } from "./rdd-possession.js";
import { RdDUtility } from "./rdd-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
/**
* tous les ajustements pouvant s'appliquer.
@ -30,7 +31,7 @@ export const referenceAjustements = {
},
diffLibre: {
isUsed: (rollData, actor) => rollData.diffLibre != undefined,
getLabel: (rollData, actor) => rollData.selectedSort?.name ?? rollData.attackerRoll ? 'Imposée' : 'Libre',
getLabel: (rollData, actor) => rollData.selectedSort?.name ?? rollData.attackerRoll ?? RdDPossession.isDefensePossession(rollData) ? 'Imposée' : 'Libre',
getValue: (rollData, actor) => rollData.selectedSort
? RdDItemSort.getDifficulte(rollData.selectedSort, rollData.diffLibre)
: rollData.diffLibre ?? rollData.competence?.system.default_diffLibre ?? 0
@ -51,19 +52,19 @@ export const referenceAjustements = {
getValue: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).attaque,
},
etat: {
isUsed: (rollData, actor) => !RdDCarac.isIgnoreEtatGeneral(rollData),
isUsed: (rollData, actor) => !RollDataAjustements.isIgnoreEtatGeneral(rollData),
getLabel: (rollData, actor) => 'Etat général',
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()
},
@ -85,7 +86,7 @@ export const referenceAjustements = {
getValue: (rollData, actor) => actor.getMoralTotal()
},
astrologique: {
isUsed: (rollData, actor) => ReglesOptionelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData),
isUsed: (rollData, actor) => ReglesOptionnelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData),
getLabel: (rollData, actor) => 'Astrologique',
getValue: (rollData, actor) => actor.ajustementAstrologique()
},
@ -139,6 +140,12 @@ export const referenceAjustements = {
isUsed: (rollData, actor) => rollData.ethylisme != undefined,
getLabel: (rollData, actor) => "Ethylisme - " + RdDUtility.getNomEthylisme(rollData.ethylisme),
getValue: (rollData, actor) => rollData.ethylisme,
},
tailleempoignade: {
isVisible: (rollData, actor) => rollData.isEmpoignade,
isUsed: (rollData, actor) => rollData.isEmpoignade,
getLabel: (rollData, actor) => "Malus de taille",
getValue: (rollData, actor) => rollData.malusTaille,
}
}
@ -171,4 +178,11 @@ export class RollDataAjustements {
return sum;
}
static isIgnoreEtatGeneral(rollData) {
const selectedCarac = rollData.selectedCarac;
return !selectedCarac ||
rollData.ethylisme ||
RdDCarac.isChance(selectedCarac) ||
(RdDCarac.isReve(selectedCarac) && !rollData.competence);
}
}

View File

@ -1,118 +0,0 @@
import { SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
const listeReglesOptionelles = [
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
{ group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },
{ group: 'Règles de combat', name: 'defenseurDesarme', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" },
{ group: 'Règles de combat', name: 'categorieParade', descr: "Le défenseur doit obtenir une significative en cas de parade avec des armes de catégories différentes" },
{ group: 'Règles de combat', name: 'tripleSignificative', descr: "En cas de demi-surprise, d'attaque particulière en finesse, et de catégories d'armes différentes, le défenseur doit obtenir 1/8 des chances de succès" },
{ group: 'Règles de combat', name: 'degat-minimum-malus-libre-simple', descr: "Le malus libre d'attaque remplace une des valeurs de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, si le plus petit dé est inférieur à 4, alors il devient 4.", default: false },
{ group: 'Règles de combat', name: 'degat-minimum-malus-libre', descr: "Le malus libre d'attaque remplace une valeur de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, tout résultat inférieur à 4 devient 4.", default: false },
{ group: 'Règles de combat', name: 'degat-ajout-malus-libre', descr: "Le malus libre d'attaque s'ajoute au jet d'encaissement et aux autres bonus. Exemple : la difficulté libre de l'attaquant est de -4. Le jet d'encaissement est effectué à 2d10+4, plus les bonus de situation et d'armes.", default: false },
{ group: 'Règles de combat', name: 'validation-encaissement-gr', descr: "Le Gardien des Rêves doit valider les jets d'encaissement et peut les changer.", default: false },
{ group: 'Règles générales', name: 'astrologie', descr: "Appliquer les ajustements astrologiques aux jets de chance et aux rituels"},
{ group: 'Règles générales', name: 'afficher-prix-joueurs', descr: "Afficher le prix de l'équipement des joueurs", uniquementJoueur: true},
{ group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"},
{ group: 'Règles générales', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false },
{ group: 'Confirmations', name: 'confirmer-combat-sans-cible', descr: "Confirmer avant une attaque sans cible", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-lien-acteur', descr: "Confirmer pour détacher un animal/suivant/véhicule", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-equipement', descr: "Confirmer la suppression des équipements", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-oeuvre', descr: "Confirmer la suppression des oeuvres", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-connaissance', descr: "Confirmer la suppression des connaissances", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-draconique', descr: "Confirmer la suppression des queues, souffles, têtes", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-effet', descr: "Confirmer la suppression des effets", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-competence', descr: "Confirmer la suppression des compétences", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-autres', descr: "Confirmer la suppression des autres types d'Objets", scope: "client"},
];
const uniquementJoueur = listeReglesOptionelles.filter(it => it.uniquementJoueur).map(it=>it.name);
export class ReglesOptionelles extends FormApplication {
static init() {
for (const regle of listeReglesOptionelles) {
const name = regle.name;
const id = ReglesOptionelles._getIdRegle(name);
game.settings.register(SYSTEM_RDD, id, { name: id, scope: regle.scope ?? "world", config: false, default: regle.default == undefined ? true : regle.default, type: Boolean });
}
game.settings.registerMenu(SYSTEM_RDD, "rdd-options-regles", {
name: "Choisir les règles optionelles",
label: "Règles optionelles",
hint: "Ouvre la fenêtre de sélection des règles optionelles",
icon: "fas fa-bars",
type: ReglesOptionelles
});
}
constructor(...args) {
super(...args);
}
static _getIdRegle(name) {
return `rdd-option-${name}`;
}
static get defaultOptions() {
const options = super.defaultOptions;
mergeObject(options, {
id: "regles-optionelles",
template: "systems/foundryvtt-reve-de-dragon/templates/settings/regles-optionelles.html",
height: 600,
width: 450,
minimizable: false,
closeOnSubmit: true,
title: "Règles optionnelles"
});
return options;
}
getData() {
let formData = super.getData();
const regles = listeReglesOptionelles.filter(it => game.user.isGM || it.scope == "client").map(it => {
it = duplicate(it);
it.id = ReglesOptionelles._getIdRegle(it.name);
it.active = ReglesOptionelles.isSet(it.name);
return it;
});
formData.regles = regles;
formData.groups = Misc.classify(regles, it => it.group);
return formData;
}
static isUsing(name) {
if (game.user.isGM && uniquementJoueur.includes(name)) {
return true;
}
return ReglesOptionelles.isSet(name);
}
static isSet(name) {
return game.settings.get(SYSTEM_RDD, ReglesOptionelles._getIdRegle(name));
}
static set(name, value) {
return game.settings.set(SYSTEM_RDD, ReglesOptionelles._getIdRegle(name), value ? true: false);
}
activateListeners(html) {
html.find(".select-option").click((event) => {
if (event.currentTarget.attributes.name) {
let id = event.currentTarget.attributes.name.value;
let isChecked = event.currentTarget.checked;
game.settings.set(SYSTEM_RDD, id, isChecked);
}
});
}
async _updateObject(event, formData) {
this.close();
}
}

View File

@ -0,0 +1,122 @@
import { SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
const listeReglesOptionnelles = [
{ group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"},
{ group: 'Règles générales', name: 'astrologie', descr: "Appliquer les ajustements astrologiques aux jets de chance et aux rituels"},
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
{ group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },
{ group: 'Règles de combat', name: 'defenseurDesarme', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" },
{ group: 'Règles de combat', name: 'categorieParade', descr: "Le défenseur doit obtenir une significative en cas de parade avec des armes de catégories différentes" },
{ group: 'Règles de combat', name: 'tripleSignificative', descr: "En cas de demi-surprise, d'attaque particulière en finesse, et de catégories d'armes différentes, le défenseur doit obtenir 1/8 des chances de succès" },
{ group: 'Règles de combat', name: 'validation-encaissement-gr', descr: "Le Gardien des Rêves doit valider les jets d'encaissement et peut les changer.", default: false },
{ group: 'Automatisation', name: 'chateau-dormant-gardien', descr: "Saisie des heures de sommeil/jets de moral par le gardien des rêves", default: true },
{ group: 'Affichage', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false },
{ group: 'Affichage', name: 'afficher-prix-joueurs', descr: "Afficher le prix de l'équipement des joueurs", uniquementJoueur: true},
{ group: 'Confirmations', name: 'confirmer-combat-sans-cible', descr: "Confirmer avant une attaque sans cible", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-lien-acteur', descr: "Confirmer pour détacher un animal/suivant/véhicule", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-equipement', descr: "Confirmer la suppression des équipements", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-oeuvre', descr: "Confirmer la suppression des oeuvres", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-connaissance', descr: "Confirmer la suppression des connaissances", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-draconique', descr: "Confirmer la suppression des queues, souffles, têtes", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-effet', descr: "Confirmer la suppression des effets", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-competence', descr: "Confirmer la suppression des compétences", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-autres', descr: "Confirmer la suppression des autres types d'Objets", scope: "client"},
{ group: 'Options alternatives', name: 'degat-minimum-malus-libre-simple', descr: "Le malus libre d'attaque remplace une des valeurs de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, si le plus petit dé est inférieur à 4, alors il devient 4.", default: false },
{ group: 'Options alternatives', name: 'degat-minimum-malus-libre', descr: "Le malus libre d'attaque remplace une valeur de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, tout résultat inférieur à 4 devient 4.", default: false },
{ group: 'Options alternatives', name: 'degat-ajout-malus-libre', descr: "Le malus libre d'attaque s'ajoute au jet d'encaissement et aux autres bonus. Exemple : la difficulté libre de l'attaquant est de -4. Le jet d'encaissement est effectué à 2d10+4, plus les bonus de situation et d'armes.", default: false },
];
const uniquementJoueur = listeReglesOptionnelles.filter(it => it.uniquementJoueur).map(it=>it.name);
export class ReglesOptionnelles extends FormApplication {
static init() {
for (const regle of listeReglesOptionnelles) {
const name = regle.name;
const id = ReglesOptionnelles._getIdRegle(name);
game.settings.register(SYSTEM_RDD, id, { name: id, scope: regle.scope ?? "world", config: false, default: regle.default == undefined ? true : regle.default, type: Boolean });
}
game.settings.registerMenu(SYSTEM_RDD, "rdd-options-regles", {
name: "Choisir les règles optionnelles",
label: "Règles optionnelles",
hint: "Ouvre la fenêtre de sélection des règles optionnelles",
icon: "fas fa-bars",
type: ReglesOptionnelles
});
}
constructor(...args) {
super(...args);
}
static _getIdRegle(name) {
return `rdd-option-${name}`;
}
static get defaultOptions() {
const options = super.defaultOptions;
mergeObject(options, {
id: "regles-optionnelles",
template: "systems/foundryvtt-reve-de-dragon/templates/settings/regles-optionnelles.html",
height: 600,
width: 450,
minimizable: false,
closeOnSubmit: true,
title: "Règles optionnelles"
});
return options;
}
getData() {
let formData = super.getData();
const regles = listeReglesOptionnelles.filter(it => game.user.isGM || it.scope == "client").map(it => {
it = duplicate(it);
it.id = ReglesOptionnelles._getIdRegle(it.name);
it.active = ReglesOptionnelles.isSet(it.name);
return it;
});
formData.regles = regles;
formData.groups = Misc.classify(regles, it => it.group);
return formData;
}
static isUsing(name) {
if (game.user.isGM && uniquementJoueur.includes(name)) {
return true;
}
return ReglesOptionnelles.isSet(name);
}
static isSet(name) {
return game.settings.get(SYSTEM_RDD, ReglesOptionnelles._getIdRegle(name));
}
static set(name, value) {
return game.settings.set(SYSTEM_RDD, ReglesOptionnelles._getIdRegle(name), value ? true: false);
}
activateListeners(html) {
html.find(".select-option").click((event) => {
if (event.currentTarget.attributes.name) {
let id = event.currentTarget.attributes.name.value;
let isChecked = event.currentTarget.checked;
game.settings.set(SYSTEM_RDD, id, isChecked);
}
});
}
async _updateObject(event, formData) {
this.close();
}
}

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 [];
@ -177,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;
@ -187,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);
}
@ -225,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);
@ -260,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,206 @@
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
import { Misc } from "../misc.js";
import { RdDTimestamp } from "../time/rdd-timestamp.js";
export const APP_ASTROLOGIE_REFRESH = `${SYSTEM_RDD}-refresh-astrologie`
export class AppAstrologie extends Application {
static async create(actor = undefined, options = {}) {
if (actor == undefined && !game.user.isGM) {
actor = game.user.character
}
if (!actor && !game.user.isGM) {
console.error("AppAstrologie uniquement accessible pour le MJ, ou depuis un personnage pour un joueur")
}
new AppAstrologie(actor, options).render(true);
}
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/sommeil/app-astrologie.hbs",
title: "Astrologie",
width: 'fit-content',
height: 'fit-content',
classes: ['calendar-astrologie'],
popOut: true,
resizable: false
});
}
constructor(actor, options = {}) {
super(options);
this.actor = actor;
this.hookReference = Hooks.on(APP_ASTROLOGIE_REFRESH, () => this.refreshAstrologie());
}
getData(options) {
this.appData = super.getData(options)
const calendrier = game.system.rdd.calendrier;
mergeObject(this.appData, {
isGM: game.user.isGM,
isActor: this.actor != undefined,
calendrier: calendrier.getTimestamp().toCalendrier(),
dates: calendrier.getJoursSuivants(10),
heures: RdDTimestamp.definitions(),
actorAstrologie: this.getActorAstrologie(),
gmAstrologie: this.getGMAstrologie(calendrier),
theme: {
signeAstral: RdDTimestamp.definition(0),
signeNaissance: RdDTimestamp.definition(0)
}
})
return this.appData;
}
getActorAstrologie() {
if (this.actor) {
return {
actor: this.actor,
nombres: this._organizeNombresAstraux(this.actor.itemTypes['nombreastral']),
ajustements: CONFIG.RDD.difficultesLibres,
etat: this.actor.getEtatGeneral(),
astrologie: this.actor.getCompetence('Astrologie')
}
}
return {}
}
_organizeNombresAstraux(nombresAstraux) {
let organized = {};
nombresAstraux.forEach(na => {
if (!organized[na.system.jourindex]) {
organized[na.system.jourindex] = {
listValues: [],
jourlabel: na.system.jourlabel
}
}
organized[na.system.jourindex].listValues.push(na.system.value);
})
return organized;
}
getGMAstrologie(calendrier) {
if (game.user.isGM) {
const nbAstral = calendrier.getNombreAstral()
const heures = RdDTimestamp.heures();
return {
ajustementsActors: game.actors.filter(it => it.isPersonnage() && it.hasPlayerOwner)
.map(actor => this.getAjustementActor(actor, nbAstral, heures)),
nombresAstraux: calendrier.getNombresAstraux().map(na => this.getDetailNombreAstral(na))
}
}
return {}
}
getAjustementActor(actor, nbAstral, heures) {
const hn = RdDTimestamp.findHeure(actor.getHeureNaissance())?.heure ?? 0;
return {
actor,
ajustements: heures.map(heure => {
return { heure, ajustement: RdDTimestamp.ajustementAstrologiqueHeure(hn, nbAstral, heure) };
})
}
}
getDetailNombreAstral(nombreAstral) {
const detail = duplicate(nombreAstral);
const timestamp = new RdDTimestamp({ indexDate: nombreAstral.index });
detail.date = { mois: timestamp.mois, jour: timestamp.jour + 1 };
detail.valeursFausses.forEach(fausse => fausse.actorName = game.actors.get(fausse.actorId).name ?? "Inconnu");
return detail;
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find('select[name="signe-astral"]').change(event => {
this.selectNombreAstral(this.html.find('select[name="signe-astral"]').val());
})
this.html.find('select[name="signe-naissance"]').change(event => {
this.selectHeureNaissance(this.html.find('select[name="signe-naissance"]').val());
})
this.html.find('td.nombre-astral').click(event => {
this.selectNombreAstral(Number.parseInt(event.currentTarget.attributes['data-nombre-astral'].value) - 1);
})
this.html.find('tr.heure-naissance').click(event => {
this.selectHeureNaissance(event.currentTarget.attributes['data-heure-naissance'].value);
})
this.html.find('[name="jet-astrologie"]').click(event => this.requestJetAstrologie());
this.html.find('[name="rebuild-nombres-astraux"]').click(event => this.rebuildNombresAstraux());
this.onCalculThemeAstral();
}
selectHeureNaissance(heureNaissance) {
this.appData.theme.signeNaissance = RdDTimestamp.definition(heureNaissance);
this.onCalculThemeAstral();
}
selectNombreAstral(nombreAstral) {
this.appData.theme.signeAstral = RdDTimestamp.definition(nombreAstral);
this.onCalculThemeAstral();
}
/* -------------------------------------------- */
async rebuildNombresAstraux() {
game.system.rdd.calendrier.resetNombresAstraux();
await game.system.rdd.calendrier.rebuildNombresAstraux();
}
onCalculThemeAstral() {
const chiffreAstral = this.appData.theme.signeAstral.heure + 1;
const heureNaissance = this.appData.theme.signeNaissance.heure + 1;
RdDTimestamp.definitions().forEach(dh => {
const ajustement = RdDTimestamp.ajustementAstrologiqueHeure(heureNaissance, chiffreAstral, dh.heure + 1);
const txtAjustement = ajustement == 0 ? '' : Misc.toSignedString(ajustement);
this.html.find(`div.horloge-ajustement.heure-${dh.hh}`).text(txtAjustement)
});
this.html.find(`select[name="signe-astral"]`).val(this.appData.theme.signeAstral.key)
this.html.find(`select[name="signe-naissance"]`).val(this.appData.theme.signeNaissance.key)
const angleAstrologie = ((chiffreAstral + heureNaissance) * 30) % 360 - 45;
this.html.find(`div.horloge-roue div.disque-astro img`).css(Misc.cssRotation(angleAstrologie));
const timestamp = game.system.rdd.calendrier.getTimestamp();
this.html.find(`div.horloge-roue div.horloge-aiguille-heure img`).css(Misc.cssRotation(timestamp.angleHeure));
this.html.find(`div.horloge-roue div.horloge-aiguille-minute img`).css(Misc.cssRotation(timestamp.angleMinute));
}
requestJetAstrologie() {
if (!this.appData?.isActor) {
return
}
let socketData = {
id: this.appData.actorAstrologie.actor.id,
carac_vue: this.actor.system.carac['vue'].value,
etat: this.actor.getEtatGeneral(),
astrologie: this.actor.getCompetence('Astrologie'),
conditions: this.html.find('[name="diffConditions"]').val(),
date: this.html.find('[name="joursAstrologie"]').val(),
userId: game.user.id
}
if (Misc.isUniqueConnectedGM()) {
game.system.rdd.calendrier.requestNombreAstral(socketData);
} else {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_request_nombre_astral",
data: socketData
});
}
}
refreshAstrologie() {
this.render(true)
}
async close(options) {
Hooks.off(APP_ASTROLOGIE_REFRESH, this.hookReference);
this.hookReference = undefined
await super.close(options)
}
}

View File

@ -0,0 +1,105 @@
export class DialogChateauDormant extends Dialog {
static async create() {
const date = game.system.rdd.calendrier.dateCourante();
const actors = game.actors.filter(actor => actor.hasPlayerOwner && actor.isPersonnage());
const dialogData = {
actors: actors,
date: date,
motifStress: `Nuit du ${date}`,
finChateauDormant: game.system.rdd.calendrier.getTimestampFinChateauDormant()
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-chateau-dormant.hbs",
dialogData);
new DialogChateauDormant(dialogData, html)
.render(true);
}
constructor(dialogData, html) {
const options = {
classes: ["rdd-dialog-chateau-dormant"],
width: 600,
height: 'fit-content',
'z-index': 99999
};
const conf = {
title: "De Chateau dormant à Vaisseau",
content: html,
buttons: {
chateauDormant: { label: "Passer à Vaisseau!", callback: it => { this.onChateauDormant(); } }
}
};
super(conf, options);
this.dialogData = dialogData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find('input.sommeil-insomnie').change(event => this.onInsomnie(event));
this._activateListenerOnActorMoral(this.html);
}
_activateListenerOnActorMoral(html) {
html.find(`span.sommeil-actor-moral a`).click(event => this.onActorMoral(event));
}
onInsomnie(event) {
const sommeilInsomnie = this.html.find(event.currentTarget);
const isInsomnie = sommeilInsomnie.is(':checked');
const sommeilHeures = sommeilInsomnie.parents('.set-sommeil-actor').find('input.sommeil-heures');
sommeilHeures.prop('disabled', isInsomnie);
if (isInsomnie) {
sommeilHeures.val('0');
}
}
async onActorMoral(event) {
const selected = this.html.find(event.currentTarget);
const actorRow = selected.parents('.set-sommeil-actor');
const actorId = actorRow.data('actor-id');
const actor = this.getActor(actorId);
actor.system.sommeil.moral = selected.data('moral');
const htmlMoral = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/sommeil/sommeil-actor-moral.hbs', actor.system.sommeil)
actorRow.find('.sommeil-actor-moral').html(htmlMoral);
// re-attach listeners for actor row
this._activateListenerOnActorMoral(actorRow);
}
getActor(actorId) {
return this.dialogData.actors.find(it => it.id == actorId);
}
async onChateauDormant() {
const motifStress = this.html.find("form input[name='motifStress']").val();
jQuery.map(
this.html.find('li.set-sommeil-actor'),
it => this.extractConsigneActor(this.html.find(it), motifStress)
).forEach(async consigne => await consigne.actor.prepareChateauDormant(consigne))
}
extractConsigneActor(actorRow, motifStress) {
const actorId = actorRow.data('actor-id');
const actor = this.getActor(actorId);
const insomnie = actorRow.find('input.sommeil-insomnie').is(':checked');
return {
actor,
ignorer: actorRow.find('input.sommeil-ignorer').is(':checked'),
stress: {
motif: motifStress,
valeur: Number.parseInt(actorRow.find('input.sommeil-stress').val()),
},
sommeil: {
nouveaujour: true,
date: this.dialogData.finChateauDormant,
insomnie: insomnie,
heures: insomnie ? 0 : Number.parseInt(actorRow.find('input.sommeil-heures').val()),
moral: actor.system.sommeil.moral ?? 'neutre',
}
};
}
}

View File

@ -0,0 +1,87 @@
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { EffetsDraconiques } from "../tmr/effets-draconiques.js";
export class DialogRepos extends Dialog {
static async create(actor) {
if (!actor.isPersonnage()) {
return
}
if (!ReglesOptionnelles.isUsing("chateau-dormant-gardien") || !actor.hasPlayerOwner) {
actor.system.sommeil = {
"nouveaujour": true,
"insomnie": EffetsDraconiques.isSujetInsomnie(actor),
"moral": "neutre",
"heures": 4
}
}
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-repos.html", actor);
const dialog = new DialogRepos(html, actor);
dialog.render(true);
}
constructor(html, actor) {
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 400, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: "Se reposer",
content: html,
default: "repos",
buttons: {
"repos": { label: "Se reposer", callback: async it => { this.repos(); } }
}
};
super(conf, options);
this.actor = actor;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find(`.sommeil-actor-moral a`).click(event => this.onActorMoral(event));
}
/* -------------------------------------------- */
async repos() {
const selection = await this.html.find("[name='repos']:checked").val();
switch (selection) {
case "sieste": return await this.sieste();
case "nuit": return await this.nuit();
case "chateau-dormant": return await this.chateauDormant();
case "gris-reve": return await this.grisReve();
}
}
async grisReve() {
await this.html.find("[name='nb-jours']").change();
const nbJours = Number.parseInt(await this.html.find("[name='nb-jours']").val());
await this.actor.grisReve(nbJours);
}
async chateauDormant() {
await this.actor.dormirChateauDormant();
}
async nuit() {
await this.html.find("[name='sommeil.heures']").change();
const val = await this.html.find("[name='sommeil.heures']").val();
const sommeilHeures = Number.parseInt(val ?? '0');
await this.actor.dormir(sommeilHeures, { chateauDormant: true });
}
async sieste() {
await this.html.find("[name='sieste.heures']").change();
const siesteHeures = Number.parseInt(await this.html.find("[name='sieste.heures']").val());
await this.actor.dormir(siesteHeures);
}
async onActorMoral(event) {
const selected = this.html.find(event.currentTarget);
const parentDiv = selected.parents().find('.sommeil-actor-moral');
const situationMoral = selected.data('moral');
await this.actor.setInfoSommeilMoral(situationMoral);
const htmlMoral = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/sommeil/sommeil-actor-moral.hbs', {
moral: situationMoral
});
parentDiv.html(htmlMoral);
this.html.find(`.sommeil-actor-moral a`).click(event => this.onActorMoral(event));
}
}

View File

@ -15,7 +15,7 @@ export class DialogStress extends Dialog {
)
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-stress.html", dialogData);
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-stress.html", dialogData);
new DialogStress(dialogData, html)
.render(true);
}
@ -50,7 +50,7 @@ export class DialogStress extends Dialog {
this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it.id))
.forEach(actor => actor.distribuerStress(compteur, stress, motif));
.forEach(async actor => await actor.distribuerStress(compteur, stress, motif));
}
async onSelectActor(event) {

View File

@ -39,18 +39,18 @@ export class Targets {
}
}
static getTarget() {
static getTarget(options = { warn: true }) {
const targets = Targets.listTargets();
switch (targets.length) {
case 1:
return targets[0];
case 0:
ui.notifications.warn("Vous devez choisir une cible à attaquer!");
if (options.warn) ui.notifications.warn("Vous devez choisir une cible à attaquer!");
break;
default:
ui.notifications.warn("Vous devez choisir une cible (et <strong>une seule</strong>) à attaquer!");
return;
if (options.warn) ui.notifications.warn("Vous devez choisir une cible (et <strong>une seule</strong>) à attaquer!");
}
return undefined;
}
}

View File

@ -4,7 +4,7 @@ import { RdDTimestamp } from "./rdd-timestamp.js";
* Extend the base Dialog entity by defining a custom window to perform roll.
* @extends {Dialog}
*/
export class RdDCalendrierEditeur extends Dialog {
export class RdDCalendrierEditor extends Dialog {
/* -------------------------------------------- */
constructor(html, calendrier, calendrierData) {
@ -30,8 +30,8 @@ export class RdDCalendrierEditeur extends Dialog {
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);
RdDCalendrierEditor.setLimited(this.html.find("input[name='calendar.jourDuMois']"), this.calendrierData.jourDuMois, 1, 28);
RdDCalendrierEditor.setLimited(this.html.find("input[name='calendar.minute']"), this.calendrierData.minute, 0, 119);
}
static setLimited(input, init, min, max) {

View File

@ -0,0 +1,455 @@
import { MAX_NOMBRE_ASTRAL, RdDTimestamp, WORLD_TIMESTAMP_SETTING } from "./rdd-timestamp.js";
import { RdDCalendrierEditor } from "./rdd-calendrier-editor.js";
import { RdDResolutionTable } from "../rdd-resolution-table.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDDice } from "../rdd-dice.js";
import { Misc } from "../misc.js";
import { DialogChronologie } from "../dialog-chronologie.js";
import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js";
import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js";
const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/calendar.hbs";
const INITIAL_CALENDAR_POS = { top: 200, left: 200, horlogeAnalogique: true };
/* -------------------------------------------- */
export class RdDCalendrier extends Application {
static init() {
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: INITIAL_CALENDAR_POS,
type: Object
});
}
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
title: "Calendrier",
template: TEMPLATE_CALENDRIER,
classes: ["calendar"],
popOut: true,
resizable: false,
width: 'fit-content',
height: 'fit-content',
});
}
constructor() {
super();
this.timestamp = RdDTimestamp.getWorldTime();
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp);
this.nombresAstraux = this.getNombresAstraux();
this.rebuildNombresAstraux(HIDE_DICE); // Ensure always up-to-date
}
Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
}
get title() {
const calendrier = this.timestamp.toCalendrier();
return `${calendrier.heure.label}, ${calendrier.jourDuMois} ${calendrier.mois.label} ${calendrier.annee} (${calendrier.mois.saison})`;
}
savePosition() {
game.settings.set(SYSTEM_RDD, "calendrier-pos", {
top: this.position.top,
left: this.position.left,
horlogeAnalogique: this.horlogeAnalogique
});
}
getSavePosition() {
const pos = game.settings.get(SYSTEM_RDD, "calendrier-pos");
if (pos?.top == undefined) {
return INITIAL_CALENDAR_POS;
}
this.horlogeAnalogique = pos.horlogeAnalogique;
return pos
}
setPosition(position) {
super.setPosition(position)
this.savePosition()
}
display() {
const pos = this.getSavePosition()
this.render(true, { left: pos.left, top: pos.top });
return this;
}
_getHeaderButtons() {
const buttons = [];
if (game.user.isGM) {
buttons.unshift({
class: "calendar-astrologie",
icon: "fa-solid fa-moon-over-sun",
onclick: ev => this.showAstrologieEditor()
},
{
class: "calendar-set-datetime",
icon: "fa-solid fa-calendar-pen",
onclick: ev => this.showCalendarEditor()
});
}
return buttons
}
/*async maximize() {
await super.maximize()
this.render(true)
}*/
async close() { }
async onUpdateSetting(setting, update, options, id) {
if (setting.key == SYSTEM_RDD + '.' + WORLD_TIMESTAMP_SETTING) {
this.timestamp = RdDTimestamp.getWorldTime();
this.positionAiguilles()
this.render(false);
Hooks.callAll(APP_ASTROLOGIE_REFRESH);
}
}
getData() {
const formData = super.getData();
this.fillCalendrierData(formData);
return formData;
}
/* -------------------------------------------- */
fillCalendrierData(formData = {}) {
mergeObject(formData, this.timestamp.toCalendrier());
formData.isGM = game.user.isGM;
formData.heures = RdDTimestamp.definitions()
formData.horlogeAnalogique = this.horlogeAnalogique;
return formData;
}
/* -------------------------------------------- */
/** @override */
async activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
this.html.find('.toggle-horloge-analogique').click(ev => this.onToggleHorlogeAnalogique())
this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
this.html.find('.horloge-roue .horloge-heure').click(event => {
const h = this.html.find(event.currentTarget)?.data('heure');
this.positionnerHeure(Number(h));
})
this.html.find('.calendar-set-datetime').click(ev => {
ev.preventDefault();
this.showCalendarEditor();
});
this.html.find('.calendar-astrologie').click(ev => {
ev.preventDefault();
this.showAstrologieEditor();
});
this.positionAiguilles()
}
positionAiguilles() {
const timestamp = this.getTimestamp();
this.html.find(`div.horloge-roue div.horloge-aiguille-heure img`).css(Misc.cssRotation(timestamp.angleHeure));
this.html.find(`div.horloge-roue div.horloge-aiguille-minute img`).css(Misc.cssRotation(timestamp.angleMinute));
}
onToggleHorlogeAnalogique() {
this.horlogeAnalogique = !this.horlogeAnalogique;
this.savePosition()
this.display()
}
/* -------------------------------------------- */
getNombresAstraux() {
return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? [];
}
/* -------------------------------------------- */
dateCourante() {
return this.timestamp.formatDate();
}
dateReel() {
return new Date().toLocaleString("sv-SE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit"
});
}
isAfterIndexDate(indexDate) {
// TODO: standardize
return indexDate < this.timestamp.indexDate;
}
/* -------------------------------------------- */
heureCourante() { return RdDTimestamp.definition(this.timestamp.heure); }
/* -------------------------------------------- */
getCurrentMinute() { return this.timestamp.indexMinute; }
getTimestamp() {
return this.timestamp;
}
getTimestampFinChateauDormant(nbJours = 0) {
return this.timestamp.nouveauJour().addJours(nbJours);
}
getTimestampFinHeure(nbHeures = 0) {
return this.timestamp.nouvelleHeure().addHeures(nbHeures);
}
/* -------------------------------------------- */
getIndexFromDate(jour, mois) {
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(count) {
return Misc.intArray(this.timestamp.indexDate, this.timestamp.indexDate + count)
.map(i => { return { label: RdDTimestamp.formatIndexDate(i), index: i } })
}
/* -------------------------------------------- */
async ajouterNombreAstral(indexDate, showDice = SHOW_DICE) {
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, rollMode: "selfroll" });
const dateFuture = RdDTimestamp.formatIndexDate(indexDate);
if (showDice != HIDE_DICE) {
ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients("GM"),
content: `Le chiffre astrologique du ${dateFuture} sera le ${nombreAstral}`
});
}
return {
nombreAstral: nombreAstral,
valeursFausses: [],
index: indexDate
}
}
/* -------------------------------------------- */
resetNombresAstraux() {
this.nombresAstraux = [];
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", []);
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_reset_nombre_astral",
data: {}
});
}
/**
*
* @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;
}
this.nombresAstraux = this.getNombresAstraux();
let astralData = this.nombresAstraux.find((nombreAstral, i) => nombreAstral.index == indexDate);
return astralData?.nombreAstral ?? 0;
}
/* -------------------------------------------- */
async rebuildNombresAstraux(showDice = HIDE_DICE) {
if (Misc.isUniqueConnectedGM()) {
console.log("Astral rebuild")
let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
let dayIndex = this.timestamp.indexDate + i;
let na = this.nombresAstraux.find(n => n.index == dayIndex);
if (na) {
newList[i] = na;
} else {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
}
}
this.nombresAstraux = newList;
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", newList);
game.actors.filter(it => it.isPersonnage()).forEach(actor => actor.supprimerAnciensNombresAstraux());
this.notifyChangeNombresAstraux();
}
}
notifyChangeNombresAstraux() {
Hooks.callAll(APP_ASTROLOGIE_REFRESH);
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_refresh_nombre_astral",
data: {}
});
}
/* -------------------------------------------- */
async setNewTimestamp(newTimestamp) {
const oldTimestamp = this.timestamp;
await Promise.all(game.actors.map(async actor => await actor.onTimeChanging(oldTimestamp, newTimestamp)));
RdDTimestamp.setWorldTime(newTimestamp);
if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionnelles.isUsing("chateau-dormant-gardien")) {
await DialogChateauDormant.create();
}
this.timestamp = newTimestamp;
await this.rebuildNombresAstraux();
this.positionAiguilles()
this.display();
}
/* -------------------------------------------- */
async onCalendarButton(ev) {
ev.preventDefault();
const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
if (calendarAvance) {
await this.incrementTime(Number(calendarAvance.value));
}
else if (calendarSet) {
this.positionnerHeure(Number(calendarSet.value));
}
this.positionAiguilles()
}
/* -------------------------------------------- */
async incrementTime(minutes = 0) {
if (game.user.isGM) {
await this.setNewTimestamp(this.timestamp.addMinutes(minutes));
Hooks.callAll(APP_ASTROLOGIE_REFRESH);
}
}
/* -------------------------------------------- */
async incrementerJour() {
await this.setNewTimestamp(this.timestamp.nouveauJour());
}
/* -------------------------------------------- */
async positionnerHeure(heure) {
if (game.user.isGM) {
const indexDate = this.timestamp.indexDate;
const addDay = this.timestamp.heure < heure ? 0 : 1;
const newTimestamp = new RdDTimestamp({ indexDate: indexDate + addDay }).addHeures(heure);
await this.setNewTimestamp(newTimestamp)
Hooks.callAll(APP_ASTROLOGIE_REFRESH);
}
}
/* -------------------------------------------- */
getLectureAstrologieDifficulte(dateIndex) {
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);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
let rollData = {
caracValue: request.carac_vue,
finalLevel: niveau,
showDice: HIDE_DICE,
rollMode: "blindroll"
};
await RdDResolutionTable.rollData(rollData);
request.rolled = rollData.rolled;
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');
}
}
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 {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_response_nombre_astral",
data: request
});
}
}
}
addNbAstralIncorect(actorId, date, nbAstral) {
const astralData = this.nombresAstraux.find((nombreAstral, i) => nombreAstral.index == date);
astralData.valeursFausses.push({ actorId: actorId, nombreAstral: nbAstral });
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.nombresAstraux);
}
/* -------------------------------------------- */
getAjustementAstrologique(heureNaissance, name = undefined) {
const defHeure = RdDTimestamp.findHeure(heureNaissance);
if (defHeure) {
return RdDTimestamp.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);
}
else {
ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
}
return 0;
}
/* -------------------------------------------- */
async saveEditeur(calendrierData) {
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() {
const calendrierData = this.fillCalendrierData();
if (this.editeur == undefined) {
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/time/calendar-editor.hbs', calendrierData);
this.editeur = new RdDCalendrierEditor(html, this, calendrierData)
}
this.editeur.updateData(calendrierData);
this.editeur.render(true);
}
/* -------------------------------------------- */
async showAstrologieEditor() {
await AppAstrologie.create();
}
}

View File

@ -1,16 +1,17 @@
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";
import { SHOW_DICE, SYSTEM_RDD } 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 RDD_MOIS_PAR_AN = 12;
export const RDD_JOURS_PAR_MOIS = 28;
export const RDD_HEURES_PAR_JOUR = 12;
export const MAX_NOMBRE_ASTRAL = 12;
export const RDD_MINUTES_PAR_HEURES = 120;
export const RDD_MINUTES_PAR_JOUR = 1440; //RDD_HEURES_PAR_JOUR * RDD_MINUTES_PAR_HEURES;
const ROUNDS_PAR_MINUTE = 10;
const DEFINITION_HEURES = [
@ -48,10 +49,6 @@ const FORMULES_PERIODE = [
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,
@ -63,24 +60,23 @@ export class RdDTimestamp {
for (let i = 0; i < DEFINITION_HEURES.length; i++) {
DEFINITION_HEURES[i].heure = i;
DEFINITION_HEURES[i].hh = RdDTimestamp.hh(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 (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 hh(heure) {
return heure < 9 ? `0${heure + 1}` : `${heure + 1}`;
}
static iconeHeure(heure) {
return `systems/foundryvtt-reve-de-dragon/icons/heures/hd${RdDTimestamp.hh(heure)}.svg`;
}
static definitions() {
return DEFINITION_HEURES
}
static formulesDuree() {
@ -91,12 +87,48 @@ export class RdDTimestamp {
return FORMULES_PERIODE
}
static heures() {
return Misc.intArray(0, RDD_HEURES_PAR_JOUR)
}
/**
* @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 imgSigneHeure(heure) {
return RdDTimestamp.imgSigne(RdDTimestamp.definition(heure));
}
static imgSigne(signe) {
return `<img class="img-signe-heure" src="${signe.webp}" alt="${signe.label}"/>`
return signe == undefined ? '' : `<img class="img-signe-heure" src="${signe.webp}" alt="${signe.label}" title="${signe.label}"/>`
}
static ajustementAstrologiqueHeure(hn, nbAstral, heure) {
let ecart = (hn + nbAstral - heure) % RDD_HEURES_PAR_JOUR;
if (ecart < 0) {
ecart = (ecart + RDD_HEURES_PAR_JOUR) % RDD_HEURES_PAR_JOUR;
}
switch (ecart) {
case 0: return 4;
case 4: case 8: return 2;
case 6: return -4;
case 3: case 9: return -2;
}
return 0;
}
static handleTimestampEditor(html, path, consumeTimestamp = async (path, timestamp) => { }) {
@ -124,7 +156,9 @@ export class RdDTimestamp {
fields.minute.change(async (event) => await onChangeTimestamp(fields, path));
}
static defHeure(heure) {
return DEFINITION_HEURES.find(it => (it.heure) == heure % RDD_HEURES_PAR_JOUR);
}
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);
@ -196,6 +230,14 @@ export class RdDTimestamp {
this.indexMinute = indexMinute ?? 0
}
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)) }
get angleHeure() { return this.indexMinute / RDD_MINUTES_PAR_JOUR * 360 - 45 }
get angleMinute() { return this.indexMinute / RDD_MINUTES_PAR_HEURES * 360 + 45 }
/**
* Convertit le timestamp en une structure avec les informations utiles
@ -213,13 +255,6 @@ export class RdDTimestamp {
};
}
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;
@ -227,6 +262,10 @@ export class RdDTimestamp {
return `${jour} ${mois}` + (annee ? ' ' + annee : '');
}
formatDateHeure() {
return `${RdDTimestamp.definition(this.heure).label}, ${this.formatDate()}`;
}
nouveauJour() { return new RdDTimestamp({ indexDate: this.indexDate + 1, indexMinute: 0 }) }
nouvelleHeure() {

View File

@ -0,0 +1,335 @@
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';
import { Grammar } from '../grammar.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-recherche').html('');
this.html.find('.section-filters-text input.recherche').val('');
}
async recherche() {
const table = await this.buildTable();
const htmlResultats = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/tirage/liste-resultats-recherche.hbs`, { resultats: table });
this.html.find('div.liste-resultats-recherche').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;
}
buildFilterRechercheName() {
const recherche = this.html.find('.section-filters-text input.recherche').val();
if (recherche) {
return (it, mi) => Grammar.includesLowerCaseNoAccent(it.name, recherche);
}
return (it, mi) => true;
}
buildCheckedGroupFilter(milieux) {
const filtersList = this.getGroupCheckedFilters()
.map(gf => this.buildOrFilter(gf.filters.map(f => f.check)));
filtersList.push(this.buildMilieuxFilter(milieux));
filtersList.push(this.buildFilterRechercheName());
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

@ -226,9 +226,6 @@ export const TMRType = {
desolation: { name: "désolation", genre: "f" }
}
/* -------------------------------------------- */
const caseSpecificModes = ["attache", "trounoir", "debordement", "reserve_extensible", "maitrisee"];
/* -------------------------------------------- */
const tmrRandomMovePatten =
[{ name: 'top', col: 0, row: -1 },
@ -239,8 +236,6 @@ const tmrRandomMovePatten =
{ name: 'topleft', col: -1, row: -1 }
]
/* -------------------------------------------- */
/* -------------------------------------------- */
export class TMRUtility {
static init() {
@ -258,32 +253,25 @@ export class TMRUtility {
/* -------------------------------------------- */
static verifyTMRCoord(coord) {
let TMRregexp = new RegExp(/([A-M])(\d+)/g);
let res = TMRregexp.exec(coord);
if (res && res[1] && res[2]) {
if (res[2] > 0 && res[2] < 16) {
return true;
}
}
return false;
return Grammar.equalsInsensitive(coord, 'Fleuve') || TMRUtility.getTMR(coord);
}
/* -------------------------------------------- */
static getTMR(coord) {
return TMRMapping[coord];
return coord == 'Fleuve' ? TMRMapping['D1'] : TMRMapping[coord];
}
static getTMRLabel(coord) {
return TMRMapping[coord]?.label ?? (coord + ": case inconnue");
return TMRUtility.getTMR(coord)?.label ?? (coord + ": case inconnue");
}
static getTMRType(coord) {
const tmr = TMRMapping[coord];
const tmr = TMRUtility.getTMR(coord);
return Misc.upperFirst(TMRType[tmr.type].name);
}
static getTMRDescr(coord) {
const tmr = TMRMapping[coord];
const tmr = TMRUtility.getTMR(coord);
return Grammar.articleDetermine(tmr.type) + ' ' + tmr.label;
}

View File

@ -1,8 +1,10 @@
import { Draconique } from "./draconique.js";
import { PixiTMR } from "./pixi-tmr.js";
export class CarteTmr extends Draconique {
constructor() {
console.log("Sprite create 1!!!!")
super();
}

View File

@ -1,4 +1,4 @@
import { Misc } from "../misc.js";
import { TYPES } from "../item.js";
import { TMRUtility } from "../tmr-utility.js";
import { PixiTMR } from "./pixi-tmr.js";
@ -9,10 +9,10 @@ const registeredEffects = [
* Définition des informations d'une "draconique" (queue, ombre, tête, souffle) qui influence les TMR
*/
export class Draconique {
static isCaseTMR(item) { return item.type == 'casetmr'; }
static isQueueDragon(item) { return item.type == 'queue' || item.type == 'ombre'; }
static isSouffleDragon(item) { return item.type == 'souffle'; }
static isTeteDragon(item) { return item.type == 'tete'; }
static isCaseTMR(item) { return item.type == TYPES.casetmr; }
static isQueueDragon(item) { return item.isQueueDragon(); }
static isSouffleDragon(item) {return item.type == TYPES.souffle; }
static isTeteDragon(item) { return item.type == TYPES.tete; }
static isQueueSouffle(item) { return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item); }
tmrLabel(linkData) { return TMRUtility.getTMRLabel(linkData.system.coord); }
@ -91,6 +91,8 @@ export class Draconique {
coordTMR: coordTMR
};
token[type ?? this.code()] = linkData;
console.log("SPRITE: ", token.sprite)
//PixiTMR.getImgFromCode()
pixiTMR.addTooltip(token.sprite, this.tooltip(linkData));
return token;
}

View File

@ -18,6 +18,7 @@ import { Periple } from "./periple.js";
import { UrgenceDraconique } from "./urgence-draconique.js";
import { Grammar } from "../grammar.js";
import { AugmentationSeuil } from "./augmentation-seuil.js";
import { TYPES } from "../item.js";
export class EffetsDraconiques {
@ -114,46 +115,57 @@ export class EffetsDraconiques {
);
}
static filterItems(actor, filter, name) {
return actor.filterItems(filter)
.filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
static tetesDragon(actor, name) {
return actor.itemTypes[TYPES.tete].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
}
static soufflesDragon(actor, name) {
return actor.itemTypes[TYPES.souffle].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
}
static queuesDragon(actor, name) {
return actor.filterItems(it => it.isQueueDragon() && Grammar.includesLowerCaseNoAccent(it.name, name));
}
static queuesSoufflesDragon(actor, name) {
return actor.filterItems(it => [TYPES.queue, TYPES.ombre, TYPES.souffle].includes(it.type) && Grammar.includesLowerCaseNoAccent(it.name, name));
}
static countAugmentationSeuil(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'Augmentation du seuil de rêve').length;
return EffetsDraconiques.tetesDragon(actor, 'Augmentation du seuil de rêve').length;
}
static isDonDoubleReve(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'Don de double-rêve').length>0;
return EffetsDraconiques.tetesDragon(actor, 'Don de double-rêve').length > 0;
}
static isConnaissanceFleuve(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'connaissance du fleuve').length>0;
return EffetsDraconiques.tetesDragon(actor, 'connaissance du fleuve').length > 0;
}
static isReserveEnSecurite(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'réserve en sécurité').length>0;
return EffetsDraconiques.tetesDragon(actor, 'réserve en sécurité').length > 0;
}
static isDeplacementAccelere(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, ' déplacement accéléré').length>0;
return EffetsDraconiques.tetesDragon(actor, 'déplacement accéléré').length > 0;
}
static isDoubleResistanceFleuve(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isSouffleDragon, 'résistance du fleuve').length>0;
return EffetsDraconiques.soufflesDragon(actor, 'résistance du fleuve').length > 0;
}
static countInertieDraconique(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isQueueDragon, 'inertie draconique').length;
return EffetsDraconiques.queuesDragon(actor, 'inertie draconique').length;
}
static countMonteeLaborieuse(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isQueueSouffle, 'montée laborieuse').length;
return EffetsDraconiques.queuesSoufflesDragon(actor, 'montée laborieuse').length;
}
static mauvaiseRencontre(actor) {
const mauvaisesRencontres = EffetsDraconiques.filterItems(actor, Draconique.isQueueSouffle, 'mauvaise rencontre');
return mauvaisesRencontres.length>0 ? mauvaisesRencontres[0] : undefined;
const mauvaisesRencontres = EffetsDraconiques.queuesSoufflesDragon(actor, 'mauvaise rencontre');
return mauvaisesRencontres.length > 0 ? mauvaisesRencontres[0] : undefined;
}
static isPontImpraticable(actor) {
@ -164,8 +176,12 @@ export class EffetsDraconiques {
return actor.items.find(it => EffetsDraconiques.urgenceDraconique.match(it));
}
static isSujetInsomnie(actor) {
return EffetsDraconiques.queuesDragon(actor, 'Insomnie').length > 0 ? true : false;
}
static isPeage(actor) {
return EffetsDraconiques.filterItems(actor, Draconique.isSouffleDragon, 'péage').length > 0;
return EffetsDraconiques.soufflesDragon(actor, 'péage').length > 0;
}

View File

@ -1,7 +1,8 @@
import { ChatUtility } from "./chat-utility.js";
import { Poetique } from "./poetique.js";
import { RdDDice } from "./rdd-dice.js";
import { TMRUtility } from "./tmr-utility.js";
import { ExperienceLog, XP_TOPIC } from "../actor/experience-log.js";
import { ChatUtility } from "../chat-utility.js";
import { Poetique } from "../poetique.js";
import { RdDDice } from "../rdd-dice.js";
import { TMRUtility } from "../tmr-utility.js";
export class EffetsRencontre {
@ -53,9 +54,10 @@ export class EffetsRencontre {
static xp_sort_force = async (dialog, context) => {
let competence = context.competence;
if (competence) {
const xpSort = Misc.toInt(competence.system.xp_sort) + context.rencontre.system.force;
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': xpSort }]);
await this.updateExperienceLog("XP Sort", xpSort, `Rencontre d'un ${context.rencontre.name} en TMR`);
const fromXpSort = Number(competence.system.xp_sort);
const toXpSort = fromXpSort + context.rencontre.system.force;
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': toXpSort }]);
await ExperienceLog.add(this, XP_TOPIC.XPSORT, fromXpSort, toXpSort, `${competence.name} - ${context.rencontre.name} en TMR`);
}
}
@ -87,7 +89,7 @@ export class EffetsRencontre {
await EffetsRencontre.$reinsertion(dialog, context.actor, it => true);
}
}
static deplacement_aleatoire = async (dialog, context) => {
const oldCoord = context.actor.system.reve.tmrpos.coord;
const newTmr = await TMRUtility.deplaceTMRAleatoire(context.actor, oldCoord);
@ -113,13 +115,13 @@ export class EffetsRencontre {
if (context.rolled.isETotal) {
context.queues.push(await context.actor.ajouterQueue());
}
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, context)
});
}
static experience_particuliere = async (dialog, context) => {
await context.actor.appliquerAjoutExperience(context)
}

View File

@ -20,18 +20,20 @@ export class PixiTMR {
this.callbacksOnAnimate = [];
}
load( onLoad = (loader, resources) => {} ) {
let loader = this.pixiApp.loader;
async load( onLoad = (loader, resources) => {} ) {
// WIP - Deprecated since v7 : let loader = new PIXI.Loader();
for (const [name, img] of Object.entries(PixiTMR.textures)) {
loader = loader.add(name, img);
const texture = await PIXI.Assets.load(img);
let image = PIXI.Sprite.from(texture);
}
loader.onError.add((error, reason) => { console.log("ERROR", error, reason) });
loader.load( (loader, resources) => {
onLoad(loader, resources);
for (let onAnimate of this.callbacksOnAnimate) {
onAnimate();
}
});
onLoad();
for (let onAnimate of this.callbacksOnAnimate) {
onAnimate();
}
}
static getImgFromCode(code) {
return PixiTMR.textures[code]
}
static register(name, img) {
@ -44,7 +46,9 @@ export class PixiTMR {
}
carteTmr(code) {
const carteTmr = new PIXI.Sprite(PIXI.utils.TextureCache[code]);
let img = PixiTMR.getImgFromCode(code)
const carteTmr = new PIXI.Sprite(PIXI.utils.TextureCache[img]);
console.log(code, carteTmr)
// Setup the position of the TMR
carteTmr.x = 0;
carteTmr.y = 0;
@ -52,7 +56,8 @@ export class PixiTMR {
carteTmr.height = 860;
// Rotate around the center
carteTmr.anchor.set(0);
carteTmr.interactive = true;
carteTmr.eventMode = 'dynamic'; // PIXI 7 : Not sure ..
// This one is deprecated ; carteTmr.interactive = true;
carteTmr.buttonMode = true;
carteTmr.tmrObject = this;
if (!this.tmrObject.viewOnly) {
@ -63,9 +68,10 @@ export class PixiTMR {
}
sprite(code, options = {}) {
const texture = PIXI.utils.TextureCache[code];
let img = PixiTMR.getImgFromCode(code)
const texture = PIXI.utils.TextureCache[img];
if (!texture) {
console.error("Texture manquante", code)
console.error("Texture manquante", code, PIXI.utils.TextureCache)
return;
}
let sprite = new PIXI.Sprite(texture);
@ -97,7 +103,8 @@ export class PixiTMR {
sprite.tooltip = new PIXI.Text(text, tooltipStyle);
sprite.tooltip.zIndex = tmrTokenZIndex.tooltip;
sprite.isOver = false;
sprite.interactive = true;
// Deprecated : sprite.interactive = true;
sprite.eventMode = 'dynamic'; // PIXI 7 To be checked
sprite.on('pointerdown', event => this.onClickBackground(event))
.on('pointerover', () => this.onShowTooltip(sprite))
.on('pointerout', () => this.onHideTooltip(sprite));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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