Compare commits

...

227 Commits

Author SHA1 Message Date
55c98d1dce Merge pull request '11.2.7' (#688) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #688
2023-12-04 09:29:35 +01:00
b5dc14687e Version 11.2.7 2023-12-04 02:35:47 +01:00
1208eb8ae1 Amélioration des tooltips
- title/alt remplacés par des data-tooltip
- description de ce que font les boutons
- description des items survolés
2023-12-04 02:32:26 +01:00
a46acb7952 Merge pull request 'Action de visualisation avec droits limités' (#687) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #687
2023-12-03 20:05:31 +01:00
a68057900d Action de visualisation avec droits limités 2023-12-02 14:40:39 +01:00
57bc1b6c1f Merge pull request '11.2.6 - Les réveils difficiles d'Akarlikarlikar' (#686) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #686
2023-11-28 00:17:04 +01:00
6101bc91a6 Version 11.2.6 2023-11-27 23:49:43 +01:00
33ced5715d Le coeur s'applique à ChâteauDormant 2023-11-27 23:45:33 +01:00
50db9ba709 Fix Chateau Dormant pour les potions 2023-11-27 23:43:45 +01:00
957e31b188 Fix subacteurs 2023-11-27 14:32:39 +01:00
74571c9966 Correction sur Items 2023-11-26 22:51:33 +01:00
5155fb4669 Correction sur Class 2023-11-26 19:58:32 +01:00
698ed75d46 Fix version 2023-11-24 08:52:42 +01:00
37ba7166bf Merge pull request 'v11.2.2 - Les tendres moments d'Akarlikarlikar' (#685) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #685
2023-11-24 08:51:05 +01:00
28af8c3c58 v11.2.2 - Les tendres moments d'Akarlikarlikar
- On peut maintenant avoir des points de cœur pour des suivants/
   compagnons
  - diminuer les points de coeurs fait perdre du moral
  - on peut proposer un tendre moment
  - les jets de volonté peuvent être ajustés selon les points de cœur
- Fixes
  - La résistance est de 1 par défaut pour les équipements
  - Les armes de créatures sont de nouveau utilisables depuis les tokens
  - Pas de notifications de signe draconique quand on regarde les TMR
   sans monter
  - Correction d'un problème de contextes WebGL causé par des
   ouvertures/fermetures de TMRs
  - On peut maintenant prendre un objet d'un acteur-token pour
    l'ajouter à un autre acteur
  - On ne peut plus donner d'objets d'un acteur à un acteur-token
2023-11-24 03:02:18 +01:00
4a03c222d5 Gestion des points de coeur
- Les suivants/compagnons/amoureux sont dans l'onglet description
- si acteurs "liés", ils peuvent avoir des points de coeur
- les jets de volonté peuvent être ajustés s'ils concernent un
  compagnon pour lequel on a du coeur
- on peut ajouter des points de coeur (entre la gestion de Chateau
  dormant par le gardien et le jet de repos si ce mode est utilisé)
- on peut retirer des points de coeur en perdant du moral (mêmes
  conditions)
- on peut passer de tendres moments si les deux acteurs acceptent
- les tendre moments font jouer un jet de moral adapté
- on peut perdre un point de coeur suite à un tendre moment qui ne fait
  pas gagner de moral
2023-11-24 02:57:43 +01:00
135546467d Envoi de message à plusieurs acteurs 2023-11-24 02:51:59 +01:00
b5db2a9ef3 Cleanup - preparation Coeur
Simplification de code:
- des Méthodes simples sur une ligne
- utilisation de item.update au lieu de updateEmbeddedDocuments
  quand possibe
- renommage des templates SubActeur
- déplacement de logs quand compétence non trouvée
2023-11-24 02:51:59 +01:00
bfb7b9b6bf taille des options 2023-11-24 02:51:59 +01:00
f0b7306885 Fix: animaux
- état général corrigé
- ajout de blessure, mise à jour endurance, ...
- calcul du malus d'état général
- ajustement des jets selon points de vie perdus
2023-11-24 02:51:52 +01:00
af410c1823 Typo: Tarot, Le Gibet 'Chute' 2023-11-24 02:49:49 +01:00
bf2b387fc4 Fix: Hooks.once('ready') not called
Sur Firefor, le onReady n'est jamais appelé, le système n'est alors
pas totalement initialisé, toute la partie calendrier n'est pas prête,
et donc les ajustement d'astrologie.
2023-11-24 02:49:49 +01:00
dfd915f8d1 Fix: prendre un objet d'un token
On peut maintenant prendre un objet à l'acteur d'un token non lié.

Avertissement quand on donne un objet à un token
2023-11-23 17:08:37 +01:00
4fe487a0ec Fix: problème de contexte WebGL
WARNING: Too many active WebGL contexts. Oldest context will be lost.

Semble lié à la destruction incorrecte de l'Application PIXI des TMR,
en cas de nombreuses ouvertures/fermetures
2023-11-23 17:08:37 +01:00
0ec31d8ddc Fix: double class 2023-11-23 00:33:19 +01:00
1bee911c35 Fix: pas de notification de signe si viewonly 2023-11-23 00:33:19 +01:00
ed7471875f Fix: armes des créatures utilisables 2023-11-23 00:33:18 +01:00
f50e9cd305 Déplacement terre d'attache 2023-11-21 21:08:46 +01:00
a2fcb9a453 Résistance 1 par défaut 2023-11-21 21:08:46 +01:00
3018e3522c Merge pull request 'v11.2.1 - La technique d'Akarlikarlikar' (#684) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #684
2023-11-20 23:32:34 +01:00
d16dec3278 Résistance par défaut de 1
Pour que les armes ne soient pas cassées par défaut
2023-11-20 18:55:01 +01:00
339e128b98 Version 11.2.1 - La technique d'Akarlikarlikar
- On peut créer des armes pour Corps à corps et Esquive. Barreaux de
chaise, armes improvisées, techniques d'art martiaux, pas de côté pour
faire trébucher l'adversaire... A vous de voir comment imaginer de
nouvelles "armes".
- Les armes avec une résistance de 0 ne peuvent pas être utilisées,
une image et un rappel indiquent qu'elles sont cassées
2023-11-20 18:42:29 +01:00
b1b56c13bf Montrer les armes cassées
Vu qu'elles ne peuvent pas être utilisées, permet de savoir pourquoi
2023-11-20 17:24:50 +01:00
b07a3e18df Autoriser les armes de corps-à-corps
Barreaux de chaise, armes improvisées, techniques d'art martiaux, ...
peuvent être gérés comme des armes.
2023-11-20 17:24:50 +01:00
f446d897a7 Merge pull request 'v11.2.0 - Les Terres médianes d'Akarlikarlikar' (#683) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #683
2023-11-20 07:04:11 +01:00
4e89720341 Version 11.2.0 2023-11-20 00:44:27 +01:00
c1a5bd6eb3 Fix: jets d'encaissement forcés par le Gardien
Les jets d'encaissement forcés par le gardien ne peuvent plus donner
le deuxième d10 négatif si le MJ force un résultat inférieur à 11
2023-11-20 00:44:26 +01:00
bfd3b0d74a Réduction de l'espace des TMR 2023-11-20 00:44:26 +01:00
da3091dc4b Fix: urgence draconique, complément 2023-11-20 00:44:26 +01:00
c7e00749c9 Fix: deplacement aleatoire
Le déplacement aléatoire ne prenait pas compte de la différence
entre colonnes paires/impaires
2023-11-15 22:11:44 +01:00
30d41861fa Support hot reload for templates and css 2023-11-15 22:03:57 +01:00
60cebe60eb Fix #99 2023-11-15 16:34:59 +01:00
ac09e1854b Typo: Tricollet 2023-11-14 01:51:32 +01:00
9ff68884ce Merge pull request 'v11.1.6 - Les dissections de Werther de Zloth' (#682) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #682
2023-11-12 22:15:26 +01:00
09986115b8 Version 11.1.6 2023-11-12 21:18:52 +01:00
511ee5f241 Fix: encaissement joueur à valider par MJ 2023-11-12 21:18:07 +01:00
306d7c2150 Fix: délai de guérison après rétrogradation
Lorsqu'une blessure critique/grave rétrograde naturellement, le délai
avant le jet de récupération suivant est correctement augmenté.
2023-11-12 21:17:38 +01:00
5624bb1bf3 Merge pull request 'Fix: ajout de compétences aux créatures' (#681) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #681
2023-11-12 16:35:40 +01:00
b588c5f7f8 Fix: ajout de compétences aux créatures 2023-11-12 15:06:58 +01:00
6de34178b3 Sync version 2023-11-12 00:35:51 +01:00
0c90fba346 Merge pull request 'Préparation 11.1.5' (#680) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #680
2023-11-12 00:35:06 +01:00
f43b7ac651 Préparation Changelog 11.1.5 2023-11-12 00:33:25 +01:00
ac436bbb25 Fix: Recevoir une queue annulait l'insomnie 2023-11-12 00:24:42 +01:00
9093eadb23 Fix: urgence draconique 2023-11-12 00:22:29 +01:00
5821fc611f Fix: case pour trou noir 2023-11-11 22:33:51 +01:00
381242c8d0 Fix: le jet de vie indique la mort
Jusque là, le 20 indiquait la mort, mais ne diminuait pas la vie
en dessous de - SConst

Désormais:
- la vie passe à -SConst - 1 sur 20 au jet de vie
- Le message indique que le personnage est mort si sa vie est
  inférieure à -SConst
- le jet de vie n'est pas fait si le personnage est déjà mort
2023-11-11 21:30:25 +01:00
3ca33e85b7 Fix case pour réserve extensible 2023-11-11 21:30:02 +01:00
01399e922a Suppression de certains warnings 2023-11-11 20:38:16 +01:00
c6afb8d445 Fix demande defense 2023-11-11 20:23:07 +01:00
4d68318d85 Fix release 2023-11-11 09:07:28 +01:00
7811eae728 Merge pull request 'v11.1.3 - Werther de Zloth l'Onirique' (#679) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #679
2023-11-11 09:06:00 +01:00
2e74ccd1fe Fix: tooltip TMR par dessus les autres sprites 2023-11-11 00:52:57 +01:00
f98ece4ffd Version 11.1.3 2023-11-11 00:44:45 +01:00
ed068a8ddd Affichage du facteur significative 2023-11-11 00:44:11 +01:00
c938778267 Fix: afficher "0" au lieu de "+0" 2023-11-11 00:44:08 +01:00
9056514951 Fix: un plat raté est mauvais, pas exotique 2023-11-11 00:33:20 +01:00
1047720c24 Fix: jet d'appréciation sans compétence 2023-11-10 23:23:28 +01:00
43bcf1c336 Fix: afficher/masquer points de tâche 2023-11-10 23:22:45 +01:00
14d4638e56 Fix: error dans les logs si aucune scène active 2023-11-10 19:56:25 +01:00
c761aeceb3 Fix: calcul de l'état général 2023-11-10 19:54:32 +01:00
1759e6d1c3 Fix: tooltips sorts TMRs 2023-11-10 03:39:35 +01:00
ecb47addba Fix changelog 2023-11-10 01:27:52 +01:00
b18f93fbfb Merge pull request 'v11.1.2' (#678) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #678
2023-11-09 17:02:31 +01:00
19bfee7ea5 Release fix 2023-11-09 08:51:39 +01:00
93e44bb982 Version 11.1.2 2023-11-09 00:53:03 +01:00
12df910b3a Macro "Mon Personnage" pour les joueurs 2023-11-09 00:53:03 +01:00
2a3989ac2e Fix: les jets d'encaissement 2023-11-09 00:53:03 +01:00
ab884713f6 Merge pull request 'v11' (#677) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #677
2023-11-06 23:22:18 +01:00
940237852b Version 11.1.1 2023-11-06 23:06:02 +01:00
78ee23da96 Fix: proportions du gibier cuisiné 2023-11-06 23:06:02 +01:00
862a267683 Fix: ouvrir contenants des véhicules/créatures 2023-11-06 23:06:02 +01:00
2373acc295 Fix: afficher les vues détaillées 2023-11-06 23:06:02 +01:00
249d171511 Merge pull request 'v11.1.0 - Les choix de Werther de Zloth' (#676) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #676
2023-11-04 23:57:27 +01:00
018b6a32dd Nouveau personnage avec token lié par défaut 2023-11-04 18:50:07 +01:00
44ec511aa6 Version 11.1.0 2023-11-04 18:46:36 +01:00
ff44de3b3c Fix: charge en force 2023-11-04 18:46:36 +01:00
8101b905d8 Fix: choisir les haut rêvant pour signes
La fenêtre de signes draconiques ne sélectionne plus tout les
haut-rêvants par défaut
2023-11-04 18:34:30 +01:00
27dd89024e Cleanup 2023-11-04 18:34:30 +01:00
69a653d1e5 Jet de moral de château dormant optionnel 2023-11-04 18:31:54 +01:00
bea4124388 Séparation de l'Actor Créature 2023-11-04 18:31:53 +01:00
e5bb2e9afc Fix bug TMR sans fatigue 2023-11-04 18:31:53 +01:00
1d3ae9bb1a Séparation de l'Actor Entités 2023-11-04 18:17:40 +01:00
d4be2957a3 cleanup commerce 2023-11-04 18:16:20 +01:00
6c9d03be92 Récupération de rêve optionnelle 2023-11-04 18:16:20 +01:00
5a50917730 Récupération d'éthylisme optionnelle 2023-11-04 18:16:20 +01:00
d285e866be Récupération de chance optionnelle 2023-11-04 18:16:19 +01:00
ece9ab6f64 Séparation de l'Actor Vehicule 2023-11-04 18:16:19 +01:00
038e922f0f Option transformation de stress 2023-11-04 18:16:19 +01:00
728c5f2f8e Sur creation de personnage, force le lien du token 2023-11-04 17:48:59 +01:00
e6592f8333 Merge pull request 'v11.0.28 - les fractures de Khrachtchoum' (#675) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #675
2023-10-30 22:34:39 +01:00
42567e7ca0 Amélioration de fenêtre de jets
- indication du type de dégâts
- affichage du moral avant l'icone d'appel au moral
- meilleur alignement des différentes options
2023-10-30 21:33:38 +01:00
9d1fe6d8fd Version 11.0.28 2023-10-30 19:47:08 +01:00
f5bd0f32f4 Fix: affichage de la gravité des blessures
Le type de blessure est indiqué dans le résumé d'encaissement
2023-10-30 19:42:56 +01:00
fad29f9652 Jet de vie par les propriétaires
Les messages de tour sont séparés en deux:
- un message pour tout le monde disant de qui c'est le tour
- un message pour les propriétaires du token pour les informations
  de santé (jets de vie à faire, ...)

Seul les propriétaires peuvent déclencher les jets de vie
2023-10-30 19:40:21 +01:00
9d51631d5c Merge pull request 'v11.0.27' (#674) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #674
2023-10-25 00:02:40 +02:00
d1b73e7658 Version 11.0.27 2023-10-24 22:51:45 +02:00
54158ee10d Fix disparition du tooltip dans les TMR
Passage den eventMode static permet de ne pas faire disparaître le
tooltip.
2023-10-24 22:44:51 +02:00
e089bdf9c8 Surencombrement seulement sur actions physiques
CF p70
2023-10-24 22:32:49 +02:00
41ab593059 Fix: fabrication de potion depuis l'herbe
La fabrication ne marchait plus
2023-10-24 22:24:54 +02:00
42ad4c5b26 Fix Restauration TMR si minimisée
Lors d'une action dans les TMRs, si la fenêtre est minimisée,
on la réaffiche
2023-10-24 22:16:49 +02:00
85f8a716d4 Merge pull request '## v11.0.26 - le crépuscule de Khrachtchoum' (#673) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #673
2023-10-24 07:07:01 +02:00
47f305d865 Les attaques de créatures fonctionnent de nouveau 2023-10-24 00:50:16 +02:00
407b4f82d9 Version 11.0.26 2023-10-24 00:43:16 +02:00
c950f568fd Fix: les TMRs sont en arrière plan 2023-10-24 00:30:00 +02:00
0ed90f6177 Macros pour le corps à corps
Cas particulier car 3 utilisations possibles, et pas d'armes
2023-10-23 23:48:07 +02:00
b74fc27079 Minor cleanup 2023-10-23 23:38:34 +02:00
a65d4511c5 Fix Macros de combat et corps à corps
- gestion des armes à 1/2 mains depuis les armes / le combat
- gestion correcte des compétences d'attaques de créatures
- message pour les macros de compétence d'arme
2023-10-23 23:29:02 +02:00
e61417c44e Merge pull request '## v11.0.25 - la vision du rêve de Khrachtchoum' (#672) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #672
2023-10-22 21:40:42 +02:00
8ca725bd38 Version 11.0.25 2023-10-22 16:17:53 +02:00
441a5965c7 Fix TMR minimize/maximize
Au lieu de minimiser les TMRs, les actions dans les TMRs sont bloquées
tant qu'une action liée au TMRs est en cours.
2023-10-22 16:17:37 +02:00
f08c8f93da Merge pull request '## v11.0.24 - les couleurs de Khrachtchoum' (#671) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #671
2023-10-22 09:40:26 +02:00
d28362bf14 Version 11.0.24 2023-10-22 00:36:14 +02:00
e506382d18 typo: Nécropole de Throat 2023-10-22 00:36:10 +02:00
c1cecc76b3 Nouvelles TMRs 2023-10-22 00:19:45 +02:00
f2a3e1db45 Merge pull request 'v11.0.23 - la lumière de Khrachtchoum' (#669) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #669
2023-10-20 07:12:09 +02:00
b69359a47d Version 11.0.23 2023-10-20 02:36:49 +02:00
fe6c2e2ff2 Ajustement de luminosité automatique
Activable par le MJ avec le bouton lumière du calendrier
2023-10-20 02:36:00 +02:00
9bd13a6021 Comptence de creatures 2023-10-18 21:18:29 +02:00
a29630f9a2 Merge pull request 'v11.0.21 - les automatismes de Khrachtchoum le Problémeux' (#668) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #668
2023-10-18 21:17:03 +02:00
c7fd9f7596 Version 11.0.22
les automatismes de Khrachtchoum le Problémeux
2023-10-18 21:13:50 +02:00
7b18fd25c3 Macro pour compétences de créature 2023-10-18 21:08:01 +02:00
5c256e2c49 F>ix version 2023-10-04 21:44:41 +02:00
1c475348d5 Gestion des armes dans hotbar 2023-10-04 21:24:51 +02:00
de5d32f88f Gestion des armes dans hotbar 2023-10-04 09:33:24 +02:00
76a02d60ca Gestion des armes dans hotbar 2023-10-04 09:07:49 +02:00
724c556b9e Merge pull request 'v11' (#667) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #667
2023-10-04 09:07:10 +02:00
7dfba94a11 Nettoyage de date
Pas de "T" entre date et heure
2023-10-03 21:03:19 +02:00
928c7bbcfe Désespoir 2023-08-28 01:46:43 +02:00
222a06a978 Fix remoteCall 2023-08-15 17:24:01 +02:00
b4edaf8584 Fix remoteActorCall 2023-08-13 20:43:32 +02:00
3c062afd56 Fix remoteActorCall 2023-08-13 20:42:54 +02:00
6de4fff403 Merge pull request 'v11.0.15 - L'apprentissage de Khrachtchoum' (#666) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #666
2023-08-03 09:10:32 +02:00
3543f081b2 Version 11.0.15 2023-08-03 00:41:16 +02:00
56f14e12a2 Fix: regression sur les particulières
L'utilisation de la méthode clone() pôur cloner la compétence
supprime l'id de la compétence, du coup l'update pour y
ajouter de l'expérience ne marchait plus et bloquait la suite de la
résolution (pas de messages dans le tchat, ...)
2023-08-03 00:41:16 +02:00
e226af5ac5 Merge pull request 'v11.0.14' (#665) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #665
2023-07-30 09:25:10 +02:00
d8be37a2ec v11.0.14 2023-07-29 23:47:08 +02:00
ba2d9de7b6 Version 11.0.13
Les pincettes de Khrachtchoum le Problémeux
- Correction du calcul de la place restante lors de l'ajout dans un conteneur
2023-07-29 23:44:56 +02:00
6b8fb3267a Fix: calcul de la place dans un conteneur
Correction de l'ajout dans un conteneur, en faisant un calcul arrondi
pour éviter les problèmes de virgule flotante
2023-07-29 23:42:22 +02:00
05d6f64a31 Merge pull request 'v11.0.13 - La multiplication de l'eau de Khrachtchoum le Problémeux' (#664) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #664
2023-07-28 10:11:26 +02:00
c0570e58b4 Version 11.0.13 2023-07-27 19:21:48 +02:00
8389d578bc Fix: achat d'objet illimités
Lors de l'achat à un commerce avec une quantité illimité, on était
tout de même limité à la quantité de l'objet, même si elle n'était
pas diminuée
2023-07-27 19:17:57 +02:00
f05ef79b97 Merge pull request 'v11.0.12 - Les poids de la mesure de Khrachtchoum le Problémeux' (#663) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #663
2023-07-14 00:36:37 +02:00
d2a8bfae4f v11.0.12 - Les poids de la mesure
- Correction des malus de surencombrement
- Le malus armure est correctement affiché dans l'onglet des
  caractéristiques
2023-07-13 01:02:03 +02:00
d54834fa71 Amélioration des messages d'art 2023-07-13 01:02:03 +02:00
c898bf5212 Merge pull request 'Encombrement et malus armure' (#661) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #661
2023-07-11 14:46:14 +02:00
a118dc7334 Correction sur-Encombrement v2
Le malus appliqué est maintenant correct.

Avant, le malus pouvait avoir une virgule, et donc être arrondi
dans le malus vraiment appliqué même si affiché correctement.
2023-07-11 02:53:34 +02:00
46401e5d63 Cleanup: simplification _computeFinalLevel
méthode supprimée dasn RdDRoll (car basée sur les ajustements),
pas utile ici non plus.
2023-07-11 02:36:39 +02:00
7eb1d9f838 Fix affichage Malus Armure
Le malus armure est correctement affiché dans l'onglet caractéristiques.

Il n'est plus stocké dans l'acteur mais calculé.
2023-07-11 02:36:39 +02:00
1d8f4ebb88 Merge pull request 'Fix: sélection de compendiums que pour le MJ' (#660) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #660
2023-07-09 11:09:28 +02:00
145a92f462 Fix: sélection de compendiums que pour le MJ 2023-07-07 00:47:05 +02:00
5148d02314 Correction sur arrondi sur-encombrement 2023-07-01 21:28:18 +02:00
000c89b11a Merge pull request 'V11.0.11 - Les bleus de Khrachtchoum le Problémeux' (#659) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #659
2023-06-25 09:48:17 +02:00
5d4a18aac5 Version 11.0.11 2023-06-24 23:59:25 +02:00
34b5df637f Notifier le joueur qu'il peut passer sa nuit
Lorsque le MJ configure la nuit des personnages, une notification est
envoyée aux joueurs pour leur dire de gérer leur nuit.
2023-06-24 23:50:05 +02:00
22572ca98c Support des armes non mortelles 2023-06-24 23:40:07 +02:00
a6a1c1009e Merge pull request 'v11.0.10 - Les Songes de Khrachtchoum le Problémeux' (#658) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #658
2023-06-24 09:22:24 +02:00
288cf9162c Version v11.0.10 2023-06-24 01:48:34 +02:00
4b1381e535 Decompte des heure en cas de réveil
Si le gardien gère la durée des nuits, en cas de rêve de dragon,
les heures restant à dormir sont mises à jour.
2023-06-24 01:41:12 +02:00
78336a3f45 Fix: affichage sommeil avec RdD première heure
Le message ne s'affichait pas en cas de rêve de dragon dès la
première récupération
2023-06-24 01:19:54 +02:00
3001ae44de Endurance limitée en cas d'insomnie 2023-06-24 01:19:54 +02:00
a43ceb3188 Fix: lire depuis un livre 2023-06-24 01:19:54 +02:00
9e95a44efd Fix: click pour se déplacer dans les TMR 2023-06-24 01:19:54 +02:00
179ea0c85b Fix: selection de TMR des signes draconiques 2023-06-24 01:19:53 +02:00
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
309 changed files with 6743 additions and 4438 deletions

4
.gitignore vendored
View File

@ -8,3 +8,7 @@ todo.md
/jsconfig.json /jsconfig.json
/package.json /package.json
/package-lock.json /package-lock.json
/packs/*/
/packs/*/CURRENT
/packs/*/LOG
/packs/*/LOCK

View File

@ -1,256 +1,215 @@
--- # 11.2
# v10.7 - L'os de Semolosse ## 11.2.7 - Les explications d'Akarlikarlikar
- Ajout de tooltips sur la plupart des boutons, liens clickables, objets, tâches, ...
- Fix: on peut de nouveau regarder l'inventaire avec les droits limités/observateur
## v10.7.13 - l'armure de Semolosse ## 11.2.6 - Les réveils difficiles d'Akarlikarlikar
- Fix: en cas d'armure variable, la détérioration diminue le dé d'armure - Les changements de points de Cœur sont temporaires jusqu'à fin Château Dormant
- Fix: tous les petits fixes (feuille qui s'ouvre plus, compagnons animaux, potions qui bloquent Château Dormant, ...)
## v10.7.12 ## 11.2.2 - Les tendres moments d'Akarlikarlikar
- Fix: si le MJ gère les changements de jours, l'option "sieste" de la fenêtre de repos est prise par défaut si chateau dormant n'est pas passé - On peut maintenant avoir des points de Cœur pour des suivants/compagnons
- diminuer les points de coeurs fait perdre du moral
- on peut proposer un tendre moment
- les jets de volonté peuvent être ajustés selon les points de Cœur
- Fixes
- La résistance est de 1 par défaut pour les équipements
- Les armes de créatures sont de nouveau utilisables depuis les tokens
- Pas de notifications de signe draconique quand on regarde les TMR sans monter
- Correction d'un problème de contextes WebGL causé par des ouvertures/fermetures de TMRs
- On peut maintenant prendre un objet d'un acteur-token pour l'ajouter à un autre acteur
- On ne peut plus donner d'objets d'un acteur à un acteur-token
- L'état général est correctement calculé, affiché, et utilisé pour les animaux
- On peut ajouter des blessures manuellement aux animaux
- Le texte de la carte de Tarot "Le Gibet" est corrigé
- Sur Firefox, le calendrier est correctement initialisé, les ajustements astrologiques
ne bloquent plus les jets de dés
## v10.7.11 - Le Pugilat de Semolosse ## v11.2.1 - La technique d'Akarlikarlikar
- Fix sur la projection au sol. - On peut créer des armes pour Corps à corps et Esquive. Barreaux de chaise, armes improvisées, techniques d'art martiaux, pas de côté pour faire trébucher l'adversaire... A vous de voir comment imaginer de nouvelles "armes".
- Les armes avec une résistance de 0 ne peuvent pas être utilisées, une image et un rappel indiquent qu'elles sont cassées
## v10.7.10 - Le Pugilat de Semolosse Vu qu'elles ne peuvent pas être utilisées, permet de savoir pourquoi
- Gestion de l'empoignade ## v11.2.0 - Les Terres médianes d'Akarlikarlikar
- Corrections sur l'initiative - Les TMRs sont redimensionables
- Correction sur l'equipement des vêtements et bijoux - Nouveaux graphismes plus lisibles dans les TMRs
- Nouveau code couleur des icônes dans les TMR:
- noir: case innaccessible
- rouge: empêche l'usage du haut-rêve
- vert: bonus de tête de dragon permanent
- bleu: la case doit être vaincue
- blanc: effet temporaire (sort en réserve, présent des cités)
- Fix: les déplacements aléatoires prennent bien compte des colonnes paires/impaires
- Fix: Le Tricollet prend deux "L"
- Fix: Les jets d'encaissement forcés par le gardien à un résultat inférieur à 11 ne peuvent plus donner un deuxième d10 négatif
## v10.7.9 - Le Pugilat de Semolosse # v11.1
## v11.1.6 - Les dissections de Werther de Zloth
- Fix: on peut de nouveau donner des compétences aux créatures
- Fix: le délai de guérison d'une blessure rétrogradée est correctement appliqué
- Fix: l'encaissement à valider par le MJ fonctionne de nouveau
- Gestion assistée de l'empoignade ## v11.1.5 - Werther de Zloth l'Onirique
1. On selectionne sa cible (ie le token qui va être empoigné) - Fixes:
2. On lance une attaque avec l'"arme" _Empoignade_ - la demande de défense ne marchait plus
3. A ce stade, si la victime a une arme, on rappelle le point de règle d'engagement - la tête réserve extensible crée bien une case de réserve extensible (à modifier)
(page 134), et un bouton permet de confirmer l'empoignade - le souffle trou noir ajoute bien une case de trou noir
4. L'empoigneur fait son jet - la queue urgence draconique ne se transforme plus en idée fixe s'il y a des sorts en réserve
5. Si réussite, l'empoigné peut se défendre, avec gestion du premier round d'engagement - l'ajout d'une nouvelle queue ne supprime plus l'insomnie
(ie Esquive autorisée ou pas) - Amélioration des jets de vie
- 4 bis. et 5 bis. L'empoigné, à son tour, peut tenter de se libérer, toujours en cliquant sur l'action "Empoignade" - un 1 sur le jet de vie est une réussite même si le personnage est dans le coma
6. Selon le résultat, incrément/décrément des points d'emp - le temps avant le prochain jet est calculé et affiché
7. Retour en 4., ou si 2 points d'Emp, alors 8. - un 20 sur le jet de vie signifie la mort immédiate
8. Affichage des options disponibles pour l'empoigneur : perte d'endurance, projection au - si on dépasse le S.Const, le personnage est bien indiqué comme mort
sol ou entrainer au sol. Ces 3 options sont gérées automatiquement ensuite, selon le - pas de jets de vie pour les morts
bouton cliqué par l'empoigneur.
Les empoignades sont des "items" supprimées à la fin d'un combat, qui peuvent aussi être ## v11.1.4 - Werther de Zloth l'Onirique
gérés par le MJ au cas ou. Hors combat, penser à les supprimer (ou commencer et - Ajout du facteur de significative à côté du pourcentage dans le résultat des jets de dés pour rappeler que le pourcentage n'est pas diviasé
arrêter un combat). - Fix: dans les TMRs, les tooltips affichent bien les informations de tous les effets sur la case
- Fix: la fatigue et l'éthylisme sont de nouveau pris en compte dans le calcul de l'éthylisme
- Fix: Le MJ peut correctement masquer les points de tâche requis
- Fix: le jet d'appréciation n'utilise pas la compétence
- Fix: la qualité négative n'est pas exotique, elle est juste mauvaise: on n'utilise pas la cuisine pour se retenir de jeter l'assiette
- Esthétique: ne pas afficher "+0" pour les ajustements de jets/encaissement
## v10.7.7 - Les bobos de Sémolosse ## v11.1.2 - Les vertèbres de Werther de Zloth
- Mise à jour du texte de l'heure pour les joueurs - Fix: les jets d'encaissement fonctionnent de nouveau normalement
- L'horloge n'empêche plus de sélectionner les tokens dessous - Macro "Mon personnage" permettant au joueur d'accéder à sa feuille de personnage depuis la barre de macros
- _Lecture & Détection d'Aura_ sous Hypnos sont des rituels
- _Lire les étoiles_ pour les joueurs de nouveau fonctionnel
- Ajout de logs pour comprendre un cas d'échec des achatVente
## v10.7.6 - L'origine des maux de Sémolosse ## v11.1.1 - Les fumebols de Werther de Zloth
- Calendrier - Fix: on peut de nouveau afficher les vues détaillées
- fix du ré-affichage de l'horloge qui ne marchait pas pour les joueurs - Fix: on peut ouvrir les sacs et contenants portés par les véhicules et créatures
- l'horloge ne se ferme plus sur Escape - Fix: cuisiner du gibier prend maintenant bien les proportaions en compte
- amélioration d'affichage
- couleurs jour/nuit plus marquées
- Divers
- correction de l'affichage de quantités diminuées d'herbes dans les contenants ouvert
- ajout d'un bouton pour diminuer les quantités dans l'équipement (si quantité > 1)
- ajout de la signature de l'acteur sur les blessures qu'il a causées
- Magie ## v11.1.0 - Les choix de Werther de Zloth
- correction des bonus de cases pour les sorts en Fleuve - Les options suivantes peuvent être désactivées:
- La transformation de stress à Château Dormant
- La récuperation de chance à Château Dormant
- La récupération d'éthylisme
- La récupération de rêve (y compris fleurs de rêve et Rêves de Dragon: la rencontre a lieu, mais ne donne pas de rêve)
- Le jet de moral de Château Dormant
- Séparation des véhicules dans leur propre acteur
- Séparation des entités dans leur propre acteur
- Séparation des créatures dans leur propre acteur
- La fenêtre de signes draconiques ne sélectionne plus tout les haut-rêvants par défaut
- Un nouveau personnage a automatiquement son token relié
- corrections de bugs
- si on n'utilise pas les règles de fatigues, un reflet de rêve pouvait garder le Haut-rêvant dans les TMRs pour toujours
- certaines macros ne marchaient pas pour les créatures/entités/véhicules/commerces
- en cas de charge, les particulières sont toujours en force (p125)
## v10.7.5 - La montre-gousset de Sémolosse # v11.0
- Amélioration de la fenêtre calendrier ## v11.0.28 - les fractures de Khrachtchoum
* plus compacte - La gravité de la blessure est affichée dans le résumé de l'encaissement
* horloge analogique (optionnelle) - Lors du changement d'acteur pendant le round
* minimizable (juste la barre de titre) - le message annonçant le joueur dont c'est le tour ne contient plus d'informations de santé
* normalement compatible pop-out - un message avec les informations de santé est envoyé au Gardienn et au propriétaire du token.acteur
- le jet de vie est bien fait par le token si besoin
- seul les propriétaires peuvent faire les jets de vie
- Amélioration de la fenêtre de jets
- le type de dégâts pour les attaques est toujours affiché
- le moral est indiqué avant l'icone d'appel au moral
## v10.7.4 - Les ligatures de Sémolosse ## v11.0.27 - Khrachtchoum le méticuleux
- Corrections diverses - le tooltip dans les TMR reste visible si on ne bouge pas la souris
- Correction des boutons pour déclencher un sort en réserve avec réserve en sécurité ou réserve extensible - le surencombrement n'affecte QUE les actions physiques
- le lien pour les jets de vie suite à une blessure critique est remplacé par un bouton - on peut de nouveau fabriquer une potion depuis la fenêtre d'édition de l'herbe
- déplacement des tâches et boutons de chirurgie dans l'onglet savoirs et tâches - si les TMR sont minimisées alors qu'une action est requise, elles sont bien réaffichées lorsque l'action est faite
- correction de l'affichage des bonus de cases des sorts
- corrections des queues non-refoulables dans le compendium
## v10.7.3 - Les tisanes de Sémolosse ## v11.0.26 - le crépuscule de Khrachtchoum
- Soins - gestion correcte des TMRs
- on peut de nouveau boire une potion de soins enchantée - les TMRs ne sont jamais minimisées (par le système) quand le haut-rêvant est en demi-rêve
- les potions non enchantées donnent de nouveau un bonus au prochain jet de récupération - lorsqu'une fenêtre liée aux demi-rêve est affichée, cliquer sur les TMRs n'a pas d'effet
- Une fois les soins complets faits, le bonus aux soins complets fournis par les premiers soins est masqué - les lancers de sorts et lectures de signes sont affichées en premier plan
- Les effets qui ouvrent une fenêtre sont bien affichés en premier plan
- en cas de rencontre suivie de maîtrises/conquêtes, les fenêtres s'enchaînent
- Le drag&drop vers la barre de macro est corrigé
- pour les créatures, possibilités d'avoir les attaques ou autres compétences
- pour les personnages, les macros sont créées:
- pour les compétences
- pour le corps à corps, trois macros sont créées: compétence, pugilat, empoignade
- pour les armes
- deux macros sont créées pour les armes à 1/2 mains
- deux macros sont créées pour les armes de mélée et lancer
- 4 macros si votre arbalête se lance, tire, et se manie à 1 ou 2 mains...
- les jets de compétences d'attaque des créatures fonctionnent de nouveau
- Horloge ## v11.0.25 - la vision du rêve de Khrachtchoum
- A l'heure de Couronne pile, les aiguilles des heures et des minutes pointent sur couronne (comme une montre) au lieu d'avoir l'aiguille des heures 15° à gauche - Les TMRs restent affichées tant que le Haut-rêvant est en demi-rêve
## v10.7.2 - les maux de dents de Semolosse ## v11.0.24 - les couleurs de Khrachtchoum
- correction des récupérations de blessures - nouvelle carte des TMRs
- la fin de château dormant se passe normalement
## v10.7.1 - L'os de Semolosse ## v11.0.23 - la lumière de Khrachtchoum
- Fix rapide sur les jets de carac qui n'étaient plus possibles - ajustement automatique de la luminosité selon l'heure pour les scènes:
- avec une vision des tokens (sinon: ce n'est pas une scène de carte pour tokens)
- avec illumination globale (correspondant à une illumination extérieure)
- quand lampe "allumée" dans la fenêtre du calendrier
## v10.7.0 - L'os de Semolosse ## v11.0.22 - les automatismes de Khrachtchoum le Problémeux
- gestion des blessures en items - Macro pour attaquer avec les compétences de créatures
- soins du token ciblé par menu contextuel (comme le combat)
- automatisation des soins et de l'affichage de l'avancement des soins
- support des changements d'opérants
--- ## v11.0.20
# v10.6 - Les recherches de Pralinor le Goûteux - Macro pour attaquer avec les armes des personnages
## v10.6.25 - Fix sur l'astrologie ## v11.0.17
- Fix: les actions de commerce ne s'appliquait pas bien aux personnages des tokens non liés
## v10.6.22 - le nuage de lait dans le thé de Pralinor ## v11.0.15 - L'apprentissage de Khrachtchoum
- Amélioration de l'affichage de l'horloge - Fix: l'expérience ne s'appliquait plus sur certaines réussites particulières (régression depuis la 11.0.7)
- Fix: affichage des points de guérison dans les potions
## v10.6.21 - La théière de Pralinor ## v11.0.14 - Les pincettes de Khrachtchoum le Problémeux
- Astrologie - Correction du calcul de la place restante lors de l'ajout dans un conteneur
- le thème astral est directement dans la fenêtre d'astrologie
- la roue des heures sert d'horloge
- sélectionner un personnage ajuste le thème astral pour son heure de naissance
- sélectionner le nombre astral d'un jour ajuste le thème astral
- Fix: les PNJs peuvent de nouveau dormir ## v11.0.13 - La multiplication de l'eau de Khrachtchoum le Problémeux
- Correction de la vente depuis un commerce ayant des quantités illimitées
## v10.6.20 - Les Oracles de Pralinor: vous mangerez à Couronne ## v11.0.12 - Les poids de la mesure de Khrachtchoum le Problémeux
- Ajout de la fenêtre pour effectuer un thème astral - Correction des malus de surencombrement
- Le malus armure est correctement affiché dans l'onglet des caractéristiques
- Correction d'orthographe et amélioration des messages des oeuvres d'art
## v10.6.19 - La cerise de Pralinor ## v11.0.11 - Les bleus de Khrachtchoum le Problémeux
- les joueurs peuvent chercher dans les commerces avec un droit limité/observateur - si le gardien configure le sommeil, les joueurs sont notifiés que chateau dormant vient de passer
- simplifications des fins de tours et nombre d'utilisations - possibilité de créer des armes et des compétences de créatures non-mortelles.
- ajout du _Haubert d'Oniros_ dans le compendium de sorts
## v10.6.17 - Les désordres de Pralinor ## v11.0.10 - Les Songes de Khrachtchoum le Problémeux
- le contenu des casseroles et autres contenants est maintenant trié dans l'ordre alphabétique - on peut de nouveau se déplacer dans les TMRs d'un clic sur la case à atteindre
- les objets dupliqués du compendium d'équipement sont de nouveaux uniques - Lire un livre depuis l'inventaire permet de nouveau de faire un jet de la tâche
créée au lieu de créer toujours une nouvelle tâche
- La sélection des TMR pour la création de signes draconiques ne cause plus d'erreurs
- la récupération d'endurance en cas d'insomnie est limitée à la moitié
- le résultat du sommeil lors d'un rêve de dragon à la première heure s'affiche normalement
- lorsque le gardien gère la durée des nuits, en cas de rêve de dragon,
les heures dormies sont déduites des heures restant à dormir
## v0.6.16 - Le pardon de Pralinor ## v11.0.9 - Les Souvenirs de Khrachtchoum le Problémeux
- Ajout d'un commerce _Liste d'équipement_ dans les archétypes de PNJs - mode de saisie de l'archétype en vue détaillée
- Séparations d'équipements groupés et corrections de quelques objets & herbes - création une nouvelle incarnation depuis l'archétype
- On peut éditer les armes stockées dans un commerce - 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
## v10.6.15 - les digestifs de Pralinor ## v11.0.8 - la poigne de Sémolosse
- amélioration des messages de sommeil (nombre d'heure dormies, uniquement les - lien vers le changelog
récupérations de rêve en dessous du seuil, affichage de la récupération d'endurance - organisation des compendiums du système
qui avait disparu, meilleur message sur le jet de moral) - correction de l'empoignade
- les insomnies ne durent bien que 12h draconique à partir du prochain - les items d'empoignade sont ajoutés par le MJ quand nécessaire
Chateau Dormant (elles pouvaient durer 3 nuits suite à une erreur). - seul le joueur propriétaire du personnage peut effectuer ses choix et actions d'empoignade
- la recherche dans l'équipement affiche correctement les conteneurs dans lesquels les - les caractéristiques du défenseur sont utilisées pour la défense
objets trouvés sont rangés - 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
## v10.6.14 - la digestion de Pralinor # v11.0.7
- Chateau dormant
- la situation du jet de moral peut être choisie lorsque l'on dort
- les queues de dragon "insomnie" empêchent de dormir, et de rêver
- ajout d'une option pour meilleure gestion de Chateau Dormant par le MJ
- avec cette option, à la fin Chateau Dormant, une fenêtre permet au gardien de
positionner pour chaque joueur:
- le stress de la journée
- les heures de sommeil
- la situation du jet de moral (neutre/heureux/malheureux)
- l'affichage des heures Chateau Dormant et Poisson Acrobate est correct - les créatures ont maintenant le droit d'avoir des compétences de tir, lancer, mêlée, armes naturelles, parade.
- le jet de moral en situation neutre fait maintenant retourner le moral vers 0, et - les créatures armées utilisent la bonne phase d'initiative
n'affecte plus un moral à 0. - correction des possessions
## v10.6.13 - la cave de Pralinor - la difficulté de la défense est imposée par l'attaque
- on peut maintenant chercher dans l'inventaire des commerces - une attaque particulière de possession est en finesse
- l'inventaire est correctement affiché en entier après suppression de la recherche - le rêve actuel des personnages est bien utilisé
- le message de chateau dormant reflète correctement un jet de moral neutre qui passe le moral de 0 à +1 - correction des achats par le MJ sans acteur sélectionné
## v10.6.12 - l'index de Pralinor Cf branche v10 pour l'historique des versions 10
- On peut désormais chercher dans l'inventaire comme dans les compétences
## v10.6.11 - l'empoisonnement de Pralinor
- 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
- ajout d'un "poison" pour bloquer la récupération sous Griffe Morbide de Thanatos.
Ajout du lien vers l'objet du compendium dans la description MJ,, qui pourra donc
ajouter ce "poison" à la victime pour empêcher ses guérisons de vie ou blessure.
## v10.6.10
- Correction de l'édition des description
- Amélioration des descriptions d'alchimie:
- difficulté calculée automatiquement
- Température pour les couleurs
- La sustentation n'est plus concaténée dans certains cas (ce qui donnait 2+2=22)
## v10.6.8 : les bon mots de Pralinor
- Dans la fenêtre de _recherche et tirages_, possibilité de chercher sur le nom des objets en plus des autres critères
## v10.6.7 : les grumelés de Pralinor
- les objets peuvent être utilisés depuis la fenêtre d'un conteneur
- dans les fenêtres de contenants, le contenu est correctement indenté
- la présentation du contenu d'un sac est améliorée
- le bouton Nouvel Objet n'est affiché que si on est propriétaire de l'acteur
- la fenêtre de vente permet de nouveau de choisir les quantités à vendre
## v10.6.6
- Corrections d'armes rudimentaires
- Inversion: Taille puis poids
- Suppression d'une ligne de caractéristique vide (causée par la beauté)
- Les messages liés aux compétences troncs deviennent des notifications
## v10.6.5
- Le +dom est de nouveau affiché
- L'édition de caractéristiques des créatures fonctionne de nouveau
## v10.6.4 - La sénilité de Pralinor
- Fenêtre _Recherches et tirages_
- les résultats de recherches sur plusieurs compendiums sont triés
- lors de recherches avec un ou des milieux sélectionnés:
- le filtre sur la rareté utilise la rareté dans ces milieux
- les tirages se basent sur la fréquence la plus élevées dans ces milieux
- les filtres par utilisation prennent les potions en compte
- les remèdes ont une catégorie de potion 'Remède' (et correspondent à une utilisation médicale)
- ajout d'un filtre d'utilisation 'cuisine'
# Divers
- fix du cas où la transformation de 0 points de stress était concaténé, (passage de 29 à 290 avec 0 points transformés)
- suppression du compendium de taches courantes, désormais inutile
## v10.6.3 - le baba-brandevin de Pralinor
- les tâches de Soins sont maintenant déplacées à côté des blessures
- on peut créer les tâches de soins directement avec un bouton par gravité.
- le round n'est plus bloqué si un acteur est sonné
- un rare cas d'initiative négative pouvait empêcher de faire une initiative (à cause de l'état général)
- dans une circonstance inconnue, une rencontre pouvait disparaître lors de la maîtrise. Ajout d'un message pour essayer d'obtenir des détails sur ce cas, et ajout d'une sécurité pour retrouver la rencontre (qui est conservée par la fenêtre de choix d'action).
- les objets temporels (queues, souffles, poisons, maladies...) créés avant la gestion temporelle ne pouvaient pas être édités.
- les particulières sur les jets de résistance de rêve actuel ne rapportent qu'un point d'expérience (p191)
- pour lutter contre l'alcoolisme, les jets d'éthylisme sont considérés comme des jets de résistance, et n'apportent qu'un point d'expérience.
## v10.6.2 - Le méli-mélo de Pralinor
- Fenêtre _Recherches et tirages_
- support de la recherche dans les compendiums choisis
- suppression des commandes `/table milieu` et `/tirer milieu` (remplacées par la fenêtre de recherche)
- ajout de fréquences à tous les équipements
## v10.6.1 - Les recherches de Pralinor
- Fenêtre _Recherches et tirages_
- Amélioration des filtres de cuisine/utilisation
- Ajout de catégories pour les poisons, urtiquants, ...
- Bouton "Effacer les filtres" plus clair
- Drag&drop depuis la recherche
- Reprise du compendium
- pour les plantes vénéneuses
- pour les plantes venimeuses
- ajout de sust pour les champignons et autres plantes comestibles
- Affichage de l'image du token pour les commerces non liés
- Les pièces d'or sont appelées 'Dragon'
## v10.6.0 - Les recherches de Pralinor le Goûteux
- Fenêtre _Recherches et tirages_
- ajout de la fenêtre _Recherches et tirages_ avec filtres paramétrables
- ouverture de la fenêtre: commande `/tirage` ou macro disponible dans les macros du système
- support des équipements, faune & flore (depuis les compendiums configurés par défaut)
- nombreux choix à activer
- possibilité de montrer les objets correspondant à la sélection
- possibilité de faire un tirage parmi ces objets (en prenant en compte la fréquence)
- Plantes & pèche
- séparation des ingrédients et plantes comestibles
- retour des poissons dans les compendiums
- ajout d'un lien depuis les plantes toxiques/dangereuses vers la maladie/poison correspondante
- On peut de nouveau ouvrir les conteneurs dans une fenêtre séparée
- Les jets de volontés d'éthylisme calculent correctement la difficulté liée au moral (ie: 0 au lieu de -22)
- si le journal de chronologie est supprimée, on peut en choisir un autre
- la taille du calendrier est ajustée pour éviter une présentation bancale quand le nom du mois est court

1
icons/tmr/attache.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-3" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M90.53 23c-18.345 0-36.688 7.002-50.686 21-27.996 27.996-27.994 73.38 0 101.375 21.776 21.776 54.08 26.603 80.53 14.5l53.69 53.688c-21.425 19.696-44 38.257-67.44 55.937l30.126 30.125c18.734-22.545 37.953-44.474 57.844-65.53l169.594 169.593c-51.845 40.444-120.866 53.838-192.813 42.562L173 424.906 72.47 404.47l95.405 88.405 1.97-26c86.593 36.97 177.603 34.61 241.343-11.75l63.062 21.313-21.47-63.594c44.61-63.62 46.408-153.412 9.908-238.875l26.03-1.97-88.406-95.375 20.438 100.53 21.344-1.624c11.278 71.983-2.168 141.017-42.656 192.876l-169.782-169.75c21.075-20.34 42.93-39.665 65.78-57.72l-30.123-30.124c-17.015 24.154-35.673 46.66-55.688 67.813l-53.97-53.97C167.834 98.183 163.032 65.814 141.22 44c-14-13.998-32.343-21-50.69-21zm0 27.03c11.434.002 22.872 4.34 31.595 13.064 17.447 17.447 17.446 45.742 0 63.187-17.446 17.447-45.71 17.447-63.156 0-17.447-17.444-17.448-45.74 0-63.186C67.69 54.37 79.097 50.03 90.53 50.03z" fill="#8eff09" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)" filter="url(#shadow-1)"></path></g></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

1
icons/tmr/conquete.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M27.084 18.248C-17.903 146.478 143.15 277.92 314.496 381.074c-4.645 13.767-5.585 27.628-3.394 40.635 4.44 26.355 20.974 48.997 42.86 62.425 21.884 13.428 49.776 17.57 75.645 5.765 25.87-11.804 48.69-38.923 62.737-84.654l-17.865-5.488c-13 42.318-32.806 64.094-52.63 73.14-19.825 9.047-40.69 5.998-58.116-4.693-17.425-10.69-30.75-29.095-34.205-49.6-3.455-20.507 2.232-43.318 24.677-65.218 20.743-20.24 32.068-41.615 30.434-61.24l-18.622 1.552c.74 8.89-4.35 22.76-16.684 37.486C222.057 230.8 73.838 128.622 27.084 18.248zm458.05 0C451.34 98.03 364.527 173.53 270.93 247.166c19.492 15.878 39.56 31.622 59.195 45.012 110.756-84.836 187.878-180.243 155.01-273.93zM127.58 292.146c-1.634 19.626 9.69 41 30.434 61.24 22.445 21.9 28.132 44.712 24.677 65.218-3.455 20.506-16.78 38.91-34.206 49.6-17.425 10.692-38.29 13.74-58.115 4.694-19.825-9.046-39.632-30.822-52.63-73.14l-17.865 5.488c14.046 45.73 36.867 72.85 62.736 84.654 25.87 11.805 53.763 7.663 75.648-5.765 21.885-13.428 38.42-36.07 42.86-62.426 2.19-13.005 1.25-26.863-3.393-40.628 13.986-8.42 27.905-17.022 41.648-25.803l-56.967-39.387c-6.55 5.103-13.063 10.2-19.52 15.293C150.55 316.46 145.46 302.59 146.2 293.7l-18.622-1.554zm18.1 73.614c-26.1 8.6-62.087 36.255-77.104 60.324 4.948 8.63 10.393 15.223 16.05 20.14 25.846-8.953 59.85-37.406 74.733-60.257-3.007-6.6-7.454-13.386-13.68-20.207zm220.863 0c-6.225 6.822-10.67 13.61-13.68 20.21 14.886 22.85 48.89 51.3 74.736 60.255 5.656-4.918 11.1-11.51 16.05-20.14-15.018-24.07-51.004-51.724-77.105-60.325z" fill="#b41e00" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)" filter="url(#shadow-1)"></path></g></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(255, 255, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="10" result="blur"></feGaussianBlur><feOffset dx="0" dy="10" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-3" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M400.9 104.8c-12 30-41 47.9-99.7 43.9-13.7-1.8-27.6-4.1-41.6-6.7-119.1-37.2-236.24-37.2-236.24 37.2 33.48-37.2 117.74-30.8 225.04-4 116.8 29.2 241.8 41.2 241.8-51.8-18.4 19.3-53.4 28.6-96.6 30.4 10-10.4 12.5-26.7 7.3-49zM147 187.5c-70.75-.3-123.64 16.1-123.64 66.1 33.48-37.2 117.74-34.8 225.04-8 116.8 29.2 241.8 45.2 241.8-47.8-35.4 37.2-130.2 39.6-230.6 8-37.7-11.9-78-18.2-112.6-18.3zm-23.9 69.6c-58.44-.2-99.74 15.6-99.74 70.9 33.48-37.2 122.34-44.3 225.04-18.6 121 30.2 241.8 37.2 241.8-37.2-35.4 37.2-132.1 22.6-230.6 4-48.4-7.5-96.5-19.1-136.5-19.1zm0 74.3c-58.44-.1-99.74 15.8-99.74 71 19.03-21.1 55.52-30.3 102.54-30.8-10.4 10.4-12.9 26.9-7.7 49.4 13.9-34.8 52-51.8 130.3-37.2 122.6 22.8 241.7 37.2 241.7-37.2-35.4 37.2-132.1 18.6-230.6 0-48.4-7.6-96.5-15.1-136.5-15.2z" fill="#48baff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)" filter="url(#shadow-1)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

124
icons/tmr/demi-reve.svg Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 512 512"
style="height: 512px; width: 512px;"
version="1.1"
id="svg30"
sodipodi:docname="demi-reve.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata34">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2014"
inkscape:window-height="1404"
id="namedview32"
showgrid="false"
inkscape:zoom="2.2094112"
inkscape:cx="256"
inkscape:cy="256"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg30" />
<defs
id="defs24">
<filter
id="shadow-6"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood2" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite4" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur6" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset8" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite10" />
</filter>
<filter
id="shadow-7"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood13" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite15" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur17" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset19" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite21" />
</filter>
</defs>
<g
class=""
transform="translate(-2.1627108,-0.3)"
id="g28">
<path
d="m 342.5,17.9 c -3.1,11.63 -2.2,21.56 -23.8,25.11 20.3,-2.7 22.3,9.58 24.8,21.49 -2.8,1.94 -5.5,4.11 -8.1,6.49 -21.9,20.84 -33,41.11 -49,61.61 -6.3,1.2 5.3,-53.52 31.1,-79.87 C 225.1,40.92 207.6,268.4 236.4,275 184.7,293.4 163.8,176.7 177,117.7 c -37.1,3.9 -62,39.8 -67.9,60 2.8,27.1 6.1,55.1 38.7,80.9 -32.1,3.6 -42,-27.8 -55.31,-54 -78.59,104.9 105.91,106.8 136.01,94.5 -93,70.5 -149.62,52.3 -196.77,39 -40.48,85.1 61.46,56 107.57,35.7 -18.4,30.7 -72.25,37.6 -88.92,41 61.62,51.3 174.42,-67 200.02,-106.5 2.5,65.7 -74.3,134.4 -122.8,171.7 43.6,2.2 83.2,-17.9 102.4,-55.5 0,10.1 -4.1,22.6 -9.6,35.8 15,-2.1 39.6,-6.2 48.8,-24.2 25,-54.1 37.8,-93.1 15.3,-138.2 29.9,33.5 63.6,65.3 58.4,114.5 26.9,-15.6 48.8,-33.6 24.7,-60.1 14.1,1.4 23.6,7.7 32.8,13.7 13.9,-2.8 34.4,-19.9 33.7,-33 -31.6,-29.8 -83.4,-43.7 -133.8,-55.9 72.1,-19.8 136.9,-10.1 175.6,5.6 5,-11.7 9.4,-29.6 5.9,-41.9 -16.4,-9.7 -62.7,-7.8 -83.3,-5.6 17.7,-15.7 56.8,-21.1 81.3,-21.2 -2,-67.7 -162.6,27.8 -182.2,42.8 32.7,-59.1 123.2,-112.7 178.7,-121.1 -13.2,-31.1 -37.2,-34 -64.3,-22.4 2.4,-9.5 6.7,-17.49 23.4,-15.29 -21.6,-3.51 -20.7,-13.44 -23.8,-25.07 -2.4,13.55 -4.1,17.11 -19.4,26.67 14.3,-2.17 16.4,6.69 17.4,14.69 -53.5,24.4 -117.8,102.8 -135.1,132.5 -22.1,-24 51,-121.5 107.7,-187.46 -3.1,-9.48 -21.8,-6.31 -38.2,4.81 1.1,-8.63 0.7,-22.16 17.9,-19.54 -15.3,-9.6 -17,-13.16 -19.4,-26.71 z m -166.3,0.3 c 5.4,10.73 12.7,17.53 -1,34.56 13.8,-16.07 23.7,-7.13 33.9,0.22 -4.6,-7.19 -16.3,-17.67 -0.7,-27.86 -17.8,3.09 -21.4,1.57 -32.2,-6.92 z M 47.71,26.61 C 44.63,38.24 45.58,48.17 23.95,51.66 44.97,48.92 46.34,62.21 49.01,74.47 50.44,66.04 48.73,50.5 67.15,53.31 51.88,43.72 50.17,40.16 47.71,26.61 Z m 419.39,5.5 c 1.6,10.83 1.3,13.93 -7.8,25.07 13.1,-6.8 15.9,5.39 19.1,11.38 C 477.2,58.59 475,48.2 491.5,44.92 474.3,47.79 472.4,40.07 467.1,32.11 Z M 125.3,84.28 c -0.6,18.02 -12,17.32 -22.7,17.92 7,2.4 20.3,3 15.3,18.2 10.2,-11.6 13.3,-12.5 25.2,-12.6 -9.4,-4.3 -17.8,-4.9 -17.8,-23.52 z M 71.21,153.9 c -8.61,8.5 -12.85,17.5 -33.24,9.6 19.47,8.3 13.98,20.4 10.08,32.4 5.46,-6.6 11.9,-20.9 26.35,-9.1 -8.38,-16 -8.02,-19.9 -3.19,-32.9 z M 453.9,282.7 c -2.4,8.9 -1.7,16.5 -18.2,19.2 16,-2.1 17.1,8.1 19.2,17.5 1.1,-6.5 -0.2,-18.4 13.8,-16.3 -11.7,-7.3 -13,-10 -14.8,-20.4 z M 69.25,293.8 c -12.82,12.7 -16.72,13.5 -30.41,12.7 10.55,5.7 20.39,7.1 18.72,29 2.3,-21.1 15.46,-19.4 28.05,-19.1 -7.83,-3.3 -23.4,-5.3 -16.36,-22.6 z m 394.55,50.7 c 3.1,11.6 8.9,19.7 -8,33.6 16.8,-12.9 24.6,-2.2 33.2,7.1 -3.1,-8 -12.4,-20.6 4.9,-27.4 -18,-0.5 -21.3,-2.8 -30.1,-13.3 z m -139.2,72.1 c -2.7,12.3 -4.1,25.5 -25.1,22.8 21.6,3.5 20.7,13.4 23.8,25 2.4,-13.5 4.1,-17.1 19.4,-26.6 C 324.3,440.6 326,425 324.6,416.6 Z M 83.9,438.2 c -2.83,16 -4.84,20.2 -22.86,31.5 21.68,-3.3 19.67,15.1 21.33,25 3.19,-14.5 4.84,-30.1 29.63,-26.9 -25.5,-4.2 -24.43,-15.9 -28.1,-29.6 z m 366.2,11.4 c -7.3,9.6 -10.2,19.1 -31.5,14.2 20.4,5.4 16.8,18.1 14.6,30.6 4.5,-7.3 8.8,-22.4 24.8,-12.8 -10.6,-14.6 -10.8,-18.6 -7.9,-32 z"
fill="#ffffff"
fill-opacity="1"
id="path26" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 512 512"
style="height: 512px; width: 512px;"
version="1.1"
id="svg6"
sodipodi:docname="desorientation.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1599"
inkscape:window-height="932"
id="namedview8"
showgrid="false"
inkscape:zoom="1.3119567"
inkscape:cx="256"
inkscape:cy="256"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<path
d="m 203.97,23 -18.032,4.844 11.656,43.468 c -25.837,8.076 -50.32,21.653 -71.594,40.75 l -31.47,-31.468 -13.218,13.22 31.376,31.374 c -19.467,21.125 -33.414,45.53 -41.813,71.343 l -42.313,-11.343 -4.843,18.063 42.25,11.313 c -6.057,27.3 -6.157,55.656 -0.345,83 l -41.904,11.216 4.843,18.064 41.812,-11.22 c 6.693,21.225 17.114,41.525 31.25,59.876 l -29.97,52.688 -16.81,29.593 29.56,-16.842 52.657,-29.97 c 18.41,14.216 38.784,24.69 60.094,31.407 l -11.22,41.844 18.033,4.81 11.218,-41.905 c 27.345,5.808 55.698,5.686 83,-0.375 l 11.312,42.28 18.063,-4.81 -11.344,-42.376 c 25.812,-8.4 50.217,-22.315 71.342,-41.78 l 31.375,31.373 13.22,-13.218 -31.47,-31.47 c 19.09,-21.266 32.643,-45.738 40.72,-71.563 l 43.53,11.657 4.813,-18.063 -43.625,-11.686 c 5.68,-27.044 5.576,-55.06 -0.344,-82.063 l 43.97,-11.78 -4.813,-18.063 L 440.908,197 c -6.73,-20.866 -17.08,-40.79 -31.032,-58.844 l 29.97,-52.656 16.842,-29.563 -29.593,16.844 -52.656,29.97 C 356.441,88.876 336.565,78.553 315.782,71.845 l 11.783,-44 L 309.5,23 297.72,66.97 c -27,-5.925 -55.02,-6.05 -82.064,-0.376 z m 201.56,85 -108.28,190.313 -0.75,0.437 -40.844,-40.875 -148.72,148.72 -2.186,1.25 109.125,-191.75 41.78,41.78 L 405.532,108 Z m -149.686,10.594 c 21.858,0 43.717,5.166 63.594,15.47 l -116.625,66.342 -2.22,1.28 -1.28,2.22 -66.25,116.406 c -26.942,-52.04 -18.616,-117.603 25.03,-161.25 26.99,-26.988 62.38,-40.468 97.75,-40.468 z m 122.72,74.594 c 26.994,52.054 18.67,117.672 -25.002,161.343 -43.66,43.662 -109.263,52.005 -161.312,25.033 l 116.438,-66.282 2.25,-1.25 1.25,-2.25 66.375,-116.592 z"
fill="#d0021b"
fill-opacity="1"
id="path2"
style="fill:#401060;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

190
icons/tmr/fermeture.svg Normal file
View File

@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 512 512"
style="height: 512px; width: 512px;"
version="1.1"
id="svg52"
sodipodi:docname="fermeture.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata56">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="1083"
id="namedview54"
showgrid="false"
inkscape:zoom="1.5990661"
inkscape:cx="256"
inkscape:cy="256"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg52" />
<defs
id="defs46">
<filter
id="shadow-1"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(248, 231, 28, 1)"
result="flood"
id="feFlood2" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite4" />
<feGaussianBlur
in="composite"
stdDeviation="40"
result="blur"
id="feGaussianBlur6" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset8" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite10" />
</filter>
<filter
id="shadow-3"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(248, 231, 28, 1)"
result="flood"
id="feFlood13" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite15" />
<feGaussianBlur
in="composite"
stdDeviation="40"
result="blur"
id="feGaussianBlur17" />
<feOffset
dx="0"
dy="0"
result="offset"
id="feOffset19" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite21" />
</filter>
<filter
id="shadow-6"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood24" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite26" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur28" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset30" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite32" />
</filter>
<filter
id="shadow-7"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood35" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite37" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur39" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset41" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite43" />
</filter>
</defs>
<g
class=""
id="g50"
transform="matrix(1.1287777,0,0,1.1287777,-32.967091,-40.026839)">
<path
d="M 72.877,31.904 C 71.887,31.89 70.919,31.91 69.889,32.002 43.67,35.408 22.545,61.005 18,93.775 v 26.15 c 2.296,16.266 8.804,30.665 17.848,41.565 -6.58,1.237 -12.504,3.53 -17.848,6.717 v 23.813 c 22.983,0.386 43.265,14.03 57.31,34.318 C 89.56,246.92 98,274.598 98,305 98,335.402 89.56,363.08 75.31,383.662 61.266,403.95 40.984,417.592 18,417.98 v 8.577 L 23.03,494 H 30.7 L 138.904,332.176 140,304 c 0.732,-41.132 16.536,-59.598 32,-48 4.26,3.195 8.3,6.024 12.135,8.533 l 23.574,-35.258 c -21.607,-17.4 -59.103,-43.23 -90.68,-68.658 10.89,-13.647 17.894,-32.612 17.894,-53.627 C 134.924,65.494 108.478,32 76,32 74.88,31.964 73.867,31.918 72.877,31.904 Z m 366.246,0 c -0.99,0.014 -2.002,0.06 -3.123,0.096 -32.478,0 -58.924,33.494 -58.924,74.99 0,21.015 7.005,39.98 17.895,53.627 -31.577,25.43 -69.073,51.26 -90.68,68.658 l 23.577,35.258 A 232.03,232.03 0 0 0 340,256 c 15.464,-11.598 31.268,6.868 32,48 l 1.096,28.174 L 481.3,494 h 7.67 L 494,426.557 v -8.578 C 471.017,417.591 450.735,403.949 436.69,383.661 422.44,363.08 414,335.402 414,305 c 0,-30.402 8.44,-58.08 22.69,-78.662 14.045,-20.288 34.327,-33.932 57.31,-34.318 v -23.813 c -5.344,-3.187 -11.27,-5.48 -17.848,-6.717 9.044,-10.9 15.552,-25.3 17.848,-41.566 V 93.774 C 489.454,61.004 468.33,35.408 442.11,32.002 a 28.52,28.52 0 0 0 -2.987,-0.098 z m -290.365,14.854 40.068,110.215 47.34,-31.653 z m 214.484,0 -87.408,78.562 47.34,31.653 z M 230.25,150.93 213.625,162.047 435.588,494 h 24.057 z m 51.5,0 -14.922,22.316 12.03,17.99 19.517,-29.19 z M 18,210.018 v 189.964 c 15.993,-0.38 30.943,-9.855 42.512,-26.566 C 72.322,356.356 80,332.036 80,305 80,277.965 72.322,253.643 60.512,236.584 48.942,219.874 33.992,210.398 18,210.018 Z m 476,0 c -15.993,0.38 -30.943,9.855 -42.512,26.566 C 439.678,253.644 432,277.964 432,305 c 0,27.035 7.678,51.357 19.488,68.416 11.57,16.71 26.52,26.186 42.512,26.566 z M 233.145,223.62 52.355,494 H 76.412 L 245.174,241.61 Z M 134.748,439.14 98.066,494 h 34.55 z m 242.504,0 2.13,54.86 h 34.552 z"
fill="#003fb2"
fill-opacity="1"
transform="matrix(0.9,0,0,0.9,25.6,25.6)"
filter="url(#shadow-1)"
id="path48" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

1
icons/tmr/maitrisee.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-3" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><g transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"><path d="M256 20C198.562 20 152 66.562 152 124C152 181.438 198.562 228 256 228C313.438 228 360 181.438 360 124C360 66.562 313.438 20 256 20Z" class="" fill="#087505" fill-opacity="0"></path><path d="M16 256L16 496L64 496C128 336 384 336 448 496L496 496L496 256L448 256L448 320L388 320L388 256L340 256L340 320L280 320L280 256L232 256L232 320L172 320L172 256L124 256L124 320L64 320L64 256L16 256Z" class="selected" fill="#087505" fill-opacity="1" filter="url(#shadow-3)"></path></g></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

1
icons/tmr/pelerinage.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M259.844 73.406l1.625 214.47-18.69.155-1.655-214.342C206.358 75.24 172.012 82.588 141 95.78c36.116 61.6 59.493 126.474 75.813 196.5l-18.22 4.25C182.46 227.29 159.504 163.924 124 103.78c-37.016 19.19-67.986 47.49-87.156 84.97 57.884 24.66 105.126 67.86 140.937 118.688l-15.28 10.75c-34.284-48.66-79.092-89.328-133.28-112.344-8.57 22.082-13.345 46.943-13.345 74.594 95.028 17.855 145.516 75.937 151.406 92 3.752 10.228-27.905 21.074-27.905 38.156 0 12.34 25.52 20.537 59.668 24.67-3.846-4.94-7.694-10.374-11.59-16.31l15.625-10.255c9.802 14.937 18.996 25.865 27.354 32.73 8.358 6.864 15.493 9.632 22.423 9.68 13.862.094 31.592-12.316 53.723-42.776l15.12 10.984c-4.31 5.93-8.553 11.385-12.76 16.35 36.362-4.006 64.125-12.375 64.125-25.074 0-17.92-35.487-28.412-33.72-39.97 2.31-15.09 55.528-74.91 156.626-90.187 0-28.807-5.284-54.622-14.72-77.437-57.322 22.41-104.478 64.46-140.22 115.188l-15.28-10.75c37.145-52.72 86.607-97.216 147.47-121.344-20.457-37.87-53.207-66.233-92.064-85.03-36.426 60.947-59.84 125.186-76.22 195.467l-18.186-4.25c16.523-70.893 40.278-136.5 77.156-198.78-32.42-12.835-68.166-19.55-104.062-20.094z" fill="#b41e00" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)" filter="url(#shadow-1)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

1
icons/tmr/periple.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-3" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M149.9 27.2L34.25 56.74v76.76L157.8 93.85l46.7-44.67-54.6-21.98zm132.8 57c-7.4.18-10.1 1.88.9 7.13C346.9 121.6 441.7 206.8 391.3 216.9 232.2 249 130.4 292.3 48.51 390.8 25.42 418.6 18 494.8 18 494.8h432.6s-139-21.1-147.8-75.7c-14.9-92.2 194.5-102.7 196.5-199.9.9-43.2-88.3-124.99-184.4-132.52-5.6-.44-22.7-2.71-32.2-2.48zm-163.5 40.9l-32.69 10.5v122.2l35.99-10-3.3-122.7z" fill="#b41e00" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)" filter="url(#shadow-1)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
icons/tmr/present.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><path d="M0 0h512v512H0z" fill="#4a4a4a" fill-opacity="0.5"></path><g class="" transform="translate(1,-1)" style=""><path d="M149.518 78.38c-6.55.117-12.45 1.736-17.35 4.91-7.465 4.84-11.765 12.904-13.063 21.34-2.595 16.874 4.747 36.355 19.862 52.31C154.08 172.893 177.643 185 208 185h2.438l-9.118-18.234c-22.194-1.554-38.46-10.777-49.287-22.205-11.885-12.545-16.543-28.064-15.138-37.19.702-4.564 2.402-7.25 5.062-8.974 2.66-1.724 7.113-2.875 14.756-1.326 13.078 2.65 34.233 13.948 62.205 39.284L220.27 135h23.408c-35.31-34.8-62.215-51.278-83.39-55.57-2.715-.55-5.363-.887-7.925-1.006-.96-.045-1.91-.06-2.845-.043zm212.964 0c-.935-.016-1.885 0-2.845.044-2.562.12-5.21.455-7.924 1.006-21.176 4.292-48.082 20.77-83.39 55.57h23.406l1.352 1.354c27.972-25.336 49.127-36.633 62.205-39.284 7.643-1.55 12.096-.398 14.756 1.326 2.66 1.725 4.36 4.41 5.062 8.973 1.405 9.126-3.253 24.645-15.138 37.19-10.827 11.43-27.093 20.652-49.287 22.206L301.562 185H304c30.357 0 53.92-12.106 69.033-28.06 15.115-15.955 22.457-35.436 19.862-52.31-1.298-8.436-5.598-16.5-13.063-21.34-4.9-3.174-10.8-4.793-17.35-4.91zM227.73 153l-8.78 8.777L229.564 183h52.875l10.61-21.223-8.777-8.777h-56.54zM73 201v46h142v-46H73zm160 0v270h46V201h-46zm64 0v46h142v-46H297zm-192 64v206h110V265H105zm192 0v206h110V265H297z" fill="#ffffff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

1
icons/tmr/rencontre.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M324.97 17.54c.03.034.057.07.087.106l-34.924 32.428 36.904-3.752-15.396 30.12 38.048-16.075c26.147 69.965.623 154.277-52.555 166.262-6.554-25.37-34.13-37.945-36.055-57.382.303.093.604.187.912.27 4.833 1.295 9.736 1.183 14.274-.07l25.138 22.89 20.653-16.377c-7.363 2.836-28.588-1.402-33.25-13.923 3.154-3.24 5.55-7.284 6.793-11.922.485-1.813.757-3.635.86-5.445l11.524 22.777 5.22-16.94c7.625 5.575 12.474 13.605 11.49 21.136l16.673-29.4-72.14-29.56-58.057-48.03 17.1 31.25-48.206-19.753 35.14 31.237c-40.602 28.158-22.085 85.04-1.796 119.29-57.5-9.685-103.128-77.435-95.763-145.03l49.21-21.366-31.08-5.14 29.207-33.417-32.015 11.54c.037-.067.07-.135.107-.202-168.36 66.33-116.413 367-63.728 417.99-.19-1.317-.364-2.58-.54-3.855-14.922-56.244-20.375-125.624-17.5-190.53 3.02-68.237 14.834-131.16 36.794-169.522l16.22 9.283c-18.894 33.008-31.4 94.563-34.345 161.064-1.942 43.86.106 90.022 6.275 132.082 6.124 1.892 15.046 9.615 27.295 23.24-4.818-13.35-6.78-26.5-6.482-38.28 20.286 41.665 67.34 69.234 104.633 62.308 22.444-4.17 41.803-12.73 57.81-24.475l7.31 15.418c-20.068 5.036-22.807 32.635-14.737 55.112 1.748-19.882 11.36-29.794 21.73-32.303-6.598 15.867-4.698 30.623-3.117 44.158 10.15-12.147 21.47-23.793 23.628-39.354 8.738 7.332 12.317 21.49 1.194 39.057 26.32-15.473 31.565-41.994 7.978-57.685l-32.07-34.297c5.918-5.55 11.24-11.6 15.947-18.066l39.28 15.776c-3.942 13.69 5.833 31.512 19.77 43.31-8.055-17.288-4.826-30.08 2.562-37.103 1.63 17.39 10.64 29.193 18.733 40.064 2.73-15.665 6.79-31.493-.213-45.987 11.016 1.56 21.2 11.568 20.338 31.877 14.362-25.313 6.11-49.702-20.742-51.52l-71.135-9.892c12.757-22.982 18.676-49.823 17.015-77.475 14.188-34.708 50.058-11.816 54.523 49.16C394.924 262.27 434.58 304 426.324 367.13c11.808-23.38 21.835-35.013 29.862-36.247-10.772-91.925-40.458-191.57-77.637-250.748l15.823-9.942c50.328 80.106 85.112 220.65 84.88 331.547 42.403-115.912-2.347-356.61-154.282-384.2zm-29.458 476.913l-.026.016-.015.05c.015-.02.027-.044.042-.067zm26.543-318.492h.01v-.007l-.01.008zm-53.348-41.716c.866-.027 1.757.073 2.652.313 4.774 1.28 7.467 5.945 6.187 10.72-1.28 4.776-5.943 7.47-10.72 6.19-4.775-1.28-7.468-5.943-6.188-10.72.96-3.584 3.823-5.993 7.21-6.435.282-.036.568-.06.857-.068zM204.904 297.13c11.878-.2 22.637 6.756 26.172 22.487-.008 35.88-9.557 68.823-42.137 77.412-27.624 7.283-69.725-11.398-84.12-53.663 12.28-21.078 37.362-21.986 62.838 22.592-12.583-41.596 14.386-68.444 37.246-68.83z" fill="#003fb2" fill-opacity="1" filter="url(#shadow-1)" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

1
icons/tmr/reserve.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M149.688 85.625c-1.234.005-2.465.033-3.72.063-33.913.806-75.48 10.704-127.25 33.718V362.78c60.77-28.82 106.718-37.067 144.22-33.092 33.502 3.55 59.685 16.66 83.562 31.187v-242.97c-23.217-17.744-50.195-30.04-85.97-32-3.52-.192-7.142-.296-10.843-.28zm211.968 0c-3.7-.016-7.322.088-10.844.28-35.773 1.96-62.75 14.256-85.968 32v242.97c23.876-14.527 50.06-27.637 83.562-31.188 37.502-3.974 83.45 4.272 144.22 33.094V119.407c-51.77-23.014-93.337-32.912-127.25-33.72-1.255-.028-2.486-.056-3.72-.06zm5.72 261.78c-1.038-.002-2.074.017-3.095.033-4.808.075-9.43.37-13.905.843-33.932 3.597-59.603 17.976-85.53 34.44v.28c-6.554-1.99-13.02-2.37-19.408-.97-25.566-16.177-51.003-30.202-84.468-33.75-5.595-.592-11.44-.883-17.564-.842-32.04.213-71.833 9.778-124.687 35.937v42.53c60.77-28.823 106.714-37.067 144.218-33.092 18.545 1.965 34.837 6.845 49.75 13.28-4.682 6.064-9.308 13.268-13.875 21.688h117.156c-5.93-8.22-11.798-15.414-17.626-21.56 14.996-6.503 31.39-11.43 50.062-13.408 37.503-3.974 83.448 4.27 144.22 33.094v-42.53c-53.16-26.31-93.115-35.863-125.25-35.97z" fill="#087505" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)" filter="url(#shadow-1)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

132
icons/tmr/sort-reserve.svg Normal file
View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 512 512"
style="height: 512px; width: 512px;"
version="1.1"
id="svg32"
sodipodi:docname="sort-reserve.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata36">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2318"
inkscape:window-height="1268"
id="namedview34"
showgrid="false"
inkscape:zoom="1.9888504"
inkscape:cx="256"
inkscape:cy="256"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg32" />
<defs
id="defs24">
<filter
id="shadow-6"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood2" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite4" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur6" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset8" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite10" />
</filter>
<filter
id="shadow-7"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood13" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite15" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur17" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset19" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite21" />
</filter>
</defs>
<path
d="M 0,0 H 512 V 512 H 0 Z"
fill="#4a4a4a"
fill-opacity="0.5"
id="path26"
style="stroke-width:1;fill:#4a4a4a;fill-opacity:0.01" />
<g
class=""
transform="translate(1,-1)"
id="g30"
style="fill:#f4e3d7">
<path
d="m 373.563,18.406 c -15.616,-0.167 -27.91,4.622 -32.563,14.75 -22.778,49.605 -48.743,87.14 -79.094,117.28 3.047,1.015 6.046,2.29 8.938,3.783 12.987,6.708 25.268,17.78 35.312,30.843 10.044,13.062 17.85,28.114 20.78,43.5 0.746,3.908 1.16,7.885 1.158,11.843 38.97,-24.36 85.058,-41.223 140.875,-51.312 14.91,-2.697 23.652,-28.632 21.405,-58.656 l -35.156,-1 30.56,-24.813 C 481.63,90.117 474.765,75.87 464.623,63.904 449.095,45.59 428.193,32.528 407.903,25.218 l -25.963,15.594 2.812,-21.5 c -3.875,-0.55 -7.61,-0.87 -11.188,-0.907 z M 246.938,166.562 c -1.063,0.052 -2.06,0.226 -3,0.47 -11.976,10.254 -24.61,19.597 -37.938,28.28 0.842,0.33 1.67,0.667 2.5,1.032 14.123,6.192 27.438,17.145 38.47,30.625 13.356,16.322 23.62,36.94 25.624,57.75 10.334,-10.367 21.24,-19.943 32.844,-28.72 4.096,-6.555 4.93,-14.468 3.125,-23.938 -2.184,-11.46 -8.642,-24.43 -17.25,-35.625 -8.61,-11.194 -19.38,-20.622 -29.063,-25.625 -6.052,-3.126 -11.154,-4.45 -15.313,-4.25 z m -61.907,43.282 c -1.385,0.053 -2.69,0.27 -3.968,0.562 -37,20.762 -79.088,37.985 -127.312,56 0.574,0.042 1.14,0.093 1.72,0.156 10.627,1.156 21.076,5.008 31.155,10.875 L 124.313,261 108.5,293.72 c 5.995,5.432 11.803,11.477 17.344,18 20.76,24.434 37.964,55.865 47.094,88.092 0.002,0.01 -0.003,0.022 0,0.032 2.98,10.508 5.11,20.916 6.312,31 20.99,-48.438 44.38,-89.26 72.344,-123 7.3,-21.48 -2.186,-48.408 -19.063,-69.03 -9.44,-11.538 -20.976,-20.718 -31.53,-25.345 -5.936,-2.604 -11.27,-3.808 -15.97,-3.626 z m 141.626,54.844 c -7.31,5.05 -14.462,10.51 -21.437,16.312 39.16,9.26 60.953,35.722 80.655,62.156 10.464,14.04 20.598,28.11 33.125,40.688 24.19,9.147 43.17,6.38 63.906,-14.938 -92.165,-27.78 -96.11,-92.61 -156.25,-104.22 z M 48.594,284.906 c -10.873,0.225 -18.26,5.755 -23.344,16.594 -5.81,12.387 -7.114,32.47 0.438,57.063 5.75,18.73 16.52,37.718 28.75,51.625 12.23,13.906 25.9,22.076 35.374,22.406 h 0.032 c 3.717,0.13 6.553,-0.682 8.812,-2.75 l -0.187,-0.188 2.093,-2.094 c 0.793,-1.168 1.52,-2.548 2.187,-4.187 2.81,-6.9 3.28,-18.552 -1.844,-33 -6.885,-19.417 -19.12,-31.932 -33.375,-34.78 l -22.968,-4.564 19.813,-12.5 38.47,-24.186 c -16.65,-16.822 -34.55,-27.607 -49.376,-29.22 -1.7,-0.184 -3.323,-0.25 -4.876,-0.218 z m 236.25,5.406 -24.53,25.375 c 100.442,17.878 55.45,141.005 159.31,176.188 l -24.78,-57.28 c 32.766,16.15 67.39,22.623 97.72,12.03 -135.77,-41.948 -96.32,-126.983 -207.72,-156.313 z m -169.47,38.22 -25.968,16.343 c 13.18,8.5 23.21,22.565 29.125,39.25 2.57,7.244 4.133,14.205 4.75,20.78 l 23.44,-23.374 c -8.08,-19.19 -19.035,-37.566 -31.345,-53 z m 38.376,72.374 -42.063,42 -0.156,-0.156 c -4.255,3.942 -9.456,6.765 -15.186,7.938 23.268,14.873 44.644,19.346 56.812,9.562 4.26,-3.426 7.043,-8.36 8.47,-14.406 -0.41,-12.684 -2.602,-26.615 -6.657,-40.906 -0.382,-1.346 -0.806,-2.686 -1.22,-4.032 z"
fill="#ffffff"
fill-opacity="1"
id="path28"
style="fill:#f4e3d7" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

126
icons/tmr/trounoir.svg Normal file
View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 512 512"
style="height: 512px; width: 512px;"
version="1.1"
id="svg32"
sodipodi:docname="trounoir.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata36">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1553"
inkscape:window-height="1145"
id="namedview34"
showgrid="false"
inkscape:zoom="1.4374483"
inkscape:cx="256"
inkscape:cy="256"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg32" />
<defs
id="defs24">
<filter
id="shadow-6"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood2" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite4" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur6" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset8" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite10" />
</filter>
<filter
id="shadow-7"
height="300%"
width="300%"
x="-100%"
y="-100%">
<feFlood
flood-color="rgba(72, 186, 255, 1)"
result="flood"
id="feFlood13" />
<feComposite
in="flood"
in2="SourceGraphic"
operator="atop"
result="composite"
id="feComposite15" />
<feGaussianBlur
in="composite"
stdDeviation="8"
result="blur"
id="feGaussianBlur17" />
<feOffset
dx="5"
dy="15"
result="offset"
id="feOffset19" />
<feComposite
in="SourceGraphic"
in2="offset"
operator="over"
id="feComposite21" />
</filter>
</defs>
<path
d="M0 0h512v512H0z"
fill="#4a4a4a"
fill-opacity="0.5"
id="path26"
style="fill:#333333;fill-opacity:0.69999999" />
<path
d="m 329.547,18.115 c -30.61,99.22 -47.583,151.205 -86.88,156.778 -18.626,2.642 -42.988,-19.225 -70.16,-50.29 15.47,30.702 21.275,55.265 10.845,61.348 -15.787,9.21 -51.095,-6.94 -106.815,-30.837 31.653,20.827 83.667,50.18 77.358,58.63 -8.074,10.81 -77.23,-4.706 -130.866,-13.163 89.224,25.398 137.61,55.572 137.61,82.387 0,18.423 -48.845,62.18 -71.888,83.928 19.558,-11.397 64.736,-24.44 76.777,-2.99 13.335,23.758 -6.577,61.6 -28.5,128.027 31.39,-46.19 73.363,-108.122 90.734,-106.49 12.248,1.15 -4.805,60.692 -10.47,98.71 21.547,-80.082 46.534,-132.5 90.153,-131.015 29.665,1.01 58.022,30.762 88.99,52.047 -16.188,-19.81 -45.975,-47.99 -39.55,-53.243 8.9,-7.276 56.48,12.547 94.224,25.726 -24.982,-17.962 -68.644,-43.88 -61.653,-50.852 10.417,-10.387 72.436,1.332 117.49,7.178 C 419.2,303.266 370.1,289.807 359.616,255.461 c -5.283,-17.31 10.853,-40.3 40.89,-68.038 -31.377,17.197 -54.588,28.694 -63.737,12.392 -11.576,-20.622 11.374,-65.883 35.238,-126.06 -21.135,32.47 -48.532,83.487 -55.254,77.174 -8.972,-8.425 5.598,-77.597 12.795,-132.813 h -0.003 z M 21.45,18.27 V 41.63 C 69.97,69.067 116.703,104.02 162.783,144.416 129.015,102.731 95.443,60.626 68.758,18.27 Z m 175.79,0 c 18.465,37.356 34.503,76.96 48.475,117.97 -5.007,-39.79 -9.898,-79.367 -12.264,-117.97 h -36.21 z m 160.022,0 c -7.18,26.672 -15.416,53.437 -25.116,80.593 15.405,-27.34 30.698,-54.514 46.723,-80.593 H 357.26 Z m 105.123,0 c -27.895,50.718 -63.73,99.873 -105.707,147.755 46.514,-37.68 92.9,-75.343 140.164,-103.37 V 18.27 Z m 34.455,160.02 c -36.077,17.98 -74.843,34.036 -115.635,47.89 38.908,-6.17 77.882,-12.105 115.635,-15.77 z m -206.266,42.868 c 9.35,0 16.93,7.58 16.93,16.932 0,9.35 -7.58,16.93 -16.93,16.93 -9.35,0 -16.93,-7.58 -16.93,-16.93 0,-9.35 7.58,-16.932 16.93,-16.932 z m -52.06,1.598 c 15.508,0 28.082,12.57 28.082,28.08 0,9.718 -4.938,18.28 -12.44,23.322 3.614,3.843 5.842,9.002 5.842,14.694 0,11.86 -9.613,21.474 -21.473,21.474 -11.86,0 -21.474,-9.615 -21.474,-21.474 0,-5.687 2.228,-10.842 5.837,-14.684 -7.51,-5.04 -12.453,-13.608 -12.453,-23.332 0,-15.51 12.57,-28.08 28.08,-28.08 z M 21.45,234.078 v 38.547 c 31.87,-4.584 64.46,-5.693 97.532,-4.09 -33.727,-10.19 -67.407,-20.35 -97.53,-34.457 z m 265.82,28.377 c 9.35,0 16.93,7.58 16.93,16.932 0,9.35 -7.58,16.93 -16.93,16.93 -9.35,0 -16.932,-7.58 -16.932,-16.93 0,-9.35 7.58,-16.932 16.932,-16.932 z M 129.494,294.05 c -36.153,11.99 -72.24,20.293 -108.043,24.313 v 51.393 c 30.994,-28.64 69.426,-52.264 108.044,-75.703 v -0.002 z m 5.84,88.645 c -37.923,30.72 -75.607,61.482 -113.885,87.02 v 23.943 h 29.784 c 24.02,-37.76 52.365,-74.765 84.1,-110.963 z m 202.07,11.096 c 26.807,33.093 53.226,66.414 76.508,99.87 h 59.568 c -46.586,-27.078 -91.877,-61.12 -136.074,-99.87 z m -52.562,9.93 c -3.175,30.26 -6.39,60.5 -10.512,89.94 h 20.44 c -4.51,-29.083 -7.904,-59.17 -9.926,-89.94 z m 26.865,13.432 c 11.346,25.473 22.374,51.18 32.705,76.508 h 23.36 c -19.395,-23.9 -38.105,-49.64 -56.065,-76.508 z"
fill="#602000"
fill-opacity="1"
id="path28"
style="fill:#401060;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

1
icons/tmr/urgence.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><defs><filter id="shadow-1" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(248, 231, 28, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="40" result="blur"></feGaussianBlur><feOffset dx="0" dy="0" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-6" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter><filter id="shadow-7" height="300%" width="300%" x="-100%" y="-100%"><feFlood flood-color="rgba(72, 186, 255, 1)" result="flood"></feFlood><feComposite in="flood" in2="SourceGraphic" operator="atop" result="composite"></feComposite><feGaussianBlur in="composite" stdDeviation="8" result="blur"></feGaussianBlur><feOffset dx="5" dy="15" result="offset"></feOffset><feComposite in="SourceGraphic" in2="offset" operator="over"></feComposite></filter></defs><g class="" transform="translate(0,0)" style=""><path d="M103.432 17.844c-1.118.005-2.234.032-3.348.08-2.547.11-5.083.334-7.604.678-20.167 2.747-39.158 13.667-52.324 33.67-24.613 37.4 2.194 98.025 56.625 98.025.536 0 1.058-.012 1.583-.022v.704h60.565c-10.758 31.994-30.298 66.596-52.448 101.43-2.162 3.4-4.254 6.878-6.29 10.406l34.878 35.733-56.263 9.423c-32.728 85.966-27.42 182.074 48.277 182.074v-.002l9.31.066c23.83-.57 46.732-4.298 61.325-12.887 4.174-2.458 7.63-5.237 10.467-8.42h-32.446c-20.33 5.95-40.8-6.94-47.396-25.922-8.956-25.77 7.52-52.36 31.867-60.452 5.803-1.93 11.723-2.834 17.565-2.834v-.406h178.33c-.57-44.403 16.35-90.125 49.184-126 23.955-26.176 42.03-60.624 51.3-94.846l-41.225-24.932 38.272-6.906-43.37-25.807h-.005l.002-.002.002.002 52.127-8.85c-5.232-39.134-28.84-68.113-77.37-68.113C341.14 32.26 222.11 35.29 149.34 28.496c-14.888-6.763-30.547-10.723-45.908-10.652zm.464 18.703c13.137.043 27.407 3.804 41.247 10.63l.033-.07c4.667 4.735 8.542 9.737 11.68 14.985H82.92l10.574 14.78c10.608 14.83 19.803 31.99 21.09 42.024.643 5.017-.11 7.167-1.814 8.836-1.705 1.67-6.228 3.875-15.99 3.875-40.587 0-56.878-44.952-41.012-69.06C66.238 46.64 79.582 39.22 95.002 37.12c2.89-.395 5.863-.583 8.894-.573zM118.5 80.78h46.28c4.275 15.734 3.656 33.07-.544 51.51H131.52c1.9-5.027 2.268-10.574 1.6-15.77-1.527-11.913-7.405-24.065-14.62-35.74zm101.553 317.095c6.44 6.84 11.192 15.31 13.37 24.914 3.797 16.736 3.092 31.208-1.767 43.204-4.526 11.175-12.576 19.79-22.29 26h237.19c14.448 0 24.887-5.678 32.2-14.318 7.312-8.64 11.2-20.514 10.705-32.352-.186-4.473-.978-8.913-2.407-13.18l-69.91-8.205 42.017-20.528c-8.32-3.442-18.64-5.537-31.375-5.537H220.053zm-42.668.506c-1.152-.003-2.306.048-3.457.153-2.633.242-5.256.775-7.824 1.63-15.11 5.02-25.338 21.54-20.11 36.583 3.673 10.57 15.347 17.71 25.654 13.938l1.555-.57h43.354c.946-6.36.754-13.882-1.358-23.192-3.71-16.358-20.543-28.483-37.815-28.54z" fill="#b41e00" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)" filter="url(#shadow-1)"></path></g></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

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

View File

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

View File

@ -7,33 +7,32 @@ import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js"; import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js"; import { RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.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 { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js"; import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js"; import { MAINS_DIRECTRICES } from "./actor.js";
import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js"; import { RdDBaseActorReveSheet } from "./actor/base-actor-reve-sheet.js";
import { RdDItem } from "./item.js"; import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.js"; import { RdDItemBlessure } from "./item/blessure.js";
import { RdDEmpoignade } from "./rdd-empoignade.js"; import { RdDEmpoignade } from "./rdd-empoignade.js";
import { RdDBaseActorSangSheet } from "./actor/base-actor-sang-sheet.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
* Extend the basic ActorSheet with some very simple modifications * Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet} * @extends {ActorSheet}
*/ */
export class RdDActorSheet extends RdDBaseActorSheet { export class RdDActorSheet extends RdDBaseActorSangSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
RdDUtility.initAfficheContenu(); return mergeObject(RdDBaseActorReveSheet.defaultOptions, {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html", template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550, width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false, showCompNiveauBase: false,
vueDetaillee: false vueArchetype: false,
}); });
} }
@ -55,7 +54,8 @@ export class RdDActorSheet extends RdDBaseActorSheet {
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr, surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures), resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute), caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.getMessageSurEncombrement(), surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "",
malusArmure: this.actor.getMalusArmure()
}) })
this.timerRecherche = undefined; this.timerRecherche = undefined;
@ -79,9 +79,12 @@ export class RdDActorSheet extends RdDBaseActorSheet {
}); });
// toujours avoir une liste d'armes (pour mettre esquive et corps à corps) // toujours avoir une liste d'armes (pour mettre esquive et corps à corps)
formData.combat = duplicate(formData.armes ?? []); const actor = this.actor;
formData.combat = duplicate(formData.armes);
RdDItemArme.computeNiveauArmes(formData.combat, formData.competences); RdDItemArme.computeNiveauArmes(formData.combat, formData.competences);
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac); formData.combat.push(RdDItemArme.mainsNues(actor));
formData.combat.push(RdDItemArme.empoignade(actor));
formData.esquives = this.actor.getCompetences("Esquive"); formData.esquives = this.actor.getCompetences("Esquive");
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac); formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
formData.empoignades = this.actor.getEmpoignades(); formData.empoignades = this.actor.getEmpoignades();
@ -110,62 +113,59 @@ export class RdDActorSheet extends RdDBaseActorSheet {
return formData; return formData;
} }
/* -------------------------------------------- */ /** @override */ /* -------------------------------------------- */
/** @override */
activateListeners(html) { activateListeners(html) {
super.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"));
this.html.find('.subacteur-open').click(async event => {
const subActorId = RdDSheetUtility.getEventItemData(event, 'subactor-id');
this.openSubActeur(subActorId);
})
this.html.find('.show-hide-competences').click(async event => {
this.options.showCompNiveauBase = !this.options.showCompNiveauBase;
this.render(true);
});
this.html.find('.visu-tmr').click(async event => this.actor.displayTMR("visu"))
// Everything below here is only needed if the sheet is editable // Everything below here is only needed if the sheet is editable
if (!this.options.editable) return; if (!this.options.editable) return;
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);
}
});
this.html.find('.experiencelog-delete').click(async event => {
const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
const key = Number(li.data("key") ?? -1);
await this.actor.deleteExperienceLog(key, 1);
});
this.html.find('.experiencelog-delete-previous').click(async event => {
const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
const key = Number(li.data("key") ?? -1);
await this.actor.deleteExperienceLog(0, key + 1);
});
this.html.find('.encaisser-direct').click(async event => {
this.actor.encaisser();
})
this.html.find('.sheet-possession-attack').click(async event => { this.html.find('.sheet-possession-attack').click(async event => {
const poss = RdDSheetUtility.getItem(event, this.actor) const poss = RdDSheetUtility.getItem(event, this.actor)
this.actor.conjurerPossession(poss) this.actor.conjurerPossession(poss)
}) })
this.html.find('.remise-a-neuf').click(async event => {
if (game.user.isGM) { this.html.find('.subacteur-coeur-toggle a').click(async event => {
this.actor.remiseANeuf(); const subActorIdactorId = RdDSheetUtility.getEventItemData(event, 'subactor-id')
} const coeurNombre = $(event.currentTarget).data('numero-coeur')
RdDCoeur.toggleSubActeurCoeur(this.actor.id, subActorIdactorId, coeurNombre)
})
this.html.find('.subacteur-tendre-moment').click(async event => {
const subActorId = RdDSheetUtility.getEventItemData(event, 'subactor-id')
RdDCoeur.startSubActeurTendreMoment(this.actor.id, subActorId)
})
this.html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const subActorId = li.data("subactor-id");
this.deleteSubActeur(subActorId, li);
})
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('.creer-tache').click(async event => { this.html.find("input.derivee-value[name='system.compteurs.experience.value']").change(async event => {
this.createEmptyTache(); this.actor.updateCompteurValue("experience", parseInt(event.target.value));
}); });
this.html.find('.creer-tache').click(async event => this.createEmptyTache());
this.html.find('.creer-une-oeuvre').click(async event => this.selectTypeOeuvreToCreate());
this.html.find('.creer-tache-blessure-legere').click(async event => RdDItemBlessure.createTacheSoinBlessure(this.actor, 2)); 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-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-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();
});
this.html.find('.blessure-premierssoins-done').change(async event => { this.html.find('.blessure-premierssoins-done').change(async event => {
const blessure = this.getBlessure(event); const blessure = this.getBlessure(event);
@ -185,87 +185,44 @@ export class RdDActorSheet extends RdDBaseActorSheet {
}); });
// Equip Inventory Item // Equip Inventory Item
this.html.find('.item-equip').click(async event => { this.html.find('.item-equip').click(async event => this.actor.equiperObjet(RdDSheetUtility.getItemId(event)))
this.actor.equiperObjet(RdDSheetUtility.getItemId(event)); this.html.find('.chance-actuelle').click(async event => this.actor.rollCarac('chance-actuelle'))
});
// Roll Carac this.html.find('.chance-appel').click(async event => this.actor.rollAppelChance())
this.html.find('.carac-label a').click(async event => {
let caracName = event.currentTarget.attributes.name.value;
this.actor.rollCarac(caracName.toLowerCase());
});
this.html.find('.chance-actuelle').click(async event => { this.html.find('[name="jet-astrologie"]').click(async event => this.actor.astrologieNombresAstraux())
this.actor.rollCarac('chance-actuelle'); this.html.find('.tache-label a').click(async event => this.actor.rollTache(RdDSheetUtility.getItemId(event)))
}); this.html.find('.meditation-label a').click(async event => this.actor.rollMeditation(RdDSheetUtility.getItemId(event)))
this.html.find('.chance-appel').click(async event => { this.html.find('.chant-label a').click(async event => this.actor.rollChant(RdDSheetUtility.getItemId(event)))
this.actor.rollAppelChance(); this.html.find('.danse-label a').click(async event => this.actor.rollDanse(RdDSheetUtility.getItemId(event)))
}); this.html.find('.musique-label a').click(async event => this.actor.rollMusique(RdDSheetUtility.getItemId(event)))
this.html.find('.oeuvre-label a').click(async event => this.actor.rollOeuvre(RdDSheetUtility.getItemId(event)))
this.html.find('.jeu-label a').click(async event => this.actor.rollJeu(RdDSheetUtility.getItemId(event)))
this.html.find('.recettecuisine-label a').click(async event => this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event)))
this.html.find('[name="jet-astrologie"]').click(async event => { if (game.user.isGM) {
this.actor.astrologieNombresAstraux(); // experience log
}); this.html.find('.experiencelog-delete').click(async event => {
const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
// Roll Skill const key = Number(li.data("key") ?? -1);
this.html.find('a.competence-label').click(async event => { await this.actor.deleteExperienceLog(key, 1);
this.actor.rollCompetence(RdDSheetUtility.getItemId(event)); });
}); this.html.find('.experiencelog-delete-previous').click(async event => {
this.html.find('.tache-label a').click(async event => { const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
this.actor.rollTache(RdDSheetUtility.getItemId(event)); const key = Number(li.data("key") ?? -1);
}); await this.actor.deleteExperienceLog(0, key + 1);
this.html.find('.meditation-label a').click(async event => { });
this.actor.rollMeditation(RdDSheetUtility.getItemId(event)); // Boutons spéciaux MJs
}); this.html.find('.forcer-tmr-aleatoire').click(async event => this.actor.reinsertionAleatoire("Action MJ"))
this.html.find('.chant-label a').click(async event => { this.html.find('.afficher-tmr').click(async event => this.actor.changeTMRVisible())
this.actor.rollChant(RdDSheetUtility.getItemId(event)); }
});
this.html.find('.danse-label a').click(async event => {
this.actor.rollDanse(RdDSheetUtility.getItemId(event));
});
this.html.find('.musique-label a').click(async event => {
this.actor.rollMusique(RdDSheetUtility.getItemId(event));
});
this.html.find('.oeuvre-label a').click(async event => {
this.actor.rollOeuvre(RdDSheetUtility.getItemId(event));
});
this.html.find('.jeu-label a').click(async event => {
this.actor.rollJeu(RdDSheetUtility.getItemId(event));
});
this.html.find('.recettecuisine-label a').click(async event => {
this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event));
});
this.html.find('.subacteur-label a').click(async event => {
let actorId = RdDSheetUtility.getEventItemData(event, 'actor-id');
let actor = game.actors.get(actorId);
if (actor) {
actor.sheet.render(true);
}
});
// Boutons spéciaux MJs
this.html.find('.forcer-tmr-aleatoire').click(async event => {
this.actor.reinsertionAleatoire("Action MJ");
});
this.html.find('.afficher-tmr').click(async event => {
this.actor.changeTMRVisible();
});
// Points de reve actuel // Points de reve actuel
this.html.find('.ptreve-actuel a').click(async event => { this.html.find('.ptreve-actuel a').click(async event => this.actor.rollCarac('reve-actuel', true))
this.actor.rollCarac('reve-actuel', true); this.html.find('.empoignade-label a').click(async event => RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor)))
}); this.html.find('.arme-label a').click(async event => this.actor.rollArme(duplicate(this._getEventArmeCombat(event))))
// 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);
this.actor.rollArme(duplicate(arme));
});
// Initiative pour l'arme // Initiative pour l'arme
this.html.find('.arme-initiative a').click(async event => { this.html.find('.arme-initiative a').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id); let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id);
@ -276,160 +233,64 @@ export class RdDActorSheet extends RdDBaseActorSheet {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat."); ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
} }
}); });
// Display TMR, visualisation // Display TMR
this.html.find('.visu-tmr').click(async event => {
this.actor.displayTMR("visu");
});
// Display TMR, normal this.html.find('.monte-tmr').click(async event => this.actor.displayTMR("normal"))
this.html.find('.monte-tmr').click(async event => { this.html.find('.monte-tmr-rapide').click(async event => this.actor.displayTMR("rapide"))
this.actor.displayTMR("normal");
});
// Display TMR, fast this.html.find('.repos').click(async event => await this.actor.repos())
this.html.find('.monte-tmr-rapide').click(async event => {
this.actor.displayTMR("rapide");
});
this.html.find('.repos').click(async event => { this.html.find('.carac-xp-augmenter').click(async event => this.actor.updateCaracXPAuto(event.currentTarget.name.replace("augmenter.", "")))
await this.actor.repos(); this.html.find('.competence-xp-augmenter').click(async event => this.actor.updateCompetenceXPAuto(RdDSheetUtility.getItemId(event)))
}); this.html.find('.competence-stress-augmenter').click(async event => this.actor.updateCompetenceStress(RdDSheetUtility.getItemId(event)))
this.html.find('.delete-active-effect').click(async event => {
if (game.user.isGM) {
let effect = this.html.find(event.currentTarget).parents(".active-effect").data('effect');
this.actor.removeEffect(effect);
}
});
this.html.find('.enlever-tous-effets').click(async event => {
if (game.user.isGM) {
await this.actor.removeEffects();
}
});
this.html.find('.carac-xp-augmenter').click(async event => {
let caracName = event.currentTarget.name.replace("augmenter.", "");
this.actor.updateCaracXPAuto(caracName);
});
this.html.find('.competence-xp-augmenter').click(async event => {
this.actor.updateCompetenceXPAuto(RdDSheetUtility.getItemId(event));
});
this.html.find('.competence-stress-augmenter').click(async event => {
this.actor.updateCompetenceStress(RdDSheetUtility.getItemId(event));
});
if (this.options.vueDetaillee) { if (this.options.vueDetaillee) {
// On carac change // On carac change
this.html.find('.carac-value').change(async event => {
let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
this.actor.updateCarac(caracName, parseInt(event.target.value));
});
this.html.find('input.carac-xp').change(async event => { this.html.find('input.carac-xp').change(async event => {
let caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", ""); let caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", "");
this.actor.updateCaracXP(caracName, parseInt(event.target.value)); this.actor.updateCaracXP(caracName, parseInt(event.target.value));
}); });
// On competence change
this.html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
//console.log("Competence changed :", compName);
this.actor.updateCompetence(compName, parseInt(event.target.value));
});
// On competence xp change // On competence xp change
this.html.find('input.competence-xp').change(async event => { this.html.find('input.competence-xp').change(async event => {
let compName = event.currentTarget.attributes.compname.value; let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXP(compName, parseInt(event.target.value)); this.actor.updateCompetenceXP(compName, parseInt(event.target.value));
}); });
// On competence xp change
this.html.find('input.competence-xp-sort').change(async event => { this.html.find('input.competence-xp-sort').change(async event => {
let compName = event.currentTarget.attributes.compname.value; let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.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 // On competence archetype change
this.html.find('.competence-archetype').change(async event => { this.html.find('.competence-archetype').change(async event => {
let compName = event.currentTarget.attributes.compname.value; let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceArchetype(compName, parseInt(event.target.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('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
});
// On pts de reve change // On pts de reve change
this.html.find('.pointsreve-value').change(async event => { this.html.find('.pointsreve-value').change(async event => this.actor.update({ "system.reve.reve.value": event.currentTarget.value }))
let reveValue = event.currentTarget.value; this.html.find('.seuil-reve-value').change(async event => this.actor.setPointsDeSeuil(event.currentTarget.value))
this.actor.update({ "system.reve.reve.value": reveValue });
});
// On seuil de reve change this.html.find('.stress-test').click(async event => this.actor.transformerStress())
this.html.find('.seuil-reve-value').change(async event => { this.html.find('.moral-malheureux').click(async event => this.actor.jetDeMoral('malheureuse'))
console.log("seuil-reve-value", event.currentTarget) this.html.find('.moral-neutre').click(async event => this.actor.jetDeMoral('neutre'))
this.actor.setPointsDeSeuil(event.currentTarget.value); this.html.find('.moral-heureux').click(async event => this.actor.jetDeMoral('heureuse'))
}); this.html.find('.ethylisme-test').click(async event => this.actor.jetEthylisme())
// On stress change this.html.find('.ptreve-actuel-plus').click(async event => this.actor.reveActuelIncDec(1))
this.html.find('.compteur-edit').change(async event => { this.html.find('.ptreve-actuel-moins').click(async event => this.actor.reveActuelIncDec(-1))
let fieldName = event.currentTarget.attributes.name.value; this.html.find('.fatigue-plus').click(async event => this.actor.santeIncDec("fatigue", 1))
this.actor.updateCompteurValue(fieldName, parseInt(event.target.value)); this.html.find('.fatigue-moins').click(async event => this.actor.santeIncDec("fatigue", -1))
});
this.html.find('.stress-test').click(async event => {
this.actor.transformerStress();
});
this.html.find('.moral-malheureux').click(async event => {
this.actor.jetDeMoral('malheureuse');
});
this.html.find('.moral-neutre').click(async event => {
this.actor.jetDeMoral('neutre');
});
this.html.find('.moral-heureux').click(async event => {
this.actor.jetDeMoral('heureuse');
});
this.html.find('.ethylisme-test').click(async event => {
this.actor.jetEthylisme();
});
this.html.find('.jet-vie').click(async event => {
this.actor.jetVie();
});
this.html.find('.jet-endurance').click(async event => {
this.actor.jetEndurance();
});
this.html.find('.vie-plus').click(async event => {
this.actor.santeIncDec("vie", 1);
});
this.html.find('.vie-moins').click(async event => {
this.actor.santeIncDec("vie", -1);
});
this.html.find('.endurance-plus').click(async event => {
this.actor.santeIncDec("endurance", 1);
});
this.html.find('.endurance-moins').click(async event => {
this.actor.santeIncDec("endurance", -1);
});
this.html.find('.ptreve-actuel-plus').click(async event => {
this.actor.reveActuelIncDec(1);
});
this.html.find('.ptreve-actuel-moins').click(async event => {
this.actor.reveActuelIncDec(-1);
});
this.html.find('.fatigue-plus').click(async event => {
this.actor.santeIncDec("fatigue", 1);
});
this.html.find('.fatigue-moins').click(async event => {
this.actor.santeIncDec("fatigue", -1);
});
} }
getBlessure(event) { getBlessure(event) {
const itemId = this.html.find(event.currentTarget).parents(".item-blessure").data('item-id'); const blessureId = this.html.find(event.currentTarget).parents(".item-blessure").data('item-id');
const blessure = this.actor.getItem(itemId, 'blessure'); return this.actor.getItem(blessureId, 'blessure');
return blessure;
} }
isCompetenceAffichable(competence) { isCompetenceAffichable(competence) {
@ -443,11 +304,26 @@ export class RdDActorSheet extends RdDBaseActorSheet {
super._onDropActor(event, dragData); super._onDropActor(event, dragData);
} }
openSubActeur(actorId) {
game.actors.get(actorId)?.sheet.render(true)
}
deleteSubActeur(actorId, li) {
if (actorId) {
const subActor = game.actors.get(actorId);
RdDUtility.confirmSubActeurDelete(this, subActor, li, () => {
console.log('Delete : ', subActor.id);
this.actor.deleteSubActeur(subActor.id);
RdDUtility.slideOnDelete(this, li);
});
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async selectTypeOeuvreToCreate() { 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">`; 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 += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
} }
content += '</select>'; content += '</select>';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
import { Grammar } from "../grammar.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDBaseActorSheet } from "./base-actor-sheet.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(RdDBaseActorSheet.defaultOptions, {
width: 550
});
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.encaisser-direct').click(async event => this.actor.encaisser())
this.html.find('.carac-label a').click(async event => this.actor.rollCarac(Grammar.toLowerCaseNoAccent(event.currentTarget.attributes.name.value)));
this.html.find('a.competence-label').click(async event => this.actor.rollCompetence(RdDSheetUtility.getItemId(event)));
this.html.find('.endurance-plus').click(async event => this.actor.santeIncDec("endurance", 1));
this.html.find('.endurance-moins').click(async event => this.actor.santeIncDec("endurance", -1));
if (game.user.isGM) {
this.html.find('.remise-a-neuf').click(async event => this.actor.remiseANeuf())
this.html.find('.delete-active-effect').click(async event => this.actor.removeEffect(this.html.find(event.currentTarget).parents(".active-effect").data('effect')));
this.html.find('.enlever-tous-effets').click(async event => await this.actor.removeEffects());
}
}
}

View File

@ -0,0 +1,512 @@
import { ChatUtility } from "../chat-utility.js";
import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js";
import { Grammar } from "../grammar.js";
import { RdDItemCompetence } from "../item-competence.js";
import { Misc } from "../misc.js";
import { RdDEmpoignade } from "../rdd-empoignade.js";
import { RdDResolutionTable } from "../rdd-resolution-table.js";
import { RdDEncaisser } from "../rdd-roll-encaisser.js";
import { RdDRoll } from "../rdd-roll.js";
import { RdDUtility } from "../rdd-utility.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { RdDBaseActor } from "./base-actor.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { StatusEffects } from "../settings/status-effects.js";
import { TYPES } from "../item.js";
import { Targets } from "../targets.js";
import { RdDPossession } from "../rdd-possession.js";
import { RdDCombat } from "../rdd-combat.js";
import { RdDConfirm } from "../rdd-confirm.js";
import { ENTITE_INCARNE, SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { RdDItemArme } from "../item-arme.js";
const POSSESSION_SANS_DRACONIC = {
img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp',
name: 'Sans draconic',
system: {
niveau: 0,
defaut_carac: "reve-actuel",
}
};
/**
* Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
* - Entités de rêve
* - Créatures de "sang": créatures et humanoides
*/
export class RdDBaseActorReve extends RdDBaseActor {
getCaracChanceActuelle() {
return {
label: 'Chance actuelle',
value: this.getChanceActuel(),
type: "number"
};
}
getCaracReveActuel() {
return {
label: 'Rêve actuel',
value: this.getReveActuel(),
type: "number"
};
}
getReveActuel() { return this.getReve() }
getChanceActuel() { return this.getChance() }
getReve() { return Number(this.system.carac.reve?.value ?? 0) }
getForce() { return this.getReve() }
getTaille() { return Number(this.system.carac.taille?.value ?? 0) }
getAgilite() { return this.getForce() }
getChance() { return this.getReve() }
getMoralTotal() { return 0 }
getBonusDegat() { return Number(this.system.attributs?.plusdom?.value ?? 0) }
getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) }
getSConst() { return 0 }
/* -------------------------------------------- */
getEncombrementMax() { return 0 }
isSurenc() { return false }
computeMalusSurEncombrement() { return 0 }
ajustementAstrologique() { return 0 }
getMalusArmure() { return 0 }
getEnduranceActuelle() {
return Number(this.system.sante?.endurance?.value ?? 0);
}
async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } }
isDead() { return false }
blessuresASoigner() { return [] }
getEtatGeneral(options = { ethylisme: false }) { return 0 }
async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
async santeIncDec(name, inc, isCritique = false) { }
async finDeRound(options = { terminer: false }) {
await this.$finDeRoundSuppressionEffetsTermines(options);
await this.finDeRoundBlessures();
await this.$finDeRoundSupprimerObsoletes();
await this.$finDeRoundEmpoignade();
}
async $finDeRoundSuppressionEffetsTermines(options) {
for (let effect of this.getEffects()) {
if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) {
await effect.delete();
ChatMessage.create({ content: `${this.name} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` });
}
}
}
async finDeRoundBlessures() {
}
async $finDeRoundSupprimerObsoletes() {
const obsoletes = []
.concat(this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
.concat(this.itemTypes[TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
.map(it => it.id);
await this.deleteEmbeddedDocuments('Item', obsoletes);
}
async $finDeRoundEmpoignade() {
const immobilisations = this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
immobilisations.forEach(emp => RdDEmpoignade.onImmobilisation(this,
game.actors.get(emp.system.empoigneid),
emp
))
}
async setSonne(sonne = true) { }
/* -------------------------------------------- */
getCompetence(idOrName, options = {}) {
if (idOrName instanceof Item) {
return idOrName.isCompetence() ? idOrName : undefined
}
return RdDItemCompetence.findCompetence(this.items, idOrName, options)
}
getCompetences(name) {
return RdDItemCompetence.findCompetences(this.items, name)
}
getCompetenceCorpsACorps(options = {}) {
return this.getCompetence("Corps à corps", options)
}
getCompetencesEsquive() {
return this.getCompetences("esquive")
}
getArmeParade(armeParadeId) {
const item = armeParadeId ? this.getEmbeddedDocument('Item', armeParadeId) : undefined;
return RdDItemArme.getArme(item);
}
getDraconicOuPossession() {
return POSSESSION_SANS_DRACONIC
}
getPossession(possessionId) {
return this.itemTypes[TYPES.possession].find(it => it.system.possessionid == possessionId);
}
getPossessions() {
return this.itemTypes[TYPES.possession];
}
getEmpoignades() {
return this.itemTypes[TYPES.empoignade];
}
/* -------------------------------------------- */
async updateCreatureCompetence(idOrName, fieldName, value) {
let competence = this.getCompetence(idOrName);
if (competence) {
function getFieldPath(fieldName) {
switch (fieldName) {
case "niveau": return 'system.niveau';
case "dommages": return 'system.dommages';
case "carac_value": return 'system.carac_value';
}
return undefined
}
const path = getFieldPath(fieldName);
if (path) {
await competence.update({ [path]: value });
}
}
}
/* -------------------------------------------- */
isEffectAllowed(statusId) { return true }
getEffects(filter = e => true) {
return this.getEmbeddedCollection("ActiveEffect").filter(filter);
}
getEffect(statusId) {
return this.getEmbeddedCollection("ActiveEffect").find(it => it.flags?.core?.statusId == statusId);
}
async setEffect(statusId, status) {
if (this.isEffectAllowed(statusId)) {
const effect = this.getEffect(statusId);
if (!status && effect) {
await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
}
if (status && !effect) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.status(statusId)]);
}
}
}
async removeEffect(statusId) {
const effect = this.getEffect(statusId);
if (effect) {
await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
}
}
async removeEffects(filter = e => true) {
if (game.user.isGM) {
const ids = this.getEffects(filter).map(it => it.id);
await this.deleteEmbeddedDocuments('ActiveEffect', ids);
}
}
/* -------------------------------------------- */
getSurprise(isCombat = undefined) {
let niveauSurprise = this.getEffects()
.map(effect => StatusEffects.valeurSurprise(effect, isCombat))
.reduce(Misc.sum(), 0);
if (niveauSurprise > 1) {
return 'totale';
}
if (niveauSurprise == 1) {
return 'demi';
}
return '';
}
/* -------------------------------------------- */
async computeEtatGeneral() {
// Par défaut, on ne calcule pas d'état général, seuls les personnages/créatures sont affectés
this.system.compteurs.etat.value = 0;
}
/* -------------------------------------------- */
async openRollDialog({ name, label, template, rollData, callbackAction }) {
const dialog = await RdDRoll.create(this, rollData,
{ html: template, close: async html => await this._onCloseRollDialog(html) },
{
name: name,
label: label,
callbacks: [
this.createCallbackExperience(),
this.createCallbackAppelAuMoral(),
{ action: callbackAction }
]
});
dialog.render(true);
return dialog
}
createEmptyCallback() {
return {
condition: r => false,
action: r => { }
};
}
createCallbackExperience() { return this.createEmptyCallback(); }
createCallbackAppelAuMoral() { return this.createEmptyCallback(); }
async _onCloseRollDialog(html) { }
/* -------------------------------------------- */
async roll() {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const carac = this.getCarac()
const selectedCaracName = ['apparence', 'perception', 'force', 'reve'].find(it => carac[it] != undefined)
await this.openRollDialog({
name: `jet-${this.id}`,
label: `Jet de ${this.name}`,
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll.html',
rollData: {
carac: carac,
selectedCarac: carac[selectedCaracName],
selectedCaracName: selectedCaracName,
competences: this.itemTypes['competence']
},
callbackAction: r => this.$onRollCaracResult(r)
});
}
getCarac() {
// TODO: le niveau d'une entité de cauchemar devrait être exclu...
const carac = mergeObject(duplicate(this.system.carac),
{
'reve-actuel': this.getCaracReveActuel(),
'chance-actuelle': this.getCaracChanceActuelle()
});
return carac;
}
/* -------------------------------------------- */
async rollCarac(caracName, jetResistance = undefined) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
let selectedCarac = this.getCaracByName(caracName)
await this.openRollDialog({
name: 'jet-' + caracName,
label: 'Jet ' + Grammar.apostrophe('de', selectedCarac.label),
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html',
rollData: {
selectedCarac: selectedCarac,
competences: this.itemTypes['competence'],
jetResistance: jetResistance ? caracName : undefined
},
callbackAction: r => this.$onRollCaracResult(r)
});
}
/* -------------------------------------------- */
async $onRollCaracResult(rollData) {
// Final chat message
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-general.html');
}
/* -------------------------------------------- */
async rollCompetence(idOrName, options = { tryTarget: true }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(idOrName);
let rollData = { carac: this.system.carac, competence: competence }
if (competence.type == TYPES.competencecreature) {
const arme = RdDItemCompetenceCreature.armeCreature(competence)
if (arme && options.tryTarget && Targets.hasTargets()) {
Targets.selectOneToken(target => {
if (arme.action == "possession") {
RdDPossession.onAttaquePossession(target, this, competence)
}
else {
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
}
});
return;
}
// Transformer la competence de créature
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
await this.openRollDialog({
name: 'jet-competence',
label: 'Jet ' + Grammar.apostrophe('de', competence.name),
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
rollData: rollData,
callbackAction: r => this.$onRollCompetence(r, options)
});
}
async $onRollCompetence(rollData, options) {
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html')
if (options?.onRollAutomate) {
options.onRollAutomate(rollData);
}
}
/** --------------------------------------------
* @param {*} arme item d'arme/compétence de créature
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
* @returns
*/
rollArme(arme, categorieArme = "competence") {
let compToUse = this.$getCompetenceArme(arme, categorieArme)
if (!RdDItemArme.isArmeUtilisable(arme)) {
ui.notifications.warn(`Arme inutilisable: ${arme.name} a une résistance de 0 ou moins`)
return
}
if (!Targets.hasTargets()) {
RdDConfirm.confirmer({
settingConfirmer: "confirmer-combat-sans-cible",
content: `<p>Voulez vous faire un jet de ${compToUse} sans choisir de cible valide?
<br>Tous les jets de combats devront être gérés à la main
</p>`,
title: 'Ne pas utiliser les automatisation de combat',
buttonLabel: "Pas d'automatisation",
onAction: async () => {
this.rollCompetence(compToUse, { tryTarget: false })
}
});
return
}
Targets.selectOneToken(target => {
if (Targets.isTargetEntite(target)) {
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
return
}
const competence = this.getCompetence(compToUse)
if (competence.isCompetencePossession()) {
return RdDPossession.onAttaquePossession(target, this, competence);
}
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme);
})
}
$getCompetenceArme(arme, competenceName) {
switch (arme.type) {
case TYPES.competencecreature:
return arme.name
case TYPES.arme:
switch (competenceName) {
case 'competence': return arme.system.competence;
case 'unemain': return RdDItemArme.competence1Mains(arme);
case 'deuxmains': return RdDItemArme.competence2Mains(arme);
case 'tir': return arme.system.tir;
case 'lancer': return arme.system.lancer;
}
}
return undefined
}
verifierForceMin(item) {
}
/* -------------------------------------------- */
async resetItemUse() { }
async incDecItemUse(itemId, inc = 1) { }
getItemUse(itemId) { return 0; }
/* -------------------------------------------- */
async encaisser() { await RdDEncaisser.encaisser(this) }
async encaisserDommages(rollData, attacker = undefined, show = undefined) {
if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
return;
}
const attackerId = attacker?.id;
if (ReglesOptionnelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'encaisserDommages',
args: [rollData, show, attackerId]
});
return;
}
const armure = await this.computeArmure(rollData);
if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
DialogValidationEncaissement.validerEncaissement(this, rollData, armure,
jet => this.$onEncaissement(jet, show, attacker));
}
else {
const jet = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
await this.$onEncaissement(jet, show, attacker);
}
}
async $onEncaissement(jet, show, attacker) {
await this.onAppliquerJetEncaissement(jet, attacker);
await this.$afficherEncaissement(jet, show);
}
async onAppliquerJetEncaissement(encaissement, attacker) { }
async $afficherEncaissement(encaissement, show) {
mergeObject(encaissement, {
alias: this.name,
hasPlayerOwner: this.hasPlayerOwner,
show: show ?? {}
});
await ChatUtility.createChatWithRollMode(this.name, {
roll: encaissement.roll,
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
});
if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) {
encaissement = duplicate(encaissement);
encaissement.isGM = true;
ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients("GM"),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
});
}
}
/* -------------------------------------------- */
async accorder(entite, when = 'avant-encaissement') {
if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
|| entite == undefined
|| !entite.isEntite([ENTITE_INCARNE])
|| entite.isEntiteAccordee(this)) {
return true;
}
const rolled = await RdDResolutionTable.roll(this.getReveActuel(), - Number(entite.system.carac.niveau.value));
const rollData = {
alias: this.name,
rolled: rolled,
entite: entite.name,
selectedCarac: this.system.carac.reve
};
if (rolled.isSuccess) {
await entite.setEntiteReveAccordee(this);
}
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.html');
if (rolled.isPart) {
await this.appliquerAjoutExperience(rollData, true);
}
return rolled.isSuccess;
}
isEntiteAccordee(attacker) { return true }
async setEntiteReveAccordee(attacker) {
ui.notifications.error("Impossible de s'accorder à " + this.name + ": ce n'est pas une entite de cauchemer/rêve");
}
}

View File

@ -0,0 +1,41 @@
import { ChatUtility } from "../chat-utility.js";
import { RdDItemBlessure } from "../item/blessure.js";
import { RdDBaseActorReveSheet } from "./base-actor-reve-sheet.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDBaseActorSangSheet extends RdDBaseActorReveSheet {
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
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('.jet-vie').click(async event => this.actor.jetDeVie())
this.html.find('.jet-endurance').click(async event => await this.jetEndurance())
this.html.find('.vie-plus').click(async event => this.actor.santeIncDec("vie", 1))
this.html.find('.vie-moins').click(async event => this.actor.santeIncDec("vie", -1))
}
async jetEndurance() {
const endurance = this.actor.getEnduranceActuelle()
const result = await this.actor.jetEndurance(endurance);
ChatMessage.create({
content: `Jet d'Endurance : ${result.jetEndurance} / ${endurance}
<br>${this.actor.name} a ${result.sonne ? 'échoué' : 'réussi'} son Jet d'Endurance ${result.sonne ? 'et devient Sonné' : ''}`,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.actor.name)
});
}
}

View File

@ -0,0 +1,280 @@
import { MAX_ENDURANCE_FATIGUE, RdDUtility } from "../rdd-utility.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { STATUSES } from "../settings/status-effects.js";
import { TYPES } from "../item.js";
import { RdDBaseActorReve } from "./base-actor-reve.js";
import { RdDDice } from "../rdd-dice.js";
import { RdDItemBlessure } from "../item/blessure.js";
/**
* Classe de base pour les acteurs qui peuvent subir des blessures
* - créatures
* - humanoides
*/
export class RdDBaseActorSang extends RdDBaseActorReve {
getForce() { return Number(this.system.carac.force?.value ?? 0) }
getBonusDegat() { return Number(this.system.attributs?.plusdom?.value ?? 0) }
getProtectionNaturelle() { return Number(this.system.attributs?.protection?.value ?? 0) }
getSConst() { return 0 }
getEnduranceMax() { return Math.max(1, Math.min(this.system.sante.endurance.max, MAX_ENDURANCE_FATIGUE)) }
getFatigueActuelle() {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
return Math.max(0, Math.min(this.getFatigueMax(), this.system.sante.fatigue?.value ?? 0));
}
return 0;
}
getFatigueRestante() { return this.getFatigueMax() - this.getFatigueActuelle() }
getFatigueMin() { return this.system.sante.endurance.max - this.system.sante.endurance.value }
getFatigueMax() { return this.getEnduranceMax() * 2 }
malusFatigue() {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
return RdDUtility.calculMalusFatigue(this.getFatigueActuelle(), this.getEnduranceMax())
}
return 0;
}
/* -------------------------------------------- */
getEncombrementMax() { return Number(this.system.attributs?.encombrement?.value ?? 0) }
isSurenc() { return this.computeMalusSurEncombrement() < 0 }
computeMalusSurEncombrement() {
return Math.min(0, Math.floor(this.getEncombrementMax() - this.encTotal));
}
isDead() { return this.system.sante.vie.value < -this.getSConst() }
nbBlessuresLegeres() { return this.itemTypes[TYPES.blessure].filter(it => it.isLegere()).length }
nbBlessuresGraves() { return this.itemTypes[TYPES.blessure].filter(it => it.isGrave()).length }
nbBlessuresCritiques() { return this.itemTypes[TYPES.blessure].filter(it => it.isCritique()).length }
/* -------------------------------------------- */
computeResumeBlessure() {
const nbLegeres = this.nbBlessuresLegeres()
const nbGraves = this.nbBlessuresGraves()
const nbCritiques = this.nbBlessuresCritiques()
if (nbLegeres + nbGraves + nbCritiques == 0) {
return "Aucune blessure";
}
let resume = "Blessures:";
if (nbLegeres > 0) {
resume += " " + nbLegeres + " légère" + (nbLegeres > 1 ? "s" : "");
}
if (nbGraves > 0) {
if (nbLegeres > 0)
resume += ",";
resume += " " + nbGraves + " grave" + (nbGraves > 1 ? "s" : "");
}
if (nbCritiques > 0) {
if (nbGraves > 0 || nbLegeres > 0)
resume += ",";
resume += " une CRITIQUE !";
}
return resume;
}
blessuresASoigner() { return [] }
async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
/* -------------------------------------------- */
async onAppliquerJetEncaissement(encaissement, attacker) {
const santeOrig = duplicate(this.system.sante);
const blessure = await this.ajouterBlessure(encaissement, attacker); // Will update the result table
const perteVie = await this.santeIncDec("vie", -encaissement.vie);
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique());
mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue,
sonne: perteEndurance.sonne,
jetEndurance: perteEndurance.jetEndurance,
endurance: perteEndurance.perte,
vie: santeOrig.vie.value - perteVie.newValue,
blessure: blessure
});
}
/* -------------------------------------------- */
async santeIncDec(name, inc, isCritique = false) {
if (name == 'fatigue' && !ReglesOptionnelles.isUsing("appliquer-fatigue")) {
return;
}
const sante = duplicate(this.system.sante)
let compteur = sante[name];
if (!compteur) {
return;
}
let result = {
sonne: false,
};
let minValue = name == "vie" ? -this.getSConst() - 1 : 0;
result.newValue = Math.max(minValue, Math.min(compteur.value + inc, compteur.max));
//console.log("New value ", inc, minValue, result.newValue);
let fatigue = 0;
if (name == "endurance") {
if (result.newValue == 0 && inc < 0 && !isCritique) { // perte endurance et endurance devient 0 (sauf critique) -> -1 vie
sante.vie.value--;
result.perteVie = true;
}
result.newValue = Math.max(0, result.newValue);
if (inc > 0) { // le max d'endurance s'applique seulement à la récupération
result.newValue = Math.min(result.newValue, this._computeEnduranceMax())
}
const perte = compteur.value - result.newValue;
result.perte = perte;
if (perte > 1) {
// Peut-être sonné si 2 points d'endurance perdus d'un coup
mergeObject(result, await this.jetEndurance(result.newValue));
} else if (inc > 0) {
await this.setSonne(false);
}
if (sante.fatigue && inc < 0) { // Each endurance lost -> fatigue lost
fatigue = perte;
}
}
compteur.value = result.newValue;
// If endurance lost, then the same amount of fatigue cannot be recovered
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) {
sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this.getFatigueMin());
}
await this.update({ "system.sante": sante })
if (this.isDead()) {
await this.setEffect(STATUSES.StatusComma, true);
}
return result
}
/* -------------------------------------------- */
_computeEnduranceMax() {
const diffVie = this.system.sante.vie.max - this.system.sante.vie.value;
const maxEndVie = this.system.sante.endurance.max - (diffVie * 2);
const nbGraves = this.countBlessures(it => it.isGrave()) > 0
const nbCritiques = this.countBlessures(it => it.isCritique()) > 0
const maxEndGraves = Math.floor(this.system.sante.endurance.max / (2 * nbGraves));
const maxEndCritiques = nbCritiques > 0 ? 1 : this.system.sante.endurance.max;
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
}
/* -------------------------------------------- */
async ajouterBlessure(encaissement, attacker = undefined) {
if (encaissement.gravite < 0) return;
if (encaissement.gravite > 0) {
while (this.countBlessures(it => it.system.gravite == encaissement.gravite) >= RdDItemBlessure.maxBlessures(encaissement.gravite) && encaissement.gravite <= 6) {
// Aggravation
encaissement.gravite += 2
if (encaissement.gravite > 2) {
encaissement.vie += 2;
}
}
}
const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg.loc.label, attacker);
if (blessure.isCritique()) {
encaissement.endurance = endActuelle;
}
if (blessure.isMort()) {
this.setEffect(STATUSES.StatusComma, true);
encaissement.mort = true;
ChatMessage.create({
content: `<img class="chat-icon" src="icons/svg/skull.svg" data-tooltip="charge" />
<strong>${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>`
});
}
return blessure;
}
async supprimerBlessures(filterToDelete) {
const toDelete = this.filterItems(filterToDelete, TYPES.blessure)
.map(it => it.id);
await this.deleteEmbeddedDocuments('Item', toDelete);
}
countBlessures(filter = it => !it.isContusion()) {
return this.filterItems(filter, 'blessure').length
}
/* -------------------------------------------- */
async jetDeVie() {
if (this.isDead()) {
ChatMessage.create({ content: `Jet de Vie: ${this.name} est déjà mort, ce n'est pas la peine d'en rajouter !!!!!`, whisper: ChatMessage.getWhisperRecipients(this.name) });
return
}
const jetDeVie = await RdDDice.roll("1d20");
const sConst = this.getSConst();
const vie = this.system.sante.vie.value;
const isCritique = this.nbBlessuresCritiques() > 0;
const isGrave = this.nbBlessuresGraves();
const isEchecTotal = jetDeVie.total == 20;
const isSuccess = jetDeVie.total == 1 || jetDeVie.total <= vie;
const perte = isSuccess ? 0 : 1 + (isEchecTotal ? vie + sConst : 0)
const prochainJet = (jetDeVie.total == 1 && vie > 0 ? 20 : 1) * (isCritique ? 1 : isGrave > 0 ? sConst : 0)
let msgText = `Jet de Vie: <strong>${jetDeVie.total} / ${vie}</strong>`
if (isSuccess) {
msgText += "<br>Réussi, pas de perte de point de vie."
} else {
msgText += `<br>Echoué, perte ${perte} point de vie`;
await this.santeIncDec("vie", -perte);
}
if (this.isDead()) {
msgText += `<br><strong>${this.name} est mort !!!!</strong>`;
}
else if (prochainJet > 0) {
msgText += `<br>Prochain jet de vie dans ${prochainJet} ${isCritique ? 'round' : 'minute'}${prochainJet > 1 ? 's' : ''} ${isCritique ? '(état critique)' : '(état grave)'}`
}
ChatMessage.create({ content: msgText, whisper: ChatMessage.getWhisperRecipients(this.name) });
}
/* -------------------------------------------- */
async jetEndurance(resteEndurance = undefined) {
const jetEndurance = (await RdDDice.roll("1d20")).total;
const sonne = jetEndurance == 20 || jetEndurance > (resteEndurance ?? this.system.sante.endurance.value)
if (sonne) {
await this.setSonne();
}
return { jetEndurance, sonne }
}
async finDeRoundBlessures() {
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length;
if (nbGraves > 0) {
// Gestion blessure graves : -1 pt endurance par blessure grave
await this.santeIncDec("endurance", -nbGraves);
}
}
async setSonne(sonne = true) {
if (!game.combat && sonne) {
ui.notifications.info(`${this.name} est hors combat, il ne reste donc pas sonné`);
return;
}
await this.setEffect(STATUSES.StatusStunned, sonne);
}
getSonne() {
return this.getEffect(STATUSES.StatusStunned);
}
/* -------------------------------------------- */
async computeEtatGeneral() { this.system.compteurs.etat.value = this.malusVie() + this.malusFatigue() + this.malusEthylisme() }
getEtatGeneral(options = { ethylisme: false }) { return this.system.compteurs.etat.value }
malusVie() { return Math.min(this.system.sante.vie.value - this.system.sante.vie.max, 0) }
malusEthylisme() { return 0 }
}

View File

@ -3,7 +3,8 @@ import { Misc } from "../misc.js";
import { DialogSplitItem } from "../dialog-split-item.js"; import { DialogSplitItem } from "../dialog-split-item.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js"; import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { Monnaie } from "../item-monnaie.js"; import { Monnaie } from "../item-monnaie.js";
import { RdDItem } from "../item.js"; import { RdDItem, TYPES } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
@ -14,14 +15,10 @@ export class RdDBaseActorSheet extends ActorSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
RdDUtility.initAfficheContenu(); return mergeObject(ActorSheet.defaultOptions, {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"], classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }], tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }], dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false vueDetaillee: false
}); });
} }
@ -30,7 +27,7 @@ export class RdDBaseActorSheet extends ActorSheet {
async getData() { async getData() {
Monnaie.validerMonnaies(this.actor.itemTypes['monnaie']); Monnaie.validerMonnaies(this.actor.itemTypes['monnaie']);
this.actor.recompute(); this.actor.computeEtatGeneral();
let formData = { let formData = {
title: this.title, title: this.title,
id: this.actor.id, id: this.actor.id,
@ -50,16 +47,18 @@ export class RdDBaseActorSheet extends ActorSheet {
encTotal: await this.actor.computeEncTotal(), encTotal: await this.actor.computeEncTotal(),
} }
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets); this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
this._appliquerRechercheObjets(formData.objets, formData.conteneurs); this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs); formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == TYPES.competencecreature)
.forEach(it => it.isdommages = RdDItemCompetenceCreature.isDommages(it))
return formData; return formData;
} }
_appliquerRechercheObjets(objets, conteneurs) { _appliquerRechercheObjets(conteneurs, inventaires) {
if (this.options.recherche?.text) { if (this.options.recherche?.text) {
const recherche = this.options.recherche; const recherche = this.options.recherche;
const allVisible = objets.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id); 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) let addVisible = conteneurs.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id)
do { do {
allVisible.push(...addVisible) allVisible.push(...addVisible)
@ -67,11 +66,11 @@ export class RdDBaseActorSheet extends ActorSheet {
addVisible = parentsIds.filter(id => !allVisible.includes(id)) addVisible = parentsIds.filter(id => !allVisible.includes(id))
} }
while (addVisible.length > 0) while (addVisible.length > 0)
objets.forEach(it => it.system.isHidden = !allVisible.includes(it.id)) inventaires.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
conteneurs.forEach(it => it.system.isHidden = !allVisible.includes(it.id)) conteneurs.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
} }
else { else {
objets.forEach(it => it.system.isHidden = false) inventaires.forEach(it => it.system.isHidden = false)
conteneurs.forEach(it => it.system.isHidden = false) conteneurs.forEach(it => it.system.isHidden = false)
} }
} }
@ -118,36 +117,45 @@ export class RdDBaseActorSheet extends ActorSheet {
formData.herbes = Misc.arrayOrEmpty(itemTypes['herbe']); formData.herbes = Misc.arrayOrEmpty(itemTypes['herbe']);
formData.nourritureboissons = Misc.arrayOrEmpty(itemTypes['nourritureboisson']); formData.nourritureboissons = Misc.arrayOrEmpty(itemTypes['nourritureboisson']);
formData.gemmes = Misc.arrayOrEmpty(itemTypes['gemme']); 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])) .map(t => Misc.arrayOrEmpty(itemTypes[t]))
.reduce((a, b) => a.concat(b), []) .reduce((a, b) => a.concat(b), [])
.sort(Misc.ascending(it => it.name)); .sort(Misc.ascending(it => it.name));
} }
/* -------------------------------------------- */ /** @override */ /* -------------------------------------------- */ /** @override */
activateListeners(html) { activateListeners(html) {
super.activateListeners(html); super.activateListeners(html);
this.html = html; this.html = html;
this.html.find('.conteneur-name a').click(async event => { this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(this.getItemId(event)); RdDUtility.toggleAfficheContenu(this.getItemId(event));
this.render(true); this.render(true);
}); });
this.html.find('.item-edit').click(async event => this.getItem(event)?.sheet.render(true)) this.html.find('.actor-montrer').click(async event => this.actor.postActorToChat());
this.html.find('.item-montrer').click(async event => this.getItem(event)?.postItemToChat()); this.html.find('.item-edit').click(async event => this.getItem(event)?.sheet.render(true))
this.html.find('.actor-montrer').click(async event => this.actor.postActorToChat()); this.html.find('.item-montrer').click(async event => this.getItem(event)?.postItemToChat());
this.html.find('.recherche')
.each((index, field) => { this.html.find('.recherche')
this._rechercheSelectArea(field); .each((index, field) => {
}) this._rechercheSelectArea(field);
.keyup(async event => this._rechercherKeyup(event)) })
.change(async event => this._rechercherKeyup(event)); .keyup(async event => this._rechercherKeyup(event))
this.html.find('.recherche').prop( "disabled", false ); .change(async event => this._rechercherKeyup(event));
this.html.find('.recherche').prop("disabled", false);
// Everything below here is only needed if the sheet is editable // Everything below here is only needed if the sheet is editable
if (!this.options.editable) return; if (!this.options.editable) return;
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('.item-split').click(async event => { this.html.find('.item-split').click(async event => {
const item = this.getItem(event); const item = this.getItem(event);
RdDSheetUtility.splitItem(item, this.actor); RdDSheetUtility.splitItem(item, this.actor);
@ -163,6 +171,26 @@ export class RdDBaseActorSheet extends ActorSheet {
this.html.find('.nettoyer-conteneurs').click(async event => { this.html.find('.nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs(); this.actor.nettoyerConteneurs();
}); });
this.html.find('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
});
if (this.options.vueDetaillee) {
// On carac change
this.html.find('.carac-value').change(async event => {
let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
this.actor.updateCarac(caracName, parseInt(event.target.value));
});
// On competence change
this.html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
//console.log("Competence changed :", compName);
this.actor.updateCompetence(compName, parseInt(event.target.value));
});
}
} }
_rechercherKeyup(event) { _rechercherKeyup(event) {
@ -230,9 +258,9 @@ export class RdDBaseActorSheet extends ActorSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
async selectObjetTypeToCreate() { 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">`; 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 += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
} }
content += '</select>'; content += '</select>';

View File

@ -1,19 +1,38 @@
import { ChatUtility } from "../chat-utility.js"; import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js"; import { SYSTEM_SOCKET_ID } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Monnaie } from "../item-monnaie.js"; import { Monnaie } from "../item-monnaie.js";
import { TYPES } from "../item.js";
import { Misc } from "../misc.js"; import { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js"; import { RdDAudio } from "../rdd-audio.js";
import { RdDConfirm } from "../rdd-confirm.js";
import { RdDUtility } from "../rdd-utility.js"; import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js"; import { SystemCompendiums } from "../settings/system-compendiums.js";
import { APP_ASTROLOGIE_REFRESH } from "../sommeil/app-astrologie.js"; import { APP_ASTROLOGIE_REFRESH } from "../sommeil/app-astrologie.js";
export class RdDBaseActor extends Actor { export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
static _findCaracByName(carac, name) {
name = Grammar.toLowerCaseNoAccent(name);
switch (name) {
case 'reve-actuel': case 'reve actuel':
return carac.reve;
case 'chance-actuelle': case 'chance actuelle':
return carac.chance;
}
const caracList = Object.entries(carac);
let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique' });
if (!entry || entry.length == 0) {
entry = Misc.findFirstLike(name, caracList, { mapper: it => it[1].label, description: 'caractéristique' });
}
return entry && entry.length > 0 ? carac[entry[0]] : undefined;
}
static getDefaultImg(itemType) { static getDefaultImg(itemType) {
return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType]; return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
} }
/* -------------------------------------------- */
static init() { static init() {
Hooks.on("preUpdateItem", (item, change, options, id) => RdDBaseActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id)); Hooks.on("preUpdateItem", (item, change, options, id) => RdDBaseActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id));
Hooks.on("createItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onCreateItem(item, options, id)); Hooks.on("createItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onCreateItem(item, options, id));
@ -49,7 +68,7 @@ export class RdDBaseActor extends Actor {
static onRemoteActorCall(callData, userId) { static onRemoteActorCall(callData, userId) {
if (userId == game.user.id) { if (userId == game.user.id) {
const actor = game.actors.get(callData?.actorId); const actor = RdDBaseActor.getRealActor(callData?.actorId, callData?.tokenId);
if (Misc.isOwnerPlayerOrUniqueConnectedGM(actor)) { // Seul le joueur choisi effectue l'appel: le joueur courant si propriétaire de l'actor, ou le MJ sinon if (Misc.isOwnerPlayerOrUniqueConnectedGM(actor)) { // Seul le joueur choisi effectue l'appel: le joueur courant si propriétaire de l'actor, ou le MJ sinon
const args = callData.args; const args = callData.args;
console.info(`RdDBaseActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDBaseActor.${callData.method}(`, ...args, ')'); console.info(`RdDBaseActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDBaseActor.${callData.method}(`, ...args, ')');
@ -58,17 +77,29 @@ export class RdDBaseActor extends Actor {
} }
} }
static getRealActor(actorId, tokenId) {
if (tokenId) {
let token = canvas.tokens.get(tokenId)
if (token) {
return token.actor
}
}
return game.actors.get(actorId)
}
static extractActorMin = (actor) => { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img }; };
static getParentActor(document) { static getParentActor(document) {
return document?.parent instanceof Actor ? document.parent : undefined return document?.parent instanceof Actor ? document.parent : undefined
} }
/** /**
* Cet methode surcharge Actor.create() pour ajouter si besoin des Items par défaut: * Cette methode surcharge Actor.create() pour ajouter si besoin des Items par défaut:
* compétences et monnaies. * compétences et monnaies.
* *
* @param {Object} actorData template d'acteur auquel ajouter des informations. * @param {Object} actorData template d'acteur auquel ajouter des informations.
* @param {Object} options optionspour customiser la création * @param {Object} options optionspour customiser la création
*/ */
static async create(actorData, options) { static async create(actorData, options) {
// import depuis un compendium // import depuis un compendium
if (actorData instanceof Array) { if (actorData instanceof Array) {
@ -104,11 +135,49 @@ export class RdDBaseActor extends Actor {
super(docData, context); super(docData, context);
} }
isCreatureEntite() { return this.type == 'creature' || this.type == 'entite'; } getCaracByName(name) {
isCreature() { return this.type == 'creature'; } switch (Grammar.toLowerCaseNoAccent(name)) {
isEntite() { return this.type == 'entite'; } case 'reve-actuel': case 'reve actuel':
isPersonnage() { return this.type == 'personnage'; } return this.getCaracReveActuel();
isVehicule() { return this.type == 'vehicule'; } case 'chance-actuelle': case 'chance-actuelle':
return this.getCaracChanceActuelle();
}
return RdDBaseActor._findCaracByName(this.system.carac, name);
}
/* -------------------------------------------- */
async _preCreate(data, options, user) {
await super._preCreate(data, options, user);
// Configure prototype token settings
const prototypeToken = {};
if (this.type === "personnage") Object.assign(prototypeToken, {
sight: { enabled: true }, actorLink: true, disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
});
this.updateSource({ prototypeToken });
}
/* -------------------------------------------- */
prepareData() {
super.prepareData()
this.prepareActorData()
this.cleanupConteneurs()
this.computeEtatGeneral()
this.computeEncTotal()
}
async prepareActorData() { }
async computeEtatGeneral() { }
/* -------------------------------------------- */
findPlayer() {
return game.users.players.find(player => player.active && player.character?.id == this.id);
}
isCreatureEntite() { return this.isCreature() || this.isEntite() }
isCreature() { return false }
isEntite(typeentite = []) { return false }
isVehicule() { return false }
isPersonnage() { return false }
getItem(id, type = undefined) { getItem(id, type = undefined) {
const item = this.items.get(id); const item = this.items.get(id);
if (type == undefined || (item?.type == type)) { if (type == undefined || (item?.type == type)) {
@ -117,17 +186,17 @@ export class RdDBaseActor extends Actor {
return undefined; return undefined;
} }
listeSuivants(filter = suivant => true) { return [] }
listeSuivants(filter = suivant =>true) { return [] }
listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); } listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
filterItems(filter, type = undefined) { return type ? this.itemTypes[type]?.filter(filter) ?? [] : []; } filterItems(filter, type = undefined) { return (type ? this.itemTypes[type] : this.items)?.filter(filter) ?? []; }
findItemLike(idOrName, type) { findItemLike(idOrName, type) {
return this.getItem(idOrName, type) return this.getItem(idOrName, type)
?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) }); ?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
} }
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); } getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
getEncombrementMax() { return 0 }
recompute() { }
/* -------------------------------------------- */ /* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { } async onPreUpdateItem(item, change, options, id) { }
@ -143,6 +212,29 @@ export class RdDBaseActor extends Actor {
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp)) .forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
} }
async creerObjetParMJ(object) {
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'creerObjetParMJ',
args: [object]
});
return;
}
await this.createEmbeddedDocuments('Item', [object])
}
/* -------------------------------------------- */
async cleanupConteneurs() {
let updates = this.itemTypes['conteneur']
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
if (updates.length > 0) {
await this.updateEmbeddedDocuments("Item", updates)
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
getFortune() { getFortune() {
return Monnaie.getFortune(this.itemTypes['monnaie']); return Monnaie.getFortune(this.itemTypes['monnaie']);
@ -153,7 +245,7 @@ export class RdDBaseActor extends Actor {
let item = this.getItem(id); let item = this.getItem(id);
if (item && item.isInventaire()) { if (item && item.isInventaire()) {
const quantite = Math.max(0, item.system.quantite + value); const quantite = Math.max(0, item.system.quantite + value);
await this.updateEmbeddedDocuments('Item', [{ _id: item.id, 'system.quantite': quantite }]); await item.update({ 'system.quantite': quantite });
} }
} }
@ -207,6 +299,7 @@ export class RdDBaseActor extends Actor {
if (fromActorId && !game.user.isGM) { if (fromActorId && !game.user.isGM) {
RdDBaseActor.remoteActorCall({ RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(), userId: Misc.connectedGMOrUser(),
tokenId: this.token?.id,
actorId: this.id, actorId: this.id,
method: 'ajouterSols', args: [sols, fromActorId] method: 'ajouterSols', args: [sols, fromActorId]
}); });
@ -247,13 +340,12 @@ export class RdDBaseActor extends Actor {
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined; const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined; const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot); const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id) ?? achat.vente.item; const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
if (!itemVendu) { if (!itemVendu) {
ui.notifications.warn("Erreur sur achat: rien à acheter<br>Si possible, transmettez les logs de la console aux développeurs"); ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !` : `Impossible de retrouver: ${achat.vente.item.name} !`);
console.log('Erreur sur achat: rien à acheter', achat);
return; return;
} }
if (!this.verifierQuantite(vendeur, itemVendu, quantite)) { if (vendeur && !vendeur.verifierQuantite(itemVendu, quantite)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`); ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
return return
} }
@ -264,7 +356,7 @@ export class RdDBaseActor extends Actor {
await this.decrementerVente(vendeur, itemVendu, quantite, cout); await this.decrementerVente(vendeur, itemVendu, quantite, cout);
if (acheteur) { if (acheteur) {
await acheteur.depenserSols(cout); await acheteur.depenserSols(cout);
const createdItemId = await acheteur.creerQuantiteItem(achat.vente.item, quantite); const createdItemId = await acheteur.creerQuantiteItem(itemVendu, quantite);
await acheteur.consommerNourritureAchetee(achat, achat.vente, createdItemId); await acheteur.consommerNourritureAchetee(achat, achat.vente, createdItemId);
} }
if (cout > 0) { if (cout > 0) {
@ -305,9 +397,9 @@ export class RdDBaseActor extends Actor {
return this.getFortune() >= cout; return this.getFortune() >= cout;
} }
verifierQuantite(vendeur, item, quantiteTotal) { verifierQuantite(item, quantiteDemande) {
const disponible = vendeur?.getQuantiteDisponible(item); const disponible = this.getQuantiteDisponible(item);
return disponible == undefined || disponible >= quantiteTotal; return disponible == undefined || disponible >= quantiteDemande;
} }
async consommerNourritureAchetee(achat, vente, createdItemId) { async consommerNourritureAchetee(achat, vente, createdItemId) {
@ -355,14 +447,6 @@ export class RdDBaseActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
computeMalusSurEncombrement() {
return 0;
}
getEncombrementMax() {
return 0;
}
async computeEncTotal() { async computeEncTotal() {
if (!this.pack) { if (!this.pack) {
this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0); this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0);
@ -371,6 +455,10 @@ export class RdDBaseActor extends Actor {
return 0; return 0;
} }
getEncTotal() {
return Math.floor(this.encTotal ?? 0);
}
async createItem(type, name = undefined) { async createItem(type, name = undefined) {
if (!name) { if (!name) {
name = 'Nouveau ' + Misc.typeName('Item', type); name = 'Nouveau ' + Misc.typeName('Item', type);
@ -383,14 +471,15 @@ export class RdDBaseActor extends Actor {
} }
async processDropItem(params) { async processDropItem(params) {
const targetActorId = this.id; const targetActorId = this.id
const sourceActorId = params.sourceActorId; const sourceActorId = params.sourceActorId
const itemId = params.itemId; const sourceTokenId = params.sourceTokenId
const destId = params.destId; const itemId = params.itemId
const srcId = params.srcId; const destId = params.destId
const srcId = params.srcId
if (sourceActorId && sourceActorId != targetActorId) { if (sourceActorId && sourceActorId != targetActorId) {
console.log("Moving objects", sourceActorId, targetActorId, itemId); console.log("Moving objects", sourceActorId, sourceTokenId, targetActorId, itemId);
this.moveItemsBetweenActors(itemId, sourceActorId); this.moveItemsBetweenActors(itemId, sourceActorId, sourceTokenId);
return false; return false;
} }
let result = true; let result = true;
@ -435,65 +524,35 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
conteneurPeutContenir(dest, item) { conteneurPeutContenir(dest, moved) {
if (!dest) { if (!dest) {
return true; return true;
} }
if (!dest.isConteneur()) { if (!dest.isConteneur()) {
return false; return false;
} }
const destData = dest if (moved.isConteneurContenu(dest)) {
if (this._isConteneurContenu(item, dest)) { ui.notifications.warn(`Impossible de déplacer un conteneur parent (${moved.name}) dans un de ses contenus ${dest.name} !`);
ui.notifications.warn(`Impossible de déplacer un conteneur parent (${item.name}) dans un de ses contenus ${destData.name} !`); return false;
return false; // Loop detected !
} }
// Calculer le total actuel des contenus // Calculer le total actuel des contenus
let encContenu = this.getRecursiveEnc(dest) - Number(destData.system.encombrement); const encContenu = dest.getEncContenu();
let newEnc = this.getRecursiveEnc(item); // Calculer le total actuel du nouvel objet const newEnc = moved.getEncTotal(); // Calculer le total actuel du nouvel objet
const placeDisponible = Math.roundDecimals(dest.system.capacite - encContenu - newEnc, 4)
// Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet // Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
if (Number(destData.system.capacite) < encContenu + newEnc) { if (placeDisponible < 0) {
ui.notifications.warn( ui.notifications.warn(
`Le conteneur ${dest.name} a une capacité de ${destData.system.capacite}, et contient déjà ${encContenu}. `Le conteneur ${dest.name} a une capacité de ${dest.system.capacite}, et contient déjà ${encContenu}.
Impossible d'y ranger: ${item.name} d'encombrement ${newEnc}!`); Impossible d'y ranger: ${moved.name} d'encombrement ${newEnc}!`);
return false; return false;
} }
return true; return true;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_isConteneurContenu(item, conteneur) { /** Ajoute un item dans un conteneur, sur la base de leurs ID */
if (item?.isConteneur()) { // Si c'est un conteneur, il faut vérifier qu'on ne le déplace pas vers un sous-conteneur lui appartenant
for (let id of item.system.contenu) {
let subObjet = this.getItem(id);
if (subObjet?.id == conteneur.id) {
return true; // Loop detected !
}
if (subObjet?.isConteneur()) {
return this._isConteneurContenu(subObjet, conteneur);
}
}
}
return false;
}
/* -------------------------------------------- */
getRecursiveEnc(objet) {
if (!objet) {
return 0;
}
const tplData = objet.system;
if (objet.type != 'conteneur') {
return Number(tplData.encombrement) * Number(tplData.quantite);
}
const encContenus = tplData.contenu.map(idContenu => this.getRecursiveEnc(this.getItem(idContenu)));
return encContenus.reduce(Misc.sum(), 0)
+ Number(tplData.encombrement) /* TODO? Number(tplData.quantite) -- on pourrait avoir plusieurs conteneurs...*/
}
/* -------------------------------------------- */
/** Ajoute un item dans un conteneur, sur la base
* de leurs ID */
async ajouterDansConteneur(item, conteneur, onAjouterDansConteneur) { async ajouterDansConteneur(item, conteneur, onAjouterDansConteneur) {
if (!conteneur) { if (!conteneur) {
// TODO: afficher // TODO: afficher
@ -501,10 +560,8 @@ export class RdDBaseActor extends Actor {
} }
else if (conteneur.isConteneur()) { else if (conteneur.isConteneur()) {
item.estContenu = true; item.estContenu = true;
await this.updateEmbeddedDocuments('Item', [{ const nouveauContenu = [...conteneur.system.contenu, item.id];
_id: conteneur.id, await conteneur.update({ 'system.contenu': nouveauContenu });
'system.contenu': [...conteneur.system.contenu, item.id]
}]);
onAjouterDansConteneur(item.id, conteneur.id); onAjouterDansConteneur(item.id, conteneur.id);
} }
} }
@ -564,19 +621,16 @@ export class RdDBaseActor extends Actor {
async enleverDeConteneur(item, conteneur, onEnleverDeConteneur) { async enleverDeConteneur(item, conteneur, onEnleverDeConteneur) {
if (conteneur?.isConteneur()) { if (conteneur?.isConteneur()) {
item.estContenu = false; item.estContenu = false;
await this.updateEmbeddedDocuments('Item', [{ const contenu = conteneur.system.contenu.filter(id => id != item.id);
_id: conteneur.id, await conteneur.update({ 'system.contenu': contenu });
'system.contenu': conteneur.system.contenu.filter(id => id != item.id)
}]);
onEnleverDeConteneur(); onEnleverDeConteneur();
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async moveItemsBetweenActors(itemId, sourceActorId) { async moveItemsBetweenActors(itemId, sourceActorId, sourceTokenId) {
let itemsList = [] let sourceActor = RdDBaseActor.getRealActor(sourceActorId, sourceTokenId)
let sourceActor = game.actors.get(sourceActorId); let itemsList = [{ id: itemId, conteneurId: undefined }]
itemsList.push({ id: itemId, conteneurId: undefined }); // Init list
sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list
const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id)) const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id))
@ -589,20 +643,17 @@ export class RdDBaseActor extends Actor {
for (let item of itemsList) { // Second boucle pour traiter la remise en conteneurs for (let item of itemsList) { // Second boucle pour traiter la remise en conteneurs
// gestion conteneur/contenu // gestion conteneur/contenu
if (item.conteneurId) { // l'Objet était dans un conteneur if (item.conteneurId) { // l'Objet était dans un conteneur
let newConteneurId = itemMap[item.conteneurId]; // Get conteneur const newConteneurId = itemMap[item.conteneurId];
let newConteneur = this.getItem(newConteneurId); const newConteneur = this.getItem(newConteneurId);
const newItemId = itemMap[item.id]; // Get newItem
let newItemId = itemMap[item.id]; // Get newItem
console.log('New conteneur filling!', newConteneur, newItemId, item); console.log('New conteneur filling!', newConteneur, newItemId, item);
let contenu = duplicate(newConteneur.system.contenu); const nouveauContenu = [...newConteneur.system.contenu, newItemId]
contenu.push(newItemId); await newConteneur.update({ 'system.contenu': nouveauContenu })
await this.updateEmbeddedDocuments('Item', [{ _id: newConteneurId, 'system.contenu': contenu }]);
} }
} }
for (let item of itemsList) { const deletedItemIds = itemsList.map(it => it.id)
await sourceActor.deleteEmbeddedDocuments('Item', [item.id]); await sourceActor.deleteEmbeddedDocuments('Item', deletedItemIds);
}
} }
_buildMapOldNewId(itemsList, newItems) { _buildMapOldNewId(itemsList, newItems) {
@ -628,5 +679,20 @@ export class RdDBaseActor extends Actor {
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride))); .then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
} }
actionImpossible(action) {
ui.notifications.info(`${this.name} ne peut pas faire cette action: ${action}`)
}
async roll() { this.actionImpossible("jet de caractéristiques") }
async jetEthylisme() { this.actionImpossible("jet d'éthylisme") }
async rollAppelChance() { this.actionImpossible("appel à la chance") }
async jetDeMoral() { this.actionImpossible("jet de moral") }
async actionPrincipale(item, onActionItem = async () => { }) {
switch (item.type) {
case TYPES.conteneur: return await item.sheet.render(true);
}
return undefined
}
} }

View File

@ -1,9 +1,7 @@
import { DialogItemAchat } from "../dialog-item-achat.js"; import { DialogItemAchat } from "../dialog-item-achat.js";
import { RdDItem } from "../item.js"; import { RdDItem } from "../item.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js"; import { RdDUtility } from "../rdd-utility.js";
import { RdDBaseActorSheet } from "./base-actor-sheet.js"; import { RdDBaseActorSheet } from "./base-actor-sheet.js";
import { RdDCommerce } from "./commerce.js";
/** /**
* Extend the basic ActorSheet with some very simple modifications * Extend the basic ActorSheet with some very simple modifications
@ -14,12 +12,9 @@ export class RdDCommerceSheet extends RdDBaseActorSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-actor-sheet.html", template: "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-actor-sheet.html",
width: 600, width: 600, height: 720,
height: 720, tabs: []
tabs: [],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
}); });
} }
get title() { get title() {

View File

@ -7,27 +7,18 @@ export class RdDCommerce extends RdDBaseActor {
return "systems/foundryvtt-reve-de-dragon/icons/services/commerce.webp"; return "systems/foundryvtt-reve-de-dragon/icons/services/commerce.webp";
} }
prepareData() {
super.prepareData();
}
prepareDerivedData() {
super.prepareDerivedData();
}
canReceive(item) { canReceive(item) {
if (item.isInventaire('all')) { return item.isInventaire('all');
return true;
}
return super.canReceive(item);
} }
getQuantiteDisponible(item) { getQuantiteDisponible(item) {
return this.system.illimite || item.isService() ? undefined : item.getQuantite(); return (this.system.illimite || item?.isService()) ? undefined : item.getQuantite();
} }
verifierFortune(cout) { verifierFortune(cout) {
return this.system.illimite || super.verifierFortune(cout); return this.system.illimite || super.verifierFortune(cout);
} }
async depenserSols(cout) { async depenserSols(cout) {
if (this.system.illimite) { if (this.system.illimite) {
return return

View File

@ -1,20 +1,17 @@
import { RdDActorSheet } from "./actor-sheet.js"; import { RdDBaseActorReveSheet } from "./base-actor-reve-sheet.js";
import { RdDBaseActorSangSheet } from "./base-actor-sang-sheet.js";
/** /**
* Extend the basic ActorSheet with some very simple modifications * Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet} * @extends {ActorSheet}
*/ */
export class RdDActorCreatureSheet extends RdDActorSheet { export class RdDCreatureSheet extends RdDBaseActorSangSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return mergeObject(super.defaultOptions, { return mergeObject(RdDBaseActorSangSheet.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-creature-sheet.html", template: "systems/foundryvtt-reve-de-dragon/templates/actor-creature-sheet.html",
width: 640, width: 640, height: 720
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
}); });
} }

63
module/actor/creature.js Normal file
View File

@ -0,0 +1,63 @@
import { ENTITE_INCARNE } from "../constants.js";
import { TYPES } from "../item.js";
import { STATUSES } from "../settings/status-effects.js";
import { RdDBaseActorSang } from "./base-actor-sang.js";
export class RdDCreature extends RdDBaseActorSang {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/creatures/bramart.svg";
}
isCreature() { return true }
canReceive(item) {
return item.type == TYPES.competencecreature || item.isInventaire();
}
async remiseANeuf() {
await this.removeEffects(e => true);
await this.supprimerBlessures(it => true);
await this.update({
'system.sante.endurance.value': this.system.sante.endurance.max,
'system.sante.vie.value': this.system.sante.vie.max,
'system.sante.fatigue.value': 0
});
}
async finDeRoundBlessures() {
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length;
if (nbGraves > 0) {
// Gestion blessure graves : -1 pt endurance par blessure grave
await this.santeIncDec("endurance", -nbGraves);
}
}
isEffectAllowed(statusId) {
return [STATUSES.StatusComma].includes(statusId);
}
isEntiteAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
let resonnance = this.system.sante.resonnance
return (resonnance.actors.find(it => it == attacker.id))
}
return true
}
/* -------------------------------------------- */
async setEntiteReveAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
let resonnance = duplicate(this.system.sante.resonnance);
if (resonnance.actors.find(it => it == attacker.id)) {
// déjà accordé
return;
}
await this.update({ "system.sante.resonnance": [...resonnance, attacker.id] });
}
else {
super.setEntiteReveAccordee(attacker)
}
}
}

View File

@ -0,0 +1,61 @@
import { RdDBaseActorReveSheet } from "./base-actor-reve-sheet.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js";
export class RdDActorEntiteSheet extends RdDBaseActorReveSheet {
/** @override */
static get defaultOptions() {
return mergeObject(RdDBaseActorReveSheet.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html",
width: 640, height: 720,
});
}
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 */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// On competence change
this.html.find('.creature-carac').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value));
});
this.html.find('.creature-niveau').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "niveau", parseInt(event.target.value));
});
this.html.find('.creature-dommages').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value));
});
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.confirmSubActeurDelete(this, actorResonance, li, () => {
console.log('Delete : ', actorId);
this.deleteSubActeur(actorId);
RdDUtility.slideOnDelete(this, li);
});
}
});
}
async deleteSubActeur(actorId) {
let newResonances = this.actor.system.sante.resonnance.actors.filter(id => id != actorId);
await this.actor.update({ 'system.sante.resonnance.actors': newResonances }, { renderSheet: false });
}
}

110
module/actor/entite.js Normal file
View File

@ -0,0 +1,110 @@
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
import { TYPES } from "../item.js";
import { Misc } from "../misc.js";
import { RdDEncaisser } from "../rdd-roll-encaisser.js";
import { STATUSES } from "../settings/status-effects.js";
import { RdDBaseActorReve } from "./base-actor-reve.js";
export class RdDEntite extends RdDBaseActorReve {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/entites/darquoine.webp";
}
canReceive(item) {
return item.type == TYPES.competencecreature
}
isEntite(typeentite = []) {
return (typeentite.length == 0 || typeentite.includes(this.system.definition.typeentite));
}
isNonIncarnee() { return this.isEntite([ENTITE_NONINCARNE]) }
getReveActuel() {
return Misc.toInt(this.system.carac.reve?.value)
}
getForce() { return this.getReve() }
getAgilite() { return this.getReve() }
getChance() { return this.getReve() }
getDraconicOuPossession() {
return this.itemTypes[TYPES.competencecreature]
.filter(it => it.system.categorie == 'possession')
.sort(Misc.descending(it => it.system.niveau))
.find(it => true);
}
async remiseANeuf() {
await this.removeEffects(e => true);
if (!this.isNonIncarnee()) {
await this.update({
'system.sante.endurance.value': this.system.sante.endurance.max
});
}
}
isDead() {
return this.isNonIncarnee() ? false : this.system.sante.endurance.value <= 0
}
async santeIncDec(name, inc, isCritique = false) {
if (name == 'endurance' && !this.isNonIncarnee()) {
const oldValue = this.system.sante.endurance.value;
const endurance = Math.max(0,
Math.min(oldValue + inc,
this.system.sante.endurance.max));
await this.update({ "system.sante.endurance.value": endurance })
await this.setEffect(STATUSES.StatusComma, endurance <= 0);
return {
perte: oldValue - endurance,
newValue: endurance
}
}
return {}
}
async encaisser() {
if (this.isNonIncarnee()) {
return
}
await RdDEncaisser.encaisser(this)
}
isEffectAllowed(statusId) {
return [STATUSES.StatusComma].includes(statusId);
}
async onAppliquerJetEncaissement(encaissement, attacker) {
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue,
endurance: perteEndurance.perte
});
}
isEntiteAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
let resonnance = this.system.sante.resonnance
return (resonnance.actors.find(it => it == attacker.id))
}
return true
}
/* -------------------------------------------- */
async setEntiteReveAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
let resonnance = duplicate(this.system.sante.resonnance);
if (resonnance.actors.find(it => it == attacker.id)) {
// déjà accordé
return;
}
resonnance.actors.push(attacker.id);
await this.update({ "system.sante.resonnance": resonnance });
}
else {
super.setEntiteReveAccordee(attacker)
}
}
}

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

@ -1,23 +1,33 @@
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "../rdd-utility.js";
import { RdDActorSheet } from "./actor-sheet.js"; import { RdDBaseActorSheet } from "./base-actor-sheet.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDActorVehiculeSheet extends RdDActorSheet { export class RdDActorVehiculeSheet extends RdDBaseActorSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
RdDUtility.initAfficheContenu(); return mergeObject(RdDBaseActorSheet.defaultOptions, {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-vehicule-sheet.html", template: "systems/foundryvtt-reve-de-dragon/templates/actor-vehicule-sheet.html",
width: 640, width: 640, height: 720,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
}); });
} }
/* -------------------------------------------- */
async getData() {
let formData = await super.getData();
mergeObject(formData,
{
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
effects: this.actor.effects.map(e => foundry.utils.deepClone(e)),
limited: this.actor.limited,
owner: this.actor.isOwner,
});
this.timerRecherche = undefined;
return formData;
}
activateListeners(html) { activateListeners(html) {
super.activateListeners(html); super.activateListeners(html);
if (!this.options.editable) return; if (!this.options.editable) return;

28
module/actor/vehicule.js Normal file
View File

@ -0,0 +1,28 @@
import { RdDBaseActor } from "./base-actor.js";
export class RdDVehicule extends RdDBaseActor {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/vehicules/charette.webp";
}
isVehicule() { return true }
canReceive(item) {
return item.isInventaire();
}
getEncombrementMax() {
return this.system.capacite_encombrement;
}
async vehicleIncDec(name, inc) {
if (!['resistance', 'structure'].includes(name)) {
return
}
const newValue = this.system.etat[name].value + inc;
if (0 <= newValue && newValue <= this.system.etat[name].max) {
await this.update({ [`system.etat.${name}.value`]: newValue })
}
}
}

View File

@ -14,8 +14,8 @@ export class ChatUtility {
case "msg_user_ui_notifications": return ChatUtility.onNotifyUser(sockmsg.data); case "msg_user_ui_notifications": return ChatUtility.onNotifyUser(sockmsg.data);
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static notifyUser(userId, level = 'info', message) { static notifyUser(userId, level = 'info', message) {
const socketData = { const socketData = {
@ -71,18 +71,14 @@ export class ChatUtility {
} }
static removeChatMessageId(messageId) { static removeChatMessageId(messageId) {
if (messageId){ if (messageId) {
ChatUtility.removeMessages({ messageId: messageId }); ChatUtility.removeMessages({ messageId: messageId });
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async createChatWithRollMode(name, chatOptions) { static async createChatWithRollMode(name, chatOptions) {
return await ChatUtility.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions); let rollMode = game.settings.get("core", "rollMode")
}
/* -------------------------------------------- */
static async createChatMessage(name, rollMode, chatOptions) {
switch (rollMode) { switch (rollMode) {
case "blindroll": // GM only case "blindroll": // GM only
if (!game.user.isGM) { if (!game.user.isGM) {
@ -122,9 +118,10 @@ export class ChatUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getWhisperRecipientsAndGMs(name) { static getWhisperRecipientsAndGMs(...names) {
let recep1 = ChatMessage.getWhisperRecipients(name) || []; let recipients = [...ChatMessage.getWhisperRecipients('GM')]
return recep1.concat(ChatMessage.getWhisperRecipients('GM')); names.forEach(name => recipients.push(...ChatMessage.getWhisperRecipients(name)))
return recipients
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

169
module/coeur/rdd-coeur.js Normal file
View File

@ -0,0 +1,169 @@
import { RdDBaseActor } from "../actor/base-actor.js";
import { ChatUtility } from "../chat-utility.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
const INFO_COEUR = 'info-coeur';
export class RdDCoeur {
static registerChatCallbacks(html) {
html.on("click", 'a.accepter-tendre-moment', event => {
RdDCoeur.accepterTendreMoment(RdDCoeur.extractInfoCoeur(event))
})
html.on("click", 'a.refuser-tendre-moment', event => {
RdDCoeur.refuserTendreMoment(RdDCoeur.extractInfoCoeur(event))
})
html.on("click", 'a.perdre-point-coeur-douceur', event => {
RdDCoeur.perdreEnDouceur(
RdDCoeur.extractInfoCoeur(event),
event.currentTarget.attributes['data-actor-id'].value)
})
}
static addTagsInfoCoeur(infoCoeur, chatMessage = undefined) {
if (chatMessage) {
infoCoeur.chatMessageId = chatMessage.id
}
else {
chatMessage = game.messages.get(infoCoeur.chatMessageId)
}
ChatUtility.setMessageData(chatMessage, INFO_COEUR, infoCoeur);
}
static extractInfoCoeur(event) {
return ChatUtility.getMessageData(ChatUtility.getChatMessage(event), INFO_COEUR)
}
static getInfoCoeur(sourceActorId, targetActorId) {
const sourceActor = game.actors.get(sourceActorId)
const targetActor = game.actors.get(targetActorId)
if (sourceActor && targetActor) {
return {
source: {
actor: RdDBaseActor.extractActorMin(sourceActor),
coeur: sourceActor.getPointsCoeur(targetActorId),
},
target: {
actor: RdDBaseActor.extractActorMin(targetActor),
coeur: targetActor.getPointsCoeur(sourceActorId),
}
}
}
return {}
}
static async toggleSubActeurCoeur(actorId, subActorId, toggleCoeur) {
const actor = game.actors.get(actorId)
const amoureux = actor.getSuivant(subActorId)
if (toggleCoeur <= amoureux.coeur) {
if (toggleCoeur > amoureux.prochainCoeur) {
toggleCoeur = amoureux.coeur
}
else {
toggleCoeur = amoureux.coeur - 1
}
}
else if (toggleCoeur <= amoureux.prochainCoeur) {
toggleCoeur = Math.max(amoureux.coeur, toggleCoeur - 1)
}
actor.setPointsCoeur(subActorId, Math.max(0, Math.min(toggleCoeur, 4)))
}
static async applyCoeurChateauDormant(actor, message) {
const newSuivants = duplicate(actor.system.subacteurs.suivants)
let count = 0
newSuivants.forEach(async link => {
const suivant = game.actors.get(link.id)
const prochainCoeur = link.prochainCoeur ?? 0;
const coeurCourant = link.coeur ?? 0;
const diff = prochainCoeur - coeurCourant
if (diff < 0) {
await actor.moralIncDec(-4);
link.coeur = Math.max(0, coeurCourant - 1)
link.prochainCoeur = link.coeur
message.content += `<br>Votre c&oelig;ur brisé pour ${suivant.name} vous fait perdre 4 points de moral, il vous reste ${link.coeur} points de C&oelig;ur.`
count++
}
else if (diff > 0) {
link.coeur = Math.min(prochainCoeur, 4)
message.content += `<br>Votre c&oelig;ur bat fort, vous avez maintenant ${link.coeur} points de C&oelig;ur pour ${suivant.name}.`
link.prochainCoeur = link.coeur
count++
}
}
)
if (count > 0) {
await actor.update({ 'system.subacteurs.suivants': newSuivants });
}
}
static async startSubActeurTendreMoment(actorId, subActeurId) {
const infoCoeur = RdDCoeur.getInfoCoeur(actorId, subActeurId)
if (infoCoeur.target?.actor.id) {
// TODO: passer par une fenêtre pour saisir sa proposition (lieu, heure, ...)
const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-proposer-tendre-moment.hbs`, infoCoeur)
const chatMessage = await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.target?.actor.name),
content: chatHtml
})
RdDCoeur.addTagsInfoCoeur(infoCoeur, chatMessage)
}
}
static async accepterTendreMoment(infoCoeur) {
const target = game.actors.get(infoCoeur.target.actor.id)
if (!target.isOwner) {
ui.notifications.warn(`vous ne pouvez pas accepter pour ${infoCoeur.target.actor.name}`)
return
}
ChatUtility.removeChatMessageId(infoCoeur.chatMessageId)
infoCoeur.target.jetTendre = (await (new Roll('1d6').evaluate({ async: true }))).total
infoCoeur.source.jetTendre = (await (new Roll('1d6').evaluate({ async: true }))).total
const diff = Math.abs(infoCoeur.source.jetTendre - infoCoeur.target.jetTendre)
for (let amoureux of [infoCoeur.source, infoCoeur.target]) {
const actorAmoureux = game.actors.get(amoureux.actor.id);
amoureux.situation = diff <= amoureux.coeur ? 'heureux' : 'neutre'
amoureux.gainMoral = await actorAmoureux.jetDeMoral(amoureux.situation)
}
const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-accepter-tendre-moment.hbs`, infoCoeur)
const chatMessage = await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.source?.actor.name, infoCoeur.target?.actor.name),
content: chatHtml
})
RdDCoeur.addTagsInfoCoeur(infoCoeur, chatMessage)
}
static async refuserTendreMoment(infoCoeur) {
const target = game.actors.get(infoCoeur.target.actor.id)
if (!target.isOwner) {
ui.notifications.warn(`vous ne pouvez pas refuser pour ${infoCoeur.target.actor.name}`)
return
}
ChatUtility.removeChatMessageId(infoCoeur.chatMessageId)
const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-refuser-tendre-moment.hbs`, infoCoeur)
await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.source?.actor.name, infoCoeur.target?.actor.name),
content: chatHtml
});
}
static async perdreEnDouceur(infoCoeur, actorId) {
const [amoureux, partenaire] = (infoCoeur.source.actor.id == actorId
? [infoCoeur.source, infoCoeur.target]
: (infoCoeur.target.actor.id == actorId
? [infoCoeur.target, infoCoeur.source]
: [undefined, undefined]))
if (amoureux.perteCoeur) {
ui.notifications.warn(`Le point de c&oelig;ur a déjà été perdu`)
}
else if (amoureux.coeur > 0) {
const actor = game.actors.get(actorId)
if (actor.isOwner) {
await actor.setPointsCoeur(partenaire?.actor.id, amoureux.coeur - 1, { immediat: true })
amoureux.perteCoeur = true
RdDCoeur.addTagsInfoCoeur(infoCoeur)
}
}
}
}

View File

@ -25,23 +25,13 @@ export class DialogChronologie extends Dialog {
journalId: game.settings.get(SYSTEM_RDD, LATEST_USED_JOURNAL_ID), journalId: game.settings.get(SYSTEM_RDD, LATEST_USED_JOURNAL_ID),
journaux: game.journal.filter(it => it.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)), journaux: game.journal.filter(it => it.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)),
timestamp: game.system.rdd.calendrier.timestamp, 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 html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-chronologie.html", dialogData);
const dialog = new DialogChronologie(html, dialogData); const dialog = new DialogChronologie(html, dialogData);
dialog.render(true); dialog.render(true);
} }
static getCurrentDateTime() {
return new Date().toLocaleString("sv-SE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit"
}).replace(" ", "T");
}
constructor(html, dialogData) { constructor(html, dialogData) {
const options = { const options = {
classes: ["DialogChronologie"], classes: ["DialogChronologie"],

View File

@ -14,7 +14,7 @@ export class DialogCreateSigneDraconique extends Dialog {
.map(actor => ({ .map(actor => ({
id: actor.id, id: actor.id,
name: actor.name, name: actor.name,
selected: true selected: false
})) }))
}; };
@ -111,7 +111,7 @@ export class DialogCreateSigneDraconique extends Dialog {
onSelectTmr(event) { onSelectTmr(event) {
const tmrName = this.html.find(event.currentTarget)?.data("tmr-name"); const tmrName = this.html.find(event.currentTarget)?.data("tmr-name");
const onTmr = this.tmrs.find(it => it.name == tmrName); const onTmr = this.dialogData.tmrs.find(it => it.name == tmrName);
if (onTmr){ if (onTmr){
onTmr.selected = event.currentTarget.checked; onTmr.selected = event.currentTarget.checked;
} }

View File

@ -1,37 +0,0 @@
export class DialogSelectTarget extends Dialog {
constructor(html, onSelectTarget, targets) {
const options = {
classes: ["rdd-dialog-select-target"],
width: 'fit-content',
height: 'fit-content',
'max-height': 600,
'z-index': 99999
};
const conf = {
title: "Choisir une cible",
content: html,
buttons: {}
};
super(conf, options);
this.onSelectTarget = onSelectTarget;
this.targets = targets;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("li.select-target").click((event) => {
this.targetSelected(this.html.find(event.currentTarget)?.data("token-id"));
});
}
targetSelected(tokenId) {
const target = this.targets.find(it => it.id == tokenId);
this.close();
if (target) {
this.onSelectTarget(target);
}
}
}

45
module/dialog-select.js Normal file
View File

@ -0,0 +1,45 @@
export class DialogSelect extends Dialog {
static extractIdNameImg(it) { return { id: it.id, name: it.name, img: it.img } }
static async select(selectData, onSelectChoice) {
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-select.html", selectData)
const dialogData = {
title: selectData.title ?? selectData.label,
content: html,
buttons: {}
}
const dialogOptions = {
classes: ["rdd-dialog-select"],
width: 'fit-content',
height: 'fit-content',
'max-height': 600,
'z-index': 99999
}
new DialogSelect(dialogData, dialogOptions, selectData, onSelectChoice).render(true)
}
constructor(dialogData, dialogOptions, selectionData, onSelectChoice) {
super(dialogData, dialogOptions)
this.selectionData = selectionData
this.onSelectChoice = onSelectChoice
}
activateListeners(html) {
super.activateListeners(html)
this.html = html
this.html.find("li.select-choice").click(event =>
this.choiceSelected(this.html.find(event.currentTarget)?.data("id"))
)
}
choiceSelected(selectedId) {
const selected = this.selectionData.find(it => it.id == selectedId)
this.close()
if (selected) {
this.onSelectChoice(selected)
}
}
}

View File

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

View File

@ -1,4 +1,5 @@
import { RdDItemCompetenceCreature } from "./item-competencecreature.js" import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
import { TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.js"; import { RdDCombatManager } from "./rdd-combat.js";
const nomCategorieParade = { const nomCategorieParade = {
@ -19,15 +20,15 @@ const nomCategorieParade = {
export class RdDItemArme extends Item { export class RdDItemArme extends Item {
static isArme(item) { static isArme(item) {
return (item.type == 'competencecreature' && item.system.iscombat) || item.type == 'arme'; return item.type == TYPES.arme || RdDItemCompetenceCreature.getCategorieAttaque(item);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getArme(arme) { static getArme(arme) {
switch (arme ? arme.type : '') { switch (arme ? arme.type : '') {
case 'arme': return arme; case TYPES.arme: return arme;
case 'competencecreature': case TYPES.competencecreature:
return RdDItemCompetenceCreature.armeNaturelle(arme); return RdDItemCompetenceCreature.armeCreature(arme);
} }
return RdDItemArme.mainsNues(); return RdDItemArme.mainsNues();
} }
@ -67,14 +68,14 @@ export class RdDItemArme extends Item {
return armeData.system.categorie_parade; return armeData.system.categorie_parade;
} }
// pour compatibilité avec des personnages existants // pour compatibilité avec des personnages existants
if (armeData.type == 'competencecreature' || armeData.system.categorie == 'creature') { if (armeData.type == TYPES.competencecreature || armeData.system.categorie == 'creature') {
return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : ''); return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '');
} }
if (!armeData.type.match(/arme|competencecreature/)) { if (!armeData.type.match(/arme|competencecreature/)) {
return ''; return '';
} }
if (armeData.system.competence == undefined) { if (armeData.system.competence == undefined) {
return 'competencecreature'; return TYPES.competencecreature;
} }
let compname = armeData.system.competence.toLowerCase(); let compname = armeData.system.competence.toLowerCase();
if (compname.match(/^(dague de jet|javelot|fouet|arc|arbalête|fronde|hache de jet|fléau)$/)) return ''; if (compname.match(/^(dague de jet|javelot|fouet|arc|arbalête|fronde|hache de jet|fléau)$/)) return '';
@ -156,23 +157,37 @@ export class RdDItemArme extends Item {
} }
return armeData; return armeData;
} }
static competence2Mains(arme) {
return arme.system.competence.replace(" 1 main", " 2 mains");
}
static competence1Mains(arme) {
return arme.system.competence.replace(" 2 mains", " 1 main");
}
static isArmeUtilisable(arme) { static isArmeUtilisable(arme) {
return arme.type == 'arme' && arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0); switch (arme.type) {
case TYPES.arme: return arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0)
case TYPES.competencecreature: return true
}
return false
} }
static ajoutCorpsACorps(armes, competences, carac) { static ajoutCorpsACorps(armes, actor) {
let corpsACorps = competences.find(it => it.name == 'Corps à corps') ?? { system: { niveau: -6 } }; armes.push(RdDItemArme.mainsNues(actor));
let init = RdDCombatManager.calculInitiative(corpsACorps.system.niveau, carac['melee'].value); armes.push(RdDItemArme.empoignade(actor));
armes.push(RdDItemArme.mainsNues({ niveau: corpsACorps.system.niveau, initiative: init }));
armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init }));
} }
static corpsACorps(mainsNuesActor) { static corpsACorps(actor) {
const corpsACorps = { let competence = actor?.getCompetenceCorpsACorps() ?? { system: { niveau: -6 } };
let melee = actor? actor.system.carac['melee'].value : 0
return {
_id: competence?.id,
name: 'Corps à corps', name: 'Corps à corps',
type: TYPES.arme,
img: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp', img: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp',
system: { system: {
initiative: RdDCombatManager.calculInitiative(competence.system.niveau, melee),
equipe: true, equipe: true,
rapide: true, rapide: true,
force: 0, force: 0,
@ -180,23 +195,23 @@ export class RdDItemArme extends Item {
dommagesReels: 0, dommagesReels: 0,
mortalite: 'non-mortel', mortalite: 'non-mortel',
competence: 'Corps à corps', competence: 'Corps à corps',
resistance: 1,
deuxmains: true,
categorie_parade: 'sans-armes' categorie_parade: 'sans-armes'
} }
}; }
mergeObject(corpsACorps.system, mainsNuesActor ?? {}, { overwrite: false });
return corpsACorps;
} }
static mainsNues(mainsNuesActor) { static mainsNues(actor) {
const mainsNues = RdDItemArme.corpsACorps(mainsNuesActor) const mainsNues = RdDItemArme.corpsACorps(actor)
mainsNues.name = 'Mains nues' mainsNues.name = 'Mains nues'
mainsNues.system.cac = 'pugilat' mainsNues.system.cac = 'pugilat'
mainsNues.system.baseInit = 4 mainsNues.system.baseInit = 4
return mainsNues; return mainsNues;
} }
static empoignade(mainsNuesActor) { static empoignade(actor) {
const empoignade = RdDItemArme.corpsACorps(mainsNuesActor) const empoignade = RdDItemArme.corpsACorps(actor)
empoignade.name = 'Empoignade' empoignade.name = 'Empoignade'
empoignade.system.cac = 'empoignade' empoignade.system.cac = 'empoignade'
empoignade.system.baseInit = 3 empoignade.system.baseInit = 3

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 niveau_max = xp_par_niveau.length - 10;
/* -------------------------------------------- */ /* -------------------------------------------- */
const limitesArchetypes = [ const limitesArchetypes = [
{ "niveau": 0, "nombreMax": 100, "reste": 100 }, { niveau: 0, nombreMax: 100 },
{ "niveau": 1, "nombreMax": 10, "reste": 10 }, { niveau: 1, nombreMax: 10 },
{ "niveau": 2, "nombreMax": 9, "reste": 9 }, { niveau: 2, nombreMax: 9 },
{ "niveau": 3, "nombreMax": 8, "reste": 8 }, { niveau: 3, nombreMax: 8 },
{ "niveau": 4, "nombreMax": 7, "reste": 7 }, { niveau: 4, nombreMax: 7 },
{ "niveau": 5, "nombreMax": 6, "reste": 6 }, { niveau: 5, nombreMax: 6 },
{ "niveau": 6, "nombreMax": 5, "reste": 5 }, { niveau: 6, nombreMax: 5 },
{ "niveau": 7, "nombreMax": 4, "reste": 4 }, { niveau: 7, nombreMax: 4 },
{ "niveau": 8, "nombreMax": 3, "reste": 3 }, { niveau: 8, nombreMax: 3 },
{ "niveau": 9, "nombreMax": 2, "reste": 2 }, { niveau: 9, nombreMax: 2 },
{ "niveau": 10, "nombreMax": 1, "reste": 1 }, { niveau: 10, nombreMax: 1 },
{ "niveau": 11, "nombreMax": 1, "reste": 1 } { niveau: 11, nombreMax: 1 },
]; ];
/* -------------------------------------------- */ /* -------------------------------------------- */
const categorieCompetences = { const categoriesCompetences = {
"generale": { base: -4, label: "Générales" }, "generale": { base: -4, label: "Générales" },
"particuliere": { base: -8, label: "Particulières" }, "particuliere": { base: -8, label: "Particulières" },
"specialisee": { base: -11, label: "Spécialisées" }, "specialisee": { base: -11, label: "Spécialisées" },
@ -49,16 +49,16 @@ const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item { export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getCategorieCompetences() { static getCategories() {
return categorieCompetences; return categoriesCompetences;
}
/* -------------------------------------------- */
static getNiveauBase(category) {
return categorieCompetences[category].base;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getLabelCategorie(category) { static getLabelCategorie(category) {
return categorieCompetences[category].label; return categoriesCompetences[category].label;
}
/* -------------------------------------------- */
static getNiveauBase(category, categories = categoriesCompetences) {
return categories[category]?.base ?? 0;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -79,10 +79,9 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static isCompetenceArme(competence) { static isCompetenceArme(competence) {
if (competence.isCompetence()) { if (competence.isCompetence() && !competence.isCorpsACorps() && !competence.isEsquive()) {
switch (competence.system.categorie) { switch (competence.system.categorie) {
case 'melee': case 'melee':
return !Grammar.toLowerCaseNoAccent(competence.name).includes('esquive');
case 'tir': case 'tir':
case 'lancer': case 'lancer':
return true; return true;
@ -93,10 +92,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static isArmeUneMain(competence) { static isArmeUneMain(competence) {
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("1 main"); return competence.isCompetenceArme() && competence.name.toLowerCase().includes("1 main");
} }
static isArme2Main(competence) { static isArme2Main(competence) {
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("2 main"); return competence.isCompetenceArme() && competence.name.toLowerCase().includes("2 main");
} }
static isThanatos(competence) { static isThanatos(competence) {
@ -192,7 +191,7 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static isNiveauBase(item) { 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());
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -259,13 +258,17 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static computeResumeArchetype(competences) { static computeResumeArchetype(competences) {
const computed = duplicate(limitesArchetypes); const computed = duplicate(limitesArchetypes);
computed.forEach(it => { it.nombre = 0; it.reste = it.nombreMax; });
competences.map(it => Math.max(0, it.system.niveau_archetype)) competences.map(it => Math.max(0, it.system.niveau_archetype))
.filter(n => n > 0) .filter(n => n > 0)
.forEach(n => { .forEach(n => {
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0 }; computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0, nombre: 0 };
computed[n].reste = computed[n].reste - 1; computed[n].reste--;
computed[n].nombre++;
}); });
return computed.filter(it => it.reste > 0 && it.niveau > 0); return computed.filter(it => it.niveau > 0);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -1,51 +1,112 @@
import { TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.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 { export class RdDItemCompetenceCreature extends Item {
/* -------------------------------------------- */ static getCategories() {
static setRollDataCreature(rollData) { return categories;
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 armeNaturelle(competencecreature) { static setRollDataCreature(rollData) {
if (RdDItemCompetenceCreature.isCompetenceAttaque(competencecreature)) { rollData.carac = { "carac_creature": { label: rollData.competence.name, value: rollData.competence.system.carac_value } }
// si c'est un Item compétence: cloner pour ne pas modifier lma compétence rollData.competence.system.defaut_carac = "carac_creature"
let arme = (competencecreature instanceof Item) ? competencecreature.clone(): competencecreature; rollData.selectedCarac = rollData.carac.carac_creature
mergeObject(arme.system, 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, action: item.isCompetencePossession() ? 'possession' : 'attaque',
initiative: RdDCombatManager.calculInitiative(competencecreature.system.niveau, competencecreature.system.carac_value), system: {
niveau: competencecreature.system.niveau, competence: arme.name,
equipe: true, cac: categorieAttaque == "naturelle" ? "naturelle" : "",
resistance: 100, niveau: item.system.niveau,
dommagesReels: arme.system.dommages, initiative: RdDCombatManager.calculInitiative(item.system.niveau, item.system.carac_value),
penetration: 0, equipe: true,
force: 0, resistance: 100,
rapide: true, dommagesReels: arme.system.dommages,
cac: competencecreature.system.isnaturelle ? "naturelle" : "", penetration: 0,
action: 'attaque' force: 0,
rapide: true,
}
}); });
return arme; return arme;
} }
console.error("RdDItemCompetenceCreature.toActionArme(", competencecreature, ") : impossible de transformer l'Item en arme");
return undefined; return undefined;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static isCompetenceAttaque(item) { static isCompetenceAttaque(item) {
return item.type == 'competencecreature' && item.system.iscombat; if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
case "possession":
return true
}
}
return undefined
} }
static getCategorieAttaque(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
case "possession":
case "parade":
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) { static isCompetenceParade(item) {
return item.type == 'competencecreature' && item.system.categorie_parade !== ""; return item.type == 'competencecreature' && item.system.categorie_parade !== "";

View File

@ -5,12 +5,14 @@ import { RdDItemCompetence } from "./item-competence.js";
import { RdDHerbes } from "./rdd-herbes.js"; import { RdDHerbes } from "./rdd-herbes.js";
import { RdDGemme } from "./rdd-gemme.js"; import { RdDGemme } from "./rdd-gemme.js";
import { HtmlUtility } from "./html-utility.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 { SYSTEM_RDD } from "./constants.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js"; import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js"; import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { RdDTimestamp } from "./time/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 * Extend the basic ItemSheet for RdD specific items
@ -98,16 +100,20 @@ export class RdDItemSheet extends ItemSheet {
isComestible: this.item.getUtilisationCuisine(), isComestible: this.item.getUtilisationCuisine(),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable) 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('personnage'); const competences = await SystemCompendiums.getCompetences('personnage');
formData.categorieCompetences = RdDItemCompetence.getCategorieCompetences() formData.categories = this.item.getCategories()
if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') { if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') {
formData.caracList = duplicate(game.system.model.Actor.personnage.carac) formData.caracList = duplicate(game.system.model.Actor.personnage.carac)
formData.caracList["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve) formData.caracList["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve)
formData.competences = competences; formData.competences = competences;
} }
if (this.item.type == 'arme') { if (this.item.type == 'arme') {
formData.competences = competences.filter(it => RdDItemCompetence.isCompetenceArme(it)) formData.competences = competences.filter(it => it.isCompetenceArme())
} }
if (['sort', 'sortreserve'].includes(this.item.type)) { if (['sort', 'sortreserve'].includes(this.item.type)) {
formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it)); formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it));
@ -151,7 +157,7 @@ export class RdDItemSheet extends ItemSheet {
super.activateListeners(html); super.activateListeners(html);
this.html = html; this.html = html;
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs') HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionnelles.isUsing('afficher-prix-joueurs')
|| game.user.isGM || game.user.isGM
|| !this.item.isOwned); || !this.item.isOwned);
HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique()); HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique());
@ -189,7 +195,8 @@ export class RdDItemSheet extends ItemSheet {
this.html.find('.creer-tache-livre').click((event) => this._getEventActor(event).creerTacheDepuisLivre(this.item)); 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.getActionRenderItem())); 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('.creer-potion-base').click((event) => this._getEventActor(event).actionHerbe(this.item));
this.html.find('input[name="system.cacher_points_de_tache"]').change(async event => await this.item.update({ 'system.cacher_points_de_tache': event.currentTarget.checked }));
this.html.find('.alchimie-tache a').click((event) => { this.html.find('.alchimie-tache a').click((event) => {
let actor = this._getEventActor(event); let actor = this._getEventActor(event);
@ -249,7 +256,8 @@ export class RdDItemSheet extends ItemSheet {
event.preventDefault(); event.preventDefault();
if (this.item.isCompetence()) { 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.item.system.base = level;
this.html.find('[name="system.base"]').val(level); this.html.find('[name="system.base"]').val(level);
} }

View File

@ -6,44 +6,55 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js"; import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDRaretes } from "./item/raretes.js"; import { RdDRaretes } from "./item/raretes.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
export const TYPES = { export const TYPES = {
competence: 'competence', competence: 'competence',
competencecreature: 'competencecreature', competencecreature: 'competencecreature',
empoignade: 'empoignade',
possession: 'possession',
blessure: 'blessure',
maladie: 'maladie',
poison: 'poison',
arme: 'arme', arme: 'arme',
armure: 'armure', armure: 'armure',
conteneur: 'conteneur', conteneur: 'conteneur',
sort: 'sort', objet: 'objet',
monnaie: 'monnaie',
gemme: 'gemme',
munition: 'munition',
nourritureboisson: 'nourritureboisson',
herbe: 'herbe', herbe: 'herbe',
faune: 'faune', plante: 'plante',
ingredient: 'ingredient', ingredient: 'ingredient',
faune: 'faune',
livre: 'livre', livre: 'livre',
potion: 'potion', 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', rencontre: 'rencontre',
queue: 'queue', queue: 'queue',
ombre: 'ombre', ombre: 'ombre',
souffle: 'souffle', souffle: 'souffle',
tete: 'tete', tete: 'tete',
casetmr: 'casetmr',
meditation: 'meditation', meditation: 'meditation',
recettealchimique: 'recettealchimique',
chant: 'chant',
danse: 'danse',
jeu: 'jeu',
recettecuisine: 'recettecuisine',
musique: 'musique',
maladie: 'maladie',
poison: 'poison',
oeuvre: 'oeuvre',
nourritureboisson: 'nourritureboisson',
service: 'service',
signedraconique: 'signedraconique', signedraconique: 'signedraconique',
gemme: 'gemme',
possession: 'possession',
sortreserve: 'sortreserve',
extraitpoetique: 'extraitpoetique',
tarot: 'tarot', tarot: 'tarot',
empoignade: 'empoignade' nombreastral: 'nombreastral',
extraitpoetique: 'extraitpoetique',
} }
const typesInventaireMateriel = [ const typesInventaireMateriel = [
TYPES.arme, TYPES.arme,
TYPES.armure, TYPES.armure,
@ -209,6 +220,23 @@ export class RdDItem extends Item {
isService() { return this.type == TYPES.service; } isService() { return this.type == TYPES.service; }
isCompetence() { return typesObjetsCompetence.includes(this.type) } isCompetence() { return typesObjetsCompetence.includes(this.type) }
isEsquive() {
return (this.isCompetence()
&& this.system.categorie == 'melee'
&& Grammar.includesLowerCaseNoAccent(this.name, 'Esquive'));
}
isCorpsACorps() {
return this.isCompetence()
&& this.system.categorie == 'melee'
&& Grammar.includesLowerCaseNoAccent(this.name, 'Corps à Corps')
}
isCompetenceArme() {
return this.isCompetence() && [ 'melee','tir', 'lancer'].includes(this.system.categorie)
}
isCompetencePossession() { return TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isTemporel() { return typesObjetsTemporels.includes(this.type) } isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) } isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
isDraconique() { return RdDItem.getItemTypesDraconiques().includes(this.type) } isDraconique() { return RdDItem.getItemTypesDraconiques().includes(this.type) }
@ -225,6 +253,13 @@ export class RdDItem extends Item {
isPresentDansMilieux(milieux) { isPresentDansMilieux(milieux) {
return this.getEnvironnements(milieux).length > 0 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) { getEnvironnements(milieux = undefined) {
const environnements = this.isInventaire() ? this.system.environnement : undefined; const environnements = this.isInventaire() ? this.system.environnement : undefined;
@ -361,7 +396,7 @@ export class RdDItem extends Item {
} }
getEncTotal() { getEncTotal() {
return (this.isService() ? 0 : this.getQuantite()) * this.getEnc(); return (this.getQuantite() ?? 0) * this.getEnc();
} }
getEnc() { getEnc() {
@ -376,6 +411,16 @@ export class RdDItem extends Item {
return Math.max(this.system.encombrement ?? 0, 0); return Math.max(this.system.encombrement ?? 0, 0);
} }
getEncContenu() {
return this.getContenu()
.map(it => it.getRecursiveEnc())
.reduce(Misc.sum(), 0);
}
getRecursiveEnc() {
return this.getEncTotal() + this.getEncContenu()
}
getEncHerbe() { getEncHerbe() {
switch (this.system.categorie) { switch (this.system.categorie) {
case 'Repos': case 'Soin': case 'Alchimie': case 'Repos': case 'Soin': case 'Alchimie':
@ -385,6 +430,18 @@ export class RdDItem extends Item {
} }
getContenu() {
if (this.isConteneur()) {
return this.system.contenu.map(idContenu => this.actor.getItem(idContenu));
}
return []
}
isConteneurContenu(conteneur) {
return this.getContenu()
.find(it => it.id == conteneur.id || it.isConteneurContenu(conteneur))
}
valeurTotale() { valeurTotale() {
return (this.isService() ? 1 : this.getQuantite()) * this.valeur() return (this.isService() ? 1 : this.getQuantite()) * this.valeur()
} }
@ -431,11 +488,11 @@ export class RdDItem extends Item {
if (this.actor?.isPersonnage()) { if (this.actor?.isPersonnage()) {
const warn = options.warnIfNot; const warn = options.warnIfNot;
if (this.getUtilisationCuisine() == 'brut') { if (this.getUtilisationCuisine() == 'brut') {
return 'Utiliser'; return 'Cuisiner';
} }
switch (this.type) { switch (this.type) {
case TYPES.nourritureboisson: return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn); case TYPES.nourritureboisson: return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
case TYPES.potion: return this._actionOrWarnQuantiteZero('Boire', warn); case TYPES.potion: return this._actionOrWarnQuantiteZero('Consommer', warn);
case TYPES.livre: return this._actionOrWarnQuantiteZero('Lire', warn); case TYPES.livre: return this._actionOrWarnQuantiteZero('Lire', warn);
case TYPES.herbe: return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined; 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; case TYPES.queue: case TYPES.ombre: return this.system.refoulement > 0 ? 'Refouler' : undefined;
@ -446,19 +503,8 @@ export class RdDItem extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
async actionPrincipale(actor, onActionItem = async () => { }) { async actionPrincipale(actor, onActionItem = async () => { }) {
if (!this.getActionPrincipale()) { if (!this.getActionPrincipale()) { return }
return; await actor?.actionPrincipale(this, onActionItem);
}
if (await actor.actionNourritureboisson(this, onActionItem)) {
return;
}
switch (this.type) {
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);
}
} }
_actionOrWarnQuantiteZero(actionName, warn) { _actionOrWarnQuantiteZero(actionName, warn) {
@ -656,7 +702,7 @@ export class RdDItem extends Item {
_armeChatData() { _armeChatData() {
return [ return [
`<b>Compétence</b>: ${this.system.competence}`, `<b>Compétence</b>: ${this.system.competence}`,
`<b>Dommages</b>: ${this.system.dommages}`, `<b>Dommages</b>: ${this.system.dommages} ${this.system.mortalite == 'non-mortel' ? '(Non mortel)' : ''}`,
`<b>Force minimum</b>: ${this.system.force}`, `<b>Force minimum</b>: ${this.system.force}`,
`<b>Resistance</b>: ${this.system.resistance}`, `<b>Resistance</b>: ${this.system.resistance}`,
...this._inventaireTemplateChatData() ...this._inventaireTemplateChatData()
@ -741,7 +787,7 @@ export class RdDItem extends Item {
`<b>Périodicité</b>: ${this.system.periodicite}`, `<b>Périodicité</b>: ${this.system.periodicite}`,
`<b>Fatigue</b>: ${this.system.fatigue}`, `<b>Fatigue</b>: ${this.system.fatigue}`,
`<b>Difficulté</b>: ${this.system.difficulte}`, `<b>Difficulté</b>: ${this.system.difficulte}`,
RdDItem.propertyIfDefined('Points de Tâche', this.system.points_de_tache, this.system.cacher_points_de_tache), RdDItem.propertyIfDefined('Points de Tâche', this.system.points_de_tache, !this.system.cacher_points_de_tache),
`<b>Points de Tâche atteints</b>: ${this.system.points_de_tache_courant}`] `<b>Points de Tâche atteints</b>: ${this.system.points_de_tache_courant}`]
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -750,7 +796,7 @@ export class RdDItem extends Item {
`<b>Compétence</b>: ${this.system.competence}`, `<b>Compétence</b>: ${this.system.competence}`,
`<b>Auteur</b>: ${this.system.auteur}`, `<b>Auteur</b>: ${this.system.auteur}`,
`<b>Difficulté</b>: ${this.system.difficulte}`, `<b>Difficulté</b>: ${this.system.difficulte}`,
RdDItem.propertyIfDefined('Points de Tâche', this.system.points_de_tache, this.system.cacher_points_de_tache), RdDItem.propertyIfDefined('Points de Tâche', this.system.points_de_tache, !this.system.cacher_points_de_tache),
...this._inventaireTemplateChatData() ...this._inventaireTemplateChatData()
] ]
} }

View File

@ -1,6 +1,6 @@
import { RdDItem } from "../item.js"; import { RdDItem } from "../item.js";
import { Misc } from "../misc.js"; import { Misc } from "../misc.js";
import { ReglesOptionelles } from "../settings/regles-optionelles.js"; import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
export class RdDItemArmure extends RdDItem { export class RdDItemArmure extends RdDItem {
@ -9,7 +9,7 @@ export class RdDItemArmure extends RdDItem {
} }
deteriorerArmure(dmg) { deteriorerArmure(dmg) {
if (!ReglesOptionelles.isUsing('deteriorationArmure') || this.system.protection == '0') { if (!ReglesOptionnelles.isUsing('deteriorationArmure') || this.system.protection == '0') {
return; return;
} }
let deterioration = (this.system.deterioration ?? 0) + dmg; let deterioration = (this.system.deterioration ?? 0) + dmg;

View File

@ -115,7 +115,10 @@ export class RdDItemBlessure extends RdDItem {
if (!isMaladeEmpoisonne && rolled.isSuccess && this.peutRetrograder(graviteMoindre, moindres)) { if (!isMaladeEmpoisonne && rolled.isSuccess && this.peutRetrograder(graviteMoindre, moindres)) {
message.content += ` -- une blessure ${label} cicatrise`; message.content += ` -- une blessure ${label} cicatrise`;
mergeObject(update, { mergeObject(update, {
system: { gravite: graviteMoindre, fin: { indexDate: timestamp.addJours(graviteMoindre).indexDate } } system: {
gravite: graviteMoindre,
temporel: { fin: { indexDate: timestamp.addJours(graviteMoindre).indexDate } }
}
}); });
} }
else { else {

View File

@ -29,7 +29,7 @@ export class RdDConteneurItemSheet extends RdDItemInventaireSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
prepareConteneurData(formData) { prepareConteneurData(formData) {
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes); RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets); this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems; formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
} }

View File

@ -2,7 +2,7 @@ import { RdDBaseActor } from "./actor/base-actor.js";
import { LOG_HEAD, SYSTEM_RDD } from "./constants.js"; import { LOG_HEAD, SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
import { Monnaie } from "./item-monnaie.js"; import { Monnaie } from "./item-monnaie.js";
import { RdDItem } from "./item.js"; import { RdDItem, TYPES } from "./item.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js"; import { RdDRaretes } from "./item/raretes.js";
@ -13,7 +13,7 @@ class Migration {
async applyItemsUpdates(computeUpdates) { async applyItemsUpdates(computeUpdates) {
await game.actors.forEach(async (actor) => { await game.actors.forEach(async (actor) => {
const actorItemUpdates = computeUpdates(actor.items); const actorItemUpdates = computeUpdates(actor.items).filter(it => it != undefined);
if (actorItemUpdates.length > 0) { if (actorItemUpdates.length > 0) {
console.log( console.log(
this.code, 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) { if (itemUpdates.length > 0) {
console.log(this.code, "Applying updates on items", itemUpdates); console.log(this.code, "Applying updates on items", itemUpdates);
await Item.updateDocuments(itemUpdates); await Item.updateDocuments(itemUpdates);
@ -65,7 +65,6 @@ class _1_5_34_migrationPngWebp {
} }
} }
class _10_0_16_MigrationSortsReserve extends Migration { class _10_0_16_MigrationSortsReserve extends Migration {
get code() { return "creation-item-sort-reserve"; } get code() { return "creation-item-sort-reserve"; }
get version() { return "10.0.16"; } get version() { return "10.0.16"; }
@ -370,6 +369,7 @@ class _10_4_6_ServicesEnCommerces extends Migration {
return itemToCreate; return itemToCreate;
} }
} }
class _10_5_0_UpdatePeriodicite extends Migration { class _10_5_0_UpdatePeriodicite extends Migration {
get code() { return "migration-periodicite-poisons-maladies"; } get code() { return "migration-periodicite-poisons-maladies"; }
get version() { return "10.5.0"; } get version() { return "10.5.0"; }
@ -458,6 +458,62 @@ class _10_7_0_MigrationBlessures extends Migration {
} }
} }
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 { export class Migrations {
static getMigrations() { static getMigrations() {
return [ return [
@ -474,6 +530,8 @@ export class Migrations {
new _10_4_6_ServicesEnCommerces(), new _10_4_6_ServicesEnCommerces(),
new _10_5_0_UpdatePeriodicite(), new _10_5_0_UpdatePeriodicite(),
new _10_7_0_MigrationBlessures(), new _10_7_0_MigrationBlessures(),
new _10_7_19_CategorieCompetenceCreature(),
new _10_7_19_PossessionsEntiteVictime(),
]; ];
} }
@ -490,7 +548,7 @@ export class Migrations {
migrate() { migrate() {
const currentVersion = game.settings.get(SYSTEM_RDD, "systemMigrationVersion"); const currentVersion = game.settings.get(SYSTEM_RDD, "systemMigrationVersion");
if (isNewerVersion(game.system.version, currentVersion)) { 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)); const migrations = Migrations.getMigrations().filter(m => isNewerVersion(m.version, currentVersion));
if (migrations.length > 0) { if (migrations.length > 0) {
migrations.sort((a, b) => this.compareVersions(a, b)); migrations.sort((a, b) => this.compareVersions(a, b));

View File

@ -42,7 +42,7 @@ export class Misc {
} }
static typeName(type, subType) { 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)}`)
: ''; : '';
} }
@ -63,8 +63,8 @@ export class Misc {
static keepDecimals(num, decimals) { static keepDecimals(num, decimals) {
if (decimals <= 0 || decimals > 6) return num; if (decimals <= 0 || decimals > 6) return num;
const decimal = Math.pow(10, parseInt(decimals)); const power10n = Math.pow(10, parseInt(decimals));
return Math.round(num * decimal) / decimal; return Math.round(num * power10n) / power10n;
} }
static getFractionHtml(diviseur) { static getFractionHtml(diviseur) {
@ -205,6 +205,7 @@ export class Misc {
const subset = this.findAllLike(value, elements, options); const subset = this.findAllLike(value, elements, options);
if (subset.length == 0) { if (subset.length == 0) {
console.log(`Aucune ${options.description} pour ${value}`);
return undefined return undefined
} }
if (subset.length == 1) { if (subset.length == 1) {

View File

@ -1,4 +1,5 @@
import { RdDCarac } from "./rdd-carac.js"; import { RdDCarac } from "./rdd-carac.js";
import { RdDPossession } from "./rdd-possession.js";
const conditionsTactiques = [ const conditionsTactiques = [
{ type: '', descr: '', dmg: 0, attaque: 0, parade: 0, esquive: true }, { type: '', descr: '', dmg: 0, attaque: 0, parade: 0, esquive: true },
@ -27,25 +28,26 @@ export class RdDBonus {
if (rollData.isEmpoignade && rollData.rolled?.isPart) { if (rollData.isEmpoignade && rollData.rolled?.isPart) {
return true return true
} }
if (RdDPossession.isDefensePossession(rollData)) {
return RdDPossession.isPossessionFinesse(rollData)
}
return rollData.attackerRoll?.particuliere == 'finesse'; return rollData.attackerRoll?.particuliere == 'finesse';
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static dmg(rollData, dmgActor, isEntiteIncarnee = false) { static dmg(rollData, dmgActor, isEntiteIncarnee = false) {
let dmg = { total: 0 }; const dmgArme = RdDBonus._dmgArme(rollData)
if (rollData.arme && rollData.arme.name.toLowerCase() == "esquive") { let dmg = {
// Specific case management total: 0,
ui.notifications.warn("Calcul de bonus dégats sur esquive !"); dmgArme: dmgArme,
} else { penetration: RdDBonus._peneration(rollData),
dmg.dmgArme = RdDBonus._dmgArme(rollData); dmgTactique: RdDBonus.dmgBonus(rollData.tactique),
dmg.penetration = RdDBonus._peneration(rollData); dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmg.dmgTactique = RdDBonus.dmgBonus(rollData.tactique); dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used),
dmg.dmgParticuliere = RdDBonus._dmgParticuliere(rollData); mortalite: RdDBonus._calculMortalite(rollData, isEntiteIncarnee),
dmg.dmgSurprise = RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used); dmgActor: RdDBonus._dmgPerso(dmgActor, rollData.selectedCarac?.label, dmgArme)
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, isEntiteIncarnee)
} }
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere;
return dmg; return dmg;
} }
@ -74,7 +76,7 @@ export class RdDBonus {
/* -------------------------------------------- */ /* -------------------------------------------- */
static _dmgArme(rollData) { static _dmgArme(rollData) {
if ( rollData.arme) { if (rollData.arme) {
let dmgBase = rollData.arme.system.dommagesReels ?? Number(rollData.arme.system.dommages ?? 0); 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) //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); return dmgBase + Math.min(dmgBase, rollData.arme.system.magique ? rollData.arme.system.ecaille_efficacite : 0);
@ -89,6 +91,9 @@ export class RdDBonus {
/* -------------------------------------------- */ /* -------------------------------------------- */
static _dmgPerso(dmgActor, categorie, dmgArme) { static _dmgPerso(dmgActor, categorie, dmgArme) {
if (categorie == undefined) {
return 0
}
switch (categorie) { switch (categorie) {
case "Tir": return 0; case "Tir": return 0;
case "Lancer": return Math.max(0, Math.min(dmgArme, dmgActor)); case "Lancer": return Math.max(0, Math.min(dmgArme, dmgActor));

View File

@ -1,7 +1,7 @@
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
const tableCaracDerivee = { const TABLE_CARACTERISTIQUES_DERIVEES = {
// xp: coût pour passer du niveau inférieur à ce niveau // xp: coût pour passer du niveau inférieur à ce niveau
1: { xp: 3, poids: "moins de 1kg", plusdom: -5, sconst: 0.5, sust: 0.1 }, 1: { xp: 3, poids: "moins de 1kg", plusdom: -5, sconst: 0.5, sust: 0.1 },
2: { xp: 3, poids: "1-5", plusdom: -4, sconst: 0.5, sust: 0.3 }, 2: { xp: 3, poids: "1-5", plusdom: -4, sconst: 0.5, sust: 0.3 },
@ -58,15 +58,10 @@ 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)/); 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) { static getCaracDerivee(value) {
const selectedCarac = rollData.selectedCarac; return TABLE_CARACTERISTIQUES_DERIVEES[Math.min(Math.max(Number(value), 1), 32)];
return !selectedCarac ||
rollData.ethylisme ||
RdDCarac.isChance(selectedCarac) ||
(RdDCarac.isReve(selectedCarac) && !rollData.competence);
} }
static computeTotal(carac, beaute = undefined) { static computeTotal(carac, beaute = undefined) {
const total = Object.values(carac ?? {}).filter(c => !c.derivee) const total = Object.values(carac ?? {}).filter(c => !c.derivee)
.map(it => parseInt(it.value)) .map(it => parseInt(it.value))
@ -82,7 +77,7 @@ export class RdDCarac {
/* -------------------------------------------- */ /* -------------------------------------------- */
static calculSConst(constitution) { static calculSConst(constitution) {
return Number(tableCaracDerivee[Number(constitution)].sconst); return RdDCarac.getCaracDerivee(constitution).sconst;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -93,7 +88,7 @@ export class RdDCarac {
} }
static getCaracXp(targetValue) { static getCaracXp(targetValue) {
return tableCaracDerivee[targetValue]?.xp ?? 200; return RdDCarac.getCaracDerivee(targetValue)?.xp ?? 200;
} }
@ -106,37 +101,4 @@ export class RdDCarac {
return Grammar.toLowerCaseNoAccent(selectedCarac?.label)?.match(/(apparence|force|agilite|dexterite|vue|ouie|odorat|empathie|melee|tir|lancer|derobee)/); return Grammar.toLowerCaseNoAccent(selectedCarac?.label)?.match(/(apparence|force|agilite|dexterite|vue|ouie|odorat|empathie|melee|tir|lancer|derobee)/);
} }
/* -------------------------------------------- */
static computeCarac(system) {
system.carac.force.value = Math.min(system.carac.force.value, parseInt(system.carac.taille.value) + 4);
system.carac.derobee.value = Math.floor(parseInt(((21 - system.carac.taille.value)) + parseInt(system.carac.agilite.value)) / 2);
let bonusDomKey = Math.floor((parseInt(system.carac.force.value) + parseInt(system.carac.taille.value)) / 2);
bonusDomKey = Math.min(Math.max(bonusDomKey, 0), 32); // Clamp de securite
let tailleData = tableCaracDerivee[bonusDomKey];
system.attributs.plusdom.value = tailleData.plusdom;
system.attributs.sconst.value = RdDCarac.calculSConst(system.carac.constitution.value);
system.attributs.sust.value = tableCaracDerivee[Number(system.carac.taille.value)].sust;
system.attributs.encombrement.value = (parseInt(system.carac.force.value) + parseInt(system.carac.taille.value)) / 2;
system.carac.melee.value = Math.floor((parseInt(system.carac.force.value) + parseInt(system.carac.agilite.value)) / 2);
system.carac.tir.value = Math.floor((parseInt(system.carac.vue.value) + parseInt(system.carac.dexterite.value)) / 2);
system.carac.lancer.value = Math.floor((parseInt(system.carac.tir.value) + parseInt(system.carac.force.value)) / 2);
system.sante.vie.max = Math.ceil((parseInt(system.carac.taille.value) + parseInt(system.carac.constitution.value)) / 2);
system.sante.vie.value = Math.min(system.sante.vie.value, system.sante.vie.max)
system.sante.endurance.max = Math.max(parseInt(system.carac.taille.value) + parseInt(system.carac.constitution.value), parseInt(system.sante.vie.max) + parseInt(system.carac.volonte.value));
system.sante.endurance.value = Math.min(system.sante.endurance.value, system.sante.endurance.max);
system.sante.fatigue.max = system.sante.endurance.max * 2;
system.sante.fatigue.value = Math.min(system.sante.fatigue.value, system.sante.fatigue.max);
//Compteurs
system.reve.reve.max = system.carac.reve.value;
system.compteurs.chance.max = system.carac.chance.value;
}
} }

View File

@ -9,7 +9,7 @@ import { RdDBonus } from "./rdd-bonus.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js"; import { RdDRoll } from "./rdd-roll.js";
import { RdDRollTables } from "./rdd-rolltables.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 { STATUSES } from "./settings/status-effects.js";
import { Targets } from "./targets.js"; import { Targets } from "./targets.js";
import { RdDEmpoignade } from "./rdd-empoignade.js"; import { RdDEmpoignade } from "./rdd-empoignade.js";
@ -87,7 +87,7 @@ export class RdDCombatManager extends Combat {
let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, 0); let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, 0);
if (!formula) { if (!formula) {
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') { 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.isCompetenceAttaque(it))
if (competence) { if (competence) {
rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, 0); rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, 0);
} }
@ -171,8 +171,7 @@ export class RdDCombatManager extends Combat {
if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) { if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) {
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)"); ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)");
} }
if ((arme.system.unemain && arme.system.competence) || if (arme.system.unemain && arme.system.competence) {
(arme.system.competence.toLowerCase().includes("corps à corps"))) {
actions.push(RdDCombatManager.$prepareAttaqueArme({ actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme, arme: arme,
infoMain: "(1 main)", infoMain: "(1 main)",
@ -187,7 +186,7 @@ export class RdDCombatManager extends Combat {
arme: arme, arme: arme,
infoMain: "(2 mains)", infoMain: "(2 mains)",
dommagesReel: Number(tableauDommages[1]), dommagesReel: Number(tableauDommages[1]),
competence: arme.system.competence.replace(" 1 main", " 2 mains"), competence: RdDItemArme.competence2Mains(arme),
carac: carac, carac: carac,
competences: competences competences: competences
})); }));
@ -230,15 +229,17 @@ export class RdDCombatManager extends Combat {
} }
static listActionsCreature(competences) { static listActionsCreature(competences) {
return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it)) return competences
.map(it => RdDItemCompetenceCreature.armeNaturelle(it)); .filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(it => RdDItemCompetenceCreature.armeCreature(it))
.filter(it => it != undefined);
} }
static listActionsPossessions(actor) { static listActionsPossessions(actor) {
return RdDCombatManager._indexActions(actor.getPossessions().map(p => { return RdDCombatManager._indexActions(actor.getPossessions().map(p => {
return { return {
name: p.name, name: p.name,
action: 'conjurer', action: 'possession',
system: { system: {
competence: p.name, competence: p.name,
possessionid: p.system.possessionid, possessionid: p.system.possessionid,
@ -255,15 +256,14 @@ export class RdDCombatManager extends Combat {
return actions; return actions;
} }
if (actor.isCreatureEntite()) { if (actor.isCreatureEntite()) {
actions = actions.concat(RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature'])); actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']);
} else if (actor.isPersonnage()) { } else if (actor.isPersonnage()) {
// Recupération des items 'arme' // Recupération des items 'arme'
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
.concat(RdDItemArme.empoignade())
.concat(RdDItemArme.mainsNues());
const competences = actor.itemTypes['competence']; const competences = actor.itemTypes['competence'];
actions = actions.concat(RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac)); const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
.concat(RdDItemArme.empoignade(actor))
.concat(RdDItemArme.mainsNues(actor));
actions = RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac);
if (actor.system.attributs.hautrevant.value) { if (actor.system.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } }); actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } });
@ -351,7 +351,7 @@ export class RdDCombatManager extends Combat {
} else if (combatant.actor.getSurprise() == "demi") { } else if (combatant.actor.getSurprise() == "demi") {
initOffset = 0; initOffset = 0;
initInfo = "Demi Surprise" initInfo = "Demi Surprise"
} else if (action.action == 'conjurer') { } else if (action.action == 'possession') {
initOffset = 10; initOffset = 10;
caracForInit = combatant.actor.getReveActuel(); caracForInit = combatant.actor.getReveActuel();
initInfo = "Possession" initInfo = "Possession"
@ -450,7 +450,7 @@ export class RdDCombat {
if (Misc.isUniqueConnectedGM()) { if (Misc.isUniqueConnectedGM()) {
let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId); let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId);
if (turn?.actor) { if (turn?.actor) {
RdDCombat.displayActorCombatStatus(combat, turn.actor); RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token.id);
// TODO Playaudio for player?? // TODO Playaudio for player??
} }
} }
@ -514,8 +514,12 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
static _callJetDeVie(event) { static _callJetDeVie(event) {
let actorId = event.currentTarget.attributes['data-actorId'].value; let actorId = event.currentTarget.attributes['data-actorId'].value;
let actor = game.actors.get(actorId); let tokenId = event.currentTarget.attributes['data-tokenId'].value;
actor.jetVie(); let token = canvas.tokens.placeables.find(t => t.id == tokenId)
const actor = token?.actor ?? game.actors.get(actorId);
if (actor?.isOwner) {
actor.jetDeVie();
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -541,7 +545,7 @@ export class RdDCombat {
} }
}); });
} }
html.on("click", '#chat-jet-vie', event => { html.on("click", 'a.chat-jet-vie', event => {
event.preventDefault(); event.preventDefault();
RdDCombat._callJetDeVie(event); RdDCombat._callJetDeVie(event);
}); });
@ -750,11 +754,11 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async attaque(competence, arme) { async attaque(competence, arme) {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) { if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return; return
} }
if (arme.system.cac == 'empoignade') { if (arme.system.cac == 'empoignade') {
RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender) RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender)
return; return
} }
RdDEmpoignade.checkEmpoignadeEnCours(this.attacker) RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
@ -789,7 +793,6 @@ export class RdDCombat {
let rollData = { let rollData = {
passeArme: randomID(16), passeArme: randomID(16),
mortalite: arme?.system.mortalite, mortalite: arme?.system.mortalite,
coupsNonMortels: false,
competence: competence, competence: competence,
surprise: this.attacker.getSurprise(true), surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true), surpriseDefenseur: this.defender.getSurprise(true),
@ -821,8 +824,8 @@ export class RdDCombat {
// finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum // finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum
// rapidité seulement en mêlée, si l'arme le permet, et si la difficulté libre est de -1 minimum // rapidité seulement en mêlée, si l'arme le permet, et si la difficulté libre est de -1 minimum
const isForce = !rollData.arme.system.empoignade; const isForce = !rollData.arme.system.empoignade;
const isFinesse = rollData.arme.system.empoignade || isMeleeDiffNegative; const isFinesse = rollData.tactique != 'charge' && (rollData.arme.system.empoignade || isMeleeDiffNegative);
const isRapide = !rollData.arme.system.empoignade && isMeleeDiffNegative && rollData.arme.system.rapide; const isRapide = rollData.tactique != 'charge' && !rollData.arme.system.empoignade && isMeleeDiffNegative && rollData.arme.system.rapide;
// si un seul choix possible, le prendre // si un seul choix possible, le prendre
if (isForce && !isFinesse && !isRapide) { if (isForce && !isFinesse && !isRapide) {
return await this.choixParticuliere(rollData, "force"); return await this.choixParticuliere(rollData, "force");
@ -886,8 +889,8 @@ export class RdDCombat {
} }
// # utilisation esquive // # utilisation esquive
const corpsACorps = this.defender.getCompetence("Corps à corps", { onMessage: it => console.info(it, this.defender) }); const corpsACorps = this.defender.getCompetenceCorpsACorps({ onMessage: it => console.info(it, this.defender) });
const esquives = duplicate(this.defender.getCompetences("esquive", { onMessage: it => console.info(it, this.defender) })) const esquives = duplicate(this.defender.getCompetencesEsquive())
esquives.forEach(e => e.system.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0); esquives.forEach(e => e.system.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0);
const paramChatDefense = { const paramChatDefense = {
@ -1048,7 +1051,7 @@ export class RdDCombat {
competence: this.defender.getCompetence(competenceParade), competence: this.defender.getCompetence(competenceParade),
arme: armeParade, arme: armeParade,
surprise: this.defender.getSurprise(true), 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), needResist: RdDItemArme.needArmeResist(attackerRoll.arme, armeParade),
carac: this.defender.system.carac, carac: this.defender.system.carac,
show: {} show: {}
@ -1166,7 +1169,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async computeDeteriorationArme(defenderRoll) { async computeDeteriorationArme(defenderRoll) {
if (!ReglesOptionelles.isUsing('resistanceArmeParade')) { if (!ReglesOptionnelles.isUsing('resistanceArmeParade')) {
return; return;
} }
const attackerRoll = defenderRoll.attackerRoll; const attackerRoll = defenderRoll.attackerRoll;
@ -1215,7 +1218,7 @@ export class RdDCombat {
} }
} }
// Si l'arme de parade n'est pas un bouclier, jet de désarmement (p.132) // 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({ let desarme = await RdDResolutionTable.rollData({
caracValue: this.defender.getForce(), caracValue: this.defender.getForce(),
finalLevel: Misc.toInt(defenderRoll.competence.system.niveau) - dmg, finalLevel: Misc.toInt(defenderRoll.competence.system.niveau) - dmg,
@ -1230,7 +1233,7 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async computeRecul(defenderRoll) { // Calcul du recul (p. 132) async computeRecul(defenderRoll) { // Calcul du recul (p. 132)
const attackerRoll = defenderRoll.attackerRoll; const attackerRoll = defenderRoll.attackerRoll;
if (ReglesOptionelles.isUsing('recul') && this._isForceOuCharge(attackerRoll)) { if (ReglesOptionnelles.isUsing('recul') && this._isForceOuCharge(attackerRoll)) {
const impact = this._computeImpactRecul(attackerRoll); const impact = this._computeImpactRecul(attackerRoll);
const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact }); const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact });
if (rollRecul.rolled.isSuccess) { if (rollRecul.rolled.isSuccess) {
@ -1295,7 +1298,7 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async displayActorCombatStatus(combat, actor) { static async displayActorCombatStatus(combat, actor, tokenId) {
let formData = { let formData = {
combatId: combat._id, combatId: combat._id,
alias: actor.name, alias: actor.name,
@ -1304,12 +1307,18 @@ export class RdDCombat {
blessuresStatus: actor.computeResumeBlessure(), blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(), SConst: actor.getSConst(),
actorId: actor.id, actorId: actor.id,
tokenId: tokenId,
isGrave: actor.countBlessures(it => it.isGrave()) > 0, isGrave: actor.countBlessures(it => it.isGrave()) > 0,
isCritique: actor.countBlessures(it => it.isCritique()) > 0 isCritique: actor.countBlessures(it => it.isCritique()) > 0
} }
await ChatMessage.create({
ChatUtility.createChatWithRollMode(actor.name, { content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs`, formData),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html`, formData) alias: actor.name
});
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData),
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name),
alias: actor.name
}); });
} }
} }

View File

@ -1,10 +1,10 @@
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js"; import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
export class RdDConfirm { export class RdDConfirm {
/* -------------------------------------------- */ /* -------------------------------------------- */
static confirmer(options, autresActions) { 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) { if (options.bypass) {
options.onAction(); options.onAction();
} }
@ -47,7 +47,7 @@ export class RdDConfirm {
icon: '<i class="fas fa-user-check"></i>', icon: '<i class="fas fa-user-check"></i>',
label: options.buttonLabel + "<br>et ne plus demander", label: options.buttonLabel + "<br>et ne plus demander",
callback: () => { callback: () => {
ReglesOptionelles.set(options.settingConfirmer, false); ReglesOptionnelles.set(options.settingConfirmer, false);
options.onAction(); options.onAction();
} }
} }

View File

@ -164,7 +164,7 @@ export class RdDDice {
if (options.showDice == HIDE_DICE || !game.modules.get("dice-so-nice")?.active || !game.dice3d) { if (options.showDice == HIDE_DICE || !game.modules.get("dice-so-nice")?.active || !game.dice3d) {
return; return;
} }
let { whisper, blind } = RdDDice._getWhisperBlind(options); let { whisper, blind } = RdDDice._getWhisperBlind(options);
if (options.forceDiceResult?.total) { if (options.forceDiceResult?.total) {
let terms = await RdDDice._getForcedTerms(options); let terms = await RdDDice._getForcedTerms(options);
@ -197,48 +197,25 @@ export class RdDDice {
function terms1d100(total) { function terms1d100(total) {
const unites = total % 10; const unites = total % 10;
const dizaines = Math.floor(total / 10); const dizaines = Math.floor(total / 10);
return [{ return [
resultLabel: dizaines * 10, { type: "d100", result: dizaines, resultLabel: dizaines * 10, vectors: [], options: {}, d100Result: total },
d100Result: total, { type: "d10", result: unites, resultLabel: unites, vectors: [], options: {}, d100Result: total }
result: dizaines, ];
type: "d100",
vectors: [],
options: {}
},
{
resultLabel: unites,
d100Result: total,
result: unites,
type: "d10",
vectors: [],
options: {}
}];
} }
async function terms2d10(total) { async function terms2d10(total) {
if (total>20 || total<2) { return undefined } if (total > 20 || total < 2) { return undefined }
let first = await RdDDice.d10(); const first = await RdDDice.fakeD10(Math.min(10, total - 1));
let second = Math.min(total-first, 10); const second = total - first;
first = Math.max(first, total-second); return [
return [{ { type: "d10", result: first, resultLabel: first, vectors: [], options: {} },
resultLabel:first, { type: "d10", result: second, resultLabel: second, vectors: [], options: {} }
result: first, ];
type: "d10",
vectors: [],
options: {}
},
{
resultLabel: second,
result: second,
type: "d10",
vectors: [],
options: {}
}];
} }
} }
static async d10() { static async fakeD10(faces) {
let roll = new Roll('1d10'); let roll = new Roll(`1d${faces}`);
await roll.evaluate({ async: true }); await roll.evaluate({ async: true });
return roll.total; return roll.total;
} }

View File

@ -4,6 +4,8 @@ import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"; import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ChatUtility } from "./chat-utility.js"; import { ChatUtility } from "./chat-utility.js";
import { STATUSES } from "./settings/status-effects.js"; import { STATUSES } from "./settings/status-effects.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { TYPES } from "./item.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -14,6 +16,47 @@ export class RdDEmpoignade {
static init() { 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) { static checkEmpoignadeEnCours(actor) {
// TODO: autoriser la perception? la comédie/séduction? // TODO: autoriser la perception? la comédie/séduction?
@ -24,74 +67,92 @@ export class RdDEmpoignade {
return false; 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) { static isEmpoignadeEnCours(actor) {
return actor.itemTypes['empoignade'].find(it => it.system.pointsemp > 0) return actor.itemTypes[TYPES.empoignade].find(it => it.system.pointsemp > 0)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getEmpoignadeById(actor, id) { static getEmpoignadeById(actor, id) {
let emp = actor.itemTypes['empoignade'].find(it => it.system.empoignadeid == id) let emp = actor.itemTypes[TYPES.empoignade].find(it => it.system.empoignadeid == id)
return emp && duplicate(emp) || undefined; return emp && duplicate(emp) || undefined;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getEmpoignade(attacker, defender) { static getEmpoignade(attacker, defender) {
let emp = attacker.itemTypes['empoignade'].find(it => it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) let emp = attacker.itemTypes[TYPES.empoignade].find(it =>
if (!emp) { (it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
emp = attacker.itemTypes['empoignade'].find(it => it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id) (it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
} )
if (emp) { if (emp) {
return duplicate(emp); return duplicate(emp);
} }
return undefined; return undefined;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getMalusTaille(emp, attacker, defender) { static getMalusTaille(emp, attacker, defender) {
// Si pas empoigné, alors 0 // Si pas empoigné, alors 0
if (emp.system.pointsemp == 0) { if (emp.system.pointsemp == 0) {
return 0 return 0
} }
// Malus de -1 si différence de taille de 2 ou plus (p 135) // 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, ...)
if (attacker.system.carac.taille.value < defender.system.carac.taille.value - 1) { const diffTaille = attacker.system.carac.taille.value - defender.system.carac.taille.value;
return attacker.system.carac.taille.value - (defender.system.carac.taille.value - 1) const diffTailleAbs = Math.abs(diffTaille)
const signDiff = diffTaille > 0 ? 1 : -1
if (diffTailleAbs > 2) {
return signDiff * (diffTailleAbs - 1)
} }
return 0 return 0
} }
/* -------------------------------------------- */ static isActionAutorisee(mode, attacker, defender) {
static async onAttaqueEmpoignadeValidee(attacker, defender) { const acting = RdDEmpoignade.isActionDefenseur(mode) ? defender : attacker;
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender) if (acting.getUserLevel(game.user) < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
const isNouvelle = empoignade == undefined; ui.notifications.warn(`Vous n'êtes pas autorisé à choisir l'action de ${acting.name}`)
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender)) return false;
}
return true
}
let mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer" static isActionDefenseur(mode) {
switch (mode) {
let rollData = { case "liberer":
mode: mode, case "contrer-empoigner":
isEmpoignade: true, return true;
competence: attacker.getCompetence("Corps à corps"),
selectedCarac: attacker.system.carac.melee,
empoignade: empoignade,
attackerId: attacker.id,
attackerName: attacker.name,
defenderName: defender.name,
defenderId: defender.id,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
if (empoignade.system.pointsemp >= 2) {
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-actions.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
} else {
await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle);
} }
return false;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async onAttaqueEmpoignade(attacker, defender) { static async onAttaqueEmpoignade(attacker, defender) {
if (!RdDEmpoignade.isActionAutorisee("empoigner", attacker, defender)) {
return
}
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender) let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const isNouvelle = empoignade == undefined; const isNouvelle = empoignade == undefined;
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender)) empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
@ -105,6 +166,38 @@ export class RdDEmpoignade {
} }
} }
/* -------------------------------------------- */
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.getCompetenceCorpsACorps(),
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) { static async onAttaqueEmpoignadeFromItem(empoignade) {
let attacker = game.actors.get(empoignade.system.empoigneurid) let attacker = game.actors.get(empoignade.system.empoigneurid)
@ -112,6 +205,20 @@ export class RdDEmpoignade {
await this.onAttaqueEmpoignadeValidee(attacker, defender) await this.onAttaqueEmpoignadeValidee(attacker, defender)
} }
static async onImmobilisation(attacker, defender, empoignade) {
const rollData = {
mode: "immobilise",
empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetenceCorpsACorps()
}
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) { static async $rollAttaqueEmpoignade(attacker, rollData, isNouvelle = false) {
const dialog = await RdDRoll.create(attacker, rollData, const dialog = await RdDRoll.create(attacker, rollData,
@ -119,35 +226,40 @@ export class RdDEmpoignade {
{ {
name: 'jet-empoignade', name: 'jet-empoignade',
label: 'Empoigner', label: 'Empoigner',
callbacks: [ callbacks: [{ action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, isNouvelle) },]
{ condition: r => (r.rolled.isSuccess), action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, true, isNouvelle) },
{ condition: r => (r.rolled.isEchec), action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, false, isNouvelle) },
]
}); });
dialog.render(true); dialog.render(true);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async $onRollEmpoignade(rollData, isSuccess, isNouvelle = false) { static async $onRollEmpoignade(rollData, isNouvelle = false) {
let attacker = game.actors.get(rollData.attackerId) let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defenderId) let defender = game.actors.get(rollData.defender.id)
let empoignade = rollData.empoignade
empoignade.isSuccess = isSuccess;
if (isSuccess && isNouvelle) { if (rollData.rolled.isSuccess && isNouvelle) {
const objectEmpoignade = rollData.empoignade.toObject();
// Creer l'empoignade sur attaquant/defenseur // Creer l'empoignade sur attaquant/defenseur
await attacker.createEmbeddedDocuments('Item', [empoignade.toObject()]) attacker.creerObjetParMJ(objectEmpoignade);
await defender.createEmbeddedDocuments('Item', [empoignade.toObject()]) 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'); let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-resultat.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async onDefenseEmpoignade(rollData, defenseMode, competenceName = "Corps à corps", carac = "melee") { static async onDefenseEmpoignade(attackerRoll, mode, competenceName = "Corps à corps", carac = "melee") {
let attacker = game.actors.get(rollData.attackerId) let attacker = game.actors.get(attackerRoll.attacker.id)
let defender = game.actors.get(rollData.defenderId) let defender = game.actors.get(attackerRoll.defender.id)
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender) let empoignade = this.getEmpoignade(attacker, defender)
if (!empoignade) { if (!empoignade) {
@ -156,18 +268,23 @@ export class RdDEmpoignade {
} }
empoignade = duplicate(empoignade) empoignade = duplicate(empoignade)
rollData.mode = defenseMode let defenderRoll = {
rollData.empoignade = empoignade mode, attacker, defender, empoignade, attackerRoll,
rollData.competence = defender.getCompetence(competenceName), diffLibre: attackerRoll.diffLibre,
rollData.selectedCarac = defender.system.carac[carac], attaqueParticuliere: attackerRoll.particuliere,
rollData.malusTaille = RdDEmpoignade.getMalusTaille(empoignade, defender, attacker) competence: defender.getCompetence(competenceName),
surprise: defender.getSurprise(true),
await RdDEmpoignade.$rollDefenseEmpoignade(defender, rollData); 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, rollData) { static async $rollDefenseEmpoignade(defender, defenderRoll) {
const dialog = await RdDRoll.create(defender, rollData, const dialog = await RdDRoll.create(defender, defenderRoll,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-empoignade.html' }, { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-empoignade.html' },
{ {
name: 'empoignade', name: 'empoignade',
@ -183,7 +300,7 @@ export class RdDEmpoignade {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async $onRollContrerLiberer(rollData) { static async $onRollContrerLiberer(rollData) {
let empoignade = rollData.empoignade let empoignade = rollData.empoignade
if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) { if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) {
empoignade.system.pointsemp++ empoignade.system.pointsemp++
RdDEmpoignade.$updateEtatEmpoignade(empoignade) RdDEmpoignade.$updateEtatEmpoignade(empoignade)
@ -192,13 +309,12 @@ export class RdDEmpoignade {
empoignade.system.pointsemp-- empoignade.system.pointsemp--
RdDEmpoignade.$updateEtatEmpoignade(empoignade) RdDEmpoignade.$updateEtatEmpoignade(empoignade)
} }
if (empoignade.system.pointsemp >= 2) {
let attacker = game.actors.get(empoignade.system.empoigneurid)
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-actions.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
}
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html') 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);
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -223,16 +339,15 @@ export class RdDEmpoignade {
let defender = game.actors.get(empoignade.system.empoigneid) let defender = game.actors.get(empoignade.system.empoigneid)
let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid) let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
await defender.deleteEmbeddedDocuments('Item', [emp._id]) await defender.deleteEmbeddedDocuments('Item', [emp._id])
//let attacker = game.actors.get(empoignade.system.empoigneurid)
//emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
//await attacker.deleteEmbeddedDocuments('Item', [emp._id])
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async entrainerAuSol(rollData) { static async entrainerAuSol(rollData) {
let attacker = game.actors.get(rollData.attackerId) let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defenderId) let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender) let empoignade = this.getEmpoignade(attacker, defender)
empoignade.system.ausol = true empoignade.system.ausol = true
@ -242,25 +357,32 @@ export class RdDEmpoignade {
await defender.setEffect(STATUSES.StatusProne, true); await defender.setEffect(STATUSES.StatusProne, true);
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html'); let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async projeterAuSol(rollData) { static async projeterAuSol(rollData) {
let attacker = game.actors.get(rollData.attackerId) let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defenderId) let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender) let empoignade = this.getEmpoignade(attacker, defender)
await defender.setEffect(STATUSES.StatusProne, true); await defender.setEffect(STATUSES.StatusProne, true);
await this.$deleteEmpoignade(empoignade) await this.$deleteEmpoignade(empoignade)
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html'); let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async perteEndurance(rollData, perteMode) { static async perteEndurance(rollData, perteMode) {
let attacker = game.actors.get(rollData.attackerId) let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defenderId) let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender) let empoignade = this.getEmpoignade(attacker, defender)
//console.log("Perte d'endurance :!!!", perteMode) //console.log("Perte d'endurance :!!!", perteMode)
@ -278,7 +400,7 @@ export class RdDEmpoignade {
await defender.santeIncDec("endurance", -(3 * Math.floor(endValue / 4))); await defender.santeIncDec("endurance", -(3 * Math.floor(endValue / 4)));
} }
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html'); let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -1,12 +1,17 @@
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { TYPES } from "./item.js";
export class RdDHotbar { export class RdDHotbar {
static async addToHotbar(item, slot) { static async createItemMacro(item, slot, armeCompetence = undefined) {
let command = `game.system.rdd.RdDHotbar.rollMacro("${item.name}", "${item.type}");`; const itemName = item.name;
let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command)); let macroName = itemName + RdDHotbar.$macroNameSuffix(armeCompetence);
let command = `game.system.rdd.RdDHotbar.rollMacro("${itemName}", "${item.type}", "${armeCompetence}");`
let macro = game.macros.contents.find(m => (m.name === itemName) && (m.command === command));
if (!macro) { if (!macro) {
macro = await Macro.create({ macro = await Macro.create({
name: item.name, name: macroName,
type: "script", type: "script",
img: item.img, img: item.img,
command: command command: command
@ -15,6 +20,59 @@ export class RdDHotbar {
await game.user.assignHotbarMacro(macro, slot); await game.user.assignHotbarMacro(macro, slot);
} }
static $macroNameSuffix(armeCompetence) {
switch (armeCompetence) {
case 'unemain': return ' (1 main)';
case 'deuxmains': return ' (2 main)';
case 'tir': return ' (tir)';
case 'lancer': return ' (lancer)';
case 'pugilat': return ' (pugilat)';
case 'empoignade': return ' (empoignade)';
}
return ''
}
static async addToHotbar(item, slot) {
switch (item?.type ?? '') {
case TYPES.arme:
{
// Les armes peuvent avoir plusieurs usages
if (item.system.competence != '') {
if (item.system.unemain) {
await this.createItemMacro(item, slot++, 'unemain')
}
if (item.system.deuxmains) {
await this.createItemMacro(item, slot++, 'deuxmains')
}
}
if (item.system.lancer != '') {
await this.createItemMacro(item, slot++, 'lancer')
}
if (item.system.tir != '') {
await this.createItemMacro(item, slot++, 'lancer')
}
}
return
case TYPES.competencecreature:
const categorie = RdDItemCompetenceCreature.getCategorieAttaque(item) ?? 'competence';
await this.createItemMacro(item, slot, categorie)
return
default:
case TYPES.competence:
await this.createItemMacro(item, slot++, 'competence')
if (item.isCorpsACorps()) {
await this.createItemMacro(item, slot++, 'pugilat')
await this.createItemMacro(item, slot++, 'empoignade')
}
else if (item.isCompetenceArme()) {
ui.notifications.info(`${item.name} est une compétence d'arme, la macro n'est pas liée à un arme.<br>
Créez la macro depuis l'arme ou l'onglet combat pour garder les automatisations de combat.`);
}
return
}
}
/** /**
* Create a macro when dropping an entity on the hotbar * Create a macro when dropping an entity on the hotbar
* Item - open roll dialog for item * Item - open roll dialog for item
@ -23,20 +81,19 @@ export class RdDHotbar {
*/ */
static initDropbar() { static initDropbar() {
Hooks.on("hotbarDrop", (bar, documentData, slot) => { Hooks.on('hotbarDrop', (bar, documentData, slot) => {
// Create item macro if rollable item - weapon, spell, prayer, trait, or skill // Create item macro if rollable item - weapon, spell, prayer, trait, or skill
if (documentData.type == "Item") { if (documentData.type == 'Item') {
let item = fromUuidSync(documentData.uuid) const item = fromUuidSync(documentData.uuid) ?? this.actor.items.get(documentData.uuid)
if (item == undefined) { console.log('DROP', documentData, item)
item = this.actor.items.get(documentData.uuid) switch (item?.type) {
case TYPES.arme:
case TYPES.competence:
case TYPES.competencecreature:
this.addToHotbar(item, slot)
return false
} }
console.log("DROP", documentData, item)
if (!item || (item.type != "arme" && item.type != "competence")) {
return true
}
this.addToHotbar(item, slot)
return false
} }
return true return true
@ -44,12 +101,14 @@ export class RdDHotbar {
} }
/** Roll macro */ /** Roll macro */
static rollMacro(itemName, itemType, bypassData) { static rollMacro(itemName, itemType, categorieArme = 'competence') {
const speaker = ChatMessage.getSpeaker(); const speaker = ChatMessage.getSpeaker();
let actor; let actor;
if (speaker.token) actor = game.actors.tokens[speaker.token]; if (speaker.token) actor = game.actors.tokens[speaker.token];
if (!actor) actor = game.actors.get(speaker.actor); if (!actor) actor = game.actors.get(speaker.actor);
if (!actor) {
return ui.notifications.warn(`Impossible de trouver le personnage concerné`);
}
let item = actor?.items.find(it => it.name === itemName && it.type == itemType) ?? undefined; let item = actor?.items.find(it => it.name === itemName && it.type == itemType) ?? undefined;
if (!item) { if (!item) {
return ui.notifications.warn(`Impossible de trouver l'objet de cette macro`); return ui.notifications.warn(`Impossible de trouver l'objet de cette macro`);
@ -57,10 +116,23 @@ export class RdDHotbar {
// Trigger the item roll // Trigger the item roll
switch (item.type) { switch (item.type) {
case "arme": case TYPES.arme:
return actor.rollArme(item); return actor.rollArme(item, categorieArme);
case "competence": case TYPES.competence:
return actor.rollCompetence(itemName); if (item.isCorpsACorps()) {
switch (categorieArme) {
case 'pugilat':
return actor.rollArme(RdDItemArme.mainsNues(actor), 'competence');
case 'empoignade':
return actor.rollArme(RdDItemArme.empoignade(actor), 'competence');
}
}
return actor.rollCompetence(item);
case TYPES.competencecreature:
return item.system.iscombat && !item.system.isparade
? actor.rollArme(item, categorieArme)
: actor.rollCompetence(item);
} }
} }

View File

@ -15,7 +15,7 @@ import { RdDCombatManager, RdDCombat } from "./rdd-combat.js";
import { ChatUtility } from "./chat-utility.js"; import { ChatUtility } from "./chat-utility.js";
import { StatusEffects } from "./settings/status-effects.js"; import { StatusEffects } from "./settings/status-effects.js";
import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js"; import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js"; import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js" import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js"; import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { RdDHerbes } from "./rdd-herbes.js"; import { RdDHerbes } from "./rdd-herbes.js";
@ -29,11 +29,13 @@ import { Environnement } from "./environnement.js";
import { RdDActor } from "./actor.js"; import { RdDActor } from "./actor.js";
import { RdDBaseActor } from "./actor/base-actor.js"; import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDCommerce } from "./actor/commerce.js"; import { RdDCommerce } from "./actor/commerce.js";
import { RdDEntite } from "./actor/entite.js";
import { RdDVehicule } from "./actor/vehicule.js";
import { RdDActorSheet } from "./actor-sheet.js"; import { RdDActorSheet } from "./actor-sheet.js";
import { RdDCommerceSheet } from "./actor/commerce-sheet.js"; import { RdDCommerceSheet } from "./actor/commerce-sheet.js";
import { RdDActorCreatureSheet } from "./actor-creature-sheet.js"; import { RdDCreatureSheet } from "./actor/creature-sheet.js";
import { RdDActorVehiculeSheet } from "./actor-vehicule-sheet.js"; import { RdDActorEntiteSheet } from "./actor/entite-sheet.js";
import { RdDActorEntiteSheet } from "./actor-entite-sheet.js"; import { RdDActorVehiculeSheet } from "./actor/vehicule-sheet.js";
import { RdDItem } from "./item.js"; import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.js"; import { RdDItemBlessure } from "./item/blessure.js";
@ -59,6 +61,9 @@ import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js";
import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js"; import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js";
import { AppAstrologie } from "./sommeil/app-astrologie.js"; import { AppAstrologie } from "./sommeil/app-astrologie.js";
import { RdDItemArmure } from "./item/armure.js"; import { RdDItemArmure } from "./item/armure.js";
import { AutoAdjustDarkness as AutoAdjustDarkness } from "./time/auto-adjust-darkness.js";
import { RdDCreature } from "./actor/creature.js";
import { RdDTMRDialog } from "./rdd-tmr-dialog.js";
/** /**
* RdD system * RdD system
@ -68,9 +73,10 @@ import { RdDItemArmure } from "./item/armure.js";
export class SystemReveDeDragon { export class SystemReveDeDragon {
static start() { static start() {
const system = new SystemReveDeDragon(); const system = new SystemReveDeDragon()
Hooks.once('init', async () => await system.onInit()); Hooks.once('init', async () => await system.onInit())
Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d)); Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d))
Hooks.once('ready', () => system.onReady())
} }
constructor() { constructor() {
@ -90,10 +96,10 @@ export class SystemReveDeDragon {
} }
this.actorClasses = { this.actorClasses = {
commerce: RdDCommerce, commerce: RdDCommerce,
creature: RdDActor, creature: RdDCreature,
entite: RdDActor, entite: RdDEntite,
personnage: RdDActor, personnage: RdDActor,
vehicule: RdDActor, vehicule: RdDVehicule,
} }
} }
@ -149,7 +155,7 @@ export class SystemReveDeDragon {
Actors.unregisterSheet("core", ActorSheet); Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet(SYSTEM_RDD, RdDCommerceSheet, { types: ["commerce"], makeDefault: true }); Actors.registerSheet(SYSTEM_RDD, RdDCommerceSheet, { types: ["commerce"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorSheet, { types: ["personnage"], makeDefault: true }); Actors.registerSheet(SYSTEM_RDD, RdDActorSheet, { types: ["personnage"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorCreatureSheet, { types: ["creature"], makeDefault: true }); Actors.registerSheet(SYSTEM_RDD, RdDCreatureSheet, { types: ["creature"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true }); Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true }); Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet); Items.unregisterSheet("core", ItemSheet);
@ -182,11 +188,12 @@ export class SystemReveDeDragon {
CONFIG.Combat.documentClass = RdDCombatManager; CONFIG.Combat.documentClass = RdDCombatManager;
// préparation des différents modules // préparation des différents modules
AutoAdjustDarkness.init();
RdDTimestamp.init(); RdDTimestamp.init();
RdDCalendrier.init(); RdDCalendrier.init();
SystemCompendiums.init(); SystemCompendiums.init();
DialogChronologie.init(); DialogChronologie.init();
ReglesOptionelles.init(); ReglesOptionnelles.init();
RdDUtility.init(); RdDUtility.init();
RdDDice.init(); RdDDice.init();
RdDCommands.init(); RdDCommands.init();
@ -196,12 +203,12 @@ export class SystemReveDeDragon {
RdDCompendiumOrganiser.init(); RdDCompendiumOrganiser.init();
EffetsDraconiques.init() EffetsDraconiques.init()
TMRUtility.init(); TMRUtility.init();
await RdDTMRDialog.init()
RdDHotbar.initDropbar(); RdDHotbar.initDropbar();
RdDPossession.init(); RdDPossession.init();
TMRRencontres.init(); TMRRencontres.init();
Environnement.init(); Environnement.init();
Hooks.once('ready', () => this.onReady());
} }
initSystemSettings() { initSystemSettings() {

View File

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

View File

@ -1,7 +1,7 @@
import { ChatUtility } from "./chat-utility.js"; import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.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 * difficultés au delà de -10
@ -28,7 +28,7 @@ const reussites = [
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false } const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
/* -------------------------------------------- */ /* -------------------------------------------- */
const caracMaximumResolution = 60; const CARAC_MAXIMUM_RESOLUTION = 40;
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDResolutionTable { export class RdDResolutionTable {
static resolutionTable = this.build() static resolutionTable = this.build()
@ -36,7 +36,7 @@ export class RdDResolutionTable {
/* -------------------------------------------- */ /* -------------------------------------------- */
static build() { static build() {
let table = [] let table = []
for (var caracValue = 0; caracValue <= caracMaximumResolution; caracValue++) { for (var caracValue = 0; caracValue <= CARAC_MAXIMUM_RESOLUTION; caracValue++) {
table[caracValue] = this._computeRow(caracValue); table[caracValue] = this._computeRow(caracValue);
} }
return table; return table;
@ -91,13 +91,17 @@ export class RdDResolutionTable {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') { static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
return await ChatUtility.createChatWithRollMode(actor?.userName ?? game.user.name, { return await ChatUtility.createChatWithRollMode(RdDResolutionTable.actorChatName(actor), {
content: await RdDResolutionTable.buildRollDataHtml(rollData, actor, template) 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 || {}; rollData.show = rollData.show || {};
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData); return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
} }
@ -122,7 +126,7 @@ export class RdDResolutionTable {
rolled.bonus = rollData.bonus; rolled.bonus = rollData.bonus;
rolled.factorHtml = Misc.getFractionHtml(rollData.diviseurSignificative); 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.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll);
rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel; rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
} }

View File

@ -112,7 +112,7 @@ export class RdDRollResolutionTable extends Dialog {
async updateRollResult() { async updateRollResult() {
let rollData = this.rollData; let rollData = this.rollData;
rollData.caracValue = parseInt(rollData.selectedCarac.value) rollData.caracValue = parseInt(rollData.selectedCarac.value)
rollData.finalLevel = this._computeFinalLevel(rollData); rollData.finalLevel = Misc.toInt(rollData.diffConditions) + Misc.toInt(rollData.diffLibre);
const htmlTable = await RdDResolutionTable.buildHTMLTable({ const htmlTable = await RdDResolutionTable.buildHTMLTable({
carac: rollData.caracValue, carac: rollData.caracValue,
@ -129,12 +129,6 @@ export class RdDRollResolutionTable extends Dialog {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_computeFinalLevel(rollData) {
const diffConditions = Misc.toInt(rollData.diffConditions);
const diffLibre = Misc.toInt(rollData.diffLibre);
return diffLibre + diffConditions;
}
async close() { async close() {
await super.close(); await super.close();

View File

@ -6,7 +6,7 @@ import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js"; import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js"; import { RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.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 * Extend the base Dialog entity to select roll parameters
@ -22,7 +22,7 @@ export class RdDRoll extends Dialog {
const html = await renderTemplate(dialogConfig.html, rollData); const html = await renderTemplate(dialogConfig.html, rollData);
let options = { classes: ["rdd-roll-dialog"], width: 600, height: 'fit-content', 'z-index': 99999, close: html => {} }; let options = { classes: ["rdd-roll-dialog"], width: 650, height: 'fit-content', 'z-index': 99999, close: html => { } };
if (dialogConfig.close) { if (dialogConfig.close) {
options.close = dialogConfig.close; options.close = dialogConfig.close;
} }
@ -37,6 +37,7 @@ export class RdDRoll extends Dialog {
difficultesLibres: CONFIG.RDD.difficultesLibres, difficultesLibres: CONFIG.RDD.difficultesLibres,
etat: actor.getEtatGeneral(), etat: actor.getEtatGeneral(),
moral: actor.getMoralTotal(), /* La valeur du moral pour les jets de volonté */ moral: actor.getMoralTotal(), /* La valeur du moral pour les jets de volonté */
amoureux: actor.listeSuivants(it => it.coeur > 0),
carac: actor.system.carac, carac: actor.system.carac,
finalLevel: 0, finalLevel: 0,
diffConditions: 0, diffConditions: 0,
@ -45,13 +46,12 @@ export class RdDRoll extends Dialog {
use: { use: {
moral: false, /* Est-ce que le joueur demande d'utiliser le moral ? Utile si le joueur change plusieurs fois de carac associée. */ moral: false, /* Est-ce que le joueur demande d'utiliser le moral ? Utile si le joueur change plusieurs fois de carac associée. */
libre: true, libre: true,
coeur: undefined,
conditions: true, conditions: true,
surenc: actor.isSurenc(), surenc: actor.isSurenc(),
encTotal: true encTotal: true
}, },
isMalusEncombrementTotal: RdDItemCompetence.isMalusEncombrementTotal(rollData.competence), isMalusEncombrementTotal: RdDItemCompetence.isMalusEncombrementTotal(rollData.competence),
malusArmureValue: actor.getMalusArmure(),
surencMalusValue: actor.computeMalusSurEncombrement(),
encTotal: actor.getEncTotal(), encTotal: actor.getEncTotal(),
ajustementAstrologique: actor.ajustementAstrologique(), ajustementAstrologique: actor.ajustementAstrologique(),
surprise: actor.getSurprise(false), surprise: actor.getSurprise(false),
@ -85,7 +85,7 @@ export class RdDRoll extends Dialog {
if (RdDBonus.isDefenseAttaqueFinesse(rollData)) { if (RdDBonus.isDefenseAttaqueFinesse(rollData)) {
facteurSign *= 2; facteurSign *= 2;
} }
if (!ReglesOptionelles.isUsing('tripleSignificative')) { if (!ReglesOptionnelles.isUsing('tripleSignificative')) {
facteurSign = Math.min(facteurSign, 4); facteurSign = Math.min(facteurSign, 4);
} }
return facteurSign; return facteurSign;
@ -176,6 +176,15 @@ export class RdDRoll extends Dialog {
this.rollData.competence = this.rollData.competences.find(it => it.name == competence); this.rollData.competence = this.rollData.competences.find(it => it.name == competence);
this.updateRollResult(html); this.updateRollResult(html);
}); });
this.html.find('.select-suivant-coeur').change((event) => {
const selectedActorId = event.currentTarget.value;
this.rollData.use.coeur = this.actor.getSuivant(selectedActorId)
if (this.rollData.use.coeur) {
this.html.find(".utilisation-coeur img.selected-suivant-coeur").attr('src', this.rollData.use.coeur?.img)
this.html.find(".utilisation-coeur img.selected-suivant-coeur").attr('title', this.rollData.use.coeur?.name)
}
this.updateRollResult(html);
});
this.html.find('.roll-signedraconique').change((event) => { this.html.find('.roll-signedraconique').change((event) => {
let sortKey = Misc.toInt(event.currentTarget.value); let sortKey = Misc.toInt(event.currentTarget.value);
this.setSelectedSigneDraconique(this.rollData.signes[sortKey]); this.setSelectedSigneDraconique(this.rollData.signes[sortKey]);
@ -187,7 +196,7 @@ export class RdDRoll extends Dialog {
console.log("RdDRollSelectDialog - Cout reve", ptreve); console.log("RdDRollSelectDialog - Cout reve", ptreve);
this.updateRollResult(html); this.updateRollResult(html);
}); });
this.html.find("[name='coupsNonMortels']").change((event) => { this.html.find("input.check-mortalite").change((event) => {
this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel"; this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel";
this.updateRollResult(html); this.updateRollResult(html);
}); });
@ -291,22 +300,14 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async updateRollResult(html) { async updateRollResult(html) {
let rollData = this.rollData; const rollData = this.rollData;
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor.getBonusDegat()) rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor.getBonusDegat())
rollData.caracValue = parseInt(rollData.selectedCarac.value) rollData.caracValue = parseInt(rollData.selectedCarac.value)
rollData.mortalite = rollData.attackerRoll?.dmg.mortalite ?? rollData.dmg.mortalite ?? 'mortel'; rollData.dmg.mortalite = rollData.dmg.mortalite ?? 'mortel';
rollData.coupsNonMortels = (rollData.attackerRoll?.dmg.mortalite ?? rollData.dmg.mortalite) == 'non-mortel';
rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac); rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac);
let dmgText = Misc.toSignedString(rollData.dmg.total);
switch (rollData.mortalite) {
case 'non-mortel': dmgText = `(${dmgText}) non-mortel`; break;
case 'empoignade': dmgText = `empoignade`; break;
}
RollDataAjustements.calcul(rollData, this.actor); RollDataAjustements.calcul(rollData, this.actor);
rollData.finalLevel = this._computeFinalLevel(rollData);
const resolutionTable = await RdDResolutionTable.buildHTMLTable(RdDResolutionTable.subTable(rollData.caracValue, rollData.finalLevel)) const resolutionTable = await RdDResolutionTable.buildHTMLTable(RdDResolutionTable.subTable(rollData.caracValue, rollData.finalLevel))
const adjustements = await this.buildAjustements(rollData); const adjustements = await this.buildAjustements(rollData);
@ -314,13 +315,18 @@ export class RdDRoll extends Dialog {
HtmlUtility.showControlWhen(this.html.find(".use-encTotal"), rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac)); 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(".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(".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(".divAppelAuMoral"), rollData.use.appelAuMoral);
HtmlUtility.showControlWhen(this.html.find(".utilisation-coeur"), rollData.ajustements.coeur.visible);
HtmlUtility.showControlWhen(this.html.find(".utilisation-coeur img.selected-suivant-coeur"), rollData.ajustements.coeur.visible && rollData.use.coeur != undefined)
// HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moral.used);
// Mise à jour valeurs // Mise à jour valeurs
this.html.find(".dialog-roll-title").text(this._getTitle(rollData)); this.html.find(".dialog-roll-title").text(this._getTitle(rollData));
this.html.find("[name='coupsNonMortels']").prop('checked', rollData.mortalite == 'non-mortel'); this.html.find("input.check-mortalite").prop('checked', rollData.dmg.mortalite == 'non-mortel');
this.html.find(".dmg-arme-actor").text(dmgText); this.html.find("label.dmg-arme-actor").text(rollData.dmg.mortalite == 'empoignade' ? 'empoignade' : Misc.toSignedString(rollData.dmg.total));
this.html.find("label.arme-mortalite").text(rollData.dmg.mortalite);
// this.html.find("[name='dmg-arme-actor']").text(rollData.dmg.mortalite == 'empoignade'? 'empoignade': Misc.toSignedString(rollData.dmg.total) );
// this.html.find("[name='arme-mortalite']").text(rollData.dmg.mortalite);
this.html.find("div.placeholder-ajustements").empty().append(adjustements); this.html.find("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable) this.html.find("div.placeholder-resolution").empty().append(resolutionTable)
} }
@ -331,30 +337,6 @@ export class RdDRoll extends Dialog {
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.html`, rollData); return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.html`, rollData);
} }
/* -------------------------------------------- */
_computeFinalLevel(rollData) {
return RollDataAjustements.sum(rollData.ajustements);
}
/* -------------------------------------------- */
_computeDiffCompetence(rollData) {
if (rollData.competence) {
return Misc.toInt(rollData.competence.system.niveau);
}
if (rollData.draconicList) {
return Misc.toInt(rollData.competence.system.niveau);
}
return 0;
}
/* -------------------------------------------- */
_computeMalusArmure(rollData) {
let malusArmureValue = 0;
if (rollData.malusArmureValue && (rollData.selectedCarac.label == "Agilité" || rollData.selectedCarac.label == "Dérobée")) {
malusArmureValue = rollData.malusArmureValue;
}
return malusArmureValue;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
_getTitle(rollData) { _getTitle(rollData) {
const carac = rollData.selectedCarac.label; const carac = rollData.selectedCarac.label;
@ -362,16 +344,19 @@ export class RdDRoll extends Dialog {
return carac; return carac;
} }
const compName = rollData.competence.name; const compName = rollData.competence.name;
if (rollData.draconicList && rollData.selectedSort) {
return compName + " - " + rollData.selectedSort.name;
}
// If a weapon is there, add it in the title
const niveau = Misc.toSignedString(rollData.competence.system.niveau) const niveau = Misc.toSignedString(rollData.competence.system.niveau)
if (compName == carac) { if (compName == carac) {
// cas des créatures // cas des créatures
return carac + " Niveau " + niveau return `${carac} Niveau ${niveau}`
} }
const armeTitle = (rollData.arme) ? " (" + rollData.arme.name + ") " : ""; if (rollData.draconicList && rollData.selectedSort) {
return carac + "/" + compName + armeTitle + " Niveau " + niveau // cas de lancer de sort
return `${rollData.competence.name} Niveau ${niveau} ${rollData.selectedSort.name}`
}
if (rollData.arme && rollData.arme.name != compName) {
// ajouter l'arme au titre si son nom n'est pas la compétence
return `${carac} / ${compName} (${rollData.arme.name}) Niveau ${niveau}`
}
return `${carac} / ${compName} Niveau ${niveau}`
} }
} }

View File

@ -43,11 +43,17 @@ export class RdDSheetUtility {
item = await RdDItem.getCorrespondingItem(item); item = await RdDItem.getCorrespondingItem(item);
} }
if (actor.canReceive(item)) { if (actor.canReceive(item)) {
if (!actor.prototypeToken.actorLink && actor.token) {
ui.notifications.warn(`Impossible de donner ${item.name} à ${actor.name}, c'est un acteur temporaire
<br>La suppression de son token entraînera la perte définitive de ${item.name}.`)
return
}
return { return {
destId: destItemId, destId: destItemId,
targetActorId: actor.id, targetActorId: actor.id,
itemId: item.id, itemId: item.id,
sourceActorId: item.actor?.id, sourceActorId: item.actor?.id,
sourceTokenId: item.actor?.token?.id,
srcId: objetVersConteneur[item.id], srcId: objetVersConteneur[item.id],
onEnleverConteneur: () => { delete objetVersConteneur[item.id]; }, onEnleverConteneur: () => { delete objetVersConteneur[item.id]; },
onAjouterDansConteneur: (itemId, conteneurId) => { objetVersConteneur[itemId] = conteneurId; } onAjouterDansConteneur: (itemId, conteneurId) => { objetVersConteneur[itemId] = conteneurId; }

View File

@ -1,8 +1,7 @@
import { SHOW_DICE } from "./constants.js"; import { SHOW_DICE, SYSTEM_RDD } from "./constants.js";
import { RollDataAjustements } from "./rolldata-ajustements.js"; import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js"; import { TMRUtility } from "./tmr-utility.js";
import { tmrConstants } from "./tmr-constants.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js"; import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js";
import { ChatUtility } from "./chat-utility.js"; import { ChatUtility } from "./chat-utility.js";
@ -12,24 +11,47 @@ import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js"; import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js"; import { Draconique } from "./tmr/draconique.js";
import { HtmlUtility } from "./html-utility.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 { RdDDice } from "./rdd-dice.js";
import { STATUSES } from "./settings/status-effects.js"; import { STATUSES } from "./settings/status-effects.js";
import { RdDRencontre } from "./item/rencontre.js"; import { RdDRencontre } from "./item/rencontre.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { TYPES } from "./item.js";
const TMR_DISPLAY_SIZE = {
code: 'tmr-display-size',
range: {
min: 32,
max: 128,
step: 8,
},
def: 64,
clamp: (size, inc = 0) => Math.max(TMR_DISPLAY_SIZE.range.min, Math.min(size + (inc * TMR_DISPLAY_SIZE.range.step), TMR_DISPLAY_SIZE.range.max)),
get: () => TMR_DISPLAY_SIZE.clamp(game.settings.get(SYSTEM_RDD, TMR_DISPLAY_SIZE.code) ?? TMR_DISPLAY_SIZE.def),
set: (size) => game.settings.set(SYSTEM_RDD, TMR_DISPLAY_SIZE.code, TMR_DISPLAY_SIZE.clamp(size)),
};
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDTMRDialog extends Dialog { export class RdDTMRDialog extends Dialog {
static async init() {
game.settings.register(SYSTEM_RDD, TMR_DISPLAY_SIZE.code, {
name: 'Taille des cases des TMR',
hint: "Taille en pixel des cases des TMR (réglable directement dans la fenêtre des TMR)",
scope: "client",
config: true,
default: TMR_DISPLAY_SIZE.def,
type: Number,
range: TMR_DISPLAY_SIZE.range
})
await PixiTMR.init()
}
static async create(actor, tmrData) { static async create(actor, tmrData) {
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', tmrData); let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', tmrData);
if (tmrData.mode != 'visu') { if (tmrData.mode != 'visu' && !game.user.isGM) {
// Notification au MJ
ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatMessage.getWhisperRecipients("GM") }); ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatMessage.getWhisperRecipients("GM") });
} }
return new RdDTMRDialog(html, actor, tmrData); return new RdDTMRDialog(html, actor, tmrData);
} }
@ -38,40 +60,131 @@ export class RdDTMRDialog extends Dialog {
const dialogConf = { const dialogConf = {
title: "Terres Médianes de Rêve", title: "Terres Médianes de Rêve",
content: html, content: html,
buttons: { buttons: {}
closeButton: { label: "Fermer", callback: html => this.close(html) }
},
default: "closeButton"
} }
const dialogOptions = { const dialogOptions = {
classes: ["tmrdialog"], classes: ["tmrdialog"],
width: 920, height: 980, width: 'fit-content',
height: 'fit-content',
'max-height': 1024,
'z-index': 40 'z-index': 40
} }
super(dialogConf, dialogOptions); super(dialogConf, dialogOptions);
this.tmrdata = duplicate(tmrData); this.tmrdata = duplicate(tmrData);
this.actor = actor; this.actor = actor;
this.actor.tmrApp = this; // reference this app in the actor structure this.actor.tmrApp = this; // reference this app in the actor structure
this.viewOnly = tmrData.mode == "visu" this.viewOnly = tmrData.mode == "visu"
this.fatigueParCase = this.viewOnly || !ReglesOptionelles.isUsing("appliquer-fatigue") ? 0 : this.actor.getTMRFatigue(); this.fatigueParCase = this.viewOnly ? 0 : this.actor.getCoutFatigueTMR();
this.cumulFatigue = 0; this.cumulFatigue = 0;
this.loadRencontres(); this.loadRencontres();
this.loadCasesSpeciales(); this.loadCasesSpeciales();
this.allTokens = []; this.allTokens = [];
this.rencontreState = 'aucune'; this.rencontreState = 'aucune';
this.pixiApp = new PIXI.Application({ width: 720, height: 860 }); this.subdialog = undefined
this.displaySize = undefined
this.pixiTMR = new PixiTMR(this, this.pixiApp);
this.callbacksOnAnimate = [];
if (!this.viewOnly) { if (!this.viewOnly) {
this._tellToGM(this.actor.name + " monte dans les terres médianes (" + tmrData.mode + ")"); this._tellToGM(this.actor.name + " monte dans les terres médianes (" + tmrData.mode + ")");
} }
this.callbacksOnAnimate = [];
const displaySize = TMR_DISPLAY_SIZE.clamp(game.settings.get(SYSTEM_RDD, TMR_DISPLAY_SIZE.code) ?? TMR_DISPLAY_SIZE.def);
this.pixiTMR = new PixiTMR(this, displaySize);
this.resizePixiTMR(displaySize)
}
// load the texture we need resizePixiTMR(displaySize) {
this.pixiTMR.load((loader, resources) => this.createPixiSprites()); if (displaySize != this.displaySize) {
this.displaySize = displaySize
this.pixiTMR.resizeTMR(displaySize);
this._removeTokens()
this.allTokens = []
this.createPixiSprites()
this.pixiTMR.loadAnimations();
}
}
/* -------------------------------------------- */
async activateListeners(html) {
super.activateListeners(html);
this.html = html;
// this.activateTMRSize()
this.addTMRMap()
this.html.find('div.tmr-size a.tmr-size-zoom-minus*').click(event => {
this.$changeTMRSize(-1)
});
this.html.find('div.tmr-size a.tmr-size-zoom-plus*').click(event => {
this.$changeTMRSize(1)
});
if (this.viewOnly) {
this.html.find('.lancer-sort').remove();
this.html.find('.lire-signe-draconique').remove();
return;
}
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getCoordActor()));
this.html.find('form.tmr-dialog *').click(event => this.subdialog?.bringToTop());
// Roll Sort
this.html.find('.lancer-sort').click(event => this.actor.rollUnSort(this._getCoordActor()));
this.html.find('.lire-signe-draconique').click(event => this.actor.rollLireSigneDraconique(this._getCoordActor()));
this.html.find('img.tmr-move').click(event => this.deplacementTMR(this.html.find(event.currentTarget)?.data('move')));
// Gestion du cout de montée en points de rêve
await this.actor.reveActuelIncDec(this.calculCoutMonteeTMR());
this.cumulFatigue += this.fatigueParCase;
// Le reste...
this.updateValuesDisplay();
}
async onDeplacement() {
await this.manageRencontre(TMRUtility.getTMR(this._getCoordActor()));
}
addTMRMap() {
const tmrCell = document.getElementsByClassName("tmr-map")[0];
tmrCell.childNodes.forEach(node => tmrCell.removeChild(node))
tmrCell.append(this.pixiTMR.view);
}
$changeTMRSize(inc) {
let displaySize = TMR_DISPLAY_SIZE.clamp(this.displaySize, inc)
if (displaySize != this.displaySize) {
game.settings.set(SYSTEM_RDD, TMR_DISPLAY_SIZE.code, TMR_DISPLAY_SIZE.clamp(displaySize))
this.resizePixiTMR(displaySize)
this.render()
}
}
async forceTMRDisplay() {
this.bringToTop()
if (this.subdialog?.bringToTop) {
this.subdialog.bringToTop();
}
}
async restoreTMRAfterAction() {
this.subdialog = undefined
await this.maximize();
this.bringToTop();
}
forceTMRContinueAction() {
ui.notifications.warn('Vous devez finir votre action avant de continuer dans les TMR');
if (this.subdialog?.bringToTop) {
this.subdialog.bringToTop();
}
return;
}
setTMRPendingAction(dialog) {
this.subdialog = dialog
this.forceTMRDisplay()
} }
isDemiReveCache() { isDemiReveCache() {
@ -84,11 +197,11 @@ export class RdDTMRDialog extends Dialog {
} }
get sortsReserve() { get sortsReserve() {
return this.actor.itemTypes['sortreserve']; return this.actor.itemTypes[TYPES.sortreserve];
} }
getSortsReserve(coord) { getSortsReserve(coord) {
return this.actor.itemTypes['sortreserve'].filter(// Reserve sur une case fleuve ou normale return this.actor.itemTypes[TYPES.sortreserve].filter(// Reserve sur une case fleuve ou normale
TMRUtility.getTMR(coord).type == 'fleuve' TMRUtility.getTMR(coord).type == 'fleuve'
? it => TMRUtility.getTMR(it.system.coord).type == 'fleuve' ? it => TMRUtility.getTMR(it.system.coord).type == 'fleuve'
: it => it.system.coord == coord : it => it.system.coord == coord
@ -102,9 +215,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
createPixiSprites() { createPixiSprites() {
EffetsDraconiques.carteTmr.createSprite(this.pixiTMR); this.pixiTMR.setup()
this.updateTokens(); this.updateTokens()
this.forceDemiRevePositionView(); this.forceDemiRevePositionView()
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -113,13 +226,9 @@ export class RdDTMRDialog extends Dialog {
this.demiReve = this._tokenDemiReve(); this.demiReve = this._tokenDemiReve();
this._trackToken(this.demiReve); this._trackToken(this.demiReve);
} }
let tokens = this._getTokensCasesTmr() this._getTokensCasesTmr().forEach(t => this._trackToken(t))
.concat(this._getTokensRencontres()) this._getTokensRencontres().forEach(t => this._trackToken(t))
.concat(this._getTokensSortsReserve()); this._getTokensSortsReserve().forEach(t => this._trackToken(t))
for (let t of tokens) {
this._trackToken(t);
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -144,7 +253,7 @@ export class RdDTMRDialog extends Dialog {
return this.rencontresExistantes.map(it => this._tokenRencontre(it)); return this.rencontresExistantes.map(it => this._tokenRencontre(it));
} }
_getTokensSortsReserve() { _getTokensSortsReserve() {
return this.actor.itemTypes['sortreserve'].map(it => this._tokenSortEnReserve(it)); return this.actor.itemTypes[TYPES.sortreserve].map(it => this._tokenSortEnReserve(it));
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -165,77 +274,28 @@ export class RdDTMRDialog extends Dialog {
} }
forceDemiRevePositionView() { forceDemiRevePositionView() {
this.notifierResonanceSigneDraconique(this._getActorCoord()); this.notifierResonanceSigneDraconique(this._getCoordActor());
this._trackToken(this.demiReve); this._trackToken(this.demiReve);
} }
_getActorCoord() { _getCoordActor() {
return this.actor.system.reve.tmrpos.coord; return this.actor.system.reve.tmrpos.coord;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async moveFromKey(move) { async deplacementTMR(move) {
let oddq = TMRUtility.coordTMRToOddq(this._getActorCoord()); if (this.subdialog) {
return this.forceTMRContinueAction();
if (move == 'top') oddq.row -= 1;
if (move == 'bottom') oddq.row += 1;
if (move.includes('left')) oddq.col -= 1;
if (move.includes('right')) oddq.col += 1;
if (oddq.col % 2 == 1) {
if (move == 'top-left') oddq.row -= 1;
if (move == 'top-right') oddq.row -= 1;
} else {
if (move == 'bottom-left') oddq.row += 1;
if (move == 'bottom-right') oddq.row += 1;
} }
let targetCoord = TMRUtility.oddqToCoordTMR(oddq); const coordOrig = this._getCoordActor();
await this._deplacerDemiReve(targetCoord, 'normal'); const coordTarget = TMRUtility.deplacement(coordOrig, move);
await this._deplacerDemiReve(coordTarget, 'normal');
this.checkQuitterTMR(); this.checkQuitterTMR();
} }
/* -------------------------------------------- */ calculCoutMonteeTMR() {
async activateListeners(html) { return ((this.tmrdata.isRapide && !EffetsDraconiques.isDeplacementAccelere(this.actor)) ? -2 : -1) - this.actor.countMonteeLaborieuse();
super.activateListeners(html);
this.html = html;
document.getElementById("tmrrow1").insertCell(0).append(this.pixiApp.view);
if (this.viewOnly) {
this.html.find('.lancer-sort').remove();
this.html.find('.lire-signe-draconique').remove();
return;
}
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
// Roll Sort
this.html.find('.lancer-sort').click((event) => {
this.actor.rollUnSort(this._getActorCoord());
});
this.html.find('.lire-signe-draconique').click((event) => {
this.actor.rollLireSigneDraconique(this._getActorCoord());
});
this.html.find('#dir-top').click((event) => this.moveFromKey("top"));
this.html.find('#dir-top-left').click((event) => this.moveFromKey("top-left"));
this.html.find('#dir-top-right').click((event) => this.moveFromKey("top-right"));
this.html.find('#dir-bottom-left').click((event) => this.moveFromKey("bottom-left"));
this.html.find('#dir-bottom-right').click((event) => this.moveFromKey("bottom-right"));
this.html.find('#dir-bottom').click((event) => this.moveFromKey("bottom"));
// 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")) {
this.cumulFatigue += this.fatigueParCase;
}
await this.actor.reveActuelIncDec(reveCout);
// Le reste...
this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this._getActorCoord());
await this.manageRencontre(tmr);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -243,7 +303,7 @@ export class RdDTMRDialog extends Dialog {
if (!this.rendered) { if (!this.rendered) {
return; return;
} }
const coord = this._getActorCoord(); const coord = this._getCoordActor();
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(coord)); HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(coord));
@ -263,7 +323,7 @@ export class RdDTMRDialog extends Dialog {
let refoulement = document.getElementById("tmr-refoulement-value"); let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = this.actor.system.reve.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"); 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>"; fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.actor.system.sante.fatigue.value, this.actor.system.sante.endurance.max).html() + "</table>";
} }
@ -271,6 +331,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async close() { async close() {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
this.descenteTMR = true; this.descenteTMR = true;
if (this.actor.tmrApp) { if (this.actor.tmrApp) {
this.actor.tmrApp = undefined; // Cleanup reference this.actor.tmrApp = undefined; // Cleanup reference
@ -278,9 +341,12 @@ export class RdDTMRDialog extends Dialog {
await this.actor.setEffect(STATUSES.StatusDemiReve, false) await this.actor.setEffect(STATUSES.StatusDemiReve, false)
this._tellToGM(this.actor.name + " a quitté les terres médianes"); this._tellToGM(this.actor.name + " a quitté les terres médianes");
} }
await this.actor.santeIncDec("fatigue", this.cumulFatigue) await this.actor.santeIncDec((ReglesOptionnelles.isUsing("appliquer-fatigue") ? "fatigue" : "endurance"),
this.cumulFatigue)
} }
await super.close(); await super.close();
this.pixiTMR.close()
this.pixiTMR = undefined
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -293,6 +359,7 @@ export class RdDTMRDialog extends Dialog {
switch (action) { switch (action) {
case 'derober': case 'derober':
await this.derober(); await this.derober();
this.restoreTMRAfterAction();
return; return;
case 'refouler': case 'refouler':
await this.refouler(); await this.refouler();
@ -305,6 +372,7 @@ export class RdDTMRDialog extends Dialog {
break; break;
} }
await this.postRencontre(tmr); await this.postRencontre(tmr);
this.restoreTMRAfterAction();
} }
async derober() { async derober() {
@ -343,35 +411,27 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
$marquerCasesTMR(listCoordTMR) { $marquerCasesTMR(listCoordTMR) {
this.currentRencontre.graphics = []; // Keep track of rectangles to delete it
this.currentRencontre.locList = duplicate(listCoordTMR); // And track of allowed location this.currentRencontre.locList = duplicate(listCoordTMR); // And track of allowed location
for (let coordTMR of listCoordTMR) { this.currentRencontre.graphics = listCoordTMR.map(coordTMR => this.pixiTMR.addMarkTMR(coordTMR))
const rect = this._getCaseRectangleCoord(coordTMR);
const rectDraw = new PIXI.Graphics();
rectDraw.beginFill(0xffff00, 0.3);
// set the line style to have a width of 5 and set the color to red
rectDraw.lineStyle(5, 0xff0000);
// draw a rectangle
rectDraw.drawRect(rect.x, rect.y, rect.w, rect.h);
this.pixiApp.stage.addChild(rectDraw);
this.currentRencontre.graphics.push(rectDraw); // garder les objets pour gestion post-click
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
checkQuitterTMR() { checkQuitterTMR() {
if (this.actor.isDead()) { if (this.actor.isDead()) {
this._tellToGM("Vous êtes mort : vous quittez les Terres médianes !"); this._tellToGM("Vous êtes mort : vous quittez les Terres médianes !");
this.close(); this.close();
return true; return true;
} }
const resteAvantInconscience = this.actor.getFatigueMax() - this.actor.getFatigueActuelle() - this.cumulFatigue;
if (ReglesOptionelles.isUsing("appliquer-fatigue") && resteAvantInconscience <= 0) { if (ReglesOptionnelles.isUsing("appliquer-fatigue")
? (this.actor.getFatigueRestante() <= this.cumulFatigue)
: (this.actor.getEnduranceActuelle() <= this.cumulFatigue)
) {
this._tellToGM("Vous vous écroulez de fatigue : vous quittez les Terres médianes !"); this._tellToGM("Vous vous écroulez de fatigue : vous quittez les Terres médianes !");
this.quitterLesTMRInconscient(); this.quitterLesTMRInconscient();
return true; return true;
} }
if (this.actor.getReveActuel() == 0) { if (this.actor.getReveActuel() == 0) {
this._tellToGM("Vos Points de Rêve sont à 0 : vous quittez les Terres médianes !"); this._tellToGM("Vos Points de Rêve sont à 0 : vous quittez les Terres médianes !");
this.quitterLesTMRInconscient(); this.quitterLesTMRInconscient();
@ -402,7 +462,7 @@ export class RdDTMRDialog extends Dialog {
nbRounds: 1, nbRounds: 1,
canClose: false, canClose: false,
selectedCarac: { label: "reve-actuel" }, selectedCarac: { label: "reve-actuel" },
tmr: TMRUtility.getTMR(this._getActorCoord()) tmr: TMRUtility.getTMR(this._getCoordActor())
} }
await this._tentativeMaitrise(rencontreData); await this._tentativeMaitrise(rencontreData);
@ -451,7 +511,7 @@ export class RdDTMRDialog extends Dialog {
setTimeout(() => { setTimeout(() => {
// TODO: remplacer par une boucle while(this.currentRencontre) ? // TODO: remplacer par une boucle while(this.currentRencontre) ?
rencData.nbRounds++; rencData.nbRounds++;
if (ReglesOptionelles.isUsing("appliquer-fatigue")) { if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase; this.cumulFatigue += this.fatigueParCase;
} }
this._tentativeMaitrise(rencData); this._tentativeMaitrise(rencData);
@ -531,8 +591,9 @@ export class RdDTMRDialog extends Dialog {
await this.maitriserRencontre(); await this.maitriserRencontre();
} }
else { else {
let dialog = new RdDTMRRencontreDialog(this, this.currentRencontre, tmr); const dialog = new RdDTMRRencontreDialog(this.actor, this.currentRencontre, tmr);
dialog.render(true); dialog.render(true);
this.setTMRPendingAction(dialog);
} }
} }
else { else {
@ -544,9 +605,12 @@ export class RdDTMRDialog extends Dialog {
_presentCite(tmr) { _presentCite(tmr) {
const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord)); const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord));
if (presentCite) { if (presentCite) {
this.minimize();
const caseData = presentCite; const caseData = presentCite;
EffetsDraconiques.presentCites.choisirUnPresent(caseData, (present => this._utiliserPresentCite(presentCite, present, tmr))); const dialog = EffetsDraconiques.presentCites.choisirUnPresent(caseData, present => {
this._utiliserPresentCite(presentCite, present, tmr)
this.restoreTMRAfterAction();
});
this.setTMRPendingAction(dialog);
} }
return presentCite; return presentCite;
} }
@ -572,8 +636,6 @@ export class RdDTMRDialog extends Dialog {
presentCite: presentCite presentCite: presentCite
}; };
await this._tentativeMaitrise(rencontreData); await this._tentativeMaitrise(rencontreData);
this.maximize();
this.postRencontre(tmr); this.postRencontre(tmr);
} }
@ -583,16 +645,18 @@ export class RdDTMRDialog extends Dialog {
if (rencontre) { if (rencontre) {
return game.system.rdd.rencontresTMR.calculRencontre(rencontre, tmr); return game.system.rdd.rencontresTMR.calculRencontre(rencontre, tmr);
} }
let locTMR = (this.isDemiReveCache() const coordTMR = (this.isDemiReveCache()
? TMRUtility.getTMRType(tmr.coord) + " ??" ? TMRUtility.getTMRType(tmr.coord) + " ??"
: tmr.label + " (" + tmr.coord + ")"); : tmr.label + " (" + tmr.coord + ")");
let myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE }); this.setTMRPendingAction({ bringToTop: () => { } })
const myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE });
this.restoreTMRAfterAction()
if (myRoll == 7) { if (myRoll == 7) {
this._tellToUser(myRoll + ": Rencontre en " + locTMR); this._tellToUser(myRoll + ": Rencontre en " + coordTMR);
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre()) return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
} else { } else {
this._tellToUser(myRoll + ": Pas de rencontre en " + locTMR); this._tellToUser(myRoll + ": Pas de rencontre en " + coordTMR);
return undefined; return undefined;
} }
} }
@ -777,22 +841,22 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _maitriserTMR(rollData, callbackMaitrise) { async _maitriserTMR(rollData, callbackMaitrise) {
this.minimize(); // Hide
rollData.isTMRCache = rollData.actor.isTMRCache(); rollData.isTMRCache = rollData.actor.isTMRCache();
const dialog = await RdDRoll.create(this.actor, rollData, const dialog = await RdDRoll.create(this.actor, rollData,
{ {
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html', html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html',
close: html => { this.maximize(); } // Re-display TMR
}, },
{ {
name: rollData.maitrise.verbe, label: rollData.maitrise.action, name: rollData.maitrise.verbe, label: rollData.maitrise.action,
callbacks: [ callbacks: [
this.actor.createCallbackExperience(), this.actor.createCallbackExperience(),
{ action: r => { this.restoreTMRAfterAction() } },
{ action: callbackMaitrise } { action: callbackMaitrise }
] ]
} }
); );
dialog.render(true); dialog.render(true);
this.setTMRPendingAction(dialog);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -860,14 +924,11 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
nettoyerRencontre() { nettoyerRencontre() {
if (!this.currentRencontre) return; // Sanity check // Suppression des dessins des zones possibles
if (this.currentRencontre.graphics) { this.currentRencontre?.graphics?.forEach(graphic => this.pixiTMR.removeGraphic(graphic))
for (let drawRect of this.currentRencontre.graphics) { // Suppression des dessins des zones possibles // Nettoyage de la structureet de l'état
this.pixiApp.stage.removeChild(drawRect); this.currentRencontre = undefined;
} this.rencontreState = 'aucune';
}
this.currentRencontre = undefined; // Nettoyage de la structure
this.rencontreState = 'aucune'; // Et de l'état
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -895,8 +956,8 @@ export class RdDTMRDialog extends Dialog {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
isConnaissanceFleuve(currentTMR, nextTMR) { isConnaissanceFleuve(tmrApp, nextTMR) {
return TMRUtility.getTMR(currentTMR).type == 'fleuve' && return TMRUtility.getTMR(tmrApp).type == 'fleuve' &&
TMRUtility.getTMR(nextTMR).type == 'fleuve' && TMRUtility.getTMR(nextTMR).type == 'fleuve' &&
EffetsDraconiques.isConnaissanceFleuve(this.actor); EffetsDraconiques.isConnaissanceFleuve(this.actor);
} }
@ -906,22 +967,21 @@ export class RdDTMRDialog extends Dialog {
if (this.viewOnly) { if (this.viewOnly) {
return; return;
} }
let clickOddq = RdDTMRDialog._computeEventOddq(event.data.originalEvent); if (this.subdialog) {
await this._onClickTMRPos(clickOddq); // Vérifier l'état des compteurs reve/fatigue/vie return this.forceTMRContinueAction()
} }
const currentCoord = this._getCoordActor()
const currentOddq = TMRUtility.coordTMRToOddq(currentCoord)
const targetOddq = this.pixiTMR.computeEventOddq(event)
const targetCoord = TMRUtility.oddqToCoordTMR(targetOddq)
/* -------------------------------------------- */
async _onClickTMRPos(clickOddq) {
let currentOddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
let targetCoord = TMRUtility.oddqToCoordTMR(clickOddq);
let currentCoord = TMRUtility.oddqToCoordTMR(currentOddq);
// Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter) // Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter)
let deplacementType = this._calculDeplacement(targetCoord, currentCoord, currentOddq, clickOddq); const typeDeplacement = this._calculDeplacement(targetCoord, currentCoord, currentOddq, targetOddq);
if (this.isDemiReveCache()) { if (this.isDemiReveCache()) {
if (this.isTerreAttache(targetCoord) if (this.isTerreAttache(targetCoord)
|| this.isConnaissanceFleuve(currentCoord, targetCoord) || this.isConnaissanceFleuve(currentCoord, targetCoord)
|| deplacementType == 'changeur') { || typeDeplacement == 'changeur') {
// déplacement possible // déplacement possible
await this.actor.setTMRVisible(true); await this.actor.setTMRVisible(true);
this.demiReve = this._tokenDemiReve(); this.demiReve = this._tokenDemiReve();
@ -936,17 +996,17 @@ export class RdDTMRDialog extends Dialog {
} }
} }
switch (deplacementType) { switch (typeDeplacement) {
case 'normal': case 'normal':
case 'changeur': case 'changeur':
case 'passeur': case 'passeur':
await this._deplacerDemiReve(targetCoord, deplacementType); await this._deplacerDemiReve(targetCoord, typeDeplacement);
break; break;
case 'messager': case 'messager':
await this._messagerDemiReve(targetCoord); await this._messagerDemiReve(targetCoord);
break; break;
default: default:
ui.notifications.error("Vous ne pouvez pas vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre"); ui.notifications.error("Vous ne pouvez vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre");
console.log("STATUS :", this.rencontreState, this.currentRencontre); console.log("STATUS :", this.rencontreState, this.currentRencontre);
} }
@ -975,9 +1035,11 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _messagerDemiReve(targetCoord) { async _messagerDemiReve(targetCoord) {
/* /*
TODO: si la case a un sort en réserve, lancer ce sort. TODO:
Si la case a un sort en réserve, lancer ce sort.
Si la case est le demi-rêve, ne pas lancer de sort. Si la case est le demi-rêve, ne pas lancer de sort.
Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre si on essaie de lancer un sort (ou bloquer le lancer de sort) Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre
si on essaie de lancer un sort (ou bloquer le lancer de sort)
*/ */
this.notifierResonanceSigneDraconique(targetCoord); this.notifierResonanceSigneDraconique(targetCoord);
await this.actor.rollUnSort(targetCoord); await this.actor.rollUnSort(targetCoord);
@ -994,6 +1056,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _deplacerDemiReve(targetCoord, deplacementType) { async _deplacerDemiReve(targetCoord, deplacementType) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
if (this.currentRencontre != 'normal') { if (this.currentRencontre != 'normal') {
this.nettoyerRencontre(); this.nettoyerRencontre();
} }
@ -1004,7 +1069,7 @@ export class RdDTMRDialog extends Dialog {
await this.actor.updateCoordTMR(tmr.coord); await this.actor.updateCoordTMR(tmr.coord);
this.forceDemiRevePositionView(); this.forceDemiRevePositionView();
if (ReglesOptionelles.isUsing("appliquer-fatigue")) { if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase; this.cumulFatigue += this.fatigueParCase;
} }
this.updateValuesDisplay(); this.updateValuesDisplay();
@ -1019,7 +1084,7 @@ export class RdDTMRDialog extends Dialog {
} }
async notifierResonanceSigneDraconique(coord) { async notifierResonanceSigneDraconique(coord) {
if (this.actor.isResonanceSigneDraconique(coord)) { if (!this.viewOnly && this.actor.isResonanceSigneDraconique(coord)) {
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-resonance.html`, { alias: this.actor.name, typeTMR: TMRUtility.getTMRType(coord) }) content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-resonance.html`, { alias: this.actor.name, typeTMR: TMRUtility.getTMRType(coord) })
@ -1043,6 +1108,10 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async positionnerDemiReve(coord) { async positionnerDemiReve(coord) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
await this.actor.updateCoordTMR(coord); await this.actor.updateCoordTMR(coord);
this.forceDemiRevePositionView(); this.forceDemiRevePositionView();
let tmr = TMRUtility.getTMR(coord); let tmr = TMRUtility.getTMR(coord);
@ -1051,28 +1120,8 @@ export class RdDTMRDialog extends Dialog {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static _computeEventOddq(origEvent) { _removeTokens(filter = it => true) {
let canvasRect = origEvent.target.getBoundingClientRect(); this.allTokens.filter(filter).forEach(token => this.pixiTMR.removeToken(token))
let x = origEvent.clientX - canvasRect.left;
let y = origEvent.clientY - canvasRect.top;
let col = Math.floor(x / tmrConstants.cellw); // [From 0 -> 12]
y -= col % 2 == 0 ? tmrConstants.col1_y : tmrConstants.col2_y;
let row = Math.floor(y / tmrConstants.cellh); // [From 0 -> 14]
return { col: col, row: row };
}
/* -------------------------------------------- */
/** Retourne les coordonnées x, h, w, h du rectangle d'une case donnée */
_getCaseRectangleCoord(coord) {
return this.pixiTMR.getCaseRectangle(TMRUtility.coordTMRToOddq(coord));
}
/* -------------------------------------------- */
_removeTokens(filter) {
const tokensToRemove = this.allTokens.filter(filter);
for (let token of tokensToRemove) {
this.pixiApp.stage.removeChild(token.sprite);
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -1080,7 +1129,9 @@ export class RdDTMRDialog extends Dialog {
if (this.demiReve === token && this.isDemiReveCache()) { if (this.demiReve === token && this.isDemiReveCache()) {
return; return;
} }
this.pixiTMR.setPosition(token.sprite, TMRUtility.coordTMRToOddq(token.coordTMR())); this.pixiTMR.positionToken(token);
this.allTokens.push(token); if (!this.allTokens.includes(token)) {
this.allTokens.push(token);
}
} }
} }

View File

@ -2,7 +2,7 @@
export class RdDTMRRencontreDialog extends Dialog { export class RdDTMRRencontreDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor(tmrApp, rencontre, tmr) { constructor(actor, rencontre, tmr) {
const dialogConf = { const dialogConf = {
title: "Rencontre en TMR!", title: "Rencontre en TMR!",
content: "Vous rencontrez un " + rencontre.name + " de force " + rencontre.system.force + "<br>", content: "Vous rencontrez un " + rencontre.name + " de force " + rencontre.system.force + "<br>",
@ -28,23 +28,30 @@ export class RdDTMRRencontreDialog extends Dialog {
this.toClose = false; this.toClose = false;
this.tmr = tmr; this.tmr = tmr;
this.tmrApp = tmrApp; this.actor = actor;
this.rencontre = rencontre; this.rencontre = rencontre;
this.tmrApp.minimize();
} }
async onButtonAction(action) { async onButtonAction(action) {
this.toClose = true; this.toClose = true;
this.tmrApp.onActionRencontre(action, this.tmr, this.rencontre) await this.actor.tmrApp?.restoreTMRAfterAction();
this.actor.tmrApp?.onActionRencontre(action, this.tmr, this.rencontre)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
close() { async close() {
if (this.toClose) { if (this.actor.tmrApp){
this.tmrApp.maximize(); if (this.toClose) {
return super.close(); return await super.close();
}
else {
ui.notifications.info("Vous devez résoudre la rencontre.");
return this.actor.tmrApp.forceTMRContinueAction();
}
}
else {
return await super.close();
} }
ui.notifications.info("Vous devez résoudre la rencontre.");
} }
} }

View File

@ -75,8 +75,8 @@ export class RdDTokenHud {
(event) => { (event) => {
const actionIndex = event.currentTarget.attributes['data-action-index']?.value; const actionIndex = event.currentTarget.attributes['data-action-index']?.value;
const action = hudData.actions[actionIndex]; const action = hudData.actions[actionIndex];
if (action.action == 'conjurer') { const possession = action.action == 'possession' ? combatant.actor.getPossession(action.system.possessionid) : undefined;
const possession = combatant.actor.getPossession(action.system.possessionid); if (possession) {
combatant.actor.conjurerPossession(possession); combatant.actor.conjurerPossession(possession);
} }
else { else {

View File

@ -5,7 +5,7 @@ import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
import { TMRUtility } from "./tmr-utility.js"; import { TMRUtility } from "./tmr-utility.js";
import { DialogItemAchat } from "./dialog-item-achat.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 { RdDDice } from "./rdd-dice.js";
import { RdDItem } from "./item.js"; import { RdDItem } from "./item.js";
import { RdDPossession } from "./rdd-possession.js"; import { RdDPossession } from "./rdd-possession.js";
@ -16,6 +16,8 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js"; import { RdDRaretes } from "./item/raretes.js";
import { RdDEmpoignade } from "./rdd-empoignade.js"; import { RdDEmpoignade } from "./rdd-empoignade.js";
import { ExperienceLog } from "./actor/experience-log.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
// This table starts at 0 -> niveau -10 // This table starts at 0 -> niveau -10
@ -27,7 +29,7 @@ const ajustementsEncaissement = Misc.intArray(-10, 26);
/* -------------------------------------------- */ /* -------------------------------------------- */
function _buildAllSegmentsFatigue(max) { function _buildAllSegmentsFatigue(max) {
const cycle = [5, 2, 4, 1, 3, 0]; const cycle = [5, 2, 4, 1, 3, 0];
let fatigue = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]; const fatigue = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
for (let i = 0; i <= max; i++) { for (let i = 0; i <= max; i++) {
const ligneFatigue = duplicate(fatigue[i]); const ligneFatigue = duplicate(fatigue[i]);
const caseIncrementee = cycle[i % 6]; const caseIncrementee = cycle[i % 6];
@ -54,7 +56,8 @@ function _cumulSegmentsFatigue(matrix) {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
const fatigueMatrix = _buildAllSegmentsFatigue(60); export const MAX_ENDURANCE_FATIGUE = 60;
const fatigueMatrix = _buildAllSegmentsFatigue(MAX_ENDURANCE_FATIGUE);
const cumulFatigueMatrix = _cumulSegmentsFatigue(fatigueMatrix); const cumulFatigueMatrix = _cumulSegmentsFatigue(fatigueMatrix);
const fatigueMalus = [0, 0, 0, -1, -1, -1, -2, -3, -4, -5, -6, -7]; // Provides the malus for each segment of fatigue const fatigueMalus = [0, 0, 0, -1, -1, -1, -2, -3, -4, -5, -6, -7]; // Provides the malus for each segment of fatigue
@ -73,22 +76,22 @@ const nomEthylisme = ["Emeché", "Gris", "Pinté", "Pas frais", "Ivre", "Bu", "C
/* -------------------------------------------- */ /* -------------------------------------------- */
const definitionsEncaissement = { const definitionsEncaissement = {
"mortel": [ "mortel": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1}, { minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0}, { minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 2}, { minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 2 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "2", gravite: 4}, { minimum: 16, maximum: 19, endurance: "2d6", vie: "2", gravite: 4 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "4 + @over20", gravite: 6}, { minimum: 20, maximum: undefined, endurance: "100", vie: "4 + @over20", gravite: 6 },
], ],
"non-mortel": [ "non-mortel": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1}, { minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 }, { minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", 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: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 2 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "0", gravite: 2 }, { minimum: 20, maximum: undefined, endurance: "100", vie: "0", gravite: 2 },
], ],
"entiteincarnee": [ "entiteincarnee": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1}, { minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0}, { minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", 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: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 0 },
{ minimum: 20, maximum: undefined, endurance: "3d6 + @over20", vie: "0", gravite: 0 }, { minimum: 20, maximum: undefined, endurance: "3d6 + @over20", vie: "0", gravite: 0 },
@ -97,7 +100,8 @@ const definitionsEncaissement = {
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDUtility { export class RdDUtility {
// persistent handling of conteneur show/hide
static afficheContenu = {}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async init() { static async init() {
Hooks.on("renderChatMessage", async (app, html, msg) => RdDUtility.onRenderChatMessage(app, html, msg)); Hooks.on("renderChatMessage", async (app, html, msg) => RdDUtility.onRenderChatMessage(app, html, msg));
@ -120,6 +124,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs-entitee.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs-entitee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-effects.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/header-effects.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-hautreve.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/vue-detaillee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-main.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/carac-main.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-derivee.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/carac-derivee.html',
@ -136,6 +141,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/blessure.hbs', '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/maladies-poisons.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/possessions.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/taches.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/taches.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.html',
@ -161,15 +167,16 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/inventaire.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.html',
"systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-monnaie.html", "systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-monnaie.html",
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-animaux.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/liens-animaux.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-suivants.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/liens-suivants.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-vehicules.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/liens-vehicules.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.html', 'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.html',
//Items //Items
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs', 'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs',
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete.hbs', 'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete.hbs',
'systems/foundryvtt-reve-de-dragon/templates/item/boutons-comestible.html', 'systems/foundryvtt-reve-de-dragon/templates/item/boutons-comestible.html',
'systems/foundryvtt-reve-de-dragon/templates/item/icon-arme-broken.hbs',
'systems/foundryvtt-reve-de-dragon/templates/item/temporel.hbs', 'systems/foundryvtt-reve-de-dragon/templates/item/temporel.hbs',
'systems/foundryvtt-reve-de-dragon/templates/item/partial-inventaire.html', 'systems/foundryvtt-reve-de-dragon/templates/item/partial-inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/item/partial-environnement.html', 'systems/foundryvtt-reve-de-dragon/templates/item/partial-environnement.html',
@ -177,25 +184,28 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/item-queue-sheet.html', 'systems/foundryvtt-reve-de-dragon/templates/item-queue-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/header-item.html', 'systems/foundryvtt-reve-de-dragon/templates/header-item.html',
// partial enums // partial enums
'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-aspect-tarot.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-competence.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-caracteristiques.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-ingredient.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-vehicule.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-vehicule.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-competence.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-draconic.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-heures.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-heures.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-initpremierround.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-initpremierround.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-mortalite.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-niveau-ethylisme.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-niveau-ethylisme.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-rarete.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-draconic.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-type.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-periode.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-periode.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-rarete.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-effet.html', 'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-effet.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-type.html',
// Partials // Partials
'systems/foundryvtt-reve-de-dragon/templates/coeur/chat-effet-tendre-moment.hbs',
'systems/foundryvtt-reve-de-dragon/templates/coeur/afficher-coeur.hbs',
'systems/foundryvtt-reve-de-dragon/templates/tirage/liste-resultats-recherche.hbs', '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/time/horloge.hbs',
'systems/foundryvtt-reve-de-dragon/templates/common/timestamp.hbs', 'systems/foundryvtt-reve-de-dragon/templates/common/timestamp.hbs',
@ -211,6 +221,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-surenc.html', 'systems/foundryvtt-reve-de-dragon/templates/partial-roll-surenc.html',
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-enctotal.html', 'systems/foundryvtt-reve-de-dragon/templates/partial-roll-enctotal.html',
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-moral.html', 'systems/foundryvtt-reve-de-dragon/templates/partial-roll-moral.html',
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-coeur.hbs',
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-forcer.html', 'systems/foundryvtt-reve-de-dragon/templates/partial-roll-forcer.html',
'systems/foundryvtt-reve-de-dragon/templates/partial-roll-competences.html', 'systems/foundryvtt-reve-de-dragon/templates/partial-roll-competences.html',
'systems/foundryvtt-reve-de-dragon/templates/partial-select-carac.html', 'systems/foundryvtt-reve-de-dragon/templates/partial-select-carac.html',
@ -241,7 +252,8 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/chat-description.html', '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-appel-au-moral.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.html', 'systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html', 'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html', 'systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html', 'systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html', 'systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html',
@ -276,13 +288,17 @@ export class RdDUtility {
Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode()); Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode());
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1))); 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('trier', list => list.sort((a, b) => a.name.localeCompare(b.name)));
Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences)); Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences));
Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name)); Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name));
Handlebars.registerHelper('uniteQuantite', (itemId, actorId) => RdDUtility.getItem(itemId, actorId)?.getUniteQuantite()); Handlebars.registerHelper('uniteQuantite', (itemId, actorId) => RdDUtility.getItem(itemId, actorId)?.getUniteQuantite());
Handlebars.registerHelper('isFieldInventaireModifiable', (type, field) => RdDItem.isFieldInventaireModifiable(type, field)); Handlebars.registerHelper('isFieldInventaireModifiable', (type, field) => RdDItem.isFieldInventaireModifiable(type, field));
Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field)); Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field));
Handlebars.registerHelper('plusMoins', diff => (diff > 0 ? '+' : '') + Math.round(diff))
Handlebars.registerHelper('experienceLog-topic', topic => ExperienceLog.labelTopic(topic));
return loadTemplates(templatePaths); return loadTemplates(templatePaths);
} }
@ -312,35 +328,27 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getNomEthylisme(niveauEthylisme) { static getNomEthylisme(niveauEthylisme) { return niveauEthylisme > 0 ? 'Aucun' : nomEthylisme[-niveauEthylisme] }
let index = -niveauEthylisme;
return index < 0 ? 'Aucun' : nomEthylisme[index];
}
/* -------------------------------------------- */
static initAfficheContenu() { // persistent handling of conteneur show/hide
if (!this.afficheContenu)
this.afficheContenu = {};
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static toggleAfficheContenu(conteneurId) { static toggleAfficheContenu(conteneurId) {
this.afficheContenu[conteneurId] = !this.afficheContenu[conteneurId]; RdDUtility.afficheContenu[conteneurId] = !RdDUtility.afficheContenu[conteneurId];
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getAfficheContenu(conteneurId) { static getAfficheContenu(conteneurId) {
if (conteneurId) if (conteneurId)
return this.afficheContenu[conteneurId]; return RdDUtility.afficheContenu[conteneurId];
return undefined; return undefined;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static buildArbreDeConteneurs(conteneurs, objets) { static buildArbreDeConteneurs(conteneurs, inventaires) {
let objetVersConteneur = {}; let objetVersConteneur = {};
// Attribution des objets aux conteneurs // Attribution des objets aux conteneurs
for (let conteneur of conteneurs) { for (let conteneur of conteneurs) {
conteneur.subItems = []; conteneur.subItems = [];
for (let id of conteneur.system.contenu ?? []) { 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) { if (objet) {
objet.estContenu = true; // Permet de filtrer ce qui est porté dans le template objet.estContenu = true; // Permet de filtrer ce qui est porté dans le template
objetVersConteneur[id] = conteneur._id; objetVersConteneur[id] = conteneur._id;
@ -349,20 +357,20 @@ export class RdDUtility {
} }
} }
for (let conteneur of conteneurs) { for (let conteneur of conteneurs) {
conteneur.system.encTotal = RdDUtility.calculEncContenu(conteneur, objets); conteneur.system.encTotal = RdDUtility.calculEncContenu(conteneur, inventaires);
} }
return objetVersConteneur; return objetVersConteneur;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static calculEncContenu(conteneur, objets) { static calculEncContenu(conteneur, inventaires) {
const contenus = (conteneur.system.contenu ?? []).filter(id => id != undefined) 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); .filter(it => it);
let enc = Number(conteneur.system.encombrement ?? 0) * Number(conteneur.system.quantite ?? 1); let enc = Number(conteneur.system.encombrement ?? 0) * Number(conteneur.system.quantite ?? 1);
for (let contenu of contenus) { for (let contenu of contenus) {
if (contenu.type == 'conteneur') { if (contenu.type == 'conteneur') {
enc += RdDUtility.calculEncContenu(contenu, objets); enc += RdDUtility.calculEncContenu(contenu, inventaires);
} }
else { else {
enc += Number(contenu.system.encombrement ?? 0) * Number(contenu.system.quantite ?? 1) enc += Number(contenu.system.encombrement ?? 0) * Number(contenu.system.quantite ?? 1)
@ -452,18 +460,15 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getSegmentsFatigue(maxEnd) { static getSegmentsFatigue(maxEndurance) {
maxEnd = Math.max(maxEnd, 1); return fatigueMatrix[Math.min(Math.max(maxEndurance, 1), fatigueMatrix.length)];
maxEnd = Math.min(maxEnd, fatigueMatrix.length);
return fatigueMatrix[maxEnd];
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static calculMalusFatigue(fatigue, maxEnd) { static calculMalusFatigue(fatigue, endurance) {
maxEnd = Math.max(maxEnd, 1); endurance = Math.min(Math.max(endurance, 1), cumulFatigueMatrix.length);
maxEnd = Math.min(maxEnd, cumulFatigueMatrix.length); let segments = cumulFatigueMatrix[endurance];
let segments = cumulFatigueMatrix[maxEnd]; for (let i = 0; i < segments.length; i++) {
for (let i = 0; i < 12; i++) {
if (fatigue <= segments[i]) { if (fatigue <= segments[i]) {
return fatigueMalus[i] return fatigueMalus[i]
} }
@ -473,7 +478,7 @@ export class RdDUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static calculFatigueHtml(fatigue, endurance) { static calculFatigueHtml(fatigue, endurance) {
return ReglesOptionelles.isUsing("appliquer-fatigue") ? { return ReglesOptionnelles.isUsing("appliquer-fatigue") ? {
malus: RdDUtility.calculMalusFatigue(fatigue, endurance), malus: RdDUtility.calculMalusFatigue(fatigue, endurance),
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(fatigue, endurance).html() + "</table>" html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(fatigue, endurance).html() + "</table>"
} : { malus: 0, html: '' }; } : { malus: 0, html: '' };
@ -483,7 +488,7 @@ export class RdDUtility {
// Build the nice (?) html table used to manage fatigue. // Build the nice (?) html table used to manage fatigue.
// max should be the endurance max value // max should be the endurance max value
static makeHTMLfatigueMatrix(fatigue, maxEndurance) { static makeHTMLfatigueMatrix(fatigue, maxEndurance) {
let segments = this.getSegmentsFatigue(maxEndurance); const segments = this.getSegmentsFatigue(maxEndurance);
return this.makeHTMLfatigueMatrixForSegment(fatigue, segments); return this.makeHTMLfatigueMatrixForSegment(fatigue, segments);
} }
@ -549,14 +554,14 @@ export class RdDUtility {
let formula = "2d10"; let formula = "2d10";
// Chaque dé fait au minmum la difficulté libre // 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) { if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre); let valeurMin = Math.abs(rollData.diffLibre);
formula += "min" + valeurMin; formula += "min" + valeurMin;
} }
} }
// Chaque dé fait au minmum la difficulté libre // 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) { if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre); let valeurMin = Math.abs(rollData.diffLibre);
formula += "+" + valeurMin; formula += "+" + valeurMin;
@ -566,7 +571,7 @@ export class RdDUtility {
let roll = await RdDDice.roll(formula, options); let roll = await RdDDice.roll(formula, options);
// 1 dé fait au minmum la difficulté libre // 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) { if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre); let valeurMin = Math.abs(rollData.diffLibre);
if (roll.terms[0].results[0].result < valeurMin) { if (roll.terms[0].results[0].result < valeurMin) {
@ -617,25 +622,6 @@ export class RdDUtility {
return perte.total; return perte.total;
} }
/* -------------------------------------------- */
static currentFatigueMalus(value, max) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
max = Math.max(1, Math.min(max, 60));
value = Math.min(max * 2, Math.max(0, value));
let fatigueTab = fatigueMatrix[max];
let fatigueRem = value;
for (let idx = 0; idx < fatigueTab.length; idx++) {
fatigueRem -= fatigueTab[idx];
if (fatigueRem <= 0) {
return fatigueMalus[idx];
}
}
return -7; // This is the max !
}
return 0;
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async responseNombreAstral(callData) { static async responseNombreAstral(callData) {
let actor = game.actors.get(callData.id); let actor = game.actors.get(callData.id);
@ -662,7 +648,9 @@ export class RdDUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async chatListeners(html) { static async chatListeners(html) {
RdDCombat.registerChatCallbacks(html); RdDCombat.registerChatCallbacks(html)
RdDEmpoignade.registerChatCallbacks(html)
RdDCoeur.registerChatCallbacks(html)
// Gestion spécifique message passeurs // Gestion spécifique message passeurs
html.on("click", '.tmr-passeur-coord a', event => { html.on("click", '.tmr-passeur-coord a', event => {
@ -689,44 +677,6 @@ export class RdDUtility {
RdDPossession.onDefensePossession(attackerId, defenderId, possessionId) RdDPossession.onDefensePossession(attackerId, defenderId, possessionId)
}); });
html.on("click", '.defense-empoignade-cac', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
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 = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
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 = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
RdDEmpoignade.entrainerAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("click", '.empoignade-projeter-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
RdDEmpoignade.projeterAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("change", '.empoignade-perte-endurance', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
if (event.currentTarget.value && event.currentTarget.value != "none") {
RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value)
ChatUtility.removeChatMessageId(chatMessage.id)
}
});
// gestion bouton tchat Acheter // gestion bouton tchat Acheter
html.on("click", '.button-acheter', event => { html.on("click", '.button-acheter', event => {
const venteData = DialogItemAchat.preparerAchat(event.currentTarget); const venteData = DialogItemAchat.preparerAchat(event.currentTarget);
@ -859,17 +809,13 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static confirmerSuppressionSubacteur(sheet, subActor, htmlToDelete) { static confirmSubActeurDelete(sheet, subActor, htmlToDelete, onSuppression = () => { }) {
RdDConfirm.confirmer({ RdDConfirm.confirmer({
settingConfirmer: "confirmation-supprimer-lien-acteur", settingConfirmer: "confirmation-supprimer-lien-acteur",
content: `<p>Etes vous certain de vouloir supprimer le lien vers ${subActor.name} ?</p>`, content: `<p>Etes vous certain de vouloir supprimer le lien vers ${subActor.name} ?</p>`,
title: 'Confirmer la suppression', title: 'Confirmer la suppression',
buttonLabel: 'Supprimer le lien', buttonLabel: 'Supprimer le lien',
onAction: () => { onAction: onSuppression
console.log('Delete : ', subActor.id);
sheet.actor.removeSubacteur(subActor.id);
RdDUtility.slideOnDelete(sheet, htmlToDelete);
}
}) })
} }

View File

@ -5,8 +5,9 @@ import { RdDItemSort } from "./item-sort.js";
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js"; import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js"; import { RdDCarac } from "./rdd-carac.js";
import { RdDPossession } from "./rdd-possession.js";
import { RdDUtility } from "./rdd-utility.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. * tous les ajustements pouvant s'appliquer.
@ -30,7 +31,7 @@ export const referenceAjustements = {
}, },
diffLibre: { diffLibre: {
isUsed: (rollData, actor) => rollData.diffLibre != undefined, 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 getValue: (rollData, actor) => rollData.selectedSort
? RdDItemSort.getDifficulte(rollData.selectedSort, rollData.diffLibre) ? RdDItemSort.getDifficulte(rollData.selectedSort, rollData.diffLibre)
: rollData.diffLibre ?? rollData.competence?.system.default_diffLibre ?? 0 : rollData.diffLibre ?? rollData.competence?.system.default_diffLibre ?? 0
@ -51,7 +52,7 @@ export const referenceAjustements = {
getValue: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).attaque, getValue: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).attaque,
}, },
etat: { etat: {
isUsed: (rollData, actor) => !RdDCarac.isIgnoreEtatGeneral(rollData), isUsed: (rollData, actor) => !RollDataAjustements.isIgnoreEtatGeneral(rollData),
getLabel: (rollData, actor) => 'Etat général', getLabel: (rollData, actor) => 'Etat général',
getValue: (rollData, actor) => actor.getEtatGeneral({ ethylisme: rollData.forceAlcool != undefined }) getValue: (rollData, actor) => actor.getEtatGeneral({ ethylisme: rollData.forceAlcool != undefined })
}, },
@ -68,8 +69,8 @@ export const referenceAjustements = {
getValue: (rollData, actor) => -actor.getEncTotal() getValue: (rollData, actor) => -actor.getEncTotal()
}, },
surenc: { surenc: {
isVisible: (rollData, actor) => actor.isSurenc(), isVisible: (rollData, actor) => RdDCarac.isActionPhysique(rollData.selectedCarac) && actor.isSurenc(),
isUsed: (rollData, actor) => rollData.use?.surenc, isUsed: (rollData, actor) => rollData.use?.surenc && RdDCarac.isActionPhysique(rollData.selectedCarac),
getLabel: (rollData, actor) => 'Sur-encombrement', getLabel: (rollData, actor) => 'Sur-encombrement',
getValue: (rollData, actor) => actor.computeMalusSurEncombrement() getValue: (rollData, actor) => actor.computeMalusSurEncombrement()
}, },
@ -79,13 +80,19 @@ export const referenceAjustements = {
getLabel: (rollData, actor) => 'Appel au moral', getLabel: (rollData, actor) => 'Appel au moral',
getValue: (rollData, actor) => 1 getValue: (rollData, actor) => 1
}, },
coeur: {
isVisible: (rollData, actor) => actor.isPersonnage() && RdDCarac.isVolonte(rollData.selectedCarac),
isUsed: (rollData, actor) => rollData.use.coeur != undefined,
getLabel: (rollData, actor) => 'Ajustement de c&oelig;ur',
getValue: (rollData, actor) => -2 * (rollData.use.coeur?.coeur ?? 0)
},
moralTotal: { moralTotal: {
isUsed: (rollData, actor) => RdDCarac.isVolonte(rollData.selectedCarac), isUsed: (rollData, actor) => RdDCarac.isVolonte(rollData.selectedCarac),
getLabel: (rollData, actor) => 'Moral', getLabel: (rollData, actor) => 'Moral',
getValue: (rollData, actor) => actor.getMoralTotal() getValue: (rollData, actor) => actor.getMoralTotal()
}, },
astrologique: { astrologique: {
isUsed: (rollData, actor) => ReglesOptionelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData), isUsed: (rollData, actor) => ReglesOptionnelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData),
getLabel: (rollData, actor) => 'Astrologique', getLabel: (rollData, actor) => 'Astrologique',
getValue: (rollData, actor) => actor.ajustementAstrologique() getValue: (rollData, actor) => actor.ajustementAstrologique()
}, },
@ -163,7 +170,7 @@ export class RollDataAjustements {
descr: reference.getDescr && reference.getDescr(rollData, actor) descr: reference.getDescr && reference.getDescr(rollData, actor)
} }
} }
rollData.finalLevel = RollDataAjustements.sum(rollData.ajustements); rollData.finalLevel = RollDataAjustements.sum(rollData.ajustements)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -177,4 +184,11 @@ export class RollDataAjustements {
return sum; return sum;
} }
static isIgnoreEtatGeneral(rollData) {
const selectedCarac = rollData.selectedCarac;
return !selectedCarac ||
rollData.ethylisme ||
RdDCarac.isChance(selectedCarac) ||
(RdDCarac.isReve(selectedCarac) && !rollData.competence);
}
} }

View File

@ -1,119 +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: 'Règles générales', name: 'chateau-dormant-gardien', descr: "Saisie des heures de sommeil/jets de moral par le gardien des rêves", default: 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"},
];
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,128 @@
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écupération', name: 'transformation-stress', descr: "Transformer le stress durant Château Dormant"},
{ group: 'Récupération', name: 'recuperation-chance', descr: "Récupérer la chance durant Château Dormant"},
{ group: 'Récupération', name: 'recuperation-ethylisme', descr: "Récupérer l'éthylisme"},
{ group: 'Récupération', name: 'recuperation-reve', descr: "Récupérer le rêve pendant la nuit (les jets sont toujours faits pour les Rêves de Dragons)"},
{ group: 'Récupération', name: 'recuperation-moral', descr: "Le moral revient vers 0 durant Château Dormant"},
{ 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: 650,
width: 550,
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

@ -47,6 +47,7 @@ export class SystemCompendiums extends FormApplication {
label: "Compendiums système", label: "Compendiums système",
hint: "Ouvre la fenêtre de sélection des compendiums système", hint: "Ouvre la fenêtre de sélection des compendiums système",
icon: "fas fa-bars", icon: "fas fa-bars",
restricted: true,
type: SystemCompendiums type: SystemCompendiums
}) })
} }
@ -300,11 +301,12 @@ export class CompendiumTableHelpers {
table, table,
isGM: game.user.isGM, isGM: game.user.isGM,
}); });
ChatMessage.create({ const messageData = {
user: game.user.id, user: game.user.id,
whisper: game.user.id, whisper: game.user.id,
content: flavorContent content: flavorContent
}, { rollMode: "gmroll" }); };
ChatMessage.create(messageData, { rollMode: "gmroll" });
} }
} }

View File

@ -128,7 +128,7 @@ export class AppAstrologie extends Application {
this.selectHeureNaissance(event.currentTarget.attributes['data-heure-naissance'].value); this.selectHeureNaissance(event.currentTarget.attributes['data-heure-naissance'].value);
}) })
this.html.find('[name="jet-astrologie"]').click(event => this.requestJetAstrologie()); this.html.find('[name="jet-astrologie"]').click(event => this.requestJetAstrologie());
this.html.find('[name="rebuild-nombres-astraux"]').click(event => this.rebuildNombresAstraux()); this.html.find('[name="rebuild-nombres-astraux"]').click(event => this.onRebuild());
this.onCalculThemeAstral(); this.onCalculThemeAstral();
} }
@ -144,7 +144,7 @@ export class AppAstrologie extends Application {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rebuildNombresAstraux() { async onRebuild() {
game.system.rdd.calendrier.resetNombresAstraux(); game.system.rdd.calendrier.resetNombresAstraux();
await game.system.rdd.calendrier.rebuildNombresAstraux(); await game.system.rdd.calendrier.rebuildNombresAstraux();

View File

@ -1,4 +1,4 @@
import { ReglesOptionelles } from "../settings/regles-optionelles.js"; import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { EffetsDraconiques } from "../tmr/effets-draconiques.js"; import { EffetsDraconiques } from "../tmr/effets-draconiques.js";
export class DialogRepos extends Dialog { export class DialogRepos extends Dialog {
@ -7,7 +7,7 @@ export class DialogRepos extends Dialog {
if (!actor.isPersonnage()) { if (!actor.isPersonnage()) {
return return
} }
if (!ReglesOptionelles.isUsing("chateau-dormant-gardien") || !actor.hasPlayerOwner) { if (!ReglesOptionnelles.isUsing("chateau-dormant-gardien") || !actor.hasPlayerOwner) {
actor.system.sommeil = { actor.system.sommeil = {
"nouveaujour": true, "nouveaujour": true,
"insomnie": EffetsDraconiques.isSujetInsomnie(actor), "insomnie": EffetsDraconiques.isSujetInsomnie(actor),

View File

@ -1,5 +1,5 @@
import { ENTITE_NONINCARNE } from "./constants.js"; import { ENTITE_NONINCARNE } from "./constants.js";
import { DialogSelectTarget } from "./dialog-select-target.js"; import { DialogSelect } from "./dialog-select.js";
export class Targets { export class Targets {
static listTargets() { static listTargets() {
@ -11,10 +11,7 @@ export class Targets {
} }
static extractTokenData(target) { static extractTokenData(target) {
if (!target) { return { id: target?.id, name: target?.document.name, img: target?.document.texture.src ?? target?.actor.img ?? 'icons/svg/mystery-man.svg' };
return undefined
}
return { id: target.id, name: target.document.name, img: target.document.texture.src ?? target.actor.img ?? 'icons/svg/mystery-man.svg' };
} }
static isTargetEntite(target) { static isTargetEntite(target) {
@ -30,11 +27,12 @@ export class Targets {
return; return;
default: default:
{ {
const tokens = targets.map(it => Targets.extractTokenData(it)) const selectData = {
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-select-target.html", { title: "Choisir une cible",
tokens: tokens label: "Choisir une seule des cibles",
}); list: targets.map(it => Targets.extractTokenData(it))
new DialogSelectTarget(html, onSelectTarget, targets).render(true); };
DialogSelect.select(selectData, onSelectTarget);
} }
} }
} }

View File

@ -0,0 +1,34 @@
import { SYSTEM_RDD } from "../constants.js";
export const AUTO_ADJUST_DARKNESS = "auto-adjust-darkness";
export class AutoAdjustDarkness {
static init() {
game.settings.register(SYSTEM_RDD, AUTO_ADJUST_DARKNESS, {
name: AUTO_ADJUST_DARKNESS,
scope: "world",
config: false,
default: true,
type: Boolean
});
}
static async adjust(darkness) {
if (AutoAdjustDarkness.isAuto()) {
const scene = game.scenes.viewed;
if (scene?.globalLight && scene?.tokenVision) {
await scene.update({ darkness });
}
}
}
static isAuto() {
return game.settings.get(SYSTEM_RDD, AUTO_ADJUST_DARKNESS);
}
static async toggle() {
const previous = AutoAdjustDarkness.isAuto();
await game.settings.set(SYSTEM_RDD, AUTO_ADJUST_DARKNESS, !previous)
}
}

View File

@ -5,10 +5,11 @@ import { RdDUtility } from "../rdd-utility.js";
import { RdDDice } from "../rdd-dice.js"; import { RdDDice } from "../rdd-dice.js";
import { Misc } from "../misc.js"; import { Misc } from "../misc.js";
import { DialogChronologie } from "../dialog-chronologie.js"; import { DialogChronologie } from "../dialog-chronologie.js";
import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js"; import { HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
import { ReglesOptionelles } from "../settings/regles-optionelles.js"; import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js"; import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js";
import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js"; import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js";
import { AutoAdjustDarkness } from "./auto-adjust-darkness.js";
const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/calendar.hbs"; const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/calendar.hbs";
@ -51,7 +52,7 @@ export class RdDCalendrier extends Application {
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp); RdDTimestamp.setWorldTime(this.timestamp);
this.nombresAstraux = this.getNombresAstraux(); this.nombresAstraux = this.getNombresAstraux();
this.rebuildNombresAstraux(HIDE_DICE); // Ensure always up-to-date this.rebuildNombresAstraux(); // Ensure always up-to-date
} }
Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id)); Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
} }
@ -84,30 +85,20 @@ export class RdDCalendrier extends Application {
} }
display() { display() {
AutoAdjustDarkness.adjust(RdDTimestamp.getWorldTime().darkness);
const pos = this.getSavePosition() const pos = this.getSavePosition()
this.render(true, { left: pos.left, top: pos.top }); this.render(true, { left: pos.left, top: pos.top });
return this; return this;
} }
_getHeaderButtons() { _getHeaderButtons() {
const buttons = [];
if (game.user.isGM) { if (game.user.isGM) {
buttons.unshift({ return [
class: "calendar-astrologie", { class: "calendar-astrologie", icon: "fa-solid fa-moon-over-sun", onclick: ev => this.showAstrologieEditor() },
icon: "fa-solid fa-moon-over-sun", { class: "calendar-set-datetime", icon: "fa-solid fa-calendar-pen", onclick: ev => this.showCalendarEditor() },
onclick: ev => this.showAstrologieEditor() ]
},
{
class: "calendar-set-datetime",
icon: "fa-solid fa-calendar-pen",
onclick: ev => this.showCalendarEditor()
});
} }
return buttons return []
}
async maximize() {
await super.maximize()
this.render(true)
} }
async close() { } async close() { }
@ -133,6 +124,7 @@ export class RdDCalendrier extends Application {
formData.isGM = game.user.isGM; formData.isGM = game.user.isGM;
formData.heures = RdDTimestamp.definitions() formData.heures = RdDTimestamp.definitions()
formData.horlogeAnalogique = this.horlogeAnalogique; formData.horlogeAnalogique = this.horlogeAnalogique;
formData.autoDarkness = AutoAdjustDarkness.isAuto()
return formData; return formData;
} }
@ -143,6 +135,7 @@ export class RdDCalendrier extends Application {
this.html = html; this.html = html;
this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create()); this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
this.html.find('.toggle-horloge-analogique').click(ev => this.onToggleHorlogeAnalogique()) this.html.find('.toggle-horloge-analogique').click(ev => this.onToggleHorlogeAnalogique())
this.html.find('.toggle-auto-darkness').click(ev => this.onToggleAutoDarkness())
this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev)); this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
this.html.find('.horloge-roue .horloge-heure').click(event => { this.html.find('.horloge-roue .horloge-heure').click(event => {
const h = this.html.find(event.currentTarget)?.data('heure'); const h = this.html.find(event.currentTarget)?.data('heure');
@ -182,6 +175,16 @@ export class RdDCalendrier extends Application {
return this.timestamp.formatDate(); 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) { isAfterIndexDate(indexDate) {
// TODO: standardize // TODO: standardize
return indexDate < this.timestamp.indexDate; return indexDate < this.timestamp.indexDate;
@ -218,15 +221,8 @@ export class RdDCalendrier extends Application {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async ajouterNombreAstral(indexDate, showDice = SHOW_DICE) { async ajouterNombreAstral(indexDate) {
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, rollMode: "selfroll" }); const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: HIDE_DICE, 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 { return {
nombreAstral: nombreAstral, nombreAstral: nombreAstral,
valeursFausses: [], valeursFausses: [],
@ -261,7 +257,7 @@ export class RdDCalendrier extends Application {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rebuildNombresAstraux(showDice = HIDE_DICE) { async rebuildNombresAstraux() {
if (Misc.isUniqueConnectedGM()) { if (Misc.isUniqueConnectedGM()) {
let newList = []; let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) { for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
@ -270,7 +266,7 @@ export class RdDCalendrier extends Application {
if (na) { if (na) {
newList[i] = na; newList[i] = na;
} else { } else {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice); newList[i] = await this.ajouterNombreAstral(dayIndex);
} }
} }
this.nombresAstraux = newList; this.nombresAstraux = newList;
@ -293,7 +289,7 @@ export class RdDCalendrier extends Application {
const oldTimestamp = this.timestamp; const oldTimestamp = this.timestamp;
await Promise.all(game.actors.map(async actor => await actor.onTimeChanging(oldTimestamp, newTimestamp))); await Promise.all(game.actors.map(async actor => await actor.onTimeChanging(oldTimestamp, newTimestamp)));
RdDTimestamp.setWorldTime(newTimestamp); RdDTimestamp.setWorldTime(newTimestamp);
if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionelles.isUsing("chateau-dormant-gardien")) { if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionnelles.isUsing("chateau-dormant-gardien")) {
await DialogChateauDormant.create(); await DialogChateauDormant.create();
} }
this.timestamp = newTimestamp; this.timestamp = newTimestamp;
@ -368,7 +364,7 @@ export class RdDCalendrier extends Application {
if (request.rolled.isSuccess) { if (request.rolled.isSuccess) {
if (request.rolled.isPart) { if (request.rolled.isPart) {
// Gestion expérience (si existante) // Gestion expérience (si existante)
request.competence = actor.getCompetence("astrologie") request.competence = actor.getCompetence('Astrologie')
request.selectedCarac = actor.system.carac["vue"]; request.selectedCarac = actor.system.carac["vue"];
actor.appliquerAjoutExperience(request, 'hide'); actor.appliquerAjoutExperience(request, 'hide');
} }
@ -440,4 +436,9 @@ export class RdDCalendrier extends Application {
async showAstrologieEditor() { async showAstrologieEditor() {
await AppAstrologie.create(); await AppAstrologie.create();
} }
async onToggleAutoDarkness() {
await AutoAdjustDarkness.toggle()
this.display()
}
} }

View File

@ -2,6 +2,7 @@ import { SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { Grammar } from "../grammar.js"; import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js"; import { Misc } from "../misc.js";
import { RdDDice } from "../rdd-dice.js"; import { RdDDice } from "../rdd-dice.js";
import { AutoAdjustDarkness } from "./auto-adjust-darkness.js";
export const WORLD_TIMESTAMP_SETTING = "calendrier"; export const WORLD_TIMESTAMP_SETTING = "calendrier";
@ -15,18 +16,18 @@ export const RDD_MINUTES_PAR_JOUR = 1440; //RDD_HEURES_PAR_JOUR * RDD_MINUTES_PA
const ROUNDS_PAR_MINUTE = 10; const ROUNDS_PAR_MINUTE = 10;
const DEFINITION_HEURES = [ const DEFINITION_HEURES = [
{ key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "Printemps" }, { key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "Printemps" , darkness: 0.7},
{ key: "sirene", label: "Sirène", lettreFont: 'i', saison: "Printemps" }, { key: "sirene", label: "Sirène", lettreFont: 'i', saison: "Printemps" , darkness: 0.4},
{ key: "faucon", label: "Faucon", lettreFont: 'f', saison: "Printemps" }, { key: "faucon", label: "Faucon", lettreFont: 'f', saison: "Printemps" , darkness: 0},
{ key: "couronne", label: "Couronne", lettreFont: '', saison: "Eté" }, { key: "couronne", label: "Couronne", lettreFont: '', saison: "Eté" , darkness: 0},
{ key: "dragon", label: "Dragon", lettreFont: 'd', saison: "Eté" }, { key: "dragon", label: "Dragon", lettreFont: 'd', saison: "Eté", darkness: 0 },
{ key: "epees", label: "Epées", lettreFont: 'e', saison: "Eté" }, { key: "epees", label: "Epées", lettreFont: 'e', saison: "Eté", darkness: 0},
{ key: "lyre", label: "Lyre", lettreFont: 'l', saison: "Automne" }, { key: "lyre", label: "Lyre", lettreFont: 'l', saison: "Automne", darkness: 0.4 },
{ key: "serpent", label: "Serpent", lettreFont: 's', saison: "Automne" }, { key: "serpent", label: "Serpent", lettreFont: 's', saison: "Automne", darkness: 0.7 },
{ key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne" }, { key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne", darkness: 1 },
{ key: "araignee", label: "Araignée", lettreFont: 'a', saison: "Hiver" }, { key: "araignee", label: "Araignée", lettreFont: 'a', saison: "Hiver", darkness: 1 },
{ key: "roseau", label: "Roseau", lettreFont: 'r', saison: "Hiver" }, { key: "roseau", label: "Roseau", lettreFont: 'r', saison: "Hiver", darkness: 1 },
{ key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "Hiver" }, { key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "Hiver", darkness: 1 },
] ]
const FORMULES_DUREE = [ const FORMULES_DUREE = [
@ -58,6 +59,7 @@ export class RdDTimestamp {
type: Object type: Object
}); });
for (let i = 0; i < DEFINITION_HEURES.length; i++) { for (let i = 0; i < DEFINITION_HEURES.length; i++) {
DEFINITION_HEURES[i].heure = i; DEFINITION_HEURES[i].heure = i;
DEFINITION_HEURES[i].hh = RdDTimestamp.hh(i); DEFINITION_HEURES[i].hh = RdDTimestamp.hh(i);
@ -66,7 +68,6 @@ export class RdDTimestamp {
} }
} }
static hh(heure) { static hh(heure) {
return heure < 9 ? `0${heure + 1}` : `${heure + 1}`; return heure < 9 ? `0${heure + 1}` : `${heure + 1}`;
} }
@ -114,7 +115,7 @@ export class RdDTimestamp {
} }
static imgSigne(signe) { static imgSigne(signe) {
return signe == undefined ? '' : `<img class="img-signe-heure" src="${signe.webp}" alt="${signe.label}" title="${signe.label}"/>` return signe == undefined ? '' : `<img class="img-signe-heure" src="${signe.webp}" data-tooltip="${signe.label}"/>`
} }
static ajustementAstrologiqueHeure(hn, nbAstral, heure) { static ajustementAstrologiqueHeure(hn, nbAstral, heure) {
@ -156,7 +157,9 @@ export class RdDTimestamp {
fields.minute.change(async (event) => await onChangeTimestamp(fields, path)); 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) { static findHeure(heure) {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure); heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
let parHeureOuLabel = DEFINITION_HEURES.filter(it => (it.heure) == parseInt(heure) % RDD_HEURES_PAR_JOUR || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure); let parHeureOuLabel = DEFINITION_HEURES.filter(it => (it.heure) == parseInt(heure) % RDD_HEURES_PAR_JOUR || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure);
@ -237,6 +240,13 @@ export class RdDTimestamp {
get angleHeure() { return this.indexMinute / RDD_MINUTES_PAR_JOUR * 360 - 45 } get angleHeure() { return this.indexMinute / RDD_MINUTES_PAR_JOUR * 360 - 45 }
get angleMinute() { return this.indexMinute / RDD_MINUTES_PAR_HEURES * 360 + 45 } get angleMinute() { return this.indexMinute / RDD_MINUTES_PAR_HEURES * 360 + 45 }
get darkness() {
const darknessDebut = RdDTimestamp.definition(this.heure).darkness *100
const darknessFin = RdDTimestamp.definition(this.heure + 1).darkness *100
const darknessMinute = Math.round((darknessFin - darknessDebut) * this.minute / RDD_MINUTES_PAR_HEURES);
return (darknessDebut + darknessMinute)/100
}
/** /**
* Convertit le timestamp en une structure avec les informations utiles * Convertit le timestamp en une structure avec les informations utiles
* pour afficher la date et l'heure * pour afficher la date et l'heure

View File

@ -1,27 +1,64 @@
/* -------------------------------------------- */ export class TMRConstants {
export const tmrConstants = { constructor({ size = 64 }) {
col1_y: 30, // tailles
col2_y: 55, this.size = size
cellw: 55, this.half = this.size / 2
cellh: 55, this.quarter = this.size / 4
gridx: 28, this.third = this.size / 3
gridy: 28, this.twoThird = this.size * 2 / 3
// tailles this.full = this.size
third: 18, // positions
half: 27.5, this.col1_y = this.half
twoThird: 36, this.col2_y = this.size
full: 55, this.cellw = this.size
// decallages this.cellh = this.size
center: { x: 0, y: 0 }, this.gridx = this.half
top: { x: 0, y: -11.5 }, this.gridy = this.half
topLeft: { x: -11.5, y: -11.5 }, // decallages
left: { x: -11.5, y: 0 }, this.center = { x: 0, y: 0 }
bottomLeft: { x: -11.5, y: 11.5 }, this.top = { x: 0, y: -this.quarter }
bottom: { x: 0, y: 11.5 }, this.topLeft = { x: -this.quarter, y: -this.quarter }
bottomRight: { x: 11.5, y: 11.5 }, this.left = { x: -this.quarter, y: 0 }
right: { x: 11.5, y: 0 }, this.bottomLeft = { x: -this.quarter, y: this.quarter }
topRight: { x: 11.5, y: -11.5 }, this.bottom = { x: 0, y: this.quarter }
this.bottomRight = { x: this.quarter, y: this.quarter }
this.right = { x: this.quarter, y: 0 }
this.topRight = { x: this.quarter, y: -this.quarter }
this.marginx = 1
this.marginy = 1
}
decallage(x, y) {
return {
x: x * this.third,
y: y * this.third
}
}
computeEventPosition(event) {
if (!event.nativeEvent.target.getBoundingClientRect) {
return { x: 0, y: 0 }
}
const canvasRect = event.nativeEvent.target.getBoundingClientRect();
return {
x: event.nativeEvent.clientX - canvasRect.left,
y: event.nativeEvent.clientY - canvasRect.top
}
}
computeEventOddq(event) {
var { x, y } = this.computeEventPosition(event);
return this.computeOddq(x, y);
}
computeOddq(x, y) {
const col = Math.floor(x / this.cellw)
const decallageColonne = col % 2 == 0 ? this.col1_y : this.col2_y
const row = Math.floor((y - decallageColonne) / this.cellh)
return { col, row, x, y }
}
} }
// couleurs // couleurs
@ -35,13 +72,15 @@ export const tmrColors = {
rencontre: 0xFF0000, rencontre: 0xFF0000,
casehumide: 0x1050F0, casehumide: 0x1050F0,
} }
export const tmrTokenZIndex = { export const tmrTokenZIndex = {
sort: 40,
tetes: 20,
casehumide: 10, casehumide: 10,
conquete: 30, tetes: 20,
sort: 30,
conquete: 40,
rencontre: 50, rencontre: 50,
trounoir: 60, trounoir: 60,
demireve: 70, demireve: 70,
tooltip: 100, tooltip: 100,
} }

View File

@ -163,7 +163,7 @@ const TMRMapping = {
C12: { type: "lac", label: "Lac de Fricassa" }, C12: { type: "lac", label: "Lac de Fricassa" },
D12: { type: "collines", label: "Collines dHuaï" }, D12: { type: "collines", label: "Collines dHuaï" },
E12: { type: "monts", label: "Monts Ajourés" }, E12: { type: "monts", label: "Monts Ajourés" },
F12: { type: "necropole", label: "Nécropole de Troat" }, F12: { type: "necropole", label: "Nécropole de Throat" },
G12: { type: "plaines", label: "Plaines de Lufmil" }, G12: { type: "plaines", label: "Plaines de Lufmil" },
H12: { type: "collines", label: "Collines de Tooth" }, H12: { type: "collines", label: "Collines de Tooth" },
I12: { type: "gouffre", label: "Gouffre Abimeux" }, I12: { type: "gouffre", label: "Gouffre Abimeux" },
@ -199,7 +199,7 @@ const TMRMapping = {
K14: { type: "necropole", label: "Nécropole dAntinéar" }, K14: { type: "necropole", label: "Nécropole dAntinéar" },
L14: { type: "plaines", label: "Plaines de Jislith" }, L14: { type: "plaines", label: "Plaines de Jislith" },
M14: { type: "desolation", label: "Désolation dAprès" }, M14: { type: "desolation", label: "Désolation dAprès" },
A15: { type: "cite", label: "Cité de Mielh" }, A15: { type: "cite", label: "Cité de Mielh" },
C15: { type: "plaines", label: "Plaines de Toué" }, C15: { type: "plaines", label: "Plaines de Toué" },
E15: { type: "foret", label: "Forêt des Furies" }, E15: { type: "foret", label: "Forêt des Furies" },
@ -227,16 +227,28 @@ export const TMRType = {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
const tmrRandomMovePatten = const TMR_MOVE = {
[{ name: 'top', col: 0, row: -1 }, "top": { even: { row: -1, col: 0 }, odd: { row: -1, col: 0 }, },
{ name: 'topright', col: 1, row: -1 }, "topleft": { even: { row: -1, col: -1 }, odd: { row: 0, col: -1 }, },
{ name: 'botright', col: 1, row: 1 }, "topright": { even: { row: -1, col: 1 }, odd: { row: 0, col: 1 }, },
{ name: 'bot', col: 0, row: 1 }, "bottomleft": { even: { row: 0, col: -1 }, odd: { row: 1, col: -1 }, },
{ name: 'botleft', col: -1, row: 1 }, "bottomright": { even: { row: 0, col: 1 }, odd: { row: 1, col: 1 }, },
{ name: 'topleft', col: -1, row: -1 } "bottom": { even: { row: 1, col: 0 }, odd: { row: 1, col: 0 }, },
] }
/* -------------------------------------------- */ /* --------------------------------------------
* Pour comprendre les conversions entre coordonnées
* - "TMR" A1, ... M15
* - oddq: {col, row}
* - axial: { q, r )
*
* Un site intéressant: https://www.redblobgames.com/grids/hexagons/#distances
*
* Pour être concis, le code TMR lettre(colonne)-ligne correspond à une grille hexagonale en coordonnées "odd-q"
* (lettre => col, ligne => row).
*
* Pour les calculs de distance, les coordonnées axiales sont beaucoup plus pratiques.
*/
export class TMRUtility { export class TMRUtility {
static init() { static init() {
for (let coord in TMRMapping) { for (let coord in TMRMapping) {
@ -274,11 +286,11 @@ export class TMRUtility {
const tmr = TMRUtility.getTMR(coord); const tmr = TMRUtility.getTMR(coord);
return Grammar.articleDetermine(tmr.type) + ' ' + tmr.label; return Grammar.articleDetermine(tmr.type) + ' ' + tmr.label;
} }
static findTMRLike(type, options = {inclusMauvaise:true}) { static findTMRLike(type, options = { inclusMauvaise: true }) {
const choix = [...Object.values(TMRType)] const choix = [...Object.values(TMRType)]
if (options.inclusMauvaise){ if (options.inclusMauvaise) {
choix.push({name: 'Mauvaise'}); choix.push({ name: 'Mauvaise' });
} }
const selection = Misc.findAllLike(type, choix).map(it => it.name); const selection = Misc.findAllLike(type, choix).map(it => it.name);
if (selection.length == 0) { if (selection.length == 0) {
@ -297,7 +309,7 @@ export class TMRUtility {
} }
static buildSelectionTypesTMR(typesTMR) { static buildSelectionTypesTMR(typesTMR) {
typesTMR = typesTMR?? []; typesTMR = typesTMR ?? [];
return Object.values(TMRType).map(value => Misc.upperFirst(value.name)) return Object.values(TMRType).map(value => Misc.upperFirst(value.name))
.sort() .sort()
.map(name => { return { name: name, selected: typesTMR.includes(name) } }); .map(name => { return { name: name, selected: typesTMR.includes(name) } });
@ -312,17 +324,33 @@ export class TMRUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async getDirectionPattern() { static deplacement(coordOrig, moveName) {
return await RdDDice.rollOneOf(tmrRandomMovePatten); const tmrMove = TMR_MOVE[moveName];
if (! tmrMove) {
ui.notifications.error(`Le déplacement dans les TMR '${moveName}' est inconnu`)
return coordOrig
}
const fromOddq = TMRUtility.coordTMRToOddq(coordOrig);
const move = TMRUtility.getOddqMove(tmrMove, fromOddq);
const toOddq = TMRUtility.addOddq(fromOddq, move);
return TMRUtility.oddqToCoordTMR(toOddq);
}
static getOddqMove(tmrMove, oddq) {
return oddq.col % 2 == 1 ? tmrMove.odd : tmrMove.even;
}
static async getDirectionPattern(oddq) {
const tmrMove = await RdDDice.rollOneOf(Object.values(TMR_MOVE));
return TMRUtility.getOddqMove(tmrMove, oddq);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async deplaceTMRAleatoire(actor, coord) { static async deplaceTMRAleatoire(actor, coord) {
const currentOddq = TMRUtility.coordTMRToOddq(coord); const oddq = TMRUtility.coordTMRToOddq(coord);
const direction = await TMRUtility.getDirectionPattern(); const direction = await TMRUtility.getDirectionPattern(oddq);
currentOddq.col = currentOddq.col + direction.col; const currentOddq = TMRUtility.addOddq(oddq, direction)
currentOddq.row = currentOddq.row + direction.row; if (TMRUtility.isOddqInTMR(currentOddq)) { // Sortie de carte ! Ré-insertion aléatoire
if (this.isOddqInTMR(currentOddq)) { // Sortie de carte ! Ré-insertion aléatoire
return TMRUtility.getTMR(TMRUtility.oddqToCoordTMR(currentOddq)); return TMRUtility.getTMR(TMRUtility.oddqToCoordTMR(currentOddq));
} else { } else {
return await actor.reinsertionAleatoire('Sortie de carte'); return await actor.reinsertionAleatoire('Sortie de carte');
@ -359,15 +387,15 @@ export class TMRUtility {
* *
*/ */
static getTMRPortee(coord, portee) { static getTMRPortee(coord, portee) {
let centerOddq = this.coordTMRToOddq(coord); let centerOddq = TMRUtility.coordTMRToOddq(coord);
let caseList = []; let caseList = [];
for (let dcol = -portee; dcol <= portee; dcol++) { // rows for (let dcol = -portee; dcol <= portee; dcol++) { // rows
for (let drow = -portee; drow <= portee; drow++) { // columns for (let drow = -portee; drow <= portee; drow++) { // columns
const currentOddq = { col: centerOddq.col + dcol, row: centerOddq.row + drow }; const currentOddq = { col: centerOddq.col + dcol, row: centerOddq.row + drow };
if (this.isOddqInTMR(currentOddq)) { if (TMRUtility.isOddqInTMR(currentOddq)) {
let dist = this.distanceOddq(centerOddq, currentOddq); let dist = TMRUtility.distanceOddq(centerOddq, currentOddq);
if (dist <= portee) { if (dist <= portee) {
caseList.push(this.oddqToCoordTMR(currentOddq)); // Inside the area caseList.push(TMRUtility.oddqToCoordTMR(currentOddq)); // Inside the area
} }
} }
} }
@ -375,10 +403,6 @@ export class TMRUtility {
return caseList; return caseList;
} }
/* -------------------------------------------- */
// https://www.redblobgames.com/grids/hexagons/#distances
// TMR Letter-row correspond to "odd-q" grid (letter => col, numeric => row )
/* -------------------------------------------- */ /* -------------------------------------------- */
static coordTMRToOddq(coordTMR) { static coordTMRToOddq(coordTMR) {
let col = coordTMR.charCodeAt(0) - 65; let col = coordTMR.charCodeAt(0) - 65;
@ -400,18 +424,14 @@ export class TMRUtility {
col >= 0 && col < 13 && col >= 0 && col < 13 &&
row >= 0 && row >= 0 &&
(row + col % 2 <= 14) (row + col % 2 <= 14)
); );
// if (x >= 0 && x < 13 && y >= 0 && y < 14) return true;
// if (x >= 0 && x < 13 && x % 2 == 0 && y == 14) return true;
// return false;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static distanceCoordTMR(coord1, coord2) { static distanceCoordTMR(coord1, coord2) {
let oddq1 = this.coordTMRToOddq(coord1); let oddq1 = TMRUtility.coordTMRToOddq(coord1);
let oddq2 = this.coordTMRToOddq(coord2); let oddq2 = TMRUtility.coordTMRToOddq(coord2);
return this.distanceOddq(oddq1, oddq2); return TMRUtility.distanceOddq(oddq1, oddq2);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -419,13 +439,13 @@ export class TMRUtility {
const axial1 = TMRUtility.oddqToAxial(oddq1); const axial1 = TMRUtility.oddqToAxial(oddq1);
const axial2 = TMRUtility.oddqToAxial(oddq2); const axial2 = TMRUtility.oddqToAxial(oddq2);
return TMRUtility.distanceAxial(axial1, axial2); return TMRUtility.distanceAxial(axial1, axial2);
}
// const dx = oddq2.col - oddq1.col; static addOddq(move, oddq) {
// const dy = oddq2.row - oddq1.row; return {
// const abs_dx = Math.abs(dx); row: oddq.row + move.row,
// const abs_dy = Math.abs(dy); col: oddq.col + move.col
// const distance = Math.sign(dx) == Math.sign(dy) ? Math.max(abs_dx, abs_dy) : (abs_dx + abs_dy); }
// return distance;
} }
static oddqToAxial(pos) { static oddqToAxial(pos) {
@ -444,25 +464,9 @@ export class TMRUtility {
static axial_subtract(a, b) { static axial_subtract(a, b) {
return { return {
q: a.q- b.q, q: a.q - b.q,
r: a.r - b.r r: a.r - b.r
}; };
} }
// function axial_to_cube(hex):
// var q = hex.q
// var r = hex.r
// var s = -q - r
// return Cube(q, r, s)
// }
// /* -------------------------------------------- */
// static computeRealPictureCoordinates(coordOddq) {
// let decallagePairImpair = (coordOddq.col % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
// return {
// x: tmrConstants.gridx + (coordOddq.col * tmrConstants.cellw),
// y: tmrConstants.gridy + (coordOddq.row * tmrConstants.cellh) + decallagePairImpair
// }
// }
} }

107
module/tmr/animation.js Normal file
View File

@ -0,0 +1,107 @@
// pixiTMR.animate(pixiApp => pixiApp.ticker.add((delta) => {
// if (!sprite.waveAnimation) {
// sprite.waveAnimation = {
// originx: sprite.x,
// movex: 0,
// step: 0.03
// }
// }
// else {
// if (Math.abs(sprite.waveAnimation.movex) > 2) {
// sprite.waveAnimation.step = -sprite.waveAnimation.step
// }
// sprite.waveAnimation.movex += sprite.waveAnimation.step;
// }
// sprite.x = sprite.waveAnimation.originx + sprite.waveAnimation.movex
// }));
// return pixiTMR.square(this.code(),
// {
// zIndex: tmrTokenZIndex.trounoir,
// tint: tmrColors.trounoir,
// alpha: 1,
// taille: () => pixiTMR.sizes.full,
// decallage: {
// x: -pixiTMR.sizes.half,
// y: -pixiTMR.sizes.half
// }
// })
export class TMRAnimations {
static withAnimation(sprite, pixiTMR, ...animations) {
if (animations.length > 0) {
animations.forEach(animation =>
pixiTMR.animate(pixiApp => pixiApp.ticker.add(delta => animation(sprite, delta)))
)
}
return sprite
}
static rotation(options = { frequence: () => 1, angle: () => 1 }) {
return (sprite, delta) => {
if (!sprite.tmrConfig) {
sprite.tmrConfig = { nextTick: 0 }
}
sprite.tmrConfig.nextTick -= delta
if (sprite.tmrConfig.nextTick <= 0) {
sprite.tmrConfig.nextTick = options.frequence(delta)
sprite.angle += options.angle(delta)
}
}
}
static changeZoom(range = { min: 0.8, max: 1.2, step: 0.005 }) {
return (sprite, delta) => {
if (!sprite.tmrConfig) {
sprite.tmrConfig = TMRAnimations.startRange(range)
}
sprite.tmrConfig.current += (sprite.tmrConfig.step * delta)
if (sprite.tmrConfig.current < sprite.tmrConfig.min) {
sprite.tmrConfig.step = Math.abs(sprite.tmrConfig.step)
}
else if (sprite.tmrConfig.current > sprite.tmrConfig.max) {
sprite.tmrConfig.step = -Math.abs(sprite.tmrConfig.step)
}
const taille = sprite.tmrConfig.current * sprite.taille()
sprite.width = taille
sprite.height = taille
}
}
static verticalAxis(options = { step: 1 }) {
return (sprite, delta) => {
if (!sprite.tmrConfig) {
sprite.tmrConfig = this.startRange({
min: -Math.PI / 2,
max: Math.PI / 2,
step: options.step * Math.PI / 180,
})
}
sprite.tmrConfig.current += (sprite.tmrConfig.step * delta)
sprite.width = Math.cos(sprite.tmrConfig.current) * sprite.taille()
}
}
static startRange(range) {
range.current = TMRAnimations.randomInSegment(range)
const min = range.min
if (min > range.max) {
range.min = range.max
range.max = min
}
return range
}
static outOfRange(range) {
return range.current < range.min || range.current > range.max
}
static randomInSegment(range) {
const min = range.min
const max = range.max
const step = range.step
return min + (Math.floor(Math.random() / step) * step) * (max - min)
}
}

View File

@ -4,10 +4,6 @@ import { Misc } from "../misc.js";
export class AugmentationSeuil extends Draconique { export class AugmentationSeuil extends Draconique {
constructor() {
super();
}
type() { return 'tete' } type() { return 'tete' }
match(item) { return Draconique.isTeteDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('augmentation du seuil de reve'); } match(item) { return Draconique.isTeteDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('augmentation du seuil de reve'); }
manualMessage() { return false } manualMessage() { return false }

View File

@ -1,20 +1,31 @@
import { TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js"; import { Draconique } from "./draconique.js";
import { PixiTMR } from "./pixi-tmr.js";
export class CarteTmr extends Draconique { export class CarteTmr extends Draconique {
constructor() {
super();
}
type() { return '' } type() { return '' }
match(item) { return false; } match(item) { return false; }
manualMessage() { return false } manualMessage() { return false }
async onActorCreateOwned(actor, item) { } async onActorCreateOwned(actor, item) { }
code() { return 'tmr' } code() { return 'tmr' }
img() { return 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmp_main_r1.webp' } img() { return 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmr.webp' }
createSprite(pixiTMR) { createSprite(pixiTMR) {
return pixiTMR.carteTmr(this.code()); const img = PixiTMR.getImgFromCode(this.code())
const sprite = new PIXI.Sprite(PIXI.utils.TextureCache[img]);
// Setup the position of the TMR
sprite.x = pixiTMR.pixiApp.screen.x;
sprite.y = pixiTMR.pixiApp.screen.y;
sprite.width = pixiTMR.pixiApp.screen.width;
sprite.height = pixiTMR.pixiApp.screen.height;
// Rotate around the center
sprite.anchor.set(0);
sprite.buttonMode = true;
sprite.tmrObject = pixiTMR;
return sprite;
} }
} }

View File

@ -1,40 +1,38 @@
import { Grammar } from "../grammar.js"; import { Grammar } from "../grammar.js";
import { RdDDice } from "../rdd-dice.js"; import { RdDDice } from "../rdd-dice.js";
import { TMRUtility } from "../tmr-utility.js"; import { TMRUtility } from "../tmr-utility.js";
import { tmrConstants, tmrColors, tmrTokenZIndex } from "../tmr-constants.js"; import { tmrTokenZIndex } from "../tmr-constants.js";
import { Draconique } from "./draconique.js"; import { Draconique } from "./draconique.js";
import { TMRAnimations } from "./animation.js";
export class Conquete extends Draconique { export class Conquete extends Draconique {
constructor() {
super();
}
type() { return 'queue' } type() { return 'queue' }
match(item) { return Draconique.isQueueDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('conquete'); } match(item) { return Draconique.isQueueDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('conquete'); }
manualMessage() { return false } manualMessage() { return false }
async onActorCreateOwned(actor, item) { await this._creerConquete(actor, item); } async onActorCreateOwned(actor, item) { await this._creerConquete(actor, item); }
code() { return 'conquete' } code() { return 'conquete' }
tooltip(linkData) { return `${this.tmrLabel(linkData)}: doit être conquis` } tooltip(linkData) { return `Doit être conquis` }
img() { return 'icons/svg/combat.svg' } img() { return 'systems/foundryvtt-reve-de-dragon/icons/tmr/conquete.svg' }
createSprite(pixiTMR) { createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), return TMRAnimations.withAnimation(
{ pixiTMR.sprite(this.code(), {
zIndex: tmrTokenZIndex.conquete, zIndex: tmrTokenZIndex.conquete,
color: tmrColors.queues, decallage: pixiTMR.sizes.decallage(0, 0),
taille: tmrConstants.full, taille: () => pixiTMR.sizes.half,
decallage: { x: 2, y: 0 } }),
}); pixiTMR,
TMRAnimations.changeZoom()
)
} }
async _creerConquete(actor, queue) { async _creerConquete(actor, queue) {
let existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord); let existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord);
let possibles = TMRUtility.filterTMR(tmr => !TMRUtility.isCaseHumide(tmr) && !existants.includes(tmr.coord)); let possibles = TMRUtility.filterTMR(tmr => !TMRUtility.isCaseHumide(tmr) && !existants.includes(tmr.coord));
let conquete = await RdDDice.rollOneOf(possibles); let conquete = await RdDDice.rollOneOf(possibles);
await this.createCaseTmr(actor, 'Conquête: ' + conquete.label, conquete, queue.id); await this.createCaseTmr(actor, 'Conquête', conquete, queue.id);
} }
async onActorDeleteCaseTmr(actor, casetmr) { async onActorDeleteCaseTmr(actor, casetmr) {

View File

@ -1,34 +1,30 @@
import { Grammar } from "../grammar.js"; import { Grammar } from "../grammar.js";
import { TMRUtility } from "../tmr-utility.js"; import { TMRUtility } from "../tmr-utility.js";
import { tmrConstants, tmrTokenZIndex } from "../tmr-constants.js"; import { tmrTokenZIndex } from "../tmr-constants.js";
import { Draconique } from "./draconique.js"; import { Draconique } from "./draconique.js";
export class Debordement extends Draconique { export class Debordement extends Draconique {
constructor() {
super();
}
type() { return 'souffle' } type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('debordement'); } match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('debordement'); }
manualMessage() { return false } manualMessage() { return false }
async onActorCreateOwned(actor, souffle) { async onActorCreateOwned(actor, souffle) {
const existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord); const existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord);
const tmr = await TMRUtility.getTMRAleatoire(it => !(TMRUtility.isCaseHumide(it) || existants.includes(it.coord))); const tmr = await TMRUtility.getTMRAleatoire(it => !(TMRUtility.isCaseHumide(it) || existants.includes(it.coord)));
await this.createCaseTmr(actor, 'Debordement: ' + tmr.label, tmr, souffle.id); await this.createCaseTmr(actor, 'Debordement', tmr, souffle.id);
} }
code() { return 'debordement' } code() { return 'debordement' }
tooltip(linkData) { return `Débordement en ${this.tmrLabel(linkData)}` } tooltip(linkData) { return `Débordement` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/tmr/wave.webp' } img() { return 'systems/foundryvtt-reve-de-dragon/icons/tmr/debordement.svg' }
createSprite(pixiTMR) { createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), { const sprite = pixiTMR.sprite(this.code(), {
zIndex: tmrTokenZIndex.casehumide, zIndex: tmrTokenZIndex.casehumide,
alpha: 0.6, decallage: pixiTMR.sizes.decallage(0, 2/3),
taille: tmrConstants.full, taille: () => pixiTMR.sizes.half,
decallage: tmrConstants.center })
}); return sprite;
} }
} }

View File

@ -1,12 +1,8 @@
import { tmrConstants, tmrColors, tmrTokenZIndex } from "../tmr-constants.js"; import { tmrColors, tmrTokenZIndex } from "../tmr-constants.js";
import { Draconique } from "./draconique.js"; import { Draconique } from "./draconique.js";
export class DemiReve extends Draconique { export class DemiReve extends Draconique {
constructor() {
super();
}
type() { return '' } type() { return '' }
match(item) { return false; } match(item) { return false; }
manualMessage() { return false } manualMessage() { return false }
@ -18,9 +14,9 @@ export class DemiReve extends Draconique {
createSprite(pixiTMR) { createSprite(pixiTMR) {
const sprite = pixiTMR.sprite(this.code(), { const sprite = pixiTMR.sprite(this.code(), {
color: tmrColors.demireve, tint: tmrColors.demireve,
zIndex: tmrTokenZIndex.demireve, zIndex: tmrTokenZIndex.demireve,
taille: (tmrConstants.full * 0.7) taille: () => pixiTMR.sizes.twoThird
}); });
pixiTMR.animate(pixiApp => pixiApp.ticker.add((delta) => sprite.rotation -= 0.01 * delta)); pixiTMR.animate(pixiApp => pixiApp.ticker.add((delta) => sprite.rotation -= 0.01 * delta));
return sprite; return sprite;

View File

@ -1,14 +1,12 @@
import { Grammar } from "../grammar.js"; import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js"; import { Misc } from "../misc.js";
import { RdDDice } from "../rdd-dice.js"; import { RdDDice } from "../rdd-dice.js";
import { TMRUtility, TMRType} from "../tmr-utility.js"; import { TMRUtility, TMRType } from "../tmr-utility.js";
import { tmrConstants, tmrColors, tmrTokenZIndex } from "../tmr-constants.js"; import { tmrTokenZIndex } from "../tmr-constants.js";
import { Draconique } from "./draconique.js"; import { Draconique } from "./draconique.js";
import { TMRAnimations } from "./animation.js";
export class Desorientation extends Draconique { export class Desorientation extends Draconique {
constructor() {
super();
}
type() { return 'souffle' } type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('desorientation'); } match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('desorientation'); }
@ -28,24 +26,27 @@ export class Desorientation extends Draconique {
code() { return 'desorientation' } code() { return 'desorientation' }
tooltip(linkData) { return `Désorientation, cette case n'existe plus !` } tooltip(linkData) { return `Désorientation, cette case n'existe plus !` }
img() { return 'icons/svg/explosion.svg' } img() { return 'systems/foundryvtt-reve-de-dragon/icons/tmr/desorientation.svg' }
createSprite(pixiTMR) { createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), return TMRAnimations.withAnimation(
{ pixiTMR.sprite(this.code(), {
zIndex: tmrTokenZIndex.trounoir, zIndex: tmrTokenZIndex.trounoir,
color: tmrColors.trounoir, taille: () => pixiTMR.sizes.full,
alpha: 1, }),
taille: tmrConstants.full, pixiTMR,
decallage: { x: 2, y: 2 }, TMRAnimations.rotation({
}); frequence: delta => 2^(2 + Math.random() * 12) * 70,
angle: delta => (Math.floor(Math.random() * 2) - 1) * 30
})
)
} }
async _creerCasesTmr(actor, type, souffle) { async _creerCasesTmr(actor, type, souffle) {
const existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord); const existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord);
let tmrs = TMRUtility.filterTMR(it => it.type == type && !existants.includes(it.coord)); let tmrs = TMRUtility.filterTMR(it => it.type == type && !existants.includes(it.coord));
for (let tmr of tmrs) { for (let tmr of tmrs) {
await this.createCaseTmr(actor, 'Désorientation: ' + tmr.label, tmr, souffle.id); await this.createCaseTmr(actor, 'Désorientation', tmr, souffle.id);
} }
} }

View File

@ -9,14 +9,15 @@ const registeredEffects = [
* Définition des informations d'une "draconique" (queue, ombre, tête, souffle) qui influence les TMR * Définition des informations d'une "draconique" (queue, ombre, tête, souffle) qui influence les TMR
*/ */
export class Draconique { export class Draconique {
static init() {
}
static isCaseTMR(item) { return item.type == TYPES.casetmr; } static isCaseTMR(item) { return item.type == TYPES.casetmr; }
static isQueueDragon(item) { return item.isQueueDragon(); } static isQueueDragon(item) { return item.isQueueDragon(); }
static isSouffleDragon(item) {return item.type == TYPES.souffle; } static isSouffleDragon(item) { return item.type == TYPES.souffle; }
static isTeteDragon(item) { return item.type == TYPES.tete; } static isTeteDragon(item) { return item.type == TYPES.tete; }
static isQueueSouffle(item) { return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item); } static isQueueSouffle(item) { return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item); }
tmrLabel(linkData) { return TMRUtility.getTMRLabel(linkData.system.coord); }
static register(draconique) { static register(draconique) {
registeredEffects[draconique.code()] = draconique; registeredEffects[draconique.code()] = draconique;
if (draconique.img()) { if (draconique.img()) {
@ -33,6 +34,8 @@ export class Draconique {
return registeredEffects[code]; return registeredEffects[code];
} }
tmrLabel(linkData) { return TMRUtility.getTMRLabel(linkData.system.coord); }
/** /**
* @param item un Item quelconque * @param item un Item quelconque
* @returns true si l'item correspond * @returns true si l'item correspond
@ -78,20 +81,21 @@ export class Draconique {
/** /**
* @param {*} img l'url du fichier image à utiliser pour le token. Si indéfini (et si createSprite n'est pas surchargé), * @param {*} img l'url du fichier image à utiliser pour le token. Si indéfini (et si createSprite n'est pas surchargé),
* un disque est utilisé. * un disque est utilisé.
*/ */
img() { return undefined } img() { return undefined }
/** /**
* factory d'élément graphique PIXI correpsondant à l'objet draconique * factory d'élément graphique PIXI correspondant à l'objet draconique
* @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks. * @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.
*/ */
token(pixiTMR, linkData, coordTMR, type = undefined) { token(pixiTMR, linkData, coordTMR, type = undefined) {
const tooltip = this.tooltip(linkData);
const token = { const token = {
sprite: this.createSprite(pixiTMR), sprite: this.createSprite(pixiTMR),
coordTMR: coordTMR coordTMR: coordTMR,
tooltip: tooltip
}; };
token[type ?? this.code()] = linkData; token[type ?? this.code()] = linkData;
pixiTMR.addTooltip(token.sprite, this.tooltip(linkData));
return token; return token;
} }
@ -101,7 +105,7 @@ export class Draconique {
*/ */
createSprite(pixiTMR) { createSprite(pixiTMR) {
if (this.img()) { if (this.img()) {
return pixiTMR.sprite(this.code()); return pixiTMR.sprite(this.code())
} }
else { else {
return pixiTMR.circle() return pixiTMR.circle()
@ -116,24 +120,26 @@ export class Draconique {
isCase(item, coord = undefined) { isCase(item, coord = undefined) {
return Draconique.isCaseTMR(item) && item.system.specific == this.code() && (coord ? item.system.coord == coord : true); return Draconique.isCaseTMR(item) && item.system.specific == this.code() && (coord ? item.system.coord == coord : true);
} }
find(list, coord = undefined) { find(list, coord = undefined) {
return list.find(c => this.isCase(c, coord)); return list.find(c => this.isCase(c, coord));
} }
async createCaseTmr(actor, label, tmr, sourceId = undefined) { async createCaseTmr(actor, label, tmr, sourceId = undefined) {
const casetmrData = { const casetmrData = {
name: label, type: 'casetmr', img: this.img(), name: label,
type: 'casetmr',
img: this.img(),
system: { coord: tmr.coord, specific: this.code(), sourceid: sourceId } system: { coord: tmr.coord, specific: this.code(), sourceid: sourceId }
}; };
await actor.createEmbeddedDocuments('Item', [casetmrData]); await actor.createEmbeddedDocuments('Item', [casetmrData]);
} }
async deleteCasesTmr(actor, draconique) { async deleteCasesTmr(actor, draconique) {
let caseTmrs = actor.items.filter(it => this.isCaseForSource(it, draconique)); let caseTmrs = actor.items.filter(it => this.isCaseForSource(it, draconique));
await actor.deleteEmbeddedDocuments('Item', caseTmrs.map(it => it.id)); await actor.deleteEmbeddedDocuments('Item', caseTmrs.map(it => it.id));
} }
isCaseForSource(item, draconique) { isCaseForSource(item, draconique) {
return Draconique.isCaseTMR(item) && item.system.specific == this.code() && item.system.sourceid == draconique.id; return Draconique.isCaseTMR(item) && item.system.specific == this.code() && item.system.sourceid == draconique.id;
} }

View File

@ -20,9 +20,9 @@ import { Grammar } from "../grammar.js";
import { AugmentationSeuil } from "./augmentation-seuil.js"; import { AugmentationSeuil } from "./augmentation-seuil.js";
import { TYPES } from "../item.js"; import { TYPES } from "../item.js";
export class EffetsDraconiques { export class EffetsDraconiques {
static carteTmr = new CarteTmr(); static carteTmr = new CarteTmr();
static demiReve = new DemiReve(); static demiReve = new DemiReve();
static rencontre = new Rencontre(); static rencontre = new Rencontre();
static sortReserve = new SortReserve(); static sortReserve = new SortReserve();
@ -39,10 +39,13 @@ export class EffetsDraconiques {
static pelerinage = new Pelerinage(); static pelerinage = new Pelerinage();
static periple = new Periple(); static periple = new Periple();
static urgenceDraconique = new UrgenceDraconique(); static urgenceDraconique = new UrgenceDraconique();
static augmentationSeuil = new AugmentationSeuil(); static augmentationSeuil = new AugmentationSeuil();
static init() { static init() {
Draconique.init();
Draconique.register(EffetsDraconiques.carteTmr); Draconique.register(EffetsDraconiques.carteTmr);
// icône TMR
Draconique.register(EffetsDraconiques.demiReve); Draconique.register(EffetsDraconiques.demiReve);
Draconique.register(EffetsDraconiques.rencontre); Draconique.register(EffetsDraconiques.rencontre);
Draconique.register(EffetsDraconiques.sortReserve); Draconique.register(EffetsDraconiques.sortReserve);
@ -59,6 +62,7 @@ export class EffetsDraconiques {
Draconique.register(EffetsDraconiques.pelerinage); Draconique.register(EffetsDraconiques.pelerinage);
Draconique.register(EffetsDraconiques.periple); Draconique.register(EffetsDraconiques.periple);
Draconique.register(EffetsDraconiques.urgenceDraconique); Draconique.register(EffetsDraconiques.urgenceDraconique);
// effets sans icône TMR
Draconique.register(EffetsDraconiques.augmentationSeuil) Draconique.register(EffetsDraconiques.augmentationSeuil)
} }
@ -184,5 +188,4 @@ export class EffetsDraconiques {
return EffetsDraconiques.soufflesDragon(actor, 'péage').length > 0; return EffetsDraconiques.soufflesDragon(actor, 'péage').length > 0;
} }
} }

View File

@ -1,6 +1,8 @@
import { ExperienceLog, XP_TOPIC } from "../actor/experience-log.js";
import { ChatUtility } from "../chat-utility.js"; import { ChatUtility } from "../chat-utility.js";
import { Poetique } from "../poetique.js"; import { Poetique } from "../poetique.js";
import { RdDDice } from "../rdd-dice.js"; import { RdDDice } from "../rdd-dice.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { TMRUtility } from "../tmr-utility.js"; import { TMRUtility } from "../tmr-utility.js";
export class EffetsRencontre { export class EffetsRencontre {
@ -8,7 +10,7 @@ export class EffetsRencontre {
static messager = async (dialog, context) => { static messager = async (dialog, context) => {
dialog.setRencontreState('messager', TMRUtility.getTMRPortee(context.tmr.coord, context.rencontre.system.force)); dialog.setRencontreState('messager', TMRUtility.getTMRPortee(context.tmr.coord, context.rencontre.system.force));
} }
static passeur = async (dialog, context) => { static passeur = async (dialog, context) => {
dialog.setRencontreState('passeur', TMRUtility.getTMRPortee(context.tmr.coord, context.rencontre.system.force)); dialog.setRencontreState('passeur', TMRUtility.getTMRPortee(context.tmr.coord, context.rencontre.system.force));
} }
@ -25,16 +27,25 @@ export class EffetsRencontre {
static reve_plus_1 = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, 1) } static reve_plus_1 = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, 1) }
static reve_moins_force = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, -context.rencontre.system.force) } static reve_moins_force = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, -context.rencontre.system.force) }
static reve_moins_1 = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, -1) } static reve_moins_1 = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, -1) }
static $reve_plus = async (actor, valeur) => { await actor.reveActuelIncDec(valeur) } static $reve_plus = async (actor, reve) => {
if (!ReglesOptionnelles.isUsing("recuperation-reve") && reve < 0) {
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name),
content: `Pas de récupération de rêve (${reve} points ignorés)`
});
return
}
await actor.reveActuelIncDec(reve)
}
static vie_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) } static vie_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) }
static vie_moins_force = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -context.rencontre.system.force) } static vie_moins_force = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -context.rencontre.system.force) }
static $vie_plus = async (actor, valeur) => { await actor.santeIncDec("vie", valeur) } static $vie_plus = async (actor, valeur) => { await actor.santeIncDec("vie", valeur) }
static moral_plus_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, 1) } static moral_plus_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, 1) }
static moral_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) } static moral_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) }
static $moral_plus = async (actor, valeur) => { await actor.moralIncDec(valeur) } static $moral_plus = async (actor, valeur) => { await actor.moralIncDec(valeur) }
static end_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) } static end_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) }
static end_moins_force = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -context.rencontre.system.force) } static end_moins_force = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -context.rencontre.system.force) }
static $end_plus = async (actor, valeur) => { await actor.santeIncDec("endurance", valeur) } static $end_plus = async (actor, valeur) => { await actor.santeIncDec("endurance", valeur) }
@ -49,16 +60,17 @@ export class EffetsRencontre {
const perte = context.rolled.isETotal ? context.rencontre.system.force : 1; const perte = context.rolled.isETotal ? context.rencontre.system.force : 1;
await context.actor.chanceActuelleIncDec("fatigue", -perte); await context.actor.chanceActuelleIncDec("fatigue", -perte);
} }
static xp_sort_force = async (dialog, context) => { static xp_sort_force = async (dialog, context) => {
let competence = context.competence; let competence = context.competence;
if (competence) { if (competence) {
const xpSort = Misc.toInt(competence.system.xp_sort) + context.rencontre.system.force; const fromXpSort = Number(competence.system.xp_sort);
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': xpSort }]); const toXpSort = fromXpSort + context.rencontre.system.force;
await this.updateExperienceLog("XP Sort", xpSort, `Rencontre d'un ${context.rencontre.name} en TMR`); 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`);
} }
} }
static stress_plus_1 = async (dialog, context) => { static stress_plus_1 = async (dialog, context) => {
await context.actor.addCompteurValue('stress', 1, `Rencontre d'un ${context.rencontre.name} en TMR`); await context.actor.addCompteurValue('stress', 1, `Rencontre d'un ${context.rencontre.name} en TMR`);
} }
@ -73,7 +85,7 @@ export class EffetsRencontre {
static demireve_rompu = async (dialog, context) => { static demireve_rompu = async (dialog, context) => {
dialog.close() dialog.close()
} }
static sort_aleatoire = async (dialog, context) => { static sort_aleatoire = async (dialog, context) => {
context.sortReserve = await RdDDice.rollOneOf(context.actor.itemTypes['sortreserve']); context.sortReserve = await RdDDice.rollOneOf(context.actor.itemTypes['sortreserve']);
@ -126,7 +138,7 @@ export class EffetsRencontre {
static regain_seuil = async (dialog, context) => { static regain_seuil = async (dialog, context) => {
await context.actor.regainPointDeSeuil() await context.actor.regainPointDeSeuil()
} }
static async $reinsertion(dialog, actor, filter) { static async $reinsertion(dialog, actor, filter) {
const newTMR = await TMRUtility.getTMRAleatoire(filter); const newTMR = await TMRUtility.getTMRAleatoire(filter);

View File

@ -1,39 +1,32 @@
import { Grammar } from "../grammar.js"; import { Grammar } from "../grammar.js";
import { TMRUtility } from "../tmr-utility.js"; import { TMRUtility } from "../tmr-utility.js";
import { tmrConstants, tmrColors, tmrTokenZIndex } from "../tmr-constants.js"; import { tmrTokenZIndex } from "../tmr-constants.js";
import { Draconique } from "./draconique.js"; import { Draconique } from "./draconique.js";
export class FermetureCites extends Draconique { export class FermetureCites extends Draconique {
constructor() {
super();
}
type() { return 'souffle' } type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('fermeture des cites'); } match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('fermeture des cites'); }
manualMessage() { return false } manualMessage() { return false }
async onActorCreateOwned(actor, souffle) { await this._fermerLesCites(actor, souffle); } async onActorCreateOwned(actor, souffle) { await this._fermerLesCites(actor, souffle); }
code() { return 'fermeture' } code() { return 'fermeture' }
tooltip(linkData) { return `La ${this.tmrLabel(linkData)} est fermée` } tooltip(linkData) { return `Cité fermée` }
img() { return 'icons/svg/door-closed.svg' } img() { return 'systems/foundryvtt-reve-de-dragon/icons/tmr/fermeture.svg' }
createSprite(pixiTMR) { createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), return pixiTMR.sprite(this.code(), {
{ zIndex: tmrTokenZIndex.conquete,
zIndex: tmrTokenZIndex.conquete, decallage: pixiTMR.sizes.decallage(0, 0),
color: tmrColors.souffle, taille: () => pixiTMR.sizes.full
alpha: 0.9, })
taille: tmrConstants.full,
decallage: { x: 2, y: 0 }
});
} }
async _fermerLesCites(actor, souffle) { async _fermerLesCites(actor, souffle) {
let existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord); let existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord);
let ouvertes = TMRUtility.filterTMR(it => it.type == 'cite' && !existants.includes(it.coord)); let ouvertes = TMRUtility.filterTMR(it => it.type == 'cite' && !existants.includes(it.coord));
for (let tmr of ouvertes) { for (let tmr of ouvertes) {
await this.createCaseTmr(actor, 'Fermeture: ' + tmr.label, tmr, souffle.id); await this.createCaseTmr(actor, 'Fermeture', tmr, souffle.id);
} }
} }
} }

View File

@ -1,37 +1,36 @@
import { Grammar } from "../grammar.js"; import { Grammar } from "../grammar.js";
import { TMRUtility } from "../tmr-utility.js"; import { TMRUtility } from "../tmr-utility.js";
import { tmrConstants, tmrTokenZIndex } from "../tmr-constants.js"; import { tmrTokenZIndex } from "../tmr-constants.js";
import { Draconique } from "./draconique.js"; import { Draconique } from "./draconique.js";
import { TMRAnimations } from "./animation.js";
export class Pelerinage extends Draconique { export class Pelerinage extends Draconique {
constructor() {
super();
}
type() { return 'queue' } type() { return 'queue' }
match(item) { return Draconique.isQueueDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('pelerinage'); } match(item) { return Draconique.isQueueDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('pelerinage'); }
manualMessage() { return false } manualMessage() { return false }
async onActorCreateOwned(actor, queue) { async onActorCreateOwned(actor, queue) {
let tmr = await TMRUtility.getTMRAleatoire(); let tmr = await TMRUtility.getTMRAleatoire();
await this.createCaseTmr(actor, 'Pèlerinage: ' + tmr.label, tmr, queue.id); await this.createCaseTmr(actor, 'Pèlerinage', tmr, queue.id);
} }
code() { return 'pelerinage' } code() { return 'pelerinage' }
tooltip(linkData) { return `Votre pèlerinage en ${this.tmrLabel(linkData)}` } tooltip(linkData) { return `Lieu de pèlerinage` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/tmr/pelerin.webp' } img() { return 'systems/foundryvtt-reve-de-dragon/icons/tmr/pelerinage.svg' }
createSprite(pixiTMR) { createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), { return TMRAnimations.withAnimation(
zIndex: tmrTokenZIndex.conquete, pixiTMR.sprite(this.code(), {
alpha: 1, zIndex: tmrTokenZIndex.conquete,
taille: tmrConstants.full, decallage: pixiTMR.sizes.decallage(0, 0),
decallage: tmrConstants.center taille: () => pixiTMR.sizes.half,
}); }),
pixiTMR,
TMRAnimations.changeZoom()
)
} }
async onActorDeleteCaseTmr(actor, casetmr) { async onActorDeleteCaseTmr(actor, casetmr) {
await actor.deleteEmbeddedDocuments('Item', [casetmr.system.sourceid]); await actor.deleteEmbeddedDocuments('Item', [casetmr.system.sourceid]);
} }

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