Compare commits

...

342 Commits

Author SHA1 Message Date
3a33f7c4fc Fix actor sheet 2022-12-15 10:41:19 +01:00
d2e77dc61c Merge pull request 'Version 10.3.14' (#595) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #595
2022-12-15 07:36:04 +01:00
320bc471e3 Version 10.3.14
- Améliorations cosmétiques
- Affichage de la fortune
- Fix dissolution
- Fix des monnaies dans les compendiums
- Nettoyage des compendiums
2022-12-15 01:03:57 +01:00
090241f0f5 Alignement des champs d'environement 2022-12-15 01:00:28 +01:00
ddc37c6969 Ajustement champs description 2022-12-15 00:56:23 +01:00
6af6e41bc9 Reprise des attributs secondaires 2022-12-15 00:56:23 +01:00
0f7f609a2a Fix dissolution
La dissolution n'est que de 1 point
2022-12-15 00:56:23 +01:00
a58f701ca6 Centrage seuil de rêve 2022-12-15 00:56:23 +01:00
757b46080a Suppression compendiums inutilisés 2022-12-15 00:56:23 +01:00
df44cd66c7 Nettoyage compendiums
- correction des monnaies des acteurs par défaut
- re-export pour garder les données systeme
- suppression des champs "competencecreature[0]" incorrects
- suppression de champs data non utilisés
2022-12-15 00:56:23 +01:00
1040ec1be2 Auto reformat 2022-12-15 00:56:23 +01:00
92643d1c46 Autocomplete Main directrice
Permet de choisir rapidement dans les valeurs proposées
2022-12-15 00:56:22 +01:00
31c4aa32d9 Affichage de la fortune 2022-12-14 19:58:52 +01:00
f5431b58fb Merge pull request 'Non à la dévaluation' (#594) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #594
2022-12-13 22:28:42 +01:00
bd32e1039a Mettre en valeur l'argent sans valeur 2022-12-13 22:27:36 +01:00
6b8f0ed51e Version 10.3.13 2022-12-12 23:33:53 +01:00
10681b3f61 DialogItemSplit height fit-content 2022-12-12 23:32:35 +01:00
bbde3b73fe Gestion des monnaies de valeur 0
Ajout d'un message d'erreur quand on met la valeur d'une monnaie à 0.
Ajout d'une notification quand on détecte des monnaies de valeur 0

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

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

Déplacement de l'en-tête dans la première ligne de la liste (avec les
boutons en vue détaillée)
2022-12-10 15:44:08 +01:00
0202938910 Ajout faune 2022-12-09 22:50:23 +01:00
512a056e59 Merge pull request 'Poissons, coquillages, crustacés' (#591) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #591
2022-12-09 22:49:29 +01:00
214377c66d Poissons, coquillages, crustacés
Ils sont frais, mes poissons!
2022-12-09 22:36:17 +01:00
752e534350 Ajout faune 2022-12-09 21:50:12 +01:00
c85a544cc9 Ajout faune + fix sur suppression milieux 2022-12-09 12:01:44 +01:00
3a90c693d9 Release 2022-12-09 10:39:21 +01:00
c04b179176 Merge pull request 'Permettre d'avoir plusieurs fenêtres' (#590) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #590
2022-12-09 10:36:48 +01:00
63770790b9 Fix multi-dialogs
Arrêter d'utiliser le jQuery $(selector) qui cause des effets de bord si
plusieurs élements de la page (ie: foundry) correspondent
au selector.

Stocker le html dans les Sheet/Dialogs lors de l'appel
activateListeners  afin de pouvoir s'y référer ensuite.

Utiliser this.html.find pour chercher dans le html de la fenêtre
courante.

Eliminer les référence par id html car l'id est unique (donc ne marche
pas en multi-fenêtres)
2022-12-09 02:07:59 +01:00
aefc7a434b Fix: lien vers rencontre du compendium 2022-12-09 02:07:59 +01:00
f02959adee Cleanup
Construction du message de jet de constitution par template hbs
2022-12-09 02:07:59 +01:00
e652027b02 Cleanup accorder entité
Méthode pour accorder une entité en double (dont une version sans xp,
et message mal formaté)
2022-12-09 02:07:45 +01:00
2122a54db7 Cleanup roll windows
- permettre plusieurs fenêtres de jets en même temps en éliminant les
  id dans le html et les jquery sur id pour éviter les interactions
- génération de la table par handlebars
2022-12-06 01:30:31 +01:00
f027e3318b Merge pull request 'Ragoût de Klampin' (#588) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #588
2022-12-05 16:50:33 +01:00
31b4d1cfcc Version 10.3.6 2022-12-05 16:39:24 +01:00
5056c35038 Faune et flore comestibles
- permettre de cuisiner les ingrédients (faune & flore)
- permettre de manger des ingrédients "crus"
2022-12-05 16:38:26 +01:00
7b58407634 Cleanup 2022-12-05 16:36:27 +01:00
7efa7be1c0 L'art ne s'encombre de rien
Ne pas appliquer le surencombrement (ou l'encombrement) aux oeuvres
d'art: on considère que pour faire de l'art, on pose son sac.
2022-12-05 16:36:27 +01:00
717bb6fc6e Fix ouverture conteneur 2022-12-05 16:36:27 +01:00
51273bcc3e Merge pull request 'Gestion de la Faune' (#587) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #587
2022-12-03 23:16:36 +01:00
b8f3a9af27 Version 10.3.5 2022-12-03 22:31:56 +01:00
ab704c46d2 Ajout de l'Item "faune" pour tables environnement 2022-12-03 22:31:42 +01:00
db8fd6dbf8 Meilleur post to tchat 2022-12-03 22:27:21 +01:00
d998a4cb08 Amélioration Classes ItemSheet séparées
Meilleur support des feuilles ItemSheet séparés
Séparation de la feuille Conteneurs

Mise en commun de la logique drag&drop
2022-12-03 22:27:20 +01:00
b1e27a9597 On ne peut pas tout donner
Limitation des types d'objets pouvant être donnés à différents acteurs
2022-12-03 18:31:14 +01:00
eaac9564b4 Merge pull request 'La recherche par milieu fonctionnelle' (#586) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #586
2022-12-03 10:05:49 +01:00
0826c7e9e3 Version 10.3.4 2022-12-03 01:26:39 +01:00
bdd3802e72 Amélioration de la recherche dans un milieu
- gestion correcte de la case
- recherche insensitive
- correction de la liste des milieux à ajouter dans la feuille
- si aucun milieu ne correspond à la recherche, affiche la
  liste des milieux disponibles
2022-12-03 01:25:24 +01:00
b07cea40e2 Fallback sur compendium system
quand le compendium configuré n'existe plus, fallback sur le
compendium du système
2022-12-03 01:25:23 +01:00
bb7f4c42ad Feuilles Ingrédient&Herbe extends RdDItemSheet
Pour bénéficier de certains comportements (description...)
2022-12-03 01:25:23 +01:00
ac15a022df Fréquences dans les milieux standard
Saisie dans le compendiums de fréquences pour la liste de milieux
par défaut
2022-12-03 01:25:23 +01:00
a43c725b06 Merge pull request '10.3.3' (#585) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #585
2022-12-02 23:51:15 +01:00
1276c64835 Version 10.3.3 2022-12-02 21:22:43 +01:00
4b4d778d9c Entrée ajoute le milieu aux fréquences 2022-12-02 21:22:11 +01:00
86f9c37b30 Liste des milieux par défaut 2022-12-02 21:22:11 +01:00
ac77c6da9e Fix calcul valeur équipement 2022-12-02 21:22:11 +01:00
344540ea8e Merge pull request 'Fix initiative' (#584) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #584
2022-12-01 08:05:14 +01:00
8a5cf3cb09 Version 10.3.2 2022-12-01 01:25:06 +01:00
3a29570bae Fix message d'expérience invisibles
Les messages d'expérience sur mêlée/tir/lancer (à répartir)
n'étaient plus visibles
2022-12-01 01:22:25 +01:00
02c48f4796 Fix initiative 2022-12-01 00:28:23 +01:00
ee01878fae Fixes suite à dernières modifs 2022-11-30 16:41:39 +01:00
2e1005e909 Merge pull request 'Fixes mineurs' (#583) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #583
2022-11-30 16:40:35 +01:00
98c016696d Correction de l'ajout de milieux parfois KO
Pas sûr du pourquoi, il semble qu'il y a un souci jQuery
(avec deux fenêtres?)
2022-11-30 16:03:56 +01:00
e67ac96e93 Fix: /roll
Regression sur /roll causée par méthode async:
return Promise(false)=>true au lieu de false...
2022-11-30 14:28:11 +01:00
979a48e4d9 Amélioration recherche dans un milieu
Si la recherche est sur un milieu exact, seul ce millieu est considéré.
ie: chercher en "Forêts" ne cherchera pas en "Forêts humides".

Le recherche en "for" cherchera dans tous les milieux contenant "for".
Un message d'avertissement est affiché, et la description de la table
contient la liste des milieux correspondants.

Si plusieurs milieux cherchés ont une fréquence pour une ressource,
la fréquence la plus élevée est utilisée.
2022-11-30 14:24:09 +01:00
faff6e54ef Fix arborescence de conteneur 2022-11-30 14:24:08 +01:00
d493e99bcb Merge pull request 'Tables de compendiums et feuilles de personnages' (#582) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #582
2022-11-30 09:37:49 +01:00
92ae0b7431 Correction aide /tirer milieu 2022-11-30 02:35:12 +01:00
13135ff2ca Version 10.3.0 2022-11-30 02:16:59 +01:00
453e7da848 Création du compendium Faune, Flore, Minéraux
- les tables d'environnement se basent sur ce seul compendium
- les tables de compendiums peuvent chercher plusieurs types d'items
- déplacement de la botanique
- déplacement des sels alchimiques depuis l'équipement
- modification de l'équipement de départ
- modification des acteurs (lien vers compendium de l'item source)
2022-11-30 02:12:31 +01:00
b7a0e5d034 Fréquences par milieu pour l'environnement
Les herbes et les ingrédients peuvent être cherchées/tirées
2022-11-30 02:06:46 +01:00
b7a8b0c08d Standardisation inventaire et prix
Les objets d'inventaire ont maintenant tous:
coût, encombrement, qualité, quantité

Le coût est toujours exprimé en sols, y compris pour les monnaies.
Les paiements et achat-ventes sont fait en sols.
2022-11-29 11:37:49 +01:00
7557d33c73 Feuille de véhicule avec compteurs 2022-11-29 11:37:49 +01:00
e35f77b5a8 Cleanup
- Partage méthode pour label des types
- Methode joining pour concaténation
- suppression template obsolete
- Déplacement de singleton rencontre dans game.system.rdd
- init des commandes lazy
2022-11-29 11:37:11 +01:00
dd4484c17b En-tête de feuille de personnage
Affichage des portraits plus grands
Les boutons sont sur la ligne du nom du personnage
Les états sont affichés sur une colonne
2022-11-29 00:04:50 +01:00
4bd2c1c2b4 Notion d'équipement/inventaire 2022-11-29 00:04:38 +01:00
42c4fe0b29 Standardisation des descriptions
Utilisation des templates sur les items pour la description
2022-11-28 21:02:32 +01:00
39c6478422 Affichage des images de tarot 2022-11-28 21:02:32 +01:00
c06491eb2f Ne pas tronquer les images
Corrections des tailles d'images pour éviter de tronquer
2022-11-28 21:02:32 +01:00
e869d15b24 Correction mise à jour état général 2022-11-28 21:02:32 +01:00
7045b6d8e1 Amélioration du message de confirmation 2022-11-28 21:02:32 +01:00
431d3199db Merge pull request 'v10.2.10: Gestion des tables depuis les compendiums' (#581) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #581
2022-11-26 18:42:10 +01:00
5555705912 10.2.10 2022-11-26 18:12:44 +01:00
4a51f698ab Fix regressions liste dans conteneurs
Les lignes étaient affichées de manière étrange et peu lisible
2022-11-26 18:12:31 +01:00
029bece877 Gestion des désir lancinant/idée fixe
Ajout d'un flag pour les identifier.
2022-11-26 16:58:53 +01:00
31eabbce23 Fix fréquence Mauvais reflet d'ancien rêve 2022-11-26 16:58:53 +01:00
7200ff529f Cleanup 2022-11-26 16:58:53 +01:00
0dacbefd6b Tirer dans les compendiums selon les fréquences 2022-11-26 16:58:53 +01:00
970be67537 Increase release, VincentVK fixes 2022-11-25 07:35:19 +01:00
53aa9cd643 Merge pull request 'Corrections des jets de dés' (#580) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #580
2022-11-25 07:33:53 +01:00
c435bfa343 Chronologie et année
Fix jour dans chronologie
Permettre de saisir l'année
2022-11-25 03:17:27 +01:00
8667d77169 Ajout de champs pour les tables-compendium
Le champ "frequence" sert à rêgler le nombre d'occurences d'une entrée
du compendium. Le total des fréquences donnera le dé à lancer pour
tirer dans la table. Il servira pour réguler l'apparition des
queues/souffles/... d'un compendium sans avoir besoin de déclarer une
RollTable.

Le champ "hautrevant" permet d'indiquer les queues/souffles/...
réservés aux haut-rêvants.
2022-11-25 03:17:27 +01:00
5e7fcf3c9b fit-content pour toutes les fenêtres roll
C'est le comportement par défaut, donc enlever les surcharges
Corrections pour les fenêtres de combat
2022-11-25 03:17:27 +01:00
e78ae3b292 Chargement depuis les compendium sélectionnés
Fix, les compendiums systèmes n'étaient pas utilisés pour les herbes
et les compétences
2022-11-25 03:17:27 +01:00
97ee5bc331 Amélioration possession
- Créer la possession lors de la première attaque
- Le personnage ciblé par la possession est affiché
2022-11-25 03:17:27 +01:00
c3c0bbc922 Fix jets de caracs dérivées
Appel à la chance, jets de rêve actuel: pas de liste de compétences
2022-11-25 03:17:26 +01:00
9992b64cae Corrections sur les ajustements
- le malus de sur-encombrement est correctement calculé (dans la
  zone d'état)
- par défaut, le sur-encombrement est appliqué
- le sur-encombrement est affiché sur les actions physiques
- l'encombrement s'applique à agilité/dérobée, avec natation/acrobatie
  (par défaut)
- le moral est géré dans le noeud 'use' du rollData
- le moral est associé aux actions physiques
2022-11-25 03:17:26 +01:00
7698147e97 Affichage de la cible dans la fenêtre d'attaque 2022-11-23 22:42:18 +01:00
81aaf9e8d7 Merge pull request 'Choisir parmi plusieurs cibles' (#579) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #579
2022-11-23 08:24:17 +01:00
8e1b33d964 Version 10.2.8 2022-11-23 00:14:55 +01:00
eca61fff57 Fix: hauteur des onglets Foundry
Le texte des onglets de configuration Foundry (vision des tokens,
lumières par exemple) sont sur plusieurs lignes.

La réduction de la hauteur des lignes éviter que le titre de l'onglet
soit par dessus le contenu.

Fixe aussi l'onglêt "haut-rêve" si la fenêtre d'acteur est de largeur
réduite.
2022-11-23 00:13:33 +01:00
acc5ddac08 Simplifier la sélection
Lorsque plusieurs tokens sont ciblés, laisser le joueur choisir parmi
ceux-là
2022-11-23 00:13:33 +01:00
06024a0007 Merg/Increase release 2022-11-22 07:39:31 +01:00
da9158e718 Merge pull request 'Autoriser le combat sans cible' (#578) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #578
2022-11-22 07:38:11 +01:00
f57f03547a Autoriser le combat sans cible 2022-11-22 02:15:51 +01:00
5424763ad6 Cleanup 2022-11-22 02:15:51 +01:00
3543ce60cb Fix armes naturelles/corps à corps 2022-11-20 16:08:48 +01:00
5ba86f12df Merge pull request 'v10.2.5' (#577) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #577
2022-11-19 16:47:17 +01:00
424cdd3e65 v10.2.5 2022-11-19 01:49:38 +01:00
3362f2473a Jet de Carac avec compétence
Pour faire plus rapidement les jets depuis les caracs
2022-11-19 01:38:30 +01:00
5565bb7a48 Fix des malus encombrement 2022-11-19 01:38:29 +01:00
f953348f4e Séparation des tir/mêlée/lancer
Migration automatique des objets du monde et de ses acteurs
Migration des objets des compendiums
2022-11-19 01:38:29 +01:00
702af37961 Cleanup 2022-11-19 01:38:29 +01:00
73718070cf Mrege Vincent + suppression doublons 2022-11-18 11:33:46 +01:00
091ce15dce Merge pull request 'v10' (#576) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #576
2022-11-18 11:24:12 +01:00
cdde7c5f2f Le lancer des armes
On peut maintenant configurer une arme de mélée pour
être lancée
2022-11-18 03:38:32 +01:00
eb0afffbd6 Information sur le mode de TMR
Affichage d'information en cas de mode visualisation, ou si le MJ
monte dans les TMR avec le personnage d'un joueur
2022-11-18 03:38:32 +01:00
6b0c7f4d38 Merge Vincent fixes + carquois en double 2022-11-17 10:38:11 +01:00
dd2109735d Merge pull request 'Fix de régressions, stabilisation' (#575) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #575
2022-11-17 10:34:57 +01:00
0b192d66c3 Fix commandes et aides
- corrections de commandes sans paramêtre pour proposer l'aide
- correction de l'aide /tmrr (qui peut lancer des dés différents si les
  compendiums sont changés)
2022-11-17 01:57:36 +01:00
472cbb372e Fix du calcul des données calendrier
- Utilisation de la méthode getDefSigne pour obtenir le détail
du signe de l'heure / du mois

- fix d'une initialisation du mois qui faussait le calendrier
2022-11-17 01:25:50 +01:00
8d2e7fd0c8 Fit content pour dialog chronologie/calendrier
Suppression de l'espace perdu
2022-11-17 01:25:50 +01:00
c8526a2270 Amélioration /tmrr
- support des mauvaises rencontres avec /tmrr Mauvaise
- recherche "intelligente": /tmrr ci pour lancer une rencontre en cité
2022-11-17 01:25:50 +01:00
8b6abcc8bb Suppression bouton default
Pour éviter qu'un dialog ouvert depuis le tchat se valide tout seul
aux ouvertures suivantes
2022-11-17 01:25:50 +01:00
468982b07b Fix décrément qty ChatMessage vente
Régression suite aux fix sur les achat-vente
2022-11-17 01:25:50 +01:00
95179ffff2 Cleanup 2022-11-17 01:25:50 +01:00
884733e54b Suppression de modèle non utlisé
Sans doute rémanensce d'un état intermédiare de l'extraction des
Item rencontres
2022-11-17 01:25:49 +01:00
a2d4b1049e Inc release 2022-11-16 21:48:41 +01:00
3ce3898326 Merge pull request 'Amélioration esthétique' (#574) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #574
2022-11-16 20:59:24 +01:00
32fc0019d5 Compendium d'extraits poétiques
Permet de surcharger le compendium. Un compendium vide permet de
ne plus avoir d'extraits. D'autres textes peuvent être utilisés avec
un compendium personalisé.
2022-11-16 03:00:55 +01:00
f3f928e43f Cleanup 2022-11-16 03:00:55 +01:00
374a0e1846 Fix: Affichage de l'heure plutôt que du code
Dans le message de déclenchement de sorts en réserve
2022-11-16 03:00:55 +01:00
1e5a99e009 Hauteur des fenêtre 'fit-content'
Evite d'avoir tout le temps des tailles mal ajustées
2022-11-16 03:00:55 +01:00
64320fc260 Merge Vincent fixes pour la boisson 2022-11-13 23:16:39 +01:00
ff923ebab2 Merge pull request '10.0.2.1' (#573) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #573
2022-11-13 23:15:44 +01:00
bcd25dd0ed Affichage de dialog en 2 temps
Sinon, la touche entrée est appliqué sur une commande et ferme
le dialogue
2022-11-13 21:57:48 +01:00
ccb6709f5b Fix: Nourriture consommée par le bon user 2022-11-13 21:57:48 +01:00
2fe6b472a2 Upgrade version to 10.2.0 2022-11-10 07:25:42 +01:00
01642ba495 Merge pull request 'Les rencontres sont configurables' (#572) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #572
2022-11-10 07:24:52 +01:00
191d23e903 Nouveaux effets de rencontre
Gain de 1 point de rêve
Perte de (force) points de vie
Gain de 1 point de moral
Perte de 1 point de moral
Gain de (force) xp sort
Perte de 1 point d'endurance
Perte de (force) points d'endurance
Coup de fatigue de 1 point
Coup de fatigue de (force) points
Récupération de 1 point de fatigue
Récupération de (force) points de fatigue
Perte de chance actuelle
Perte de 1 point de chance actuelle
Gain de 1 point de stress
2022-11-10 02:05:16 +01:00
be57a52f61 Suppression du jet de rencontre de test 2022-11-10 00:59:25 +01:00
c2e8621405 Idées de nouvelles rencontres 2022-11-10 00:42:01 +01:00
d3533f5627 Fix description mauvaise rencontre
Le tirage est fait automatiquement
2022-11-10 00:42:01 +01:00
db448028f9 Gestion de la tête Augmentation de seuil 2022-11-10 00:42:01 +01:00
f659a7508a Remplacement progressif rencontres 2022-11-10 00:42:01 +01:00
d20a6a1506 Suppression de setForceRencontre
On pourra se créer des rencontres et les placer sur la feuille du
personnage
2022-11-10 00:42:00 +01:00
d724e9eb17 Affichage des Items rencontres sur Actor 2022-11-10 00:42:00 +01:00
2106e6ebef Table de rencontre sur une TMR
Au lieu de passer le type de TMR, passer la TMR {type,
coordonnées}

Ce qui permettra de créer une rencontre avec les coordonnées
si disponible
2022-11-10 00:42:00 +01:00
dc07b60acf Compendium de rencontres 2022-11-10 00:42:00 +01:00
d77f046a6a Ajout d'Item rencontre
Pour l'instant, pas utilisée. Une rencontre permettra de
définir les différents effets de la rencontre.

Un compendium dédié donnera les rencontres possibles.
2022-11-10 00:41:47 +01:00
73c490a91d Compendiums paramétrables 2022-11-10 00:41:47 +01:00
ce562b6b8a Déplacement des différents settings 2022-11-10 00:41:47 +01:00
9ea4c05199 Compendiums privés par défaut
Pour que le Gardien n'ait pas à configurer tout en privé à la main
2022-11-10 00:41:47 +01:00
e198cb60b1 Cleanup & Reformatage 2022-11-10 00:41:47 +01:00
d183ce505a gitignore dossier "ignored"
Pour stocker des fichiers temporaires/intermédiaires
2022-11-07 00:05:55 +01:00
9d1dec4179 Amélioration de l'apparence de recherche 2022-11-05 18:20:24 +01:00
e5e4ca75ea New relase v10.1.0 - Chrono features 2022-11-04 23:55:49 +01:00
adc8645453 Merge pull request 'Notes chronologiques et autres' (#571) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #571
2022-11-04 23:53:44 +01:00
708d00e75c Version 10.0.33
- petite amélioration visuelle de la recherche de compétence
- ajout de notes chronologiques
- utilisation des noms de compétences draconiques (ce qui permet
  au gardien de créer ses propres voies draconiques)

Technique:
- normalisation des noms de fichiers templates enum
- suppressions de templates inutiles
- petit nettoyage de paramêtres et imports en passant
- petite factorisation css
2022-11-04 20:45:52 +01:00
ecd1652403 Ajout de notes chronologiques 2022-11-04 20:41:16 +01:00
42e4f5b391 Simplification css
Utiliser :is(...)  permet de factoriser
2022-11-01 23:45:17 +01:00
afb0f58ec1 Renommage paramètre 2022-11-01 23:44:18 +01:00
42e3caa448 Suppression import inutile 2022-11-01 23:43:53 +01:00
12a5cebc2d Amélioration rendu de la recherche
Icône dans la zone de saisie pour plus de clarté
2022-11-01 23:43:33 +01:00
65e7574106 Renommage des templates d'enums
(suppression de templates inutilisés)
2022-11-01 01:03:35 +01:00
ca01bc2605 Utilisation de la compétence draconic
Utiliser le nom de la compétence au lieu du nom de la voie:

=> "Voie d'Hypnos" au lieu de "hypnos"

Permet de définir d'autres compétences de draconic.

Fix de la mise de sort en réserve: utilise correctement l'id du sort
Migration des sorts et sorts en réserve du monde
Migration des compendiums
2022-11-01 00:49:28 +01:00
ad9f04de4a Renommage templates options
prefix enum

Objectif long terme: faire le tri des templates partials vs autres
2022-11-01 00:49:28 +01:00
f6a3ec8634 Merge pull request '10.0.32' (#570) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #570
2022-10-31 23:04:41 +01:00
7870263656 Fix tooltip de niveau
La présence des deux tooltips se génaient (car sur le même span):
- augmenter de niveau
- expérience requise
2022-10-31 19:29:44 +01:00
fd934a5eae Ne pas re-trier les compétences
dans les catégories, elles sont déjà triées (avec la survie en
extérieur avant les autres)
2022-10-31 19:28:13 +01:00
2ab8db94c5 Lien d'aperçu et nouvel aperçu 2022-10-31 19:26:59 +01:00
37bbcf38df Version 10.0.32
- tâches sans compétences
- tri alphabétique des compétences
- affichage du jet de refoulement
2022-10-31 15:23:35 +01:00
086ad4f23a Affichage du jet de refoulement 2022-10-31 15:21:03 +01:00
cf4be389b4 Tri des alphabétique des compétences
Dans lmes sélections, c'est plus facile de s'y retrouver
2022-10-31 15:21:03 +01:00
f1d7cfa1f8 Tâches sans compétences 2022-10-31 15:21:03 +01:00
269b4ec0ca Fix odorat-gout 2022-10-28 08:47:35 +02:00
637be29b7b Merge pull request 'Fix roll Odorat-goût' (#568) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #568
2022-10-28 08:45:47 +02:00
f388d6550c Fix: recherche carac plus lisible 2022-10-27 22:37:20 +02:00
5d32f7e493 Fix: rollCarac pour odorat-goût
Recherche d'abord par clé (name)
2022-10-27 22:28:31 +02:00
d10c86c684 Support manifest-plus
https://foundryvtt.wiki/en/development/manifest-plus
2022-10-22 00:53:56 +02:00
484f02277b Merge pull request 'Version 10.0.30' (#567) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #567
2022-10-21 22:18:05 +02:00
0ef9123cd5 Version 10.0.30
* Fix de bug de socket qui pouvait conduire à un plantage de Foundry,
  par exemple lors d'un achat par un joueur
* Fix de l'édition des sorts des personnages: la sauvegarde fonctionne
  de nouveau
* Ajout d'une demande de confirmation lors d'un refoulement: l'action
  refouler est très proche de la suppression, un mauvais clic est vite
  arrivé
2022-10-21 22:10:38 +02:00
e6b71faa02 Passer un async à game.socket.on()
Corrige le problème causé par l'update d'un objet pendant le traitement
d'un message du socket, qui peut échouer quand le socket est occupé,
et cause donc un rejet du message du socket, qui est réessayé en boucle
infinier.

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

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

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

defender.system.name => defender.name
2022-10-08 17:37:32 +02:00
d75eef1926 Correction configuration affichage prix
L'option de configuration étaity toujours vrai quand vue par le MJ
2022-10-08 17:37:32 +02:00
18039e905b Compétences & herbes personalisées
* permettre d'ajouter des compétences dans un monde, qui seront
  ajoutés aux acteurs créés dans ce monde
* les herbes de repos/soins du monde sont bien considérées comme
  des herbes pour les potions
2022-10-08 17:37:32 +02:00
6d0e5321a2 Nommage homogène 2022-10-08 11:56:52 +02:00
3073670afa Ajout visibilité 2022-10-07 23:46:36 +02:00
690dd1f0a2 Merge pull request 'Monnaies et armes à distance' (#563) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #563
2022-10-07 23:35:40 +02:00
0dcce5456b Inatteignable à -10
au lieu de hyphen non affiché
2022-10-07 23:31:57 +02:00
35f1f2437c Garde fous
En cas d'exception dans le traitement d'un message websocket,
faire un catch pour être sûr de ne pas réémettre si l'exception
revient à l'émetteur (boucle infinie d'envois sinon)
2022-10-07 23:30:06 +02:00
3e17dd9b7e Ajout d'informations pour armes à distance
* prise en compte de la taille de la cible
* prise en compte de l'activité
* valeur indicative
2022-10-07 23:28:41 +02:00
4f8406360f Suppression de création de personnage
la feuille n'est pas encore prête
2022-10-07 19:07:25 +02:00
d316bba8fa Déplacement de méthode compendium 2022-10-07 19:07:25 +02:00
9621d72f92 Correction des monnaies
* Des deniers sont créés si on n'a rien d'autre
* Gagner ou dépenser de l'argent fonctionne même si on n'a pas
  tous les types de pièces
* Tous les acteurs peuvent acheter/vendre s'ils ont de l'argent
  => Pratique pour créer une auberge!
* Seuls les personnages peuvent boire et manger
* plus de problèmes de monnaies manquantes
2022-10-07 19:07:25 +02:00
5382fb5df3 Ajout affichage distance 2022-10-06 14:07:47 +02:00
fd6fbba9cb Merge vk 2022-10-05 20:00:42 +02:00
3739204b42 Merge pull request 'Confirmations' (#562) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #562
2022-10-05 19:58:41 +02:00
7e2a867bdd Regroupement d'onglets 2022-10-05 19:40:31 +02:00
e983715ad2 Suppression des malusarmure
Les animaux/entités n'ont pas de malus armure.

La présence dans les compendium faisait apparaître un
attribut sans nom avec comme valeur à 0.
2022-10-05 19:30:57 +02:00
d8d5a20904 Simplification des feuilles de créatures
* Homogénisation des fiches de créatures / entités
* Regroupement d'onglets (peu de compétences/caracs)
* ajout du bouton vue détaillée/simplifiée pour toutes les feuilles
* la santé des créatures est dans l'en-tête
* bouton pour boire pour els personnages
* agrandissement des caractéristiques dérivées
2022-10-05 19:29:16 +02:00
5410dd6ec0 Texte moins long 2022-10-05 00:07:16 +02:00
4443548b0e Bouton "Toujours supprimer"
Label et icône plus clairs
2022-10-04 23:45:14 +02:00
3c86e1b97c Correction largeur confirmations 2022-10-04 23:42:07 +02:00
5cde57e07c Ajout de la possession au compendium
Ajout de la compétence au compendium pour les entités
2022-10-04 23:27:05 +02:00
970c8b0244 Messages de confirmations
* Confirmer pour monter dans les TMR
* les confirmations peuvent être désactivées (par utilisateur)
* plusieurs groupes différents sont gérés
2022-10-04 23:27:04 +02:00
ade977ed68 Increase releas 2022-10-04 06:48:31 +02:00
f83d51b72d Merge pull request 'Petites améliorations de la feuille de personnage' (#561) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #561
2022-10-04 06:47:38 +02:00
34f2e33d6d Déplacer la remise à neuf
C'est plus cohérent de grouper les boutons joueur pour avoir un même
aspect que la feuille soit affichée en joueur ou MJ
2022-10-04 03:33:17 +02:00
0a5c5c5486 Bouton pour utiliser la chance
Déplacé dans les boutons d'action de la partie haute
2022-10-04 03:21:41 +02:00
6becca6672 Bouton pour créer une oeuvre 2022-10-04 02:25:06 +02:00
b2d4dc5d00 Bouton Ré-insertion aléatoire
N'indique plus que les TMR sont masquées:
le bouton cacher/montrer permet de le savoir.
2022-10-04 02:16:05 +02:00
22091ef431 Corrections prix et enc. équipement
Affichage de la valeur si option activée
Utilisation de boutons au lieu des liens
2022-10-04 01:58:49 +02:00
efdc676776 Ajout confirmation pour vider les conteneurs 2022-10-04 01:53:18 +02:00
d25c6b7f1c Montrer/cacher les TMR
Le bouton "Montrer les TMR" devient "Cacher les TMR"/"Montrer les TMR"
selon qu'elles sont visibles ou pas

Lors de l'utilisation d'une terre d'attache, c'un changeur ou de la
connaissance du fleuve, le demi-rêve redevient visible
2022-09-30 01:55:19 +02:00
0cc6b1de98 Release 10.0.22 2022-09-28 08:26:35 +02:00
c41b59b703 Merge pull request 'v10.0.22' (#560) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #560
2022-09-28 08:25:28 +02:00
b4ca941065 Nettoyage de journal d'expérience pour MJ
Permet au MJ de vider les anciennes entrées du journal d'expérience
(jusqu'à une entrée, comprise)

Permet de supprimer une entrée spécifique
2022-09-28 01:24:32 +02:00
322506250b Regrouper vue détaillée avec dévérouiller 2022-09-27 22:38:49 +02:00
5d36ea9e0c Ajout Résistance/structure max aux véhicules
Pour qu'on puisse les réparer
2022-09-27 22:37:33 +02:00
dc0fab2957 Nettoyage acteurs compendium 2022-09-27 22:37:33 +02:00
2e158f9d39 Organisation des parties de la feuille 2022-09-27 22:37:24 +02:00
7e1bbcada0 Séparation actor-sheet en sous-parties 2022-09-27 22:35:25 +02:00
02ccb1f287 Nouvelle visualisation des blessures
Extractions des parties de la feuille pour préparer la possibilité de
proposer une autre fiche
2022-09-27 22:35:25 +02:00
4afa313ffc Enable links in editor 2022-09-27 21:03:18 +02:00
1b8ad316b9 Merge conteneur + fix hotbar 2022-09-25 21:21:28 +02:00
b35eaad757 Merge conteneur + fix hotbar 2022-09-25 21:17:47 +02:00
e325ab9278 Merge pull request 'Drop sur un objet met dans le conteneur parent' (#559) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #559
2022-09-25 21:17:01 +02:00
ebe19959fa Drop sur un objet met dans le conteneur parent
Augmenter la zone pour lacher un objet:
* si c'est un objet similaire, on regroupe
* si c'est un conteneur: on met dans le conteneur
* si c'est un objet dans un conteneur, on met dans le conteneur
* si c'est un objet porté, on met dans les objets portés
2022-09-25 18:45:40 +02:00
5a2bc69fbb Typo "nuage" dans les signes draconiques 2022-09-25 17:50:07 +02:00
57dfdf0b65 Merge fixes from Vincent 2022-09-24 09:58:45 +02:00
028e8b883f Merge pull request 'Corrections et quelques améliorations' (#557) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #557
2022-09-24 09:57:44 +02:00
4bab69b88f Fix: déplacement de contenu rangé 2022-09-24 01:05:32 +02:00
be0c0fbf3a Correction de la monnaie de la serveuse 2022-09-24 00:53:27 +02:00
cd4bf203e6 Corrections d'images dans les compendiums 2022-09-24 00:53:27 +02:00
fc5674a7d8 TMR et acteurs à cocher pour les signe draconiques
Au lieu d'une liste à sélection multiple, choisir les types de TMR
avec des cases à cocher.

Lors de l'ajout de signes éphémères, sélectionner les personnages
avec des cases à cocher.
2022-09-24 00:53:27 +02:00
fe80881405 Fenêtre de stress avec acteurs à cocher
Remplacement de la liste d'acteurs par des cases à cocher car la
sélection n'est pas visible
2022-09-24 00:53:27 +02:00
3a29c25b09 Fix: calculs des bonus de potions
Le calcul du bonus d'herbe d'une potion dépend des brins manquants.

La fabrication de potion propose les nombres de brins pour avoir un
effet. Impossible de faire une potion si on n'a pas assez de brins.

La puissance/points de guérison des potions magiques sont affichées lors
 de la consomation
2022-09-23 02:23:20 +02:00
e2ff813226 Fix: pas plus de lots que disponibles
On pouvait saisir à la main un nombre de lots plus grand que le nombre
disponible, ce qui multipliait la quantité par ce nombre de lots
2022-09-23 01:32:18 +02:00
b67f230212 Fix actionPrincipale de l'équipement 2022-09-23 00:57:02 +02:00
47f9e1adaa Fix boutons d'avancement des heures
Bloqués par la recherche de maladies/poisons pour afficher le
message sur leurs effets
2022-09-23 00:46:57 +02:00
33808b8cf0 Fix transfert de contenants 2022-09-23 00:46:57 +02:00
0d67d9af88 New v10.0.18 release 2022-09-22 08:35:57 +02:00
45a562f502 Merge pull request 'Stabilisation v10.0.18' (#556) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #556
2022-09-22 08:34:46 +02:00
21fc62b302 Fix de la séquence TMR
Lors d'une rencontre/case humide/conquête/..., le haut rêvant
déclenchait tout de même les sorts en réserve
2022-09-22 01:19:10 +02:00
c37c68e6db Fix erreur quand la fenêtre est fermée
Erreur dans les logs, par exemple, suite à une rencontre
2022-09-22 01:19:10 +02:00
ca893088d2 Détails de case pour les sorts en réserve 2022-09-22 01:19:10 +02:00
e69fb222c3 Affiche le type de case si TMR cachées 2022-09-22 01:19:10 +02:00
c76bf912d1 Fix compteur d'utilisations d'esquive 2022-09-22 01:19:10 +02:00
4b1573ab8b Rétablissement des icônes RdD des onglets Foundry
Perdus avec le changement d'entrée FontAwsome
2022-09-22 01:19:10 +02:00
e0049cc2f7 v10 mods+fixes 2022-09-20 07:48:36 +02:00
8e0949442d Merge pull request 'Stabilisation de la v10' (#555) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #555
2022-09-20 07:46:17 +02:00
28427bc7c7 Initiative et competences créatures
Fix de l'initiative des compétences de créatures
* armes naturelles en général
* lancer pour les pierres lancées des Glous et Mariols

La catégorie de parade: remplace l'utilisation de "isparade"

Migration autoimatique des items du monde
Modification des compendiums
2022-09-20 00:14:34 +02:00
50980d5216 Fix Initiative: utilise l'action, pas l'arme 2022-09-20 00:14:18 +02:00
a75ee8fbc6 Fix RollTables: utilisent data, pas system 2022-09-20 00:14:18 +02:00
78c757a21a Ajout des images dans le haut-rêve 2022-09-20 00:14:18 +02:00
6d06c96497 chgt endurance ne rend plus inconscient 2022-09-20 00:14:18 +02:00
5402508b26 Le "label for" indique le "name" du contrôle 2022-09-20 00:14:18 +02:00
43d097581e Log de l'acteur avec monnaies manquantes 2022-09-19 20:22:30 +02:00
7785851719 Log de l'actor dont les sorts sont migrés 2022-09-19 20:22:30 +02:00
8f127bc66b Corrections diverses suite v10 2022-09-18 08:57:45 +02:00
8c5f6b8f1b Merge pull request 'Corrections des TMRs et des Statuis' (#554) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #554
2022-09-18 08:55:40 +02:00
00630849cb Réinsertion sortie de carte
En sortant de la carte (avec les flèches de direction), le demi-rêve
est maintenant bien réinséré aléatoirement
2022-09-18 01:00:38 +02:00
c4392f0320 Fix TMR accessibles par une rencontre
Les cases n'étaient polus accessibles, ni coloriées
2022-09-18 01:00:38 +02:00
7a92ee85ef Nettoyage des status effects
* Recherche et suppression toujours par flags.core.statusId
* l'ajout d'un status depuis le token est maintenant équivallent
  à l'ajout par le code
* Correction des demi-surprises
* Correction du Demi-rêve (qui ne disparaissait pas)
* fix de la selection dans la configuration système
2022-09-18 01:00:36 +02:00
56ea9dd2e4 Sorts en réserve éditables 2022-09-17 22:35:17 +02:00
ee42902b5c Gestion reserve et queues ameliores(Vincent) 2022-09-17 09:09:09 +02:00
838d4381a4 Merge pull request 'Refouler des queues et supprimer des sorts en réserve' (#553) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #553
2022-09-17 09:04:19 +02:00
2232224951 Suppression de sorts en réserve 2022-09-17 01:54:27 +02:00
1251d04860 Action Refoulement de queue 2022-09-17 01:54:27 +02:00
63c6d5ff0f Integration fixes Vincent 2022-09-16 08:35:48 +02:00
c0d37e42ca Merge pull request 'Petits fixes suite à tests' (#552) from VincentVk/foundryvtt-reve-de-dragon:v10-fixes into v10
Reviewed-on: #552
2022-09-16 08:34:18 +02:00
c8c13d626c Fix drop item sans actor 2022-09-16 02:41:54 +02:00
e1ca7ab738 Amélioration des monnaies
On peut maintenant supprimer es monnaies tant qu'on garde une monnaie
pour chaque valeur de base
2022-09-16 02:41:08 +02:00
8f1ee315ef Template en-tête standard 2022-09-16 02:24:08 +02:00
5daf15901a Correction erreur xp restant 2022-09-16 02:24:08 +02:00
bddaecbc74 Fix entite 2022-09-11 16:14:27 +02:00
87fdd655d4 Merge v10 fixes 2022-09-07 23:09:21 +02:00
9cbb12e900 Merge pull request 'Utilisation de system dans les Item/Actor Sheet' (#551) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #551
2022-09-07 23:08:26 +02:00
509b7f97dc Utilisation de system dans les Item/Actor Sheet
Utilisation de system dans les data de formulaire pour tous
les Item/Actor (à la base, ou les sous-éléments)

Corrections sur les sorts en réserve (ce ne sont pas des Item)

Petites améliorations:

* `actor.itemTypes[type]`
   revient à faire (sans besoin de filtrer)
    `actor.items.filter(it => it.type == type)`
* dans les ItemSheet, this.object et this.document
  remplacés par this.item
* dans les ActorSheet, this.object et this.document
  remplacés par this.actor

Quelques corrections en plus:
* parade ne marchait pas
* problèmes sur le commerce
2022-09-07 18:47:56 +02:00
5cd9fb3a1c v10 WIP 2022-09-07 09:14:42 +02:00
336767c19e v10 WIP 2022-09-07 09:01:23 +02:00
5a32cf26dc Merge pull request 'Corrections v10' (#550) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #550
2022-09-07 08:17:41 +02:00
67b0555b11 Corrections v10
Il y en avait partout dans des dialogues, des options,
le drag&drop d'acteur sur acteur, l'empilage d'objet...
2022-09-07 00:36:52 +02:00
59613c3bf8 Sync 2022-09-06 23:52:21 +02:00
e257b6fbee v10 sync 2022-08-31 22:31:27 +02:00
ea990d7c4e Merge VK mods 2022-08-28 08:46:26 +02:00
cfe8bee1c2 Merge brnch 2022-08-27 19:16:27 +02:00
d4f0cce62b Sync system.json 2022-08-27 19:14:15 +02:00
59b4f62145 Merge1 2022-07-23 18:36:03 +02:00
5f3361ecc6 Merge v1.5 2022-07-22 22:53:48 +02:00
c80cde6d18 Merge v1.5 2022-07-17 11:51:23 +02:00
15e4bfb713 Fix combat.js 2022-07-14 22:32:57 +02:00
8c2d49652c v10 branch - Update manifest 2022-07-13 08:04:48 +02:00
ed02972a34 v10 branch - Update manifest 2022-07-13 08:03:45 +02:00
7d32a70e00 Fixes pour possession 2022-07-09 09:04:35 +02:00
0d288c56d4 Merge from v1.5 possession fix 2022-07-02 09:02:31 +02:00
09c4e691c7 First v10 release 2022-07-01 14:34:29 +02:00
3774fef20c Merge v1.5 -> v10 2022-06-26 16:55:59 +02:00
9bc4260ae1 Fix actor 2022-06-26 16:54:16 +02:00
255c0a77b4 Big WIP... 2022-06-12 19:40:44 +02:00
698ff79d41 Big WIP... 2022-06-12 13:58:55 +02:00
dffe191d6e Big WIP... 2022-06-12 12:23:33 +02:00
1f3fd0bb46 Big WIP... 2022-06-12 12:14:55 +02:00
0228d5bc56 Big WIP... 2022-06-12 09:46:58 +02:00
6b48839841 Big WIP... 2022-06-12 08:20:19 +02:00
451ee677d2 Big WIP... 2022-06-12 08:17:59 +02:00
a3fb328b7d v10 migration, ongoing WIP 2022-05-03 09:01:10 +02:00
4538439c02 v10 migration, ongoing WIP 2022-05-01 23:37:52 +02:00
d83fd27193 Various fixes - WIP 2022-05-01 00:46:24 +02:00
580fdb996b Various fixes - WIP 2022-05-01 00:38:09 +02:00
5214b036d3 Various fixes 2022-04-30 23:42:55 +02:00
f64928858c Prepare for v10 2022-04-30 19:15:55 +02:00
378 changed files with 13002 additions and 10904 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
.vscode/settings.json
.idea
todo.txt
todo.md
/.vscode
/ignored/

View File

@ -1 +0,0 @@
foundryvtt-reve-de-dragon

BIN
icons/faune/Escargot.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
icons/faune/andurak.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
icons/faune/barbon.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
icons/faune/brocart.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
icons/faune/cancre.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
icons/faune/cancrelas.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
icons/faune/cerf.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
icons/faune/chamois.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/faune/chevre.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
icons/faune/colimace.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
icons/faune/coquille.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
icons/faune/crabe.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
icons/faune/fretin.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
icons/faune/lapin.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
icons/faune/oie.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
icons/faune/oiseau.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
icons/faune/ours.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/faune/padongre.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
icons/faune/poisson.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
icons/faune/rongeur.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
icons/faune/sanglier.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/faune/saumon.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
icons/faune/singe-vert.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
icons/faune/soldieze.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
icons/faune/ver.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -15,11 +15,12 @@
"TypeNombreastral": "Nombre astral",
"TypeTarot": "Carte de tarot",
"TypeCasetmr": "TMR spéciale",
"TypeRencontrestmr": "Rencontre TMR",
"TypeRencontre": "Rencontre TMR",
"TypeMunition": "Munition",
"TypeMonnaie": "Monnaie",
"TypeHerbe": "Herbe ou plante",
"TypeIngredient": "Ingrédient",
"TypeFaune": "Faune",
"TypeLivre": "Livre",
"TypePotion": "Potion",
"TypeArme": "Arme",
@ -41,7 +42,9 @@
"TypeOmbre": "Ombre de Thanatos",
"TypeSouffle": "Souffle de Dragon",
"TypeTete": "Tête de Dragon",
"TypePossession": "Possession"
"TypePossession": "Possession",
"TypeSortreserve": "Sort en réserve",
"TypeExtraitpoetique": "Extrait poetique"
},
"EFFECT": {
"StatusStunned": "Sonné",

View File

@ -1,15 +1,9 @@
import { RdDActorSheet } from "./actor-sheet.js";
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { HtmlUtility } from "./html-utility.js";
import { RdDUtility } from "./rdd-utility.js";
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDCarac } from "./rdd-carac.js";
/* -------------------------------------------- */
export class RdDActorCreatureSheet extends RdDActorSheet {
/** @override */
@ -20,51 +14,28 @@ export class RdDActorCreatureSheet extends RdDActorSheet {
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }]
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
});
}
/* -------------------------------------------- */
async getData() {
let formData = await super.getData();
console.log("Creature : ", formData);
formData.calc = {
caracTotal: RdDCarac.computeTotal(formData.data.carac),
resumeBlessures: this.actor.computeResumeBlessure(formData.data.blessures),
encTotal: await this.actor.computeEncombrementTotalEtMalusArmure(),
surEncombrementMessage: this.actor.getMessageSurEncombrement()
}
RdDUtility.filterItemsPerTypeForSheet(formData);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
console.log("Creature : ", this.objetVersConteneur, formData);
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen($(".gm-only"), game.user.isGM);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// On competence change
html.find('.creature-carac').change(async event => {
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));
});
html.find('.creature-niveau').change(async event => {
this.html.find('.creature-niveau').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "niveau", parseInt(event.target.value));
});
html.find('.creature-dommages').change(async event => {
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

@ -1,14 +1,6 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { RdDActorSheet } from "./actor-sheet.js";
import { HtmlUtility } from "./html-utility.js";
import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js";
/* -------------------------------------------- */
export class RdDActorEntiteSheet extends ActorSheet {
export class RdDActorEntiteSheet extends RdDActorSheet {
/** @override */
static get defaultOptions() {
@ -18,129 +10,31 @@ export class RdDActorEntiteSheet extends ActorSheet {
width: 640,
height: 720,
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac"}],
dragDrop: [{dragSelector: ".item-list .item", dropSelector: null}]
dragDrop: [{dragSelector: ".item-list .item", dropSelector: undefined}]
});
}
/* -------------------------------------------- */
async getData() {
const objectData = Misc.data(this.object);
let formData = {
title: this.title,
id: objectData.id,
type: objectData.type,
img: objectData.img,
name: objectData.name,
// actor: this.object,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
data: foundry.utils.deepClone(Misc.templateData(this.object)),
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
// items: items,
limited: this.object.limited,
options: this.options,
owner: this.document.isOwner,
itemsByType: Misc.classify(this.object.items.map(i => foundry.utils.deepClone(i.data))),
};
formData.options.isGM = game.user.isGM;
RdDUtility.filterItemsPerTypeForSheet(formData);
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen($(".gm-only"), game.user.isGM);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Update Inventory Item
html.find('.item-edit').click(event => {
const li = $(event.currentTarget).parents(".item");
const item = this.actor.getEmbeddedDocument('Item', li.data("itemId"));
item.sheet.render(true);
});
// Delete Inventory Item
html.find('.item-delete').click(event => {
const li = $(event.currentTarget).parents(".item");
this.actor.deleteEmbeddedDocuments('Item', [li.data("itemId")]);
li.slideUp(200, () => this.render(false));
});
// Roll Carac
html.find('.carac-label a').click(async event => {
let caracName = event.currentTarget.attributes.name.value;
this.actor.rollCarac( caracName.toLowerCase() );
});
// On competence change
html.find('.creature-carac').change(async event => {
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) );
} );
html.find('.creature-niveau').change(async event => {
this.html.find('.creature-niveau').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "niveau", parseInt(event.target.value) );
} );
html.find('.creature-dommages').change(async event => {
this.html.find('.creature-dommages').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "dommages", parseInt(event.target.value) );
} );
// Roll Skill
html.find('.competence-label a').click(async event => {
let compName = event.currentTarget.text;
this.actor.rollCompetence( compName );
});
html.find('.endurance-plus').click(event => {
this.actor.santeIncDec("endurance", 1);
this.render(true);
});
html.find('.endurance-moins').click(event => {
this.actor.santeIncDec("endurance", -1);
this.render(true);
});
html.find('.encaisser-direct').click(event => {
this.actor.encaisser();
});
html.find('.remise-a-neuf').click(event => {
if (game.user.isGM) {
this.actor.remiseANeuf();
}
});
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - sheetHeader[0].clientHeight - sheetTabs[0].clientHeight;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.object.update(formData);
}
}

View File

@ -1,8 +1,3 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { RdDUtility } from "./rdd-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemArme } from "./item-arme.js";
@ -12,11 +7,18 @@ import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { DialogRepos } from "./dialog-repos.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { Monnaie } from "./item-monnaie.js";
import { MAINS_DIRECTRICES } from "./actor.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDActorSheet extends ActorSheet {
/** @override */
@ -25,10 +27,9 @@ export class RdDActorSheet extends ActorSheet {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 640,
width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
editCaracComp: false,
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false
});
@ -36,64 +37,66 @@ export class RdDActorSheet extends ActorSheet {
/* -------------------------------------------- */
async getData() {
const objectData = Misc.data(this.object);
this.timerRecherche = undefined;
this.actor.computeEtatGeneral();
let formData = {
title: this.title,
id: objectData.id,
type: objectData.type,
img: objectData.img,
name: objectData.name,
id: this.actor.id,
type: this.actor.type,
img: this.actor.img,
name: this.actor.name,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
data: foundry.utils.deepClone(Misc.templateData(this.object)),
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
limited: this.object.limited,
system: foundry.utils.deepClone(this.actor.system),
effects: this.actor.effects.map(e => foundry.utils.deepClone(e)),
limited: this.actor.limited,
options: this.options,
owner: this.document.isOwner,
itemsByType: Misc.classify(this.object.items.map(i => foundry.utils.deepClone(i.data))),
};
RdDUtility.filterItemsPerTypeForSheet(formData);
formData.options.isGM = game.user.isGM;
if (formData.type == 'creature') return formData; // Shortcut
formData.competenceByCategory = Misc.classify(formData.competences, it => it.data.categorie);
formData.calc = {
comptageArchetype: RdDItemCompetence.computeResumeArchetype(formData.competences),
competenceXPTotal: RdDItemCompetence.computeTotalXP(formData.competences),
caracTotal: RdDCarac.computeTotal(formData.data.carac, formData.data.beaute),
// Mise à jour de l'encombrement total et du prix de l'équipement
owner: this.actor.isOwner,
description: await TextEditor.enrichHTML(this.object.system.description, { async: true }),
biographie: await TextEditor.enrichHTML(this.object.system.biographie, { async: true }),
notes: await TextEditor.enrichHTML(this.object.system.notes, { async: true }),
notesmj: await TextEditor.enrichHTML(this.object.system.notesmj, { async: true }),
calc: {
fortune: Monnaie.getFortune(this.actor),
encTotal: await this.actor.computeEncombrementTotalEtMalusArmure(),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
fatigue: RdDUtility.calculFatigueHtml(formData.data.sante.fatigue.value, formData.data.sante.endurance.max),
resumeBlessures: this.actor.computeResumeBlessure(formData.data.blessures),
surEncombrementMessage: this.actor.getMessageSurEncombrement()
};
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.getMessageSurEncombrement(),
},
}
formData.options.isGM = game.user.isGM;
RdDUtility.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
if (formData.type == 'personnage') {
formData.options.mainsDirectrices = MAINS_DIRECTRICES;
formData.byCateg = Misc.classify(formData.competences, it => it.system.categorie)
formData.calc.comptageArchetype = RdDItemCompetence.computeResumeArchetype(formData.competences);
formData.calc.competenceXPTotal = RdDItemCompetence.computeTotalXP(formData.competences);
formData.calc.fatigue = RdDUtility.calculFatigueHtml(formData.system.sante.fatigue.value, formData.system.sante.endurance.max);
formData.competences.forEach(item => {
item.visible = this.options.recherche
item.system.isVisible = this.options.recherche
? RdDItemCompetence.nomContientTexte(item, this.options.recherche.text)
: (!this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(item));
RdDItemCompetence.levelUp(item, formData.data.compteurs.experience.value);
RdDItemCompetence.levelUp(item, formData.system.compteurs.experience.value);
});
Object.values(formData.data.carac).forEach(c => {
Object.values(formData.system.carac).forEach(c => {
RdDCarac.levelUp(c);
});
// toujours avoir une liste d'armes (pour mettre esquive et corps à corps)
formData.combat = duplicate(formData.armes ?? []);
RdDItemArme.computeNiveauArmes(formData.combat, formData.competences);
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.data.carac);
formData.esquives = this.actor.getCompetences("Esquive").map(i => foundry.utils.deepClone(i.data));
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.data.carac);
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac);
formData.esquives = this.actor.getCompetences("Esquive");
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
this.armesList = formData.combat;
@ -102,145 +105,98 @@ export class RdDActorSheet extends ActorSheet {
formData.difficultesLibres = CONFIG.RDD.difficultesLibres;
formData.hautreve = {
isDemiReve: this.actor.getEffectByLabel("Demi-rêve"),
sortsReserve: formData.data.reve.reserve.list,
rencontres: duplicate(formData.data.reve.rencontre.list),
casesTmr: formData.itemsByType.casetmr,
isDemiReve: this.actor.getEffect(STATUSES.StatusDemiReve),
cacheTMR: this.actor.isTMRCache()
}
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.subacteurs = {
vehicules: this.actor.listeVehicules(),
montures: this.actor.listeMontures(),
suivants: this.actor.listeSuivants()
}
if (this.actor.getBestDraconic().data.niveau > -11 && !this.actor.isHautRevant()) {
if (this.actor.getBestDraconic().system.niveau > -11 && !this.actor.isHautRevant()) {
ui.notifications.error(`${this.actor.name} a des compétences draconiques, mais pas le don de Haut-Rêve!
<br>Ajoutez-lui la tête "Don de Haut-Rêve" pour lui permettre d'utiliser ses compétences et d'accéder aux terres médianes du rêve`);
}
}
return formData;
}
isCompetenceAffichable(competence) {
return !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence);
}
/* -------------------------------------------- */
async _onDropActor(event, dragData) {
console.log("_onDropActor", this.actor.id, dragData);
this.actor.addSubActeur(dragData.id || dragData.data._id);
super._onDropActor(event, dragData);
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
const destItemId = $(event.target)?.closest('.item').attr('data-item-id');
const dropParams = RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor.id, dragData, this.objetVersConteneur);
const callSuper = await this.actor.processDropItem(dropParams);
if (callSuper) {
await super._onDropItem(event, dragData)
}
}
/* -------------------------------------------- */
async createItem(name, type) {
await this.actor.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
}
/* -------------------------------------------- */
async createEmptyTache() {
await this.createItem('Nouvelle tache', 'tache');
}
/* -------------------------------------------- */ /** @override */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
HtmlUtility._showControlWhen($(".gm-only"), game.user.isGM);
HtmlUtility._showControlWhen($(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility._showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
html.find('.item-split').click(async event => {
this.html.find('.item-split').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
RdDSheetUtility.splitItem(item, this.actor);
});
html.find('.item-edit').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor)
console.log("ITEM :", item)
item.sheet.render(true)
})
html.find('.display-label a').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item.sheet.render(true);
});
html.find('.rencontre-delete').click(async event => {
this.actor.deleteTMRRencontre(RdDSheetUtility.getItemId(event));
});
html.find('.item-delete').click(async event => {
this.html.find('.item-edit').click(async event => RdDSheetUtility.getItem(event, this.actor)?.sheet.render(true))
this.html.find('.item-delete').click(async event => RdDUtility.confirmerSuppressionItem(this, RdDSheetUtility.getItem(event, this.actor)));
this.html.find('.item-vendre').click(async event => RdDSheetUtility.getItem(event, this.actor)?.proposerVente());
this.html.find('.item-montrer').click(async event => RdDSheetUtility.getItem(event, this.actor)?.postItem());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor));
this.html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
RdDUtility.confirmerSuppression(this, li);
const actorId = li.data("actor-id");
if (actorId) {
const subActor = game.actors.get(actorId);
RdDUtility.confirmerSuppressionSubacteur(this, subActor, li);
}
});
html.find('.item-vendre').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.proposerVente();
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);
});
html.find('.item-montrer').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.postItem();
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);
});
html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor)
this.actor.actionItem(item);
});
html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
RdDUtility.confirmerSuppressionSubacteur(this, li);
});
html.find('.encaisser-direct').click(async event => {
this.html.find('.encaisser-direct').click(async event => {
this.actor.encaisser();
})
html.find('.sheet-possession-attack').click(async event => {
this.html.find('.sheet-possession-attack').click(async event => {
const poss = RdDSheetUtility.getItem(event, this.actor)
this.actor.conjurerPossession(poss)
})
html.find('.remise-a-neuf').click(async event => {
this.html.find('.remise-a-neuf').click(async event => {
if (game.user.isGM) {
this.actor.remiseANeuf();
}
});
html.find('.creer-tache').click(async event => {
this.html.find('.creer-tache').click(async event => {
this.createEmptyTache();
});
html.find('.creer-un-objet').click(async event => {
this.html.find('.creer-un-objet').click(async event => {
RdDUtility.selectObjetType(this);
});
html.find('.creer-une-oeuvre').click(async event => {
this.html.find('.creer-une-oeuvre').click(async event => {
RdDUtility.selectTypeOeuvre(this);
});
html.find('#nettoyer-conteneurs').click(async event => {
this.html.find('.nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs();
});
// Blessure control
html.find('.blessure-control').click(async event => {
const tr = $(event.currentTarget).parents(".item");
this.html.find('.blessure-control').click(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
let btype = tr.data("blessure-type");
let index = tr.data('blessure-index');
let active = $(event.currentTarget).data('blessure-active');
let active = this.html.find(event.currentTarget).data('blessure-active');
//console.log(btype, index, active);
await this.actor.manageBlessureFromSheet(btype, index, active);
});
// Blessure data
html.find('.blessures-soins').change(async event => {
const tr = $(event.currentTarget).parents(".item");
this.html.find('.blessure-soins').change(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
let btype = tr.data('blessure-type');
let index = tr.data('blessure-index');
let psoins = tr.find('.blessure-premiers_soins').val();
@ -254,57 +210,57 @@ export class RdDActorSheet extends ActorSheet {
});
// Equip Inventory Item
html.find('.item-equip').click(async event => {
this.html.find('.item-equip').click(async event => {
this.actor.equiperObjet(RdDSheetUtility.getItemId(event));
});
// Roll Carac
html.find('.carac-label a').click(async event => {
this.html.find('.carac-label a').click(async event => {
let caracName = event.currentTarget.attributes.name.value;
this.actor.rollCarac(caracName.toLowerCase());
});
html.find('.chance-actuelle').click(async event => {
this.html.find('.chance-actuelle').click(async event => {
this.actor.rollCarac('chance-actuelle');
});
html.find('.chance-appel').click(async event => {
this.html.find('.chance-appel').click(async event => {
this.actor.rollAppelChance();
});
html.find('#jet-astrologie').click(async event => {
this.html.find('[name="jet-astrologie"]').click(async event => {
this.actor.astrologieNombresAstraux();
});
// Roll Skill
html.find('a.competence-label').click(async event => {
this.html.find('a.competence-label').click(async event => {
this.actor.rollCompetence(RdDSheetUtility.getItemId(event));
});
html.find('.tache-label a').click(async event => {
this.html.find('.tache-label a').click(async event => {
this.actor.rollTache(RdDSheetUtility.getItemId(event));
});
html.find('.meditation-label a').click(async event => {
this.html.find('.meditation-label a').click(async event => {
this.actor.rollMeditation(RdDSheetUtility.getItemId(event));
});
html.find('.chant-label a').click(async event => {
this.html.find('.chant-label a').click(async event => {
this.actor.rollChant(RdDSheetUtility.getItemId(event));
});
html.find('.danse-label a').click(async event => {
this.html.find('.danse-label a').click(async event => {
this.actor.rollDanse(RdDSheetUtility.getItemId(event));
});
html.find('.musique-label a').click(async event => {
this.html.find('.musique-label a').click(async event => {
this.actor.rollMusique(RdDSheetUtility.getItemId(event));
});
html.find('.oeuvre-label a').click(async event => {
this.html.find('.oeuvre-label a').click(async event => {
this.actor.rollOeuvre(RdDSheetUtility.getItemId(event));
});
html.find('.jeu-label a').click(async event => {
this.html.find('.jeu-label a').click(async event => {
this.actor.rollJeu(RdDSheetUtility.getItemId(event));
});
html.find('.recettecuisine-label a').click(async event => {
this.html.find('.recettecuisine-label a').click(async event => {
this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event));
});
html.find('.subacteur-label a').click(async 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) {
@ -313,26 +269,26 @@ export class RdDActorSheet extends ActorSheet {
});
// Boutons spéciaux MJs
html.find('.forcer-tmr-aleatoire').click(async event => {
this.html.find('.forcer-tmr-aleatoire').click(async event => {
this.actor.reinsertionAleatoire("Action MJ");
});
html.find('.afficher-tmr').click(async event => {
this.actor.afficheTMRetMessage();
this.html.find('.afficher-tmr').click(async event => {
this.actor.changeTMRVisible();
});
// Points de reve actuel
html.find('.ptreve-actuel a').click(async event => {
this.html.find('.ptreve-actuel a').click(async event => {
this.actor.rollCarac('reve-actuel');
});
// Roll Weapon1
html.find('.arme-label a').click(async event => {
this.html.find('.arme-label a').click(async event => {
let arme = this._getEventArmeCombat(event);
this.actor.rollArme(duplicate(arme));
});
// Initiative pour l'arme
html.find('.arme-initiative a').click(async event => {
let combatant = game.combat.data.combatants.find(c => c.actor.data._id == this.actor.data._id);
this.html.find('.arme-initiative a').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id);
if (combatant) {
let action = this._getEventArmeCombat(event);
RdDCombatManager.rollInitiativeAction(combatant._id, action);
@ -341,90 +297,88 @@ export class RdDActorSheet extends ActorSheet {
}
});
// Display TMR, visualisation
html.find('.visu-tmr').click(async event => {
this.html.find('.visu-tmr').click(async event => {
this.actor.displayTMR("visu");
});
// Display TMR, normal
html.find('.monte-tmr').click(async event => {
this.html.find('.monte-tmr').click(async event => {
this.actor.displayTMR("normal");
});
// Display TMR, fast
html.find('.monte-tmr-rapide').click(async event => {
this.html.find('.monte-tmr-rapide').click(async event => {
this.actor.displayTMR("rapide");
});
html.find('.repos').click(async event => {
this.html.find('.repos').click(async event => {
await DialogRepos.create(this.actor);
});
html.find('.delete-active-effect').click(async event => {
let id = $(event.currentTarget).parents(".active-effect").data('id');
this.actor.enleverActiveEffectById(id);
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);
}
});
html.find('.enlever-tous-effets').click(async event => {
this.actor.enleverTousLesEffets();
this.html.find('.enlever-tous-effets').click(async event => {
if (game.user.isGM) {
await this.actor.removeEffects();
}
});
html.find('.conteneur-name a').click(async event => {
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
html.find('.carac-xp-augmenter').click(async event => {
this.html.find('.carac-xp-augmenter').click(async event => {
let caracName = event.currentTarget.name.replace("augmenter.", "");
this.actor.updateCaracXPAuto(caracName);
});
html.find('.competence-xp-augmenter').click(async event => {
this.html.find('.competence-xp-augmenter').click(async event => {
this.actor.updateCompetenceXPAuto(RdDSheetUtility.getItemId(event));
});
html.find('.competence-stress-augmenter').click(async event => {
this.html.find('.competence-stress-augmenter').click(async event => {
this.actor.updateCompetenceStress(RdDSheetUtility.getItemId(event));
});
if (this.options.editCaracComp) {
if (this.options.vueDetaillee) {
// On carac change
html.find('.carac-value').change(async event => {
let caracName = event.currentTarget.name.replace(".value", "").replace("data.carac.", "");
//console.log("Value changed :", event, caracName);
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));
});
html.find('.carac-xp').change(async event => {
let caracName = event.currentTarget.name.replace(".xp", "").replace("data.carac.", "");
//console.log("Value changed :", event, caracName);
this.html.find('input.carac-xp').change(async event => {
let caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", "");
this.actor.updateCaracXP(caracName, parseInt(event.target.value));
});
// On competence change
html.find('.competence-value').change(async event => {
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
html.find('.competence-xp').change(async event => {
this.html.find('input.competence-xp').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXP(compName, parseInt(event.target.value));
});
// On competence xp change
html.find('.competence-xp-sort').change(async event => {
this.html.find('input.competence-xp-sort').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value));
});
// On competence archetype change
html.find('.competence-archetype').change(async event => {
this.html.find('.competence-archetype').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceArchetype(compName, parseInt(event.target.value));
});
}
html.find('.show-hide-competences').click(async event => {
this.html.find('.show-hide-competences').click(async event => {
this.options.showCompNiveauBase = !this.options.showCompNiveauBase;
this.render(true);
});
html.find('.lock-unlock-sheet').click(async event => {
this.options.editCaracComp = !this.options.editCaracComp;
this.render(true);
});
html.find('.recherche')
this.html.find('.recherche')
.each((index, field) => {
if (this.options.recherche) {
field.focus();
@ -433,7 +387,7 @@ export class RdDActorSheet extends ActorSheet {
})
.keyup(async event => {
const nouvelleRecherche = this._optionRecherche(event.currentTarget);
if (this.options.recherche?.text != nouvelleRecherche?.text){
if (this.options.recherche?.text != nouvelleRecherche?.text) {
this.options.recherche = nouvelleRecherche;
if (this.timerRecherche) {
clearTimeout(this.timerRecherche);
@ -447,94 +401,127 @@ export class RdDActorSheet extends ActorSheet {
.change(async event =>
this.options.recherche = this._optionRecherche(event.currentTarget)
);
html.find('.vue-detaillee').click(async event => {
this.html.find('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
});
// On pts de reve change
html.find('.pointsreve-value').change(async event => {
this.html.find('.pointsreve-value').change(async event => {
let reveValue = event.currentTarget.value;
this.actor.update({ "data.reve.reve.value": reveValue });
this.actor.update({ "system.reve.reve.value": reveValue });
});
// On seuil de reve change
html.find('.seuil-reve-value').change(async event => {
this.html.find('.seuil-reve-value').change(async event => {
console.log("seuil-reve-value", event.currentTarget)
this.actor.setPointsDeSeuil(event.currentTarget.value);
});
html.find('#attribut-protection-edit').change(async event => {
this.html.find('#attribut-protection-edit').change(async event => {
this.actor.updateAttributeValue(event.currentTarget.attributes.name.value, parseInt(event.target.value));
});
// On stress change
html.find('.compteur-edit').change(async event => {
this.html.find('.compteur-edit').change(async event => {
let fieldName = event.currentTarget.attributes.name.value;
this.actor.updateCompteurValue(fieldName, parseInt(event.target.value));
});
html.find('#ethylisme').change(async event => {
this.html.find('#ethylisme').change(async event => {
this.actor.setEthylisme(parseInt(event.target.value));
});
html.find('.stress-test').click(async event => {
this.html.find('.stress-test').click(async event => {
this.actor.transformerStress();
});
html.find('.moral-malheureux').click(async event => {
this.html.find('.moral-malheureux').click(async event => {
this.actor.jetDeMoral('malheureuse');
});
html.find('.moral-neutre').click(async event => {
this.html.find('.moral-neutre').click(async event => {
this.actor.jetDeMoral('neutre');
});
html.find('.moral-heureux').click(async event => {
this.html.find('.moral-heureux').click(async event => {
this.actor.jetDeMoral('heureuse');
});
html.find('#ethylisme-test').click(async event => {
this.html.find('.ethylisme-test').click(async event => {
this.actor.jetEthylisme();
});
html.find('.jet-vie').click(async event => {
this.html.find('.jet-vie').click(async event => {
this.actor.jetVie();
});
html.find('.jet-endurance').click(async event => {
this.html.find('.jet-endurance').click(async event => {
this.actor.jetEndurance();
});
html.find('.monnaie-plus').click(async event => {
this.html.find('.monnaie-plus').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), 1);
});
html.find('.monnaie-moins').click(async event => {
this.html.find('.monnaie-moins').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), -1);
});
html.find('.vie-plus').click(async event => {
this.html.find('.vie-plus').click(async event => {
this.actor.santeIncDec("vie", 1);
});
html.find('.vie-moins').click(async event => {
this.html.find('.vie-moins').click(async event => {
this.actor.santeIncDec("vie", -1);
});
html.find('.endurance-plus').click(async event => {
this.html.find('.endurance-plus').click(async event => {
this.actor.santeIncDec("endurance", 1);
});
html.find('.endurance-moins').click(async event => {
this.html.find('.endurance-moins').click(async event => {
this.actor.santeIncDec("endurance", -1);
});
html.find('.ptreve-actuel-plus').click(async event => {
this.html.find('.ptreve-actuel-plus').click(async event => {
this.actor.reveActuelIncDec(1);
});
html.find('.ptreve-actuel-moins').click(async event => {
this.html.find('.ptreve-actuel-moins').click(async event => {
this.actor.reveActuelIncDec(-1);
});
html.find('.fatigue-plus').click(async event => {
this.html.find('.fatigue-plus').click(async event => {
this.actor.santeIncDec("fatigue", 1);
});
html.find('.fatigue-moins').click(async event => {
this.html.find('.fatigue-moins').click(async event => {
this.actor.santeIncDec("fatigue", -1);
});
}
isCompetenceAffichable(competence) {
return !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence);
}
/* -------------------------------------------- */
async _onDropActor(event, dragData) {
const dropActor = fromUuidSync(dragData.uuid);
this.actor.addSubActeur(dropActor);
super._onDropActor(event, dragData);
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
const destItemId = this.html.find(event.target)?.closest('.item').attr('data-item-id')
const dropParams = RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor, dragData, this.objetVersConteneur)
if (dropParams) {
const callSuper = await this.actor.processDropItem(dropParams)
if (callSuper) {
await super._onDropItem(event, dragData)
}
}
}
/* -------------------------------------------- */
async createItem(name, type) {
await this.actor.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
}
/* -------------------------------------------- */
async createEmptyTache() {
await this.createItem('Nouvelle tache', 'tache');
}
_optionRecherche(target) {
if (!target.value?.length){
if (!target.value?.length) {
return undefined;
}
return {
@ -545,12 +532,12 @@ export class RdDActorSheet extends ActorSheet {
}
_getEventArmeCombat(event) {
const li = $(event.currentTarget)?.parents(".item");
const li = this.html.find(event.currentTarget)?.parents(".item");
let armeName = li.data("arme-name");
let compName = li.data('competence-name');
const arme = this.armesList.find(a => a.name == armeName && a.data.competence == compName);
const arme = this.armesList.find(a => a.name == armeName && a.system.competence == compName);
if (!arme) {
return { name: armeName, data: { competence: compName } };
return { name: armeName, system: { competence: compName } };
}
return arme;
}
@ -562,7 +549,10 @@ export class RdDActorSheet extends ActorSheet {
const sheetHeader = this.element.find(".sheet-header");
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - sheetHeader[0].clientHeight - sheetTabs[0].clientHeight;
let bodyHeight = position.height - sheetHeader[0].clientHeight;
if (sheetTabs.length > 0) {
bodyHeight -= sheetTabs[0].clientHeight;
}
sheetBody.css("height", bodyHeight);
return position;
}
@ -572,7 +562,7 @@ export class RdDActorSheet extends ActorSheet {
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.object.update(formData);
return this.actor.update(formData);
}
async splitItem(item) {
@ -581,11 +571,11 @@ export class RdDActorSheet extends ActorSheet {
}
async _onSplitItem(item, split) {
if (split >= 1 && split < Misc.data(item).data.quantite) {
if (split >= 1 && split < item.system.quantite) {
await item.diminuerQuantite(split);
const itemData = duplicate(Misc.data(item));
itemData.data.quantite = split;
await this.actor.createEmbeddedDocuments('Item', [itemData])
const splitItem = duplicate(item);
splitItem.system.quantite = split;
await this.actor.createEmbeddedDocuments('Item', [splitItem])
}
}

View File

@ -1,15 +1,8 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { RdDUtility } from "./rdd-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { Misc } from "./misc.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { RdDActorSheet } from "./actor-sheet.js";
/* -------------------------------------------- */
export class RdDActorVehiculeSheet extends ActorSheet {
export class RdDActorVehiculeSheet extends RdDActorSheet {
/** @override */
static get defaultOptions() {
@ -21,145 +14,26 @@ export class RdDActorVehiculeSheet extends ActorSheet {
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }]
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
});
}
/* -------------------------------------------- */
async getData() {
const objectData = Misc.data(this.object);
let formData = {
title: this.title,
id: objectData.id,
type: objectData.type,
img: objectData.img,
name: objectData.name,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
data: foundry.utils.deepClone(Misc.templateData(this.object)),
effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)),
limited: this.object.limited,
options: this.options,
owner: this.document.isOwner,
itemsByType: Misc.classify(this.object.items.map(i => foundry.utils.deepClone(i.data))),
};
RdDUtility.filterItemsPerTypeForSheet(formData);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.options.isGM = game.user.isGM;
formData.calc = {
encTotal: await this.actor.computeEncombrementTotalEtMalusArmure(),
surEncombrementMessage: this.actor.getMessageSurEncombrement()
}
console.log("DATA", formData);
return formData;
}
async computeMalusArmure() {
// pas de malus armure
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
const destItemId = $(event.target)?.closest('.item').attr('data-item-id');
const dropParams = RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor.id, dragData, this.objetVersConteneur);
const callSuper = await this.actor.processDropItem(dropParams);
if (callSuper) {
await super._onDropItem(event, dragData)
}
}
/* -------------------------------------------- */
async createItem(name, type) {
await this.actor.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
}
/* -------------------------------------------- */
async monnaieIncDec(id, value) {
let monnaie = this.getMonnaie(id);
if (monnaie) {
const quantite = Math.max(0, Misc.templateData(monnaie).quantite + value);
await this.updateEmbeddedDocuments('Item', [{ _id: monnaie.id, 'data.quantite': quantite }]);
}
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen($(".gm-only"), game.user.isGM);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Update Inventory Item
html.find('.item-edit').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item.sheet.render(true);
this.html.find('.resistance-moins').click(async event => {
this.actor.vehicleIncDec("resistance", -1);
});
// Delete Inventory Item
html.find('.item-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
RdDUtility.confirmerSuppression(this, li);
this.html.find('.resistance-plus').click(async event => {
this.actor.vehicleIncDec("resistance", 1);
});
html.find('.item-vendre').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.proposerVente();
this.html.find('.structure-moins').click(async event => {
this.actor.vehicleIncDec("structure", -1);
});
html.find('.item-montrer').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.postItem();
this.html.find('.structure-plus').click(async event => {
this.actor.vehicleIncDec("structure", 1);
});
html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
this.actor.actionItem(item);
});
html.find('.creer-un-objet').click(async event => {
RdDUtility.selectObjetType(this);
});
html.find('#nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs();
});
html.find('.monnaie-plus').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), 1);
});
html.find('.monnaie-moins').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), -1);
});
// Display info about queue
html.find('.conteneur-name a').click((event) => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - sheetHeader[0].clientHeight - sheetTabs[0].clientHeight;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.object.update(formData);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
import { Misc } from "./misc.js";
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
export const MESSAGE_DATA = 'message-data';
/**
* Class providing helper methods to get the list of users, and
@ -19,61 +18,50 @@ export class ChatUtility {
/* -------------------------------------------- */
static notifyUser(userId, level = 'info', message) {
const data = {
const socketData = {
userId: userId, level: level, message: message
};
if (game.user.id == userId) {
ChatUtility.onNotifyUser(data);
ChatUtility.onNotifyUser(socketData);
}
else {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_user_ui_notifications", data: data
msg: "msg_user_ui_notifications", data: socketData
});
}
}
static onNotifyUser(data) {
if (game.user.id == data.userId) {
switch (data.level) {
case 'warn': ui.notifications.warn(data.message); break;
case 'error': ui.notifications.error(data.message); break;
default: ui.notifications.info(data.message); break;
static onNotifyUser(socketData) {
if (game.user.id == socketData.userId) {
switch (socketData.level) {
case 'warn': ui.notifications.warn(socketData.message); break;
case 'error': ui.notifications.error(socketData.message); break;
default: ui.notifications.info(socketData.message); break;
}
}
}
/* -------------------------------------------- */
static onRemoveMessages(data) {
static onRemoveMessages(socketData) {
if (Misc.isUniqueConnectedGM()) {
if (data.part) {
const toDelete = game.messages.filter(it => it.data.content.includes(data.part));
if (socketData.part) {
const toDelete = game.messages.filter(it => it.content.includes(socketData.part));
toDelete.forEach(it => it.delete());
}
if (data.messageId) {
game.messages.get(data.messageId)?.delete();
if (socketData.messageId) {
game.messages.get(socketData.messageId)?.delete();
}
}
}
static onRemoveMessages(data) {
if (Misc.isUniqueConnectedGM()) {
if (data.part) {
const toDelete = game.messages.filter(it => it.data.content.includes(data.part));
toDelete.forEach(it => it.delete());
}
if (data.messageId) {
game.messages.get(data.messageId)?.delete();
}
}
}
/* -------------------------------------------- */
static removeMessages(data) {
static removeMessages(socketData) {
if (Misc.isUniqueConnectedGM()) {
ChatUtility.onRemoveMessages(data);
ChatUtility.onRemoveMessages(socketData);
}
else {
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_delete_chat_message", data: data });
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_delete_chat_message", data: socketData });
}
}
@ -141,7 +129,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static getUsers(filter) {
return Misc.getUsers().filter(filter).map(user => user.data._id);
return game.users.filter(filter).map(user => user.id);
}
/* -------------------------------------------- */
@ -154,17 +142,17 @@ export class ChatUtility {
}
/* -------------------------------------------- */
static handleGMChatMessage(data) {
console.log("blindMessageToGM", data);
static handleGMChatMessage(socketData) {
console.log("blindMessageToGM", socketData);
if (game.user.isGM) { // message privé pour GM only
data.user = game.user.id;
ChatMessage.create(data);
socketData.user = game.user.id;
ChatMessage.create(socketData);
}
}
static async setMessageData(chatMessage, key, data) {
if (data) {
await chatMessage.setFlag(SYSTEM_RDD, key, JSON.stringify(data));
static async setMessageData(chatMessage, key, flag) {
if (flag) {
await chatMessage.setFlag(SYSTEM_RDD, key, JSON.stringify(flag));
}
}

View File

@ -1,5 +1,6 @@
export const SYSTEM_RDD = 'foundryvtt-reve-de-dragon';
export const SYSTEM_SOCKET_ID = 'system.foundryvtt-reve-de-dragon';
export const LOG_HEAD = 'RdD | ';
export const HIDE_DICE = 'hide';
export const SHOW_DICE = 'show';

View File

@ -0,0 +1,127 @@
import { SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
const LATEST_USED_JOURNAL_ID = "chronologie-dernier-journal";
export class DialogChronologie extends Dialog {
static init() {
game.settings.register(SYSTEM_RDD, LATEST_USED_JOURNAL_ID, {
name: "Dernier article de journal utilisé pour enregistrer la chronologie",
scope: "client",
config: false,
default: "",
type: String
});
}
static async create() {
const dateRdD = game.system.rdd.calendrier.getCalendrier();
const dialogData = {
auteur: game.user.name,
isGM: game.user.isGM,
information: "",
journalId: game.settings.get(SYSTEM_RDD, LATEST_USED_JOURNAL_ID),
journaux: game.journal.filter(it => it.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)),
dateRdD: dateRdD,
jourRdD: dateRdD.jour +1,
heureRdD: game.system.rdd.calendrier.getCurrentHeure(),
dateReel: DialogChronologie.getCurrentDateTime()
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-chronologie.html", dialogData);
const dialog = new DialogChronologie(html);
dialog.render(true);
}
constructor(html) {
const options = {
classes: ["DialogChronologie"],
width: 500,
height: 'fit-content',
'z-index': 99999
};
const conf = {
title: "Chronologie",
content: html,
buttons: {
ajout: { label: "Ajouter", callback: it => this.ajouter() },
}
};
super(conf, options);
}
static getCurrentDateTime() {
return new Date().toLocaleString("sv-SE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit"
}).replace(" ", "T");
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
}
async ajouter() {
await this.forceValidation();
const { journalId, journalEntry } = this.findJournal();
// ajouter à la page ou créer une page
this.addContentToJournal(journalEntry, await this.prepareChronologieEntry());
this.storeLatestUsedJournalEntry(journalId);
}
async forceValidation() {
await this.html.find("form.rdddialogchrono :input").change();
}
findJournal() {
const journalId = this.html.find("form.rdddialogchrono :input[name='journalId']").val();
const journalEntry = game.journal.get(journalId);
return { journalId, journalEntry };
}
async prepareChronologieEntry() {
return await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chronologie-entry.html", this.extractJournalParameters());
}
extractJournalParameters() {
return {
auteur: this.html.find("form.rdddialogchrono :input[name='auteur']").val(),
information: this.html.find("form.rdddialogchrono :input[name='information']").val(),
dateRdD: {
jour: this.html.find("form.rdddialogchrono :input[name='jourRdD']").val(),
moisRdD: this.html.find("form.rdddialogchrono :input[name='dateRdD.moisRdD.key']").val(),
annee: this.html.find("form.rdddialogchrono :input[name='dateRdD.annee']").val()
},
heureRdD: this.html.find("form.rdddialogchrono :input[name='heureRdD']").val(),
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val().replace('T', ' ')
}
}
addContentToJournal(journalEntry, content) {
let page = journalEntry.pages.find(p => p.type == 'text' && Grammar.equalsInsensitive(p.name, 'Chronologie'));
if (page) {
page.update({ 'text.content': content + '\n' + page.text.content });
}
else {
journalEntry.createEmbeddedDocuments('JournalEntryPage', [this.newPageChronologie(content)]);
}
}
newPageChronologie(content) {
return new JournalEntryPage({
name: 'Chronologie',
type: 'text',
title: { show: true, level: 1 },
text: { content: content, format: 1 }
});
}
storeLatestUsedJournalEntry(journalId) {
game.settings.set(SYSTEM_RDD, LATEST_USED_JOURNAL_ID, journalId);
}
}

View File

@ -1,7 +1,6 @@
import { ChatUtility } from "./chat-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemSigneDraconique } from "./item-signedraconique.js";
import { Misc } from "./misc.js";
import { TMRUtility } from "./tmr-utility.js";
export class DialogCreateSigneDraconique extends Dialog {
@ -10,12 +9,13 @@ export class DialogCreateSigneDraconique extends Dialog {
const signe = await RdDItemSigneDraconique.randomSigneDraconique({ephemere: true});
let dialogData = {
signe: signe,
tmrs: TMRUtility.listSelectedTMR(signe.data.typesTMR ?? []),
actors: game.actors.filter(actor => actor.isHautRevant()).map(actor => {
let actorData = duplicate(Misc.data(actor));
actorData.selected = actor.hasPlayerOwner;
return actorData;
})
tmrs: TMRUtility.buildSelectionTypesTMR(signe.system.typesTMR),
actors: game.actors.filter(actor => actor.isPersonnage() && actor.isHautRevant())
.map(actor => ({
id: actor.id,
name: actor.name,
selected: true
}))
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-create-signedraconique.html", dialogData);
@ -23,12 +23,11 @@ export class DialogCreateSigneDraconique extends Dialog {
.render(true);
}
constructor(dialogData, html, callback) {
constructor(dialogData, html) {
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 500, height: 650, 'z-index': 99999 };
let conf = {
title: "Créer un signe",
content: html,
default: "Ajouter aux haut-rêvants",
buttons: {
"Ajouter aux haut-rêvants": { label: "Ajouter aux haut-rêvants", callback: it => { this._onCreerSigneActeurs(); } }
}
@ -38,81 +37,92 @@ export class DialogCreateSigneDraconique extends Dialog {
}
async _onCreerSigneActeurs() {
await $("[name='signe.data.ephemere']").change();
await $(".signe-xp-sort").change();
await this.html.find("[name='signe.system.ephemere']").change();
await this.html.find(".signe-xp-sort").change();
this.validerSigne();
this.dialogData.actors.filter(it => it.selected).map(it => game.actors.get(it._id))
this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it.id))
.forEach(actor => this._createSigneForActor(actor, this.dialogData.signe));
}
async _createSigneForActor(actor, signe) {
actor.createEmbeddedDocuments("Item", [signe]);
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(Misc.data(actor).name),
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name),
content: await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-actor.html", {
signe: signe,
alias: Misc.data(actor).name
alias: actor.name
})
});
}
validerSigne() {
this.dialogData.signe.name = $("[name='signe.name']").val();
this.dialogData.signe.data.valeur.norm = $("[name='signe.data.valeur.norm']").val();
this.dialogData.signe.data.valeur.sign = $("[name='signe.data.valeur.sign']").val();
this.dialogData.signe.data.valeur.part = $("[name='signe.data.valeur.part']").val();
this.dialogData.signe.data.difficulte = $("[name='signe.data.difficulte']").val();
this.dialogData.signe.data.ephemere = $("[name='signe.data.ephemere']").prop("checked");
this.dialogData.signe.data.duree = $("[name='signe.data.duree']").val();
this.dialogData.signe.data.typesTMR = $(".select-tmr").val();
this.dialogData.signe.name = this.html.find("[name='signe.name']").val();
this.dialogData.signe.system.valeur.norm = this.html.find("[name='signe.system.valeur.norm']").val();
this.dialogData.signe.system.valeur.sign = this.html.find("[name='signe.system.valeur.sign']").val();
this.dialogData.signe.system.valeur.part = this.html.find("[name='signe.system.valeur.part']").val();
this.dialogData.signe.system.difficulte = this.html.find("[name='signe.system.difficulte']").val();
this.dialogData.signe.system.ephemere = this.html.find("[name='signe.system.ephemere']").prop("checked");
this.dialogData.signe.system.duree = this.html.find("[name='signe.system.duree']").val();
this.dialogData.signe.system.typesTMR = TMRUtility.buildListTypesTMRSelection(this.dialogData.tmrs);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.setEphemere(this.dialogData.signe.data.ephemere);
this.html = html;
this.setEphemere(this.dialogData.signe.system.ephemere);
html.find(".signe-aleatoire").click(event => this.setSigneAleatoire());
html.find("[name='signe.data.ephemere']").change((event) => this.setEphemere(event.currentTarget.checked));
html.find(".select-actor").change((event) => this.onSelectActor(event));
html.find("[name='signe.system.ephemere']").change((event) => this.setEphemere(event.currentTarget.checked));
html.find(".signe-xp-sort").change((event) => this.onValeurXpSort(event));
html.find("input.select-actor").change((event) => this.onSelectActor(event));
html.find("input.select-tmr").change((event) => this.onSelectTmr(event));
}
async setSigneAleatoire() {
const newSigne = await RdDItemSigneDraconique.randomSigneDraconique({ephemere: true});
$("[name='signe.name']").val(newSigne.name);
$("[name='signe.data.valeur.norm']").val(newSigne.data.valeur.norm);
$("[name='signe.data.valeur.sign']").val(newSigne.data.valeur.sign);
$("[name='signe.data.valeur.part']").val(newSigne.data.valeur.part);
$("[name='signe.data.difficulte']").val(newSigne.data.difficulte);
$("[name='signe.data.duree']").val(newSigne.data.duree);
$("[name='signe.data.ephemere']").prop("checked", newSigne.data.ephemere);
$(".select-tmr").val(newSigne.data.typesTMR);
this.setEphemere(newSigne.data.ephemere);
this.html.find("[name='signe.name']").val(newSigne.name);
this.html.find("[name='signe.system.valeur.norm']").val(newSigne.system.valeur.norm);
this.html.find("[name='signe.system.valeur.sign']").val(newSigne.system.valeur.sign);
this.html.find("[name='signe.system.valeur.part']").val(newSigne.system.valeur.part);
this.html.find("[name='signe.system.difficulte']").val(newSigne.system.difficulte);
this.html.find("[name='signe.system.duree']").val(newSigne.system.duree);
this.html.find("[name='signe.system.ephemere']").prop("checked", newSigne.system.ephemere);
this.dialogData.tmrs = TMRUtility.buildSelectionTypesTMR(newSigne.system.typesTMR);
this.dialogData.tmrs.forEach(t => {
this.html.find(`[data-tmr-name='${t.name}']`).prop( "checked", t.selected);
})
this.setEphemere(newSigne.system.ephemere);
}
async setEphemere(ephemere) {
this.dialogData.signe.data.ephemere = ephemere;
HtmlUtility._showControlWhen($(".signe-data-duree"), ephemere);
this.dialogData.signe.system.ephemere = ephemere;
HtmlUtility._showControlWhen(this.html.find(".signe-system-duree"), ephemere);
}
async onSelectActor(event) {
event.preventDefault();
const options = event.currentTarget.options;
for (var i = 0; i < options.length; i++) { // looping over the options
const actorId = options[i].attributes["data-actor-id"].value;
const actor = this.dialogData.actors.find(it => it._id == actorId);
const actorId = this.html.find(event.currentTarget)?.data("actor-id");
const actor = this.dialogData.actors.find(it => it.id == actorId);
if (actor) {
actor.selected = options[i].selected;
actor.selected = event.currentTarget.checked;
}
};
}
onSelectTmr(event) {
const tmrName = this.html.find(event.currentTarget)?.data("tmr-name");
const onTmr = this.tmrs.find(it => it.name == tmrName);
if (onTmr){
onTmr.selected = event.currentTarget.checked;
}
}
onValeurXpSort(event) {
const codeReussite = event.currentTarget.attributes['data-typereussite']?.value ?? 0;
const xp = Number(event.currentTarget.value);
const oldValeur = this.dialogData.signe.data.valeur;
this.dialogData.signe.data.valeur = RdDItemSigneDraconique.calculValeursXpSort(codeReussite, xp, oldValeur);
const oldValeur = this.dialogData.signe.system.valeur;
this.dialogData.signe.system.valeur = RdDItemSigneDraconique.calculValeursXpSort(codeReussite, xp, oldValeur);
}
}

View File

@ -6,36 +6,43 @@ export class DialogFabriquerPotion extends Dialog {
/* -------------------------------------------- */
static async create(actor, item, dialogConfig) {
const min = DialogFabriquerPotion.nombreBrinsMinimum(item);
if (item.system.quantite < min) {
ui.notifications.warn(`Vous avez ${item.system.quantite} brins de ${item.name}, il en faut au moins ${min} pour faire une potion!`);
return;
}
let potionData = DialogFabriquerPotion.prepareData(actor, item);
let conf = {
title: `Fabriquer une potion de ${potionData.data.categorie}`,
content: await renderTemplate(dialogConfig.html, potionData),
default: potionData.buttonName,
};
const html = await renderTemplate(dialogConfig.html, potionData);
let options = { classes: ["dialogfabriquerpotion"], width: 600, height: 160, 'z-index': 99999 };
mergeObject(options, dialogConfig.options ?? {}, { overwrite: true })
const dialog = new DialogFabriquerPotion(actor, potionData, conf, options);
dialog.render(true);
return dialog;
new DialogFabriquerPotion(actor, potionData, html, options).render(true);
}
/* -------------------------------------------- */
static prepareData(actor, item) {
let potionData = duplicate(Misc.data(item));
potionData.nbBrinsSelect = RdDUtility.buildListOptions(1, potionData.data.quantite);
potionData.nbBrins = Math.min(potionData.data.quantite, DialogFabriquerPotion.getNombreBrinOptimal(potionData));
let potionData = duplicate(item)
potionData.nbBrinsSelect = RdDUtility.buildListOptions(
DialogFabriquerPotion.nombreBrinsMinimum(item),
DialogFabriquerPotion.nombreBrinsOptimal(item));
potionData.nbBrins = Math.min(potionData.system.quantite, DialogFabriquerPotion.nombreBrinsOptimal(potionData));
potionData.herbebonus = item.system.niveau;
potionData.buttonName = "Fabriquer";
return potionData;
}
/* -------------------------------------------- */
constructor(actor, potionData, conf, options) {
conf.buttons = {
[potionData.buttonName]: {
label: potionData.buttonName, callback: it => this.onFabriquer(it)
constructor(actor, potionData, html, options) {
const conf = {
title: `Fabriquer une potion de ${potionData.system.categorie}`,
content: html,
default: 'fabriquer',
buttons: {
'fabriquer': {
label: potionData.buttonName, callback: it => this.onFabriquer(html)
}
}
};
@ -45,27 +52,37 @@ export class DialogFabriquerPotion extends Dialog {
this.potionData = potionData;
}
static getNombreBrinOptimal(herbeData) {
switch (herbeData.data.categorie ?? '') {
case "Soin": return 12 - herbeData.data.niveau;
case "Repos": return 7 - herbeData.data.niveau;
}
return 1;
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find("#nbBrins").change(event => {
this.html = html;
this.html.find("[name='nbBrins']").change(event => {
this.potionData.nbBrins = Misc.toInt(event.currentTarget.value);
const brinsManquants = Math.max(0, DialogFabriquerPotion.nombreBrinsOptimal(this.potionData) - this.potionData.nbBrins);
this.potionData.herbebonus = Math.max(0, this.potionData.system.niveau - brinsManquants)
});
}
/* -------------------------------------------- */
async onFabriquer(it) {
await $("#nbBrins").change();
async onFabriquer(html) {
await this.html.find("[name='nbBrins']").change();
this.actor.fabriquerPotion(this.potionData);
this.close();
}
static nombreBrinsMinimum(herbeData) {
switch (herbeData.system.categorie ?? '') {
case "Soin": return 1 + Math.max(0, 12 - 2 * herbeData.system.niveau);
case "Repos": return 1 + Math.max(0, 7 - 2 * herbeData.system.niveau);
}
return 1;
}
static nombreBrinsOptimal(herbeData) {
switch (herbeData.system.categorie ?? '') {
case "Soin": return 12 - herbeData.system.niveau;
case "Repos": return 7 - herbeData.system.niveau;
}
return 1;
}
}

View File

@ -1,69 +1,33 @@
import { Misc } from "./misc.js";
import { Monnaie } from "./item-monnaie.js";
import { RdDUtility } from "./rdd-utility.js";
export class DialogItemAchat extends Dialog {
static async onButtonAcheter(event) {
const buttonAcheter = event.currentTarget;
if (!buttonAcheter.attributes['data-jsondata']?.value) {
ui.notifications.warn("Impossible d'acheter: informations sur l'objet manquantes")
return;
}
const chatMessageIdVente = RdDUtility.findChatMessageId(buttonAcheter);
const vendeurId = buttonAcheter.attributes['data-vendeurId']?.value;
static venteData(button) {
const vendeurId = button.attributes['data-vendeurId']?.value;
const vendeur = vendeurId ? game.actors.get(vendeurId) : undefined;
const acheteur = RdDUtility.getSelectedActor();
const json = button.attributes['data-jsondata']?.value;
if (!acheteur && !vendeur) {
ui.notifications.info("Pas d'acheteur ni de vendeur, aucun changement");
return;
return undefined;
}
if (!json) {
ui.notifications.warn("Impossible d'acheter: informations sur l'objet manquantes")
return undefined;
}
let venteData = DialogItemAchat.prepareVenteData(buttonAcheter, vendeurId, vendeur, acheteur);
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.html`, venteData);
const dialog = new DialogItemAchat(html, vendeur, acheteur, venteData, chatMessageIdVente);
dialog.render(true);
}
constructor(html, vendeur, acheteur, venteData, chatMessageIdVente) {
const isConsommable = venteData.item.type == 'nourritureboisson';
let options = { classes: ["dialogachat"], width: 400, height: isConsommable ? 450 : 350, 'z-index': 99999 };
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre";
const buttons = {};
if (isConsommable) {
buttons["consommer"] = { label: venteData.item.data.boisson ? "Boire" : "Manger", callback: it => { this.onAchatConsommer(); } }
}
buttons[actionAchat] = { label: actionAchat, callback: it => { this.onAchat(); } };
buttons["decliner"] = { label: "Décliner", callback: it => { } };
let conf = {
title: venteData.acheteur? venteData.acheteur.name + " - " + actionAchat : actionAchat,
content: html,
default: actionAchat,
buttons: buttons
};
super(conf, options);
this.vendeur = vendeur;
this.acheteur = acheteur;
this.chatMessageIdVente = chatMessageIdVente;
this.venteData = venteData;
}
static prepareVenteData(buttonAcheter, vendeurId, vendeur, acheteur) {
const jsondata = buttonAcheter.attributes['data-jsondata']?.value;
const prixLot = parseInt(buttonAcheter.attributes['data-prixLot']?.value ?? 0);
let venteData = {
item: JSON.parse(jsondata),
const prixLot = Number(button.attributes['data-prixLot']?.value ?? 0);
return {
item: json ? JSON.parse(json) : undefined,
actingUserId: game.user.id,
vendeurId: vendeurId,
vendeur: Misc.data(vendeur),
acheteur: Misc.data(acheteur),
tailleLot: parseInt(buttonAcheter.attributes['data-tailleLot']?.value ?? 1),
quantiteIllimite: buttonAcheter.attributes['data-quantiteIllimite']?.value == 'true',
quantiteNbLots: parseInt(buttonAcheter.attributes['data-quantiteNbLots']?.value),
vendeur: vendeur,
acheteur: acheteur,
tailleLot: parseInt(button.attributes['data-tailleLot']?.value ?? 1),
quantiteIllimite: button.attributes['data-quantiteIllimite']?.value == 'true',
quantiteNbLots: parseInt(button.attributes['data-quantiteNbLots']?.value),
choix: {
nombreLots: 1,
seForcer: false,
@ -71,20 +35,48 @@ export class DialogItemAchat extends Dialog {
},
prixLot: prixLot,
prixTotal: prixLot,
isVente: prixLot > 0
isVente: prixLot > 0,
chatMessageIdVente: RdDUtility.findChatMessageId(button)
};
return venteData;
}
static async onAcheter(venteData) {
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.html`, venteData);
new DialogItemAchat(html, venteData).render(true);
}
constructor(html, venteData) {
const isConsommable = venteData.item.type == 'nourritureboisson' && venteData.acheteur?.isPersonnage();
let options = { classes: ["dialogachat"], width: 400, height: 'fit-content', 'z-index': 99999 };
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre";
const buttons = {};
if (isConsommable) {
buttons["consommer"] = { label: venteData.item.system.boisson ? "Boire" : "Manger", callback: it => this.onAchatConsommer() }
}
buttons[actionAchat] = { label: actionAchat, callback: it => { this.onAchat(); } };
buttons["decliner"] = { label: "Décliner", callback: it => { } };
let conf = {
title: venteData.acheteur ? venteData.acheteur.name + " - " + actionAchat : actionAchat,
content: html,
default: actionAchat,
buttons: buttons
};
super(conf, options);
this.venteData = venteData;
}
async onAchat() {
await $(".nombreLots").change();
(this.vendeur ?? this.acheteur).achatVente({
await this.html.find(".nombreLots").change();
(this.venteData.vendeur ?? this.venteData.acheteur).achatVente({
userId: game.user.id,
vendeurId: this.vendeur?.id,
acheteurId: this.acheteur?.id,
vendeurId: this.venteData.vendeur?.id,
acheteurId: this.venteData.acheteur?.id,
prixTotal: this.venteData.prixTotal,
chatMessageIdVente: this.chatMessageIdVente,
choix: this.venteData.choix
chatMessageIdVente: this.venteData.chatMessageIdVente,
choix: this.venteData.choix,
vente: this.venteData
});
}
@ -96,9 +88,9 @@ export class DialogItemAchat extends Dialog {
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find(".nombreLots").change(event => this.setNombreLots(Number(event.currentTarget.value)));
html.find(".se-forcer").change(event => this.setSeForcer(event));
this.html = html;
this.html.find(".nombreLots").change(event => this.setNombreLots(Number(event.currentTarget.value)));
this.html.find(".se-forcer").change(event => this.setSeForcer(event));
}
setSeForcer(event) {
@ -106,9 +98,13 @@ export class DialogItemAchat extends Dialog {
}
setNombreLots(nombreLots) {
this.venteData.choix.nombreLots = nombreLots;
if (nombreLots > this.venteData.quantiteNbLots) {
ui.notifications.warn(`Seulement ${this.venteData.quantiteNbLots} lots disponibles, vous ne pouvez pas en prendre ${nombreLots}`)
}
this.venteData.choix.nombreLots = Math.min(nombreLots, this.venteData.quantiteNbLots);
this.venteData.prixTotal = (nombreLots * this.venteData.prixLot).toFixed(2);
$(".prixTotal").text(this.venteData.prixTotal);
this.html.find(".nombreLots").val(this.venteData.choix.nombreLots);
this.html.find(".prixTotal").text(this.venteData.prixTotal);
}
}

View File

@ -2,14 +2,14 @@ import { Misc } from "./misc.js";
export class DialogConsommer extends Dialog {
static async create(actor, item, onActionItem = async ()=>{}) {
static async create(actor, item, onActionItem = async () => { }) {
const consommerData = DialogConsommer.prepareData(actor, item);
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-item-consommer.html', consommerData);
return new DialogConsommer(actor, item, consommerData, html, onActionItem)
}
constructor(actor, item, consommerData, html, onActionItem = async ()=>{}) {
const options = { classes: ["dialogconsommer"], width: 350, height: 450, 'z-index': 99999 };
constructor(actor, item, consommerData, html, onActionItem = async () => { }) {
const options = { classes: ["dialogconsommer"], width: 350, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: consommerData.title,
content: html,
@ -17,8 +17,9 @@ export class DialogConsommer extends Dialog {
buttons: {
[consommerData.buttonName]: {
label: consommerData.buttonName, callback: async it => {
await this.onConsommer(it);
await onActionItem();}
await this.onConsommer();
await onActionItem();
}
}
}
};
@ -30,51 +31,71 @@ export class DialogConsommer extends Dialog {
this.consommerData = consommerData;
}
async onConsommer(event) {
await $(".se-forcer").change();
await $(".consommer-doses").change();
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find(".se-forcer").change(event => this.setSeForcer(event));
this.html.find(".consommer-doses").change(event => this.selectDoses(event));
}
async onConsommer() {
await this.html.find(".se-forcer").change();
await this.html.find(".consommer-doses").change();
await this.actor.consommer(this.item, this.consommerData.choix);
}
/* -------------------------------------------- */
static prepareData(actor, item) {
const itemData = duplicate(Misc.data(item));
let consommerData = {
item: itemData,
cuisine: Misc.data(actor.getCompetence('cuisine')),
item: duplicate(item),
cuisine: actor.getCompetence('cuisine'),
choix: {
doses: 1,
seForcer: false,
}
}
switch (itemData.type) {
switch (item.type) {
case 'herbe': case 'faune':
consommerData.title = 'Manger une portion crue: ';
consommerData.buttonName = "Manger";
break;
case 'nourritureboisson':
consommerData.title = itemData.data.boisson ? `${itemData.name}: boire une dose` : `${itemData.name}: manger une portion`;
consommerData.buttonName = itemData.data.boisson ? "Boire" : "Manger";
consommerData.title = item.system.boisson ? 'Boire une dose: ' : 'Manger une portion: ';
consommerData.buttonName = item.system.boisson ? "Boire" : "Manger";
break;
case 'potion':
consommerData.title = `${itemData.name}: boire la potion`;
consommerData.title = 'Boire la potion: ';
consommerData.buttonName = "Boire";
break;
}
DialogConsommer.calculDoses(consommerData, consommerData.choix.doses)
consommerData.title += item.name;
DialogConsommer.calculDoses(consommerData, item)
return consommerData;
}
static calculDoses(consommerData) {
const doses = consommerData.choix.doses;
consommerData.totalSust = Misc.keepDecimals(doses * (consommerData.item.data.sust ?? 0), 2);
consommerData.totalDesaltere = consommerData.item.data.boisson
? Misc.keepDecimals(doses * (consommerData.item.data.desaltere ?? 0), 2)
static calculDoses(consommer, item) {
const doses = consommer.choix.doses;
switch (item.type) {
case 'herbe': case 'faune':
consommer.totalSust = doses;
consommer.totalDesaltere = 0;
consommer.choix.sust = 1;
consommer.choix.quantite = 0;
consommer.choix.encombrement = Misc.keepDecimals(consommer.item.system.encombrement / item.system.sust, 2);
return;
case 'nourritureboisson':
consommer.choix.sust = consommer.item.system.sust;
consommer.choix.quantite = doses;
consommer.choix.encombrement = 0
consommer.totalSust = Misc.keepDecimals(doses * (consommer.item.system.sust ?? 0), 2);
consommer.totalDesaltere = consommer.item.system.boisson
? Misc.keepDecimals(doses * (consommer.item.system.desaltere ?? 0), 2)
: 0;
break;
case 'potion':
consommer.totalSust = 0
consommer.totalDesaltere = 0
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find(".se-forcer").change(event => this.setSeForcer(event));
html.find(".consommer-doses").change(event => this.selectDoses(event));
}
@ -84,8 +105,8 @@ export class DialogConsommer extends Dialog {
selectDoses(event) {
this.consommerData.choix.doses = Number(event.currentTarget.value);
DialogConsommer.calculDoses(this.consommerData);
$(".total-sust").text(this.consommerData.totalSust);
$(".total-desaltere").text(this.consommerData.totalDesaltere);
DialogConsommer.calculDoses(this.consommerData, this.item);
this.html.find(".total-sust").text(this.consommerData.totalSust);
this.html.find(".total-desaltere").text(this.consommerData.totalDesaltere);
}
}

View File

@ -1,18 +1,16 @@
import { HtmlUtility } from "./html-utility.js";
import { Misc } from "./misc.js";
export class DialogItemVente extends Dialog {
static async create(item, callback) {
const itemData = Misc.data(item);
const quantite = item.isConteneur() ? 1 : itemData.data.quantite;
static async display(item, callback) {
const quantite = item.isConteneur() ? 1 : item.system.quantite;
const venteData = {
item: itemData,
item: item,
alias: item.actor?.name ?? game.user.name,
vendeurId: item.actor?.id,
prixOrigine: itemData.data.cout,
prixUnitaire: itemData.data.cout,
prixLot: itemData.data.cout,
prixOrigine: item.system.cout,
prixUnitaire: item.system.cout,
prixLot: item.system.cout,
tailleLot: 1,
quantiteNbLots: quantite,
quantiteMaxLots: quantite,
@ -21,11 +19,11 @@ export class DialogItemVente extends Dialog {
isOwned: item.isOwned,
};
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.html`, venteData);
return new DialogItemVente(venteData, html, callback);
return new DialogItemVente(venteData, html, callback).render(true);
}
constructor(venteData, html, callback) {
let options = { classes: ["dialogvente"], width: 400, height: 300, 'z-index': 99999 };
let options = { classes: ["dialogvente"], width: 400, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: "Proposer",
@ -39,26 +37,26 @@ export class DialogItemVente extends Dialog {
this.venteData = venteData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
HtmlUtility._showControlWhen(this.html.find(".quantiteNbLots"), !this.venteData.quantiteIllimite)
this.html.find(".tailleLot").change(event => this.setTailleLot(Number(event.currentTarget.value)));
this.html.find(".quantiteNbLots").change(event => this.setNbLots(Number(event.currentTarget.value)));
this.html.find(".quantiteIllimite").change(event => this.setQuantiteIllimite(event.currentTarget.checked));
this.html.find(".prixLot").change(event => this.setPrixLot(Number(event.currentTarget.value)));
}
async onProposer(it) {
await $(".tailleLot").change();
await $(".quantiteNbLots").change();
await $(".quantiteIllimite").change();
await $(".prixLot").change();
await this.html.find(".tailleLot").change();
await this.html.find(".quantiteNbLots").change();
await this.html.find(".quantiteIllimite").change();
await this.html.find(".prixLot").change();
this.callback(this.venteData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen($(".quantiteNbLots"), !this.venteData.quantiteIllimite)
html.find(".tailleLot").change(event => this.setTailleLot(Number(event.currentTarget.value)));
html.find(".quantiteNbLots").change(event => this.setNbLots(Number(event.currentTarget.value)));
html.find(".quantiteIllimite").change(event => this.setQuantiteIllimite(event.currentTarget.checked));
html.find(".prixLot").change(event => this.setPrixLot(Number(event.currentTarget.value)));
}
setPrixLot(prixLot) {
this.venteData.prixLot = prixLot;
}
@ -67,15 +65,15 @@ export class DialogItemVente extends Dialog {
// recalculer le prix du lot
if (tailleLot != this.venteData.tailleLot) {
this.venteData.prixLot = (tailleLot * this.venteData.prixOrigine).toFixed(2);
$(".prixLot").val(this.venteData.prixLot);
this.html.find(".prixLot").val(this.venteData.prixLot);
}
this.venteData.tailleLot = tailleLot;
if (this.venteData.isOwned) {
// recalculer le nombre de lots max
this.venteData.quantiteMaxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
this.venteData.quantiteNbLots = Math.min(this.venteData.quantiteMaxLots, this.venteData.quantiteNbLots);
$(".quantiteNbLots").val(this.venteData.quantiteNbLots);
$(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
}
}
@ -84,12 +82,12 @@ export class DialogItemVente extends Dialog {
nbLots = Math.max(0, Math.min(nbLots, this.venteData.quantiteMaxLots));
}
this.venteData.quantiteNbLots = nbLots;
$(".quantiteNbLots").val(this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
}
setQuantiteIllimite(checked) {
this.venteData.quantiteIllimite = checked;
$(".label-quantiteIllimite").text(this.venteData.quantiteIllimite ? "Illimités" : "disponibles");
HtmlUtility._showControlWhen($(".quantiteNbLots"), !this.venteData.quantiteIllimite)
this.html.find(".label-quantiteIllimite").text(this.venteData.quantiteIllimite ? "Illimités" : "disponibles");
HtmlUtility._showControlWhen(this.html.find(".quantiteNbLots"), !this.venteData.quantiteIllimite)
}
}

View File

@ -3,13 +3,13 @@ import { Misc } from "./misc.js";
export class DialogRepos extends Dialog {
static async create(actor) {
let actorData = Misc.data(actor)
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-repos.html", actorData);
new DialogRepos(html, actor).render(true);
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-repos.html", actor);
const dialog = new DialogRepos(html, actor);
dialog.render(true);
}
constructor(html, actor) {
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 500, height: 400, 'z-index': 99999 };
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 400, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: "Se reposer",
content: html,
@ -21,13 +21,18 @@ export class DialogRepos extends Dialog {
super(conf, options);
this.actor = actor;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
}
/* -------------------------------------------- */
async repos() {
await $("[name='nb-heures']").change();
await $("[name='nb-jours']").change();
const selection = await $("[name='repos']:checked").val();
const nbHeures = Number.parseInt(await $("[name='nb-heures']").val());
const nbJours = Number.parseInt(await $("[name='nb-jours']").val());
await this.html.find("[name='nb-heures']").change();
await this.html.find("[name='nb-jours']").change();
const selection = await this.html.find("[name='repos']:checked").val();
const nbHeures = Number.parseInt(await this.html.find("[name='nb-heures']").val());
const nbJours = Number.parseInt(await this.html.find("[name='nb-jours']").val());
switch (selection) {
case "sieste": {
await this.actor.dormir(nbHeures);
@ -35,7 +40,7 @@ export class DialogRepos extends Dialog {
}
case "nuit": {
let heuresDormies = await this.actor.dormir(nbHeures);
if (heuresDormies == nbHeures){
if (heuresDormies == nbHeures) {
await this.actor.dormirChateauDormant();
}
return;
@ -49,8 +54,4 @@ export class DialogRepos extends Dialog {
}
}
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
}
}

View File

@ -0,0 +1,37 @@
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);
}
}
}

View File

@ -3,31 +3,24 @@ import { Misc } from "./misc.js";
export class DialogSplitItem extends Dialog {
static async create(item, callback) {
const itemData = Misc.data(item);
const splitData = {
item: itemData,
choix: { quantite: 1, max: itemData.data.quantite - 1 }
item: item,
choix: { quantite: 1, max: item.system.quantite - 1 }
};
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-split.html`, splitData);
return new DialogSplitItem(item, splitData, html, callback)
}
constructor(item, splitData, html, callback) {
let options = { classes: ["dialogsplit"], width: 300, height: 160, 'z-index': 99999 };
let options = { classes: ["dialogsplit"], width: 300, height: 'fit-content', 'z-index': 99999 };
let conf = {
title: "Séparer en deux",
content: html,
default: "separer",
buttons: {
"separer": {
label: "Séparer", callback: it => {
this.onSplit();
}
}
"separer": { label: "Séparer", callback: it => this.onSplit() }
}
};
super(conf, options);
this.callback = callback;
@ -35,18 +28,18 @@ export class DialogSplitItem extends Dialog {
this.splitData = splitData;
}
async onSplit(){
await $(".choix-quantite").change();
this.callback(this.item, this.splitData.choix.quantite);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find(".choix-quantite").change(event => {
this.html = html;
this.html.find(".choix-quantite").change(event => {
this.splitData.choix.quantite = Number(event.currentTarget.value);
});
}
/* -------------------------------------------- */
async onSplit() {
await this.html.find(".choix-quantite").change();
this.callback(this.item, this.splitData.choix.quantite);
}
}

View File

@ -1,18 +1,18 @@
import { Misc } from "./misc.js";
export class DialogStress extends Dialog {
static async distribuerStress() {
let dialogData = {
const dialogData = {
motif: "Motif",
stress: 10,
immediat: false,
actors: game.actors.filter(actor => actor.hasPlayerOwner && actor.isPersonnage())
.map(actor => {
let actorData = duplicate(Misc.data(actor));
actorData.selected = actor.hasPlayerOwner;
return actorData;
.map(actor => ({
id: actor.id,
name: actor.name,
selected: true
})
)
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-stress.html", dialogData);
@ -21,52 +21,43 @@ export class DialogStress extends Dialog {
}
constructor(dialogData, html) {
let options = { classes: ["DialogStress"], width: 400, height: 320, 'z-index': 99999 };
let conf = {
const options = { classes: ["DialogStress"],
width: 400,
height: 'fit-content',
'z-index': 99999
};
const conf = {
title: "Donner du stress",
content: html,
buttons: {
"Stress": { label: "Stress !", callback: it => { this._onStress(); } }
stress: { label: "Stress !", callback: it => { this.onStress(); } }
}
};
super(conf, options);
this.dialogData = dialogData;
}
async _onStress() {
this.validerStress();
const compteur = this.dialogData.immediat ? 'experience' : 'stress';
const stress = this.dialogData.stress;
const motif = this.dialogData.motif;
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("input.select-actor").change((event) => this.onSelectActor(event));
}
async onStress() {
const motif = this.html.find("form.rdddialogstress input[name='motif']").val();
const stress = Number(this.html.find("form.rdddialogstress input[name='stress']").val());
const compteur = (this.html.find("form.rdddialogstress input[name='immediat']").prop("checked")) ? 'experience' : 'stress';
this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it._id))
.map(it => game.actors.get(it.id))
.forEach(actor => actor.distribuerStress(compteur, stress, motif));
}
validerStress() {
this.dialogData.motif = $("form.rdddialogstress input[name='motif']").val();
this.dialogData.stress = $("form.rdddialogstress input[name='stress']").val();
this.dialogData.immediat = $("form.rdddialogstress input[name='immediat']").prop("checked");;
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find(".select-actor").change((event) => this.onSelectActor(event));
}
async onSelectActor(event) {
event.preventDefault();
const options = event.currentTarget.options;
for (var i = 0; i < options.length; i++) { // looping over the options
const actorId = options[i].attributes["data-actor-id"].value;
const actor = this.dialogData.actors.find(it => it._id == actorId);
const actorId = this.html.find(event.currentTarget)?.data("actor-id");
const actor = this.dialogData.actors.find(it => it.id == actorId);
if (actor) {
actor.selected = options[i].selected;
actor.selected = event.currentTarget.checked;
}
};
}
}

View File

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

137
module/effets-rencontres.js Normal file
View File

@ -0,0 +1,137 @@
import { ChatUtility } from "./chat-utility.js";
import { Poetique } from "./poetique.js";
import { RdDDice } from "./rdd-dice.js";
import { TMRUtility } from "./tmr-utility.js";
export class EffetsRencontre {
static messager = async (dialog, context) => {
dialog.setRencontreState('messager', TMRUtility.getTMRPortee(context.tmr.coord, context.rencontre.system.force));
}
static passeur = async (dialog, context) => {
dialog.setRencontreState('passeur', TMRUtility.getTMRPortee(context.tmr.coord, context.rencontre.system.force));
}
static teleportation_typecase = async (dialog, context) => {
dialog.setRencontreState('changeur', TMRUtility.getCasesType(context.tmr.type));
}
static rencontre_persistante = async (dialog, context) => {
dialog.setRencontreState('persistant', []);
}
static reve_plus_force = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, context.rencontre.system.force) }
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_1 = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, -1) }
static $reve_plus = async (actor, valeur) => { await actor.reveActuelIncDec(valeur) }
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_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_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) }
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_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 fatigue_plus_1 = async (dialog, context) => { await EffetsRencontre.$fatigue_plus(context.actor, 1) }
static fatigue_plus_force = async (dialog, context) => { await EffetsRencontre.$fatigue_plus(context.actor, context.rencontre.system.force) }
static fatigue_moins_1 = async (dialog, context) => { await EffetsRencontre.$fatigue_plus(context.actor, -1) }
static fatigue_moins_force = async (dialog, context) => { await EffetsRencontre.$fatigue_plus(context.actor, -context.rencontre.system.force) }
static $fatigue_plus = async (actor, valeur) => { await actor.santeIncDec("fatigue", valeur) }
static perte_chance = async (dialog, context) => {
const perte = context.rolled.isETotal ? context.rencontre.system.force : 1;
await context.actor.chanceActuelleIncDec("fatigue", -perte);
}
static xp_sort_force = async (dialog, context) => {
let competence = context.competence;
if (competence) {
const xpSort = Misc.toInt(competence.system.xp_sort) + context.rencontre.system.force;
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': xpSort }]);
await this.updateExperienceLog("XP Sort", xpSort, `Rencontre d'un ${context.rencontre.name} en TMR`);
}
}
static stress_plus_1 = async (dialog, context) => {
await context.actor.addCompteurValue('stress', 1, `Rencontre d'un ${context.rencontre.name} en TMR`);
}
static reinsertion = async (dialog, context) => {
await EffetsRencontre.$reinsertion(dialog, context.actor, it => true)
}
static teleportation_aleatoire_typecase = async (dialog, context) => {
await EffetsRencontre.$reinsertion(dialog, context.actor, it => it.type == context.tmr.type && it.coord != context.tmr.coord)
}
static demireve_rompu = async (dialog, context) => {
dialog.close()
}
static sort_aleatoire = async (dialog, context) => {
context.sortReserve = await RdDDice.rollOneOf(context.actor.itemTypes['sortreserve']);
if (context.sortReserve) {
context.newTMR = TMRUtility.getTMR(context.sortReserve.system.coord);
await dialog.positionnerDemiReve(context.newTMR.coord);
await dialog.processSortReserve(context.sortReserve);
dialog.close();
}
else {
await EffetsRencontre.$reinsertion(dialog, context.actor, it => true);
}
}
static deplacement_aleatoire = async (dialog, context) => {
const oldCoord = context.actor.system.reve.tmrpos.coord;
const newTmr = await TMRUtility.deplaceTMRAleatoire(context.actor, oldCoord);
await dialog.positionnerDemiReve(newTmr.coord)
}
static rdd_part_tete = async (dialog, context) => {
mergeObject(context, {
tete: context.rolled.isPart,
poesie: await Poetique.getExtrait()
})
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(context.actor.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, context)
});
}
static rdd_echec_queue = async (dialog, context) => {
mergeObject(context, {
queues: [await context.actor.ajouterQueue()],
poesie: await Poetique.getExtrait()
})
if (context.rolled.isETotal) {
context.queues.push(await context.actor.ajouterQueue());
}
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, context)
});
}
static experience_particuliere = async (dialog, context) => {
await context.actor.appliquerAjoutExperience(context)
}
static regain_seuil = async (dialog, context) => {
await context.actor.regainPointDeSeuil()
}
static async $reinsertion(dialog, actor, filter) {
const newTMR = await TMRUtility.getTMRAleatoire(filter);
await actor.forcerPositionTMRInconnue(newTMR);
await dialog.positionnerDemiReve(newTMR.coord);
}
}

223
module/environnement.js Normal file
View File

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

View File

@ -19,10 +19,14 @@ export class Grammar {
return word.match(/^[aeiouy]/i)
}
static equalsInsensitive(a, b) {
return Grammar.toLowerCaseNoAccent(a) == Grammar.toLowerCaseNoAccent(b)
}
static includesLowerCaseNoAccent(value, content) {
return Grammar.toLowerCaseNoAccent(value).includes(Grammar.toLowerCaseNoAccent(content));
}
/* -------------------------------------------- */
static toLowerCaseNoAccent(words) {
return words?.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") ?? words;

View File

@ -1,9 +0,0 @@
/* -------------------------------------------- */
import { RdDUtility } from "./rdd-utility.js";
/* -------------------------------------------- */
// Activate chat listeners defined
// Hooks.on('renderChatLog', (log, html, data) => {
// RdDUtility.chatListeners(html);
// });

View File

@ -1,10 +1,10 @@
export class HtmlUtility{
static _showControlWhen(control, condition) {
static _showControlWhen(jQuerySelector, condition) {
if (condition) {
control.show();
jQuerySelector.show();
}
else {
control.hide();
jQuerySelector.hide();
}
}
}

View File

@ -19,36 +19,34 @@ const nomCategorieParade = {
/* -------------------------------------------- */
export class RdDItemArme extends Item {
static isArme(itemData) {
itemData = Misc.data(itemData);
return (itemData.type == 'competencecreature' && itemData.data.iscombat) || itemData.type == 'arme';
static isArme(item) {
return (item.type == 'competencecreature' && item.system.iscombat) || item.type == 'arme';
}
/* -------------------------------------------- */
static getArmeData(armeData) {
armeData = Misc.data(armeData);
switch (armeData ? armeData.type : '') {
case 'arme': return armeData;
static getArme(arme) {
switch (arme ? arme.type : '') {
case 'arme': return arme;
case 'competencecreature':
return RdDItemCompetenceCreature.toActionArme(armeData);
return RdDItemCompetenceCreature.armeNaturelle(arme);
}
return RdDItemArme.mainsNues();
}
static computeNiveauArmes(armes, competences) {
for (const arme of armes) {
arme.data.niveau = RdDItemArme.niveauCompetenceArme(arme, competences);
arme.system.niveau = RdDItemArme.niveauCompetenceArme(arme, competences);
}
}
static niveauCompetenceArme(arme, competences) {
const compArme = competences.find(it => it.name == arme.data.competence);
return compArme?.data.niveau ?? -8;
const compArme = competences.find(it => it.name == arme.system.competence);
return compArme?.system.niveau ?? -8;
}
/* -------------------------------------------- */
static getNomCategorieParade(arme) {
const categorie = arme?.data ? RdDItemArme.getCategorieParade(arme) : arme;
const categorie = arme?.system ? RdDItemArme.getCategorieParade(arme) : arme;
return nomCategorieParade[categorie];
}
@ -66,21 +64,20 @@ export class RdDItemArme extends Item {
/* -------------------------------------------- */
static getCategorieParade(armeData) {
armeData = Misc.data(armeData);
if (armeData.data.categorie_parade) {
return armeData.data.categorie_parade;
if (armeData.system.categorie_parade) {
return armeData.system.categorie_parade;
}
// pour compatibilité avec des personnages existants
if (armeData.type == 'competencecreature' || armeData.data.categorie == 'creature') {
return armeData.data.categorie_parade || (armeData.data.isparade ? 'armes-naturelles' : '');
if (armeData.type == 'competencecreature' || armeData.system.categorie == 'creature') {
return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '');
}
if (!armeData.type.match(/arme|competencecreature/)) {
return '';
}
if (armeData.data.competence == undefined) {
if (armeData.system.competence == undefined) {
return 'competencecreature';
}
let compname = armeData.data.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('hache')) return 'haches';
@ -137,22 +134,21 @@ export class RdDItemArme extends Item {
/* -------------------------------------------- */
static armeUneOuDeuxMains(armeData, aUneMain) {
armeData = Misc.data(armeData);
if (armeData && !armeData.data.cac) {
armeData.data.unemain = armeData.data.unemain || !armeData.data.deuxmains;
const uneOuDeuxMains = armeData.data.unemain && armeData.data.deuxmains;
const containsSlash = !Number.isInteger(armeData.data.dommages) && armeData.data.dommages.includes("/");
if (armeData && !armeData.system.cac) {
armeData.system.unemain = armeData.system.unemain || !armeData.system.deuxmains;
const uneOuDeuxMains = armeData.system.unemain && armeData.system.deuxmains;
const containsSlash = !Number.isInteger(armeData.system.dommages) && armeData.system.dommages.includes("/");
if (containsSlash) { // Sanity check
armeData = duplicate(armeData);
const tableauDegats = armeData.data.dommages.split("/");
const tableauDegats = armeData.system.dommages.split("/");
if (aUneMain)
armeData.data.dommagesReels = Number(tableauDegats[0]);
armeData.system.dommagesReels = Number(tableauDegats[0]);
else // 2 mains
armeData.data.dommagesReels = Number(tableauDegats[1]);
armeData.system.dommagesReels = Number(tableauDegats[1]);
}
else {
armeData.data.dommagesReels = Number(armeData.data.dommages);
armeData.system.dommagesReels = Number(armeData.system.dommages);
}
if (uneOuDeuxMains != containsSlash) {
@ -162,51 +158,50 @@ export class RdDItemArme extends Item {
return armeData;
}
static isArmeUtilisable(itemData) {
itemData = Misc.data(itemData);
return itemData.type == 'arme' && itemData.data.equipe && (itemData.data.resistance > 0 || itemData.data.portee_courte > 0);
static isArmeUtilisable(arme) {
return arme.type == 'arme' && arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0);
}
static ajoutCorpsACorps(armes, competences, carac) {
let corpsACorps = competences.find(it => it.name == 'Corps à corps') ?? { data: { niveau: -6 } };
let init = RdDCombatManager.calculInitiative(corpsACorps.data.niveau, carac['melee'].value);
armes.push(RdDItemArme.mainsNues({ niveau: corpsACorps.data.niveau, initiative: init }));
//armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.data.niveau, initiative: init }));
let corpsACorps = competences.find(it => it.name == 'Corps à corps') ?? { system: { niveau: -6 } };
let init = RdDCombatManager.calculInitiative(corpsACorps.system.niveau, carac['melee'].value);
armes.push(RdDItemArme.mainsNues({ niveau: corpsACorps.system.niveau, initiative: init }));
//armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init }));
}
static corpsACorps(actorData) {
static corpsACorps(mainsNuesActor) {
const corpsACorps = {
name: 'Corps à corps',
img: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp',
data: {
system: {
equipe: true,
rapide: true,
force: 0,
dommages: 0,
dommages: "0",
dommagesReels: 0,
mortalite: 'non-mortel',
competence: 'Corps à corps',
categorie_parade: 'sans-armes'
}
};
mergeObject(corpsACorps.data, actorData ?? {}, { overwrite: false });
mergeObject(corpsACorps.system, mainsNuesActor ?? {}, { overwrite: false });
return corpsACorps;
}
static mainsNues(actorData) {
const mainsNues = RdDItemArme.corpsACorps(actorData);
mainsNues.name = 'Mains nues';
mainsNues.data.cac = 'pugilat';
mainsNues.data.baseInit = 4;
static mainsNues(mainsNuesActor) {
const mainsNues = RdDItemArme.corpsACorps(mainsNuesActor)
mainsNues.name = 'Mains nues'
mainsNues.system.cac = 'pugilat'
mainsNues.system.baseInit = 4
return mainsNues;
}
static empoignade(actorData) {
const empoignade = RdDItemArme.corpsACorps(actorData);
empoignade.name = 'Empoignade';
empoignade.data.cac = 'empoignade';
empoignade.data.baseInit = 3;
empoignade.data.mortalite = 'empoignade';
return empoignade;
static empoignade(mainsNuesActor) {
const empoignade = RdDItemArme.corpsACorps(mainsNuesActor)
empoignade.name = 'Empoignade'
empoignade.system.cac = 'empoignade'
empoignade.system.baseInit = 3
empoignade.system.mortalite = 'empoignade'
return empoignade
}
}

View File

@ -8,18 +8,18 @@ const xp_par_niveau = [5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20,
const niveau_max = xp_par_niveau.length - 10;
/* -------------------------------------------- */
const limitesArchetypes = [
{ "niveau": 0, "nombreMax": 100, "nombre": 0 },
{ "niveau": 1, "nombreMax": 10, "nombre": 0 },
{ "niveau": 2, "nombreMax": 9, "nombre": 0 },
{ "niveau": 3, "nombreMax": 8, "nombre": 0 },
{ "niveau": 4, "nombreMax": 7, "nombre": 0 },
{ "niveau": 5, "nombreMax": 6, "nombre": 0 },
{ "niveau": 6, "nombreMax": 5, "nombre": 0 },
{ "niveau": 7, "nombreMax": 4, "nombre": 0 },
{ "niveau": 8, "nombreMax": 3, "nombre": 0 },
{ "niveau": 9, "nombreMax": 2, "nombre": 0 },
{ "niveau": 10, "nombreMax": 1, "nombre": 0 },
{ "niveau": 11, "nombreMax": 1, "nombre": 0 }
{ "niveau": 0, "nombreMax": 100, "reste": 100 },
{ "niveau": 1, "nombreMax": 10, "reste": 10 },
{ "niveau": 2, "nombreMax": 9, "reste": 9 },
{ "niveau": 3, "nombreMax": 8, "reste": 8 },
{ "niveau": 4, "nombreMax": 7, "reste": 7 },
{ "niveau": 5, "nombreMax": 6, "reste": 6 },
{ "niveau": 6, "nombreMax": 5, "reste": 5 },
{ "niveau": 7, "nombreMax": 4, "reste": 4 },
{ "niveau": 8, "nombreMax": 3, "reste": 3 },
{ "niveau": 9, "nombreMax": 2, "reste": 2 },
{ "niveau": 10, "nombreMax": 1, "reste": 1 },
{ "niveau": 11, "nombreMax": 1, "reste": 1 }
];
/* -------------------------------------------- */
@ -34,13 +34,6 @@ const categorieCompetences = {
"lancer": { base: -8, label: "Lancer" }
}
const compendiumCompetences = {
"personnage": "foundryvtt-reve-de-dragon.competences",
"creature": "foundryvtt-reve-de-dragon.competences-creatures",
"entite": "foundryvtt-reve-de-dragon.competences-entites"
};
function _buildCumulXP() {
let cumulXP = { "-11": 0 };
let cumul = 0;
@ -55,12 +48,6 @@ function _buildCumulXP() {
const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static actorCompendium(actorType) {
return compendiumCompetences[actorType];
}
/* -------------------------------------------- */
static getCategorieCompetences() {
return categorieCompetences;
@ -76,40 +63,49 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static getCategorie(competence) {
return Misc.data(competence)?.data.categorie;
return competence?.system.categorie;
}
static isDraconic(competence) {
return Misc.data(competence)?.data.categorie == 'draconic';
return competence?.system.categorie == 'draconic';
}
/* -------------------------------------------- */
static getVoieDraconic(competences, voie) {
return RdDItemCompetence.findCompetence(competences.filter(it => RdDItemCompetence.isDraconic(it)), voie);
return RdDItemCompetence.findFirstItem(competences, voie, {
preFilter: it => it.isCompetence() && RdDItemCompetence.isDraconic(it),
description: 'Draconic',
});
}
/* -------------------------------------------- */
static isCompetenceArme(competence) {
switch (Misc.templateData(competence).categorie) {
if (competence.isCompetence()) {
switch (competence.system.categorie) {
case 'melee':
return Misc.data(competence).name != 'Esquive';
return !Grammar.toLowerCaseNoAccent(competence.name).includes('esquive');
case 'tir':
case 'lancer':
return true;
}
}
return false;
}
/* -------------------------------------------- */
static isArmeUneMain(competence) {
return Misc.data(competence)?.name.toLowerCase().includes("1 main");
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("1 main");
}
static isArme2Main(competence) {
return Misc.data(competence)?.name.toLowerCase().includes("2 main");
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("2 main");
}
static isThanatos(competence) {
return competence.isCompetencePersonnage() && Grammar.toLowerCaseNoAccent(competence.name).includes('thanatos');
}
/* -------------------------------------------- */
static isMalusEncombrementTotal(competence) {
return Misc.data(competence)?.name.toLowerCase().match(/(natation|acrobatie)/);
return competence?.name.toLowerCase().match(/(natation|acrobatie)/) || 0;
}
/* -------------------------------------------- */
@ -133,11 +129,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeXP(competence) {
const itemData = Misc.data(competence);
const factor = itemData.name.includes('Thanatos') ? 2 : 1; // Thanatos compte double !
const xpNiveau = RdDItemCompetence.computeDeltaXP(itemData.data.base, itemData.data.niveau ?? itemData.data.base);
const xp = itemData.data.xp ?? 0;
const xpSort = itemData.data.xp_sort ?? 0;
const factor = RdDItemCompetence.isThanatos(competence) ? 2 : 1; // Thanatos compte double !
const xpNiveau = RdDItemCompetence.computeDeltaXP(competence.system.base, competence.system.niveau ?? competence.system.base);
const xp = competence.system.xp ?? 0;
const xpSort = competence.system.xp_sort ?? 0;
return factor * (xpNiveau + xp) + xpSort;
}
@ -146,7 +141,7 @@ export class RdDItemCompetence extends Item {
return competenceTroncs.map(
list => list.map(name => RdDItemCompetence.findCompetence(competences, name))
// calcul du coût xp jusqu'au niveau 0 maximum
.map(it => RdDItemCompetence.computeDeltaXP(it?.data.base ?? -11, Math.min(it?.data.niveau ?? -11, 0)))
.map(it => RdDItemCompetence.computeDeltaXP(it?.system.base ?? -11, Math.min(it?.system.niveau ?? -11, 0)))
.sort(Misc.ascending())
.splice(0, list.length - 1) // prendre toutes les valeurs sauf l'une des plus élevées
.reduce(Misc.sum(), 0)
@ -162,11 +157,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeCompetenceXPCost(competence) {
const compData = Misc.data(competence);
let xp = RdDItemCompetence.getDeltaXp(compData.data.base, compData.data.niveau ?? compData.data.base);
xp += compData.data.xp ?? 0;
let xp = RdDItemCompetence.getDeltaXp(competence.system.base, competence.system.niveau ?? competence.system.base);
xp += competence.system.xp ?? 0;
if (compData.name.includes('Thanatos')) xp *= 2; /// Thanatos compte double !
xp += compData.data.xp_sort ?? 0;
xp += competence.system.xp_sort ?? 0;
return xp;
}
@ -175,61 +169,75 @@ export class RdDItemCompetence extends Item {
let economie = 0;
for (let troncList of competenceTroncs) {
let list = troncList.map(name => RdDItemCompetence.findCompetence(competences, name))
.sort(Misc.descending(c => Misc.templateData(c).niveau)); // tri du plus haut au plus bas
.sort(Misc.descending(c => this.system.niveau)); // tri du plus haut au plus bas
list.splice(0, 1); // ignorer la plus élevée
list.map(c => Misc.templateData(c)).forEach(tplData => {
economie += RdDItemCompetence.getDeltaXp(tplData.base, Math.min(tplData.niveau, 0));
list.map(c => c).forEach(c => {
economie += RdDItemCompetence.getDeltaXp(c.system.base, Math.min(c.system.niveau, 0))
});
}
return economie;
}
/* -------------------------------------------- */
static levelUp(itemData, stressTransforme) {
itemData.data.xpNext = RdDItemCompetence.getCompetenceNextXp(itemData.data.niveau);
const xpManquant = itemData.data.xpNext - itemData.data.xp;
itemData.data.isLevelUp = xpManquant <= 0;
itemData.data.isStressLevelUp = (xpManquant > 0 && stressTransforme >= xpManquant && itemData.data.niveau < itemData.data.niveau_archetype);
itemData.data.stressXpMax = 0;
if (xpManquant > 0 && stressTransforme > 0 && itemData.data.niveau < itemData.data.niveau_archetype) {
itemData.data.stressXpMax = Math.min(xpManquant , stressTransforme);
static levelUp(item, stressTransforme) {
item.system.xpNext = RdDItemCompetence.getCompetenceNextXp(item.system.niveau);
const xpManquant = item.system.xpNext - item.system.xp;
item.system.isLevelUp = xpManquant <= 0;
item.system.isStressLevelUp = (xpManquant > 0 && stressTransforme >= xpManquant && item.system.niveau < item.system.niveau_archetype);
item.system.stressXpMax = 0;
if (xpManquant > 0 && stressTransforme > 0 && item.system.niveau < item.system.niveau_archetype) {
item.system.stressXpMax = Math.min(xpManquant, stressTransforme);
}
}
/* -------------------------------------------- */
static isVisible(itemData) {
return Number(itemData.data.niveau) != RdDItemCompetence.getNiveauBase(itemData.data.categorie);
static isVisible(item) {
return Number(item.system.niveau) != RdDItemCompetence.getNiveauBase(item.system.categorie);
}
static nomContientTexte(itemData, texte) {
return Grammar.toLowerCaseNoAccent(itemData.name).includes(Grammar.toLowerCaseNoAccent(texte))
static nomContientTexte(item, texte) {
return Grammar.toLowerCaseNoAccent(item.name).includes(Grammar.toLowerCaseNoAccent(texte))
}
/* -------------------------------------------- */
static isNiveauBase(itemData) {
return Number(itemData.data.niveau) == RdDItemCompetence.getNiveauBase(itemData.data.categorie);
static isNiveauBase(item) {
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie);
}
/* -------------------------------------------- */
static findCompetence(list, idOrName, options = {}) {
if (idOrName == undefined) {
return undefined;
if (idOrName == undefined || idOrName == "") {
return RdDItemCompetence.sansCompetence();
}
options = mergeObject(options, {
preFilter: it => RdDItemCompetence.isCompetence(it),
description: 'compétence',
});
return list.find(it => it.id == idOrName && RdDItemCompetence.isCompetence(it))
?? Misc.findFirstLike(idOrName, list, options);
options = mergeObject(options, { preFilter: it => it.isCompetence(), description: 'compétence' }, { overwrite: false });
return RdDItemCompetence.findFirstItem(list, idOrName, options);
}
/* -------------------------------------------- */
static findCompetences(list, name) {
return Misc.findAllLike(name, list, { filter: it => RdDItemCompetence.isCompetence(it), description: 'compétence' });
return Misc.findAllLike(name, list, { filter: it => it.isCompetence(), description: 'compétence' });
}
static isCompetence(item) {
return item.type == 'competence' || item.type == 'competencecreature';
static sansCompetence() {
return {
name: "Sans compétence",
type: "competence",
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp",
system: {
niveau: 0,
default_diffLibre: 0,
base: 0,
categorie: "Aucune",
description: "",
descriptionmj: "",
defaut_carac: "",
}
};
}
static findFirstItem(list, idOrName, options) {
return list.find(it => it.id == idOrName && options.preFilter(it))
?? Misc.findFirstLike(idOrName, list, options);
}
/* -------------------------------------------- */
@ -259,18 +267,47 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const archetype = RdDItemCompetence.getLimitesArchetypes();
competences.map(it => Math.max(0, Misc.templateData(it).niveau_archetype))
.forEach(niveau => {
archetype[niveau] = archetype[niveau] ?? { "niveau": niveau, "nombreMax": 0, "nombre": 0 };
archetype[niveau].nombre = (archetype[niveau]?.nombre ?? 0) + 1;
const computed = duplicate(limitesArchetypes);
competences.map(it => Math.max(0, it.system.niveau_archetype))
.filter(n => n > 0)
.forEach(n => {
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0 };
computed[n].reste = computed[n].reste - 1;
});
return archetype;
return computed.filter(it => it.reste > 0 && it.niveau > 0);
}
/* -------------------------------------------- */
static getLimitesArchetypes() {
return duplicate(limitesArchetypes);
static triVisible(competences) {
return competences.filter(it => it.system.isVisible)
.sort((a, b) => RdDItemCompetence.compare(a, b))
}
static $positionTri(comp) {
if (comp.name.startsWith("Survie")) {
if (comp.name.includes("Cité")) return 0;
if (comp.name.includes("Extérieur")) return 1;
return 2;
}
if (comp.system.categorie.startsWith("melee")) {
if (comp.name.includes("Corps")) return 0;
if (comp.name.includes("Dague")) return 1;
if (comp.name.includes("Esquive")) return 2;
return 3;
}
if (comp.system.categorie.startsWith("draconic")) {
if (comp.name.includes("Oniros")) return 0;
if (comp.name.includes("Hypnos")) return 1;
if (comp.name.includes("Narcos")) return 2;
if (comp.name.includes("Thanatos")) return 3;
return 4;
}
return 0;
}
static compare(a, b) {
const diff = RdDItemCompetence.$positionTri(a) - RdDItemCompetence.$positionTri(b);
return diff ? diff : a.name.localeCompare(b.name);
}
}

View File

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

View File

@ -0,0 +1,60 @@
import { RdDItemSheet } from "./item-sheet.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { RdDUtility } from "./rdd-utility.js";
export class RdDConteneurItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "conteneur" };
async getData() {
const formData = await super.getData();
if (this.actor) {
this.prepareConteneurData(formData);
}
return formData;
}
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
}
/* -------------------------------------------- */
prepareConteneurData(formData) {
RdDUtility.filterEquipementParType(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
}
async _onDragStart(event) {
console.log("_onDragStart", event);
if (event.target.classList.contains("entity-link")) return;
const itemId = event.srcElement?.attributes["data-item-id"].value;
const item = this.actor.items.get(itemId);
// Create drag data
const dragData = {
actorId: this.actor.id,
type: "Item",
data: item.system
};
event.dataTransfer.setData("text/plain", JSON.stringify(dragData));
}
async _onDropItem(event, dragData) {
if (this.actor) {
const dropParams = RdDSheetUtility.prepareItemDropParameters(this.item.id, this.actor, dragData, this.objetVersConteneur);
await this.actor.processDropItem(dropParams);
await this.render(true);
}
}
}

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ export class RdDItemMeditation {
static calculDifficulte(rollData) {
if (rollData.meditation) {
// Malus permanent éventuel
let diff = rollData.meditation.data.malus ?? 0;
let diff = rollData.meditation.system.malus ?? 0;
if (!rollData.conditionMeditation.isHeure) diff -= 2;
if (!rollData.conditionMeditation.isVeture) diff -= 2;
if (!rollData.conditionMeditation.isComportement) diff -= 2;

View File

@ -1,61 +1,110 @@
import { Misc } from "./misc.js";
import { LOG_HEAD } from "./constants.js";
const monnaiesData = [
{
const MONNAIE_ETAIN = {
name: "Etain (1 denier)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp",
data: { quantite: 0, valeur_deniers: 1, encombrement: 0.001, description: "" }
},
{
system: { quantite: 0, cout: 0.01, encombrement: 0.001, description: "" }
};
const MONNAIE_BRONZE = {
name: "Bronze (10 deniers)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp",
data: { quantite: 0, valeur_deniers: 10, encombrement: 0.002, description: "" }
},
{
system: { quantite: 0, cout: 0.10, encombrement: 0.002, description: "" }
};
const MONNAIE_ARGENT = {
name: "Argent (1 sol)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp",
data: { quantite: 0, valeur_deniers: 100, encombrement: 0.003, description: "" }
},
{
system: { quantite: 0, cout: 1, encombrement: 0.003, description: "" }
};
const MONNAIE_OR = {
name: "Or (10 sols)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp",
data: { quantite: 0, valeur_deniers: 1000, encombrement: 0.004, description: "" }
}
]
system: { quantite: 0, cout: 10, encombrement: 0.004, description: "" }
};
const MONNAIES_STANDARD = [MONNAIE_ETAIN, MONNAIE_BRONZE, MONNAIE_ARGENT, MONNAIE_OR];
const VALEUR_DENIERS = sols => Math.max(Math.floor((sols ?? 0) * 100), 0);
export class Monnaie {
static isSystemMonnaie(item) {
let present = monnaiesData.find(monnaie => monnaie.data.valeur_deniers == Misc.data(item)?.data?.valeur_deniers);
return present;
static monnaiesStandard() {
return MONNAIES_STANDARD;
}
static monnaiesData() {
return monnaiesData;
static monnaiesManquantes(actor) {
const disponibles = actor.itemTypes['monnaie'];
const manquantes = MONNAIES_STANDARD.filter(standard => !disponibles.find(disponible => Monnaie.deValeur(disponible, standard.system?.cout)));
if (manquantes.length > 0) {
console.error(`${LOG_HEAD} monnaiesManquantes pour ${actor.name}`, manquantes, ' avec monnaies', disponibles, MONNAIES_STANDARD);
}
return manquantes;
}
static filtrerMonnaies(items) {
return items.filter(it => Misc.data(it).type == 'monnaie');
static deValeur(monnaie, valeur) {
return VALEUR_DENIERS(valeur) == VALEUR_DENIERS(monnaie.system.cout)
}
static monnaiesManquantes(items) {
const valeurs = Monnaie.filtrerMonnaies(items)
.map(it => Misc.templateData(it).valeur_deniers);
const manquantes = monnaiesData.filter(monnaie => !valeurs.find(v => v != Misc.templateData(monnaie).valeur_deniers));
//const manquantes = monnaiesData.filter(monnaie => !valeurs.find(v => v != Misc.templateData(monnaie).valeur_deniers) );
//console.log("Valeurs : ", valeurs, manquantes);
return []; //manquantes;
static triValeurEntiere() {
return Misc.ascending(item => VALEUR_DENIERS(item.system.cout))
}
static deValeur(monnaie, v) {
return v != monnaie.data.valeur_deniers;
static async creerMonnaiesStandard(actor) {
await actor.createEmbeddedDocuments('Item', MONNAIES_STANDARD, { renderSheet: false });
}
static arrondiDeniers(sols) {
return sols.toFixed(2);
static async creerMonnaiesDeniers(actor, fortune) {
await actor.createEmbeddedDocuments('Item', [Monnaie.creerDeniers(fortune)], { renderSheet: false });
}
static triValeurDenier() {
return Misc.ascending(item => Misc.data(item).data.valeur_deniers);
static creerDeniers(fortune) {
const deniers = duplicate(MONNAIE_ETAIN);
deniers.system.quantite = fortune;
return deniers;
}
static getFortune(actor) {
if (actor) {
Monnaie.validerMonnaies(actor);
return actor.itemTypes['monnaie']
.map(m => Number(m.system.cout) * Number(m.system.quantite))
.reduce(Misc.sum(), 0);
}
return 0;
}
static async optimiserFortune(actor, fortune) {
let resteEnDeniers = Math.round(fortune * 100);
let monnaies = actor.itemTypes['monnaie'];
let updates = [];
Monnaie.validerMonnaies(actor);
let parValeur = Misc.classifyFirst(monnaies, it => VALEUR_DENIERS(it.system.cout));
for (let valeurDeniers of [1000, 100, 10, 1]) {
const itemPiece = parValeur[valeurDeniers];
if (itemPiece) {
const quantite = Math.floor(resteEnDeniers / valeurDeniers);
if (quantite != itemPiece.system.quantite) {
updates.push({ _id: parValeur[valeurDeniers].id, 'system.quantite': quantite });
}
resteEnDeniers -= quantite * valeurDeniers;
}
}
console.log('Monnaie.optimiserFortune', actor.name, 'total', fortune, 'parValeur', parValeur, 'updates', updates, 'reste', resteEnDeniers);
if (updates.length > 0) {
await actor.updateEmbeddedDocuments('Item', updates);
}
if (resteEnDeniers > 0) {
// créer le reste en deniers fortune en deniers
await Monnaie.creerMonnaiesDeniers(actor, resteEnDeniers);
}
}
static validerMonnaies(actor) {
actor.itemTypes['monnaie'].filter(it => VALEUR_DENIERS(it.system.cout) == 0)
.map(it => `La monnaie ${it.name} de l'acteur ${actor.name} a une valeur de 0!`)
.forEach(message => {
ui.notifications.warn(message);
console.warn(message);
});
}
}

View File

@ -0,0 +1,85 @@
import { RdDRencontre } from "./item-rencontre.js";
import { RdDItemSheet } from "./item-sheet.js";
export class RdDRencontreItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "rencontre" };
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }]
});
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetBody = this.element.find(".sheet-body");
sheetBody.css("height", position.height - sheetHeader[0].clientHeight)
return position;
}
/* -------------------------------------------- */
async getData() {
const formData = duplicate(this.item);
mergeObject(formData, {
title: formData.name,
isGM: game.user.isGM,
owner: this.actor?.isOwner,
isOwned: this.actor ? true : false,
actorId: this.actor?.id,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
effets: {
succes: {
liste: RdDRencontre.getEffetsSucces(),
select: RdDRencontre.mapEffets(this.item.system.succes.effets)
},
echec: {
liste: RdDRencontre.getEffetsEchec(),
select: RdDRencontre.mapEffets(this.item.system.echec.effets)
}
}
});
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
this.html.find("a.effet-add").click(event => this.onAddEffet(event));
this.html.find("a.effet-delete").click(event => this.onDeleteEffet(event));
}
async onAddEffet(event) {
const resultat = this.html.find(event.currentTarget)?.data("effet-resultat");
const keyEffets = `system.${resultat}.effets`;
const code = this.html.find(event.currentTarget)?.data("effet-code");
const liste = RdDRencontre.getListeEffets(this.item, resultat);
liste.push(code);
await this._updateEffetsRencontre(keyEffets, liste);
}
async onDeleteEffet(event) {
const resultat = this.html.find(event.currentTarget)?.data("effet-resultat");
const keyEffets = `system.${resultat}.effets`;
const pos = this.html.find(event.currentTarget)?.data("effet-pos");
const liste = RdDRencontre.getListeEffets(this.item, resultat);
liste.splice(pos, 1);
await this._updateEffetsRencontre(keyEffets, liste);
}
async _updateEffetsRencontre(key, liste) {
const updates = {};
updates[key] = liste;
this.item.update(updates);
}
}

71
module/item-rencontre.js Normal file
View File

@ -0,0 +1,71 @@
import { EffetsRencontre } from "./effets-rencontres.js";
const tableEffets = [
{ code: "messager", resultat: "succes", description: "Envoie un message à (force) cases", method: EffetsRencontre.messager },
{ code: "passeur", resultat: "succes", description: "Déplacer le demi-rêve à (force) cases", method: EffetsRencontre.passeur},
{ code: "reve+f", resultat: "succes", description: "Gain de (force) points de rêve" , method: EffetsRencontre.reve_plus_force},
{ code: "teleport", resultat: "succes", description: "Déplacer le demi-rêve (même type)", method: EffetsRencontre.teleportation_typecase },
{ code: "part+tete", resultat: "succes", description: "Tête de dragon sur réussite particulière", method: EffetsRencontre.rdd_part_tete },
{ code: "part+xp", resultat: "succes", description: "Expérience sur réussite particulière", method: EffetsRencontre.experience_particuliere },
{ code: "seuil", resultat: "succes", description: "Récupération de seuil de rêve", method: EffetsRencontre.regain_seuil },
{ code: "reve-1", resultat: "echec", description: "Perte de 1 point de rêve", method: EffetsRencontre.reve_moins_1 },
{ code: "reve-f", resultat: "echec", description: "Perte de (force) points de rêve", method: EffetsRencontre.reve_moins_force },
{ code: "vie-1", resultat: "echec", description: "Perte de 1 point de vie", method: EffetsRencontre.vie_moins_1 },
{ code: "reinsere", resultat: "echec", description: "Réinsertion aléatoire", method: EffetsRencontre.reinsertion },
{ code: "persistant", resultat: "echec", description: "Bloque le demi-rêve", method: EffetsRencontre.rencontre_persistante },
{ code: "teleport-aleatoire", resultat: "echec", description: "Déplacement aléatoire (même type)", method: EffetsRencontre.teleportation_aleatoire_typecase },
{ code: "aleatoire", resultat: "echec", description: "Déplacement aléatoire", method: EffetsRencontre.deplacement_aleatoire },
{ code: "sort-aleatoire", resultat: "echec", description: "Déclenche un sort en réserve aléatoire", method: EffetsRencontre.sort_aleatoire },
{ code: "rompu", resultat: "echec", description: "Demi-rêve interrompu", method: EffetsRencontre.demireve_rompu },
{ code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon sur échec", method: EffetsRencontre.rdd_echec_queue },
{ code: "reve+1", resultat: "succes", description: "Gain de 1 point de rêve", method: EffetsRencontre.reve_plus_1 },
{ code: "vie-f", resultat: "echec", description: "Perte de (force) points de vie", method: EffetsRencontre.vie_moins_force },
{ code: "moral+1", resultat: "succes", description: "Gain de 1 point de moral", method: EffetsRencontre.moral_plus_1 },
{ code: "moral-1", resultat: "echec", description: "Perte de 1 point de moral", method: EffetsRencontre.moral_moins_1 },
{ code: "xpsort+f", resultat: "succes", description: "Gain de (force) xp sort", method: EffetsRencontre.xp_sort_force },
{ code: "endurance-1", resultat: "echec", description: "Perte de 1 point d'endurance", method: EffetsRencontre.end_moins_1 },
{ code: "endurance-f", resultat: "echec", description: "Perte de (force) points d'endurance", method: EffetsRencontre.end_moins_force },
{ code: "fatigue+1", resultat: "echec", description: "Coup de fatigue de 1 point", method: EffetsRencontre.fatigue_plus_1},
{ code: "fatigue+f", resultat: "echec", description: "Coup de fatigue de 1 (force) points", method: EffetsRencontre.fatigue_plus_force },
{ code: "fatigue-1", resultat: "succes", description: "Récupération de 1 point de fatigue", method: EffetsRencontre.fatigue_moins_1},
{ code: "fatigue-f", resultat: "succes", description: "Récupération de 1 (force) points de fatigue", method: EffetsRencontre.fatigue_moins_force },
{ code: "perte-chance", resultat: "echec", description: "Perte de chance actuelle", method: EffetsRencontre.perte_chance },
{ code: "stress+1", resultat: "succes", description: "Gain de 1 point de stress", method: EffetsRencontre.stress_plus_1 },
// { code: "epart-souffle", resultat: "echec", description: "Souffle de dragon sur échec particulier" },
];
export class RdDRencontre {
static getEffetsSucces() { return RdDRencontre.getEffets("succes"); }
static getEffetsEchec() { return RdDRencontre.getEffets("echec"); }
static getEffets(resultat) {
return tableEffets.filter(e => resultat == e.resultat);
}
static mapEffets(liste) {
return liste.map(it => RdDRencontre.getEffet(it));
}
static getListeEffets(item, reussite) {
if (reussite == 'echec') {
return [...item.system.echec.effets];
}
if (reussite == 'succes') {
return [...item.system.succes.effets];
}
return [];
}
static getEffet(code) {
return tableEffets.find(it => code == it.code)
}
static async appliquer(codes, tmrDialog, rencData) {
for(const effet of RdDRencontre.mapEffets(codes)){
await effet.method(tmrDialog, rencData);
}
}
}

View File

@ -4,35 +4,59 @@ import { RdDAlchimie } from "./rdd-alchimie.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDGemme } from "./rdd-gemme.js";
import { Misc } from "./misc.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { SYSTEM_RDD } from "./constants.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Misc } from "./misc.js";
/**
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
* Extend the basic ItemSheet for RdD specific items
*/
export class RdDItemSheet extends ItemSheet {
static get ITEM_TYPE() {
return undefined
}
static defaultTemplate(type) {
return type ?
`systems/foundryvtt-reve-de-dragon/templates/item-${type}-sheet.html` :
"systems/foundryvtt-reve-de-dragon/templates/item-sheet.html";
}
static register(sheetClass) {
Items.registerSheet(SYSTEM_RDD, sheetClass, {
label: Misc.typeName('Item', sheetClass.ITEM_TYPE),
types: [sheetClass.ITEM_TYPE],
makeDefault: true
})
}
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: [SYSTEM_RDD, "sheet", "item"],
template: "systems/foundryvtt-reve-de-dragon/templates/item-sheet.html",
template: RdDItemSheet.defaultTemplate(RdDItemSheet.ITEM_TYPE),
width: 550,
height: 550
});
}
/* -------------------------------------------- */
get template() {
return RdDItemSheet.defaultTemplate(this.item.type);
}
get title() {
return `${Misc.typeName('Item', this.item.type)}: ${this.item.name}`;
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
// Add "Post to chat" button
// We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry!
if ("cout" in Misc.templateData(this.object) && this.object.isVideOuNonConteneur()) {
if (this.item.isInventaire() && this.item.isVideOuNonConteneur()) {
buttons.unshift({
class: "vendre",
icon: "fas fa-comments-dollar",
@ -60,233 +84,200 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */
async getData() {
const objectData = Misc.data(this.object)
let formData = {
id: this.object.id,
title: objectData.name,
type: objectData.type,
img: objectData.img,
name: objectData.name,
data: objectData.data,
id: this.item.id,
title: this.item.name,
type: this.item.type,
img: this.item.img,
name: this.item.name,
system: this.item.system,
isGM: game.user.isGM,
actorId: this.actor?.id,
owner: this.document.isOwner,
isOwned: this.actor ? true : false,
owner: this.item.isOwner,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
isSoins: false
}
if (this.actor) {
formData.isOwned = true;
if (objectData.type == 'conteneur') {
this.prepareConteneurData(formData);
}
isSoins: false,
description: await TextEditor.enrichHTML(this.item.system.description, { async: true }),
descriptionmj: await TextEditor.enrichHTML(this.item.system.descriptionmj, { async: true }),
isComestible: this.item.isComestible()
}
const competences = await SystemCompendiums.getCompetences(this.actor?.type);
formData.categorieCompetences = RdDItemCompetence.getCategorieCompetences()
if (formData.type == 'tache' || formData.type == 'livre' || formData.type == 'meditation' || formData.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["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve)
formData.competences = await RdDUtility.loadCompendium('foundryvtt-reve-de-dragon.competences')
formData.competences = competences;
}
if (formData.type == 'arme') {
formData.competences = await RdDUtility.loadCompendium('foundryvtt-reve-de-dragon.competences', it => RdDItemCompetence.isCompetenceArme(it));
console.log(formData.competences)
if (this.item.type == 'arme') {
formData.competences = competences.filter(it => RdDItemCompetence.isCompetenceArme(it));
}
if (formData.type == 'recettealchimique') {
RdDAlchimie.processManipulation(objectData, this.actor && this.actor.id);
if (['sort', 'sortreserve'].includes(this.item.type)) {
formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it));
}
if (formData.type == 'gemme') {
if (this.item.type == 'recettecuisine') {
formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, { async: true })
}
if (this.item.type == 'extraitpoetique') {
formData.extrait = await TextEditor.enrichHTML(this.object.system.extrait, { async: true })
formData.texte = await TextEditor.enrichHTML(this.object.system.texte, { async: true })
}
if (this.item.type == 'recettealchimique') {
RdDAlchimie.processManipulation(this.item, this.actor && this.actor.id);
formData.manipulation_update = await TextEditor.enrichHTML(this.object.system.manipulation_update, { async: true })
formData.utilisation = await TextEditor.enrichHTML(this.object.system.utilisation, { async: true })
formData.enchantement = await TextEditor.enrichHTML(this.object.system.enchantement, { async: true })
formData.sureffet = await TextEditor.enrichHTML(this.object.system.sureffet, { async: true })
}
if (this.item.type == 'gemme') {
formData.gemmeTypeList = RdDGemme.getGemmeTypeOptionList();
RdDGemme.calculDataDerivees(formData.data);
RdDGemme.calculDataDerivees(this.item);
}
if (formData.type == 'potion') {
if (this.item.type == 'potion') {
if (this.dateUpdated) {
formData.data.prdate = this.dateUpdated;
formData.system.prdate = this.dateUpdated;
this.dateUpdated = undefined;
}
RdDHerbes.updatePotionData(formData);
await RdDHerbes.updatePotionData(formData);
}
if (formData.isOwned && formData.type == 'herbe' && (formData.data.categorie == 'Soin' || formData.data.categorie == 'Repos')) {
if (formData.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) {
formData.isIngredientPotionBase = true;
}
if (this.item.type == 'sortreserve') {
const sortId = this.item.system.sortid;
formData.sort = formData.isOwned ? this.item.actor.items.get(sortId) : game.items.get(sortId);
}
formData.bonusCaseList = RdDItemSort.getBonusCaseList(formData, true);
return formData;
}
/* -------------------------------------------- */
prepareConteneurData(formData) {
formData.itemsByType = Misc.classify(this.actor.items.map(i => foundry.utils.deepClone(i.data)));
RdDUtility.filterEquipementParType(formData);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.subItems = formData.conteneurs.find(it => it._id == this.object.id)?.subItems;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
if (this.object.type == 'conteneur') {
this.form.ondragstart = (event) => this._onDragStart(event);
this.form.ondrop = (event) => this._onDrop(event);
}
let itemSheetDialog = this;
HtmlUtility._showControlWhen($(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs') || game.user.isGM || !this.object.isOwned);
HtmlUtility._showControlWhen($(".item-magique"), this.object.isMagique());
HtmlUtility._showControlWhen(this.html.find(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs') || game.user.isGM || !this.item.isOwned);
HtmlUtility._showControlWhen(this.html.find(".item-magique"), this.item.isMagique());
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Select competence categorie
html.find(".categorie").change(event => this._onSelectCategorie(event));
this.form.ondragstart = (event) => this._onDragStart(event);
this.form.ondrop = (event) => this._onDrop(event);
html.find('.sheet-competence-xp').change((event) => {
if (this.object.data.type == 'competence') {
RdDUtility.checkThanatosXP(this.object.data.name);
// Select competence categorie
this.html.find(".categorie").change(event => this._onSelectCategorie(event));
this.html.find('.sheet-competence-xp').change((event) => {
if (this.item.isCompetencePersonnage()) {
RdDUtility.checkThanatosXP(this.item.name);
}
});
this.html.find(".item-cout input[name='system.cout']").change(event => {
if (this.item.isMonnaie()) {
const value = event.currentTarget.value;
if (Number(value) == 0) {
ui.notifications.error(`${this.actor?.name ?? 'Monnaie'}: La monnaie ${this.item.name} a maintenant une valeur de 0, et ne peut plus être utilisée pour payer!`)
}
}
})
html.find('.enchanteDate').change((event) => {
let jour = Number($('#jourMois').val());
let mois = $('#nomMois').val();
this.html.find('.enchanteDate').change((event) => {
let jour = Number(this.html.find('[name="splitDate.day"]').val());
let mois = this.html.find('[name="splitDate.month"]').val();
this.dateUpdated = game.system.rdd.calendrier.getIndexFromDate(jour, mois);
});
html.find('.creer-tache-livre').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
actor.creerTacheDepuisLivre(this.item);
});
html.find('.consommer-potion').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
actor.consommerPotion(this.item);
});
html.find('.creer-potion-base').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
actor.dialogFabriquerPotion(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.html.find('.creer-potion-base').click((event) => this._getEventActor(event).dialogFabriquerPotion(this.item));
html.find('.alchimie-tache a').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
this.html.find('.alchimie-tache a').click((event) => {
let actor = this._getEventActor(event);
if (actor) {
let recetteId = event.currentTarget.attributes['data-recette-id'].value;
let tacheName = event.currentTarget.attributes['data-alchimie-tache'].value;
let tacheData = event.currentTarget.attributes['data-alchimie-data'].value;
let actor = game.actors.get(actorId);
if (actor) {
actor.effectuerTacheAlchimie(recetteId, tacheName, tacheData);
} else {
ui.notifications.info("Impossible trouver un actur pour réaliser cette tache Alchimique.");
ui.notifications.info("Impossible trouver un acteur pour réaliser cette tache Alchimique.");
}
});
html.find('.item-split').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
await RdDSheetUtility.splitItem(item, this.actor, async () => itemSheetDialog.render(true));
});
html.find('.item-edit').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item.sheet.render(true);
});
html.find('.item-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
RdDUtility.confirmerSuppression(this, li);
});
html.find('.item-vendre').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.proposerVente();
});
html.find('.item-montrer').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.postItem();
});
html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
this.actor.actionItem(item, async () => itemSheetDialog.render(true));
});
html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
this.html.find('.item-split').click(async event => RdDSheetUtility.splitItem(RdDSheetUtility.getItem(event, this.actor), this.actor, async () => this.render(true)));
this.html.find('.item-edit').click(async event => RdDSheetUtility.getItem(event, this.actor)?.sheet.render(true));
this.html.find('.item-delete').click(async event => RdDUtility.confirmerSuppressionItem(this, RdDSheetUtility.getItem(event, this.actor)));
this.html.find('.item-vendre').click(async event => RdDSheetUtility.getItem(event, this.actor)?.proposerVente());
this.html.find('.item-montrer').click(async event => RdDSheetUtility.getItem(event, this.actor)?.postItem());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor, async () => this.render(true)));
}
_getEventActor(event) {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
return actor;
}
/* -------------------------------------------- */
async _onSelectCategorie(event) {
event.preventDefault();
if (this.object.isCompetence()) {
if (this.item.isCompetence()) {
let level = RdDItemCompetence.getNiveauBase(event.currentTarget.value);
Misc.templateData(this.object).base = level;
$("#base").val(level);
this.item.system.base = level;
this.html.find('[name="system.base"]').val(level);
}
}
/* -------------------------------------------- */
get template() {
let type = this.object.data.type;
return `systems/foundryvtt-reve-de-dragon/templates/item-${type}-sheet.html`;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) { // Deprecated en v0.8 à clarifier
_updateObject(event, formData) {
if (this.item.type == 'sort') {
// Données de bonus de cases ?
formData = RdDItemSort.buildBonusCaseStringFromFormData(formData);
return this.object.update(formData);
formData['system.bonuscase'] = RdDItemSort.buildBonusCaseStringFromFormData(formData.bonusValue, formData.caseValue);
}
return this.item.update(formData);
}
/* -------------------------------------------- */
async _onDragStart(event) {
console.log("_onDragStart", event);
if (event.target.classList.contains("entity-link")) return;
const itemId = event.srcElement?.attributes["data-item-id"].value;
const item = this.actor.items.get(itemId);
// Create drag data
const dragData = {
actorId: this.actor.id,
type: "Item",
data: item.data
};
event.dataTransfer.setData("text/plain", JSON.stringify(dragData));
}
async _onDrop(event) {
// Try to extract the data
let data;
try {
data = JSON.parse(event.dataTransfer.getData('text/plain'));
} catch (err) {
return false;
}
// Try to extract the dragData
let dragData = RdDItemSheet.$extractDragData(event);
if (!dragData) return false;
const allowed = Hooks.call("dropActorSheetData", this.actor, this, dragData);
if (allowed === false) return false;
const allowed = Hooks.call("dropActorSheetData", this.actor, this, data);
if (allowed === false) return;
// Handle different data types
switch (data.type) {
// Handle different dragData types
switch (dragData.type) {
case "Item":
return this._onDropItem(event, data);
return this._onDropItem(event, dragData);
case "Actor":
return this._onDropActor(event, dragData);
}
return super._onDrop(event);
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
if (this.actor) {
const dropParams = RdDSheetUtility.prepareItemDropParameters(this.object.id, this.actor.id, dragData, this.objetVersConteneur);
await this.actor.processDropItem(dropParams);
await this.render(true);
static $extractDragData(event) {
try {
const eventData = event?.dataTransfer?.getData('text/plain');
if (eventData) {
return JSON.parse(eventData);
}
} catch (err) { }
return undefined;
}
async _onDropItem(event, dragData) {
}
async _onDropActor(event, dragData) {
}
}

View File

@ -1,30 +1,14 @@
import { SYSTEM_RDD } from "./constants.js";
import { RdDItemSheet } from "./item-sheet.js";
import { RdDItemSigneDraconique } from "./item-signedraconique.js";
import { Misc } from "./misc.js";
import { TMRType, TMRUtility } from "./tmr-utility.js";
import { TMRUtility } from "./tmr-utility.js";
/**
* Item sheet pour signes draconiques
* @extends {ItemSheet}
* @extends {RdDItemSheet}
*/
export class RdDSigneDraconiqueItemSheet extends ItemSheet {
export class RdDSigneDraconiqueItemSheet extends RdDItemSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: [SYSTEM_RDD, "sheet", "item"],
template: "systems/foundryvtt-reve-de-dragon/templates/item-signedraconique-sheet.html",
width: 550,
height: 550
});
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
buttons.unshift({ class: "post", icon: "fas fa-comment", onclick: ev => this.item.postItem() });
return buttons;
}
static get ITEM_TYPE() { return "signedraconique" }
/* -------------------------------------------- */
/** @override */
@ -32,25 +16,25 @@ export class RdDSigneDraconiqueItemSheet extends ItemSheet {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - sheetHeader[0].clientHeight;
sheetBody.css("height", bodyHeight);
sheetBody.css("height", position.height - sheetHeader[0].clientHeight)
return position;
}
/* -------------------------------------------- */
async getData() {
const formData = duplicate(Misc.data(this.object));
const formData = duplicate(this.item);
this.tmrs = TMRUtility.buildSelectionTypesTMR(this.item.system.typesTMR);
mergeObject(formData, {
tmrs: this.tmrs,
title: formData.name,
isGM: game.user.isGM,
owner: this.document.isOwner,
owner: this.actor?.isOwner,
isOwned: this.actor ? true : false,
actorId: this.actor?.id,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
});
formData.tmrs = TMRUtility.listSelectedTMR(formData.data.typesTMR ?? []);
return formData;
}
@ -62,35 +46,32 @@ export class RdDSigneDraconiqueItemSheet extends ItemSheet {
if (!this.options.editable) return;
html.find(".signe-aleatoire").click(event => this.setSigneAleatoire());
html.find(".select-tmr").change((event) => this.onSelectTmr(event));
html.find(".signe-xp-sort").change((event) => this.onValeurXpSort(event.currentTarget.attributes['data-typereussite']?.value, Number(event.currentTarget.value)));
html.find("input.select-tmr").change(event => this.onSelectTmr(event));
html.find(".signe-xp-sort").change(event => this.onValeurXpSort(event.currentTarget.attributes['data-typereussite']?.value, Number(event.currentTarget.value)));
}
async setSigneAleatoire() {
const newSigne = await RdDItemSigneDraconique.randomSigneDraconique();
this.object.update(newSigne);
newSigne.name = this.item.name;
this.item.update(newSigne);
}
async onSelectTmr(event) {
event.preventDefault();
const selectedTMR = $(".select-tmr").val();
this.object.update({ 'data.typesTMR': selectedTMR });
const tmrName = $(event.currentTarget)?.data("tmr-name");
const onTmr = this.tmrs.find(it => it.name == tmrName);
if (onTmr){
onTmr.selected = event.currentTarget.checked;
}
this.item.update({ 'system.typesTMR': TMRUtility.buildListTypesTMRSelection(this.tmrs) });
}
async onValeurXpSort(event) {
const codeReussite = event.currentTarget.attributes['data-typereussite']?.value ?? 0;
const xp = Number(event.currentTarget.value);
const oldValeur = Misc.templateData(this.object).valeur;
const oldValeur = this.item.system.valeur;
const newValeur = RdDItemSigneDraconique.calculValeursXpSort(codeReussite, xp, oldValeur);
await this.object.update({ 'data.valeur': newValeur });
await this.item.update({ 'system.valeur': newValeur });
}
/* -------------------------------------------- */
get template() {
return `systems/foundryvtt-reve-de-dragon/templates/item-signedraconique-sheet.html`;
}
get title() {
return `Signe draconique: ${this.object.name}`;
}
}

View File

@ -18,13 +18,12 @@ const DIFFICULTE_LECTURE_SIGNE_MANQUE = +11;
export class RdDItemSigneDraconique {
static prepareSigneDraconiqueMeditation(meditation, rolled) {
meditation = Misc.data(meditation);
return {
name: "de la " + meditation.name,
type: "signedraconique",
img: meditation.img,
data: {
typesTMR: [TMRUtility.typeTmrName(meditation.data.tmr)],
system: {
typesTMR: [TMRUtility.typeTmrName(meditation.system.tmr)],
difficulte: rolled.isSuccess ? RdDItemSigneDraconique.getDiffSigneMeditation(rolled.code) : DIFFICULTE_LECTURE_SIGNE_MANQUE,
ephemere: true,
duree: "1 round",
@ -43,7 +42,7 @@ export class RdDItemSigneDraconique {
}
static getXpSortSigneDraconique(code, signe) {
return Misc.toInt(Misc.data(signe).data.valeur[code] ?? 0);
return Misc.toInt(signe.system.valeur[code] ?? 0);
}
static calculValeursXpSort(qualite, valeur, avant) {
@ -75,7 +74,7 @@ export class RdDItemSigneDraconique {
name: await RdDItemSigneDraconique.randomSigneDescription(),
type: "signedraconique",
img: defaultItemImg.signedraconique,
data: {
system: {
typesTMR: await RdDItemSigneDraconique.randomTmrs(modele.nbCases),
ephemere: options?.ephemere == undefined ? RdDDice.rollTotal("1d2") == 2 : options.ephemere,
duree: "1 round",

View File

@ -7,25 +7,25 @@ export class RdDItemSort extends Item {
/* -------------------------------------------- */
static isDifficulteVariable(sort) {
return sort && (sort.data.difficulte.toLowerCase() == "variable");
return sort && (sort.system.difficulte.toLowerCase() == "variable");
}
/* -------------------------------------------- */
static isCoutVariable(sort) {
return sort && (sort.data.ptreve.toLowerCase() == "variable" || sort.data.ptreve.indexOf("+") >= 0);
return sort && (sort.system.ptreve.toLowerCase() == "variable" || sort.system.ptreve.indexOf("+") >= 0);
}
/* -------------------------------------------- */
static setCoutReveReel(sort){
if (sort) {
sort.data.ptreve_reel = this.isCoutVariable(sort) ? 1 : sort.data.ptreve;
sort.system.ptreve_reel = this.isCoutVariable(sort) ? 1 : sort.system.ptreve;
}
}
/* -------------------------------------------- */
static getDifficulte(sort, variable) {
if (sort && !RdDItemSort.isDifficulteVariable(sort)) {
return Misc.toInt(sort.data.difficulte);
return Misc.toInt(sort.system.difficulte);
}
return variable;
}
@ -54,7 +54,7 @@ export class RdDItemSort extends Item {
static getBonusCaseList( item, newCase = false ) {
// Gestion spéciale case bonus
if ( item.type == 'sort') {
return this.buildBonusCaseList(item.data.bonuscase, newCase );
return this.buildBonusCaseList(item.system.bonuscase, newCase );
}
return undefined;
}
@ -63,31 +63,26 @@ export class RdDItemSort extends Item {
/** Met à jour les données de formulaire
* si static des bonus de cases sont présents
* */
static buildBonusCaseStringFromFormData( formData ) {
if ( formData.bonusValue ) {
static buildBonusCaseStringFromFormData( bonuses, cases ) {
if ( bonuses ) {
let list = [];
let caseCheck = {};
for(let i=0; i<formData.bonusValue.length; i++) {
let coord = formData.caseValue[i] || 'A1';
coord = coord.toUpperCase();
if ( TMRUtility.verifyTMRCoord( coord ) ) { // Sanity check
let bonus = formData.bonusValue[i] || 0;
if ( bonus > 0 && caseCheck[coord] == undefined ) {
for (let i=0; i<bonuses.length; i++) {
let coord = cases[i]?.toUpperCase() || 'A1';
let bonus = bonuses[i] || 0;
if ( TMRUtility.verifyTMRCoord( coord ) && bonus > 0 && caseCheck[coord] == undefined ) {
caseCheck[coord] = bonus;
list.push( coord+":"+bonus );
}
}
return list.toString();
}
formData.bonusValue = undefined;
formData.caseValue = undefined;
formData['data.bonuscase'] = list.toString(); // Reset
}
return formData;
return undefined;
}
/* -------------------------------------------- */
static incrementBonusCase( actor, sort, coord ) {
let bonusCaseList = this.buildBonusCaseList(sort.data.bonuscase, false);
let bonusCaseList = this.buildBonusCaseList(sort.system.bonuscase, false);
//console.log("ITEMSORT", sort, bonusCaseList);
let found = false;
@ -106,12 +101,12 @@ export class RdDItemSort extends Item {
// Sauvegarde/update
let bonuscase = StringList.toString();
//console.log("Bonus cae :", bonuscase);
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'data.bonuscase': bonuscase }] );
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }] );
}
/* -------------------------------------------- */
static getCaseBonus( sort, coord) {
let bonusCaseList = this.buildBonusCaseList(sort.data.bonuscase, false);
let bonusCaseList = this.buildBonusCaseList(sort.system.bonuscase, false);
for( let bc of bonusCaseList) {
if (bc.case == coord) { // Case existante
return Number(bc.bonus);

View File

@ -1,23 +1,47 @@
import { DialogItemVente } from "./dialog-item-vente.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDUtility } from "./rdd-utility.js";
const typesObjetsEquipement = ["objet", "arme", "armure", "gemme", "conteneur", "herbe", "ingredient", "livre", "potion", "munition", "nourritureboisson", "monnaie"]
const typesObjetsInventaire = [
"arme",
"armure",
"conteneur",
"gemme",
"herbe",
"ingredient",
"faune",
"livre",
"monnaie",
"munition",
"nourritureboisson",
"objet",
"potion",
]
const typesObjetsOeuvres = ["oeuvre", "recettecuisine", "musique", "chant", "danse", "jeu"]
const encBrin = 0.00005;// un brin = 1 décigramme = 1/10g = 1/10000kg = 1/20000 enc
const typesObjetsDraconiques = ["queue", "ombre", "souffle", "tete", "signedraconique", "sortreserve", "rencontre"]
const typesObjetsConnaissance = ["meditation", "recettealchimique", "sort"]
const typesObjetsEffet = ["possession", "poison", "maladie"]
const typesObjetsCompetence = ["competence", "competencecreature"]
const encBrin = 0.00005; // un brin = 1 décigramme = 1/10g = 1/10000kg = 1/20000 enc
const encPepin = 0.0007; /* un pépin de gemme = 1/10 cm3 = 1/1000 l = 3.5/1000 kg = 7/2000 kg = 7/1000 enc
densité 3.5 (~2.3 à 4, parfois plus) -- https://www.juwelo.fr/guide-des-pierres/faits-et-chiffres/
*/
export const defaultItemImg = {
competence: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
compcreature: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
competencecreature: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
arme: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/epee_gnome.webp",
armure: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/armure_plaques.webp",
conteneur: "systems/foundryvtt-reve-de-dragon/icons/objets/sac_a_dos.webp",
sort: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp",
herbe: "systems/foundryvtt-reve-de-dragon/icons/botanique/Endorlotte.webp",
faune: "systems/foundryvtt-reve-de-dragon/icons/faune/rongeur.webp",
ingredient: "systems/foundryvtt-reve-de-dragon/icons/objets/sable_poudre.webp",
livre: "systems/foundryvtt-reve-de-dragon/icons/objets/livre.webp",
potion: "systems/foundryvtt-reve-de-dragon/icons/objets/liqueur_de_bagdol.webp",
rencontre: "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp",
queue: "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp",
ombre: "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp",
souffle: "systems/foundryvtt-reve-de-dragon/icons/souffle_dragon.webp",
@ -35,231 +59,389 @@ export const defaultItemImg = {
nourritureboisson: "systems/foundryvtt-reve-de-dragon/icons/objets/provision_crue.webp",
signedraconique: "systems/foundryvtt-reve-de-dragon/icons/tmr/signe_draconique.webp",
gemme: "systems/foundryvtt-reve-de-dragon/icons/gemmes/almaze.webp",
possession: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp"
possession: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
sortreserve: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp",
extraitpoetique: "systems/foundryvtt-reve-de-dragon/icons/competence_ecriture.webp",
tarot: "systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp",
}
/* -------------------------------------------- */
export class RdDItem extends Item {
constructor(data, context) {
if (!data.img) {
data.img = defaultItemImg[data.type];
}
super(data, context);
static getDefaultImg(itemType) {
return defaultItemImg[itemType];
}
static getTypeObjetsEquipement() {
return typesObjetsEquipement
static isItemInventaire(newLocal) {
return typesObjetsInventaire.includes(newLocal.type);
}
static isFieldInventaireModifiable(type, field) {
switch (field) {
case 'quantite':
if (['conteneur'].includes(type)) {
return false;
}
break;
case 'cout':
if (['monnaie'].includes(type)) {
return game.user.isGM;
}
break;
}
return true;
}
getUniteQuantite() {
switch (this.type) {
case "monnaie": return "(Pièces)"
case "herbe":
switch (this.system.categorie) {
case 'Alchimie': case 'Repos': case 'Soin':
return "(Brins)"
case 'Cuisine': return '';
}
return '';
case "ingredient": return "(Pépins ou Brins)"
}
return '';
}
constructor(itemData, context) {
if (!itemData.img) {
itemData.img = RdDItem.getDefaultImg(itemData.type);
}
super(itemData, context);
}
static getItemTypesInventaire() {
return typesObjetsInventaire
}
static getTypesOeuvres() {
return typesObjetsOeuvres
}
isCompetencePersonnage() {
return this.type == 'competence'
}
isCompetenceCreature() {
return this.type == 'competencecreature'
}
isCompetence() {
return Misc.data(this).type == 'competence';
return typesObjetsCompetence.includes(this.type)
}
isInventaire() {
return RdDItem.isItemInventaire(this)
}
isOeuvre() {
return typesObjetsOeuvres.includes(this.type)
}
isDraconique() {
return typesObjetsDraconiques.includes(this.type)
}
isEffet() {
return typesObjetsEffet.includes(this.type)
}
isConnaissance() {
return typesObjetsConnaissance.includes(this.type)
}
isConteneur() {
return this.type == 'conteneur';
}
isMonnaie() {
return this.type == 'monnaie';
}
isConteneur() {
return Misc.data(this).type == 'conteneur';
getItemGroup() {
if (this.isInventaire()) return "equipement";
if (this.isOeuvre()) return "oeuvre";
if (this.isDraconique()) return "draconique";
if (this.isConnaissance()) return "connaissance";
if (this.isEffet()) return "effet";
if (this.isCompetence()) return "competence";
return "autres";
}
isConteneurNonVide() {
return this.isConteneur() && (Misc.templateData(this).contenu?.length ?? 0) > 0;
return this.isConteneur() && (this.system.contenu?.length ?? 0) > 0;
}
isConteneurVide() {
return this.isConteneur() && (Misc.templateData(this).contenu?.length ?? 0) == 0;
return this.isConteneur() && (this.system.contenu?.length ?? 0) == 0;
}
isVideOuNonConteneur() {
return !this.isConteneur() || (Misc.templateData(this).contenu?.length ?? 0) == 0;
return !this.isConteneur() || (this.system.contenu?.length ?? 0) == 0;
}
isNourritureBoisson() {
return this.type == 'nourritureboisson';
}
isComestible() {
switch (this.type) {
case 'nourritureboisson': return 'pret';
case 'herbe':
return this.system.categorie == 'Cuisine' && this.system.sust > 0 ? 'brut' : '';
case 'faune':
return this.system.sust > 0 ? 'brut' : '';
}
return '';
}
isAlcool() {
const itemData = Misc.data(this);
return itemData.type == 'nourritureboisson' && itemData.data.boisson && itemData.data.alcoolise;
return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise;
}
isHerbeAPotion() {
const itemData = Misc.data(this);
return itemData.type == 'herbe' && (itemData.data.categorie == 'Soin' || itemData.data.categorie == 'Repos');
return this.type == 'herbe' && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos');
}
isPotion() {
return Misc.data(this).type == 'potion';
return this.type == 'potion';
}
isEquipement() {
return RdDItem.getTypeObjetsEquipement().includes(Misc.data(this).type);
}
isCristalAlchimique() {
const itemData = Misc.data(this);
return itemData.type == 'objet' && Grammar.toLowerCaseNoAccent(itemData.name) == 'cristal alchimique' && itemData.data.quantite > 0;
return this.type == 'objet' && Grammar.toLowerCaseNoAccent(this.name) == 'cristal alchimique' && this.system.quantite > 0;
}
isMagique() {
return Misc.templateData(this).magique;
return this.system.magique
}
getQuantite() {
return Math.round(this.isConteneur() ? 1 : (this.system.quantite ?? 0))
}
getEncTotal() {
const itemData = Misc.data(this);
return Number(itemData.data.encombrement ?? 0) * Number(itemData.data.quantite ?? 1);
return this.getEnc() * this.getQuantite();
}
getEnc() {
const itemData = Misc.data(this);
switch (itemData.type) {
switch (this.type) {
case 'herbe':
return this.getEncHerbe();
case 'gemme':
return encPepin * this.system.taille;
}
return Math.max(this.system.encombrement ?? 0, 0);
}
getEncHerbe() {
switch (this.system.categorie) {
case 'Repos': case 'Soin': case 'Alchimie':
return encBrin;
}
return itemData.data.encombrement ?? 0;
return this.system.encombrement;
}
valeurTotale() {
return this.getQuantite() * this.valeur()
}
valeur() {
return this.system.cout ?? 0
}
prepareDerivedData() {
super.prepareDerivedData();
if (this.isEquipement()) {
this._calculsEquipement();
if (this.isInventaire()) {
this.system.encTotal = this.getEncTotal();
if (this.isPotion()) {
this.prepareDataPotion()
}
const itemData = Misc.data(this);
itemData.data.actionPrincipale = this.getActionPrincipale({ warnIfNot: false });
this.system.actionPrincipale = this.getActionPrincipale({ warnIfNot: false });
}
}
prepareDataPotion() {
const tplData = Misc.templateData(this);
const categorie = Grammar.toLowerCaseNoAccent(tplData.categorie);
tplData.magique = categorie.includes('enchante');
if (tplData.magique) {
const categorie = Grammar.toLowerCaseNoAccent(this.system.categorie);
this.system.magique = categorie.includes('enchante');
if (this.system.magique) {
if (categorie.includes('soin') || categorie.includes('repos')) {
tplData.puissance = tplData.herbebonus * tplData.pr;
// TODO: utiliser calculPointsRepos / calculPointsGuerison
this.system.puissance = RdDHerbes.calculPuissancePotion(this);
}
}
}
_calculsEquipement() {
const tplData = Misc.templateData(this);
const quantite = this.isConteneur() ? 1 : (tplData.quantite ?? 0);
const enc = this.getEnc();
if (enc != undefined) {
tplData.encTotal = Math.max(enc, 0) * quantite;
}
if (tplData.cout != undefined) {
tplData.prixTotal = Math.max(tplData.cout, 0) * quantite;
}
}
getActionPrincipale(options = { warnIfNot: true }) {
const itemData = Misc.data(this);
if (!this.isConteneur() && (itemData.data.quantite ?? 0) <= 0) {
if (options.warnIfNot) {
ui.notifications.warn(`Vous n'avez plus de ${itemData.name}.`);
}
return undefined;
}
switch (itemData.type) {
case 'nourritureboisson': return itemData.data.boisson ? 'Boire' : 'Manger';
case 'potion': return 'Boire';
case 'livre': return 'Lire';
switch (this.type) {
case 'conteneur': return 'Ouvrir';
}
if (this.isHerbeAPotion()) { return 'Décoction'; }
if (options.warnIfNot) {
ui.notifications.warn(`Impossible d'utiliser un ${itemData.name}, aucune action associée définie.`);
if (this.actor?.isPersonnage()) {
const warn = options.warnIfNot;
if (this.isComestible() == 'brut') {
return 'Utiliser';
}
switch (this.type) {
case 'nourritureboisson': return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
case 'potion': return this._actionOrWarnQuantiteZero('Boire', warn);
case 'livre': return this._actionOrWarnQuantiteZero('Lire', warn);
case 'herbe': return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
case 'queue': case 'ombre': return this.system.refoulement > 0 ? 'Refouler' : undefined;
}
}
return undefined;
}
/* -------------------------------------------- */
async actionPrincipale(actor, onActionItem = async () => { }) {
if (!this.getActionPrincipale()) {
return;
}
if (await actor.actionNourritureboisson(this, onActionItem)) {
return;
}
switch (this.type) {
case 'potion': return await actor.consommerPotion(this, onActionItem);
case 'livre': return await actor.actionLire(this);
case 'conteneur': return await this.sheet.render(true);
case 'herbe': return await actor.actionHerbe(this);
case 'queue': case 'ombre': return await actor.actionRefoulement(this);
}
}
_actionOrWarnQuantiteZero(actionName, warn) {
if ((this.system.quantite ?? 0) <= 0) {
if (warn) {
ui.notifications.warn(`Vous n'avez plus de ${this.name}.`);
}
return undefined;
}
else {
return actionName;
}
}
async diminuerQuantite(nombre, options = { diminuerQuantite: true, supprimerSiZero: false }) {
if (options.diminuerQuantite == false) return;
await this.quantiteIncDec(-nombre, options);
}
async onCreateDecoupeComestible(actor) {
if (actor && this.isComestible() == 'brut' && this.system.sust != 1) {
if (this.system.sust < 1) {
await actor.updateEmbeddedDocuments('Item', [{
_id: this.id,
'system.sust': 0
}])
}
else {
const sust = Math.floor(this.system.sust);
await actor.updateEmbeddedDocuments('Item', [{
_id: this.id,
'system.quantite': this.system.quantite * sust,
'system.encombrement': Misc.keepDecimals(this.system.encombrement / sust, 2),
'system.cout': Misc.keepDecimals(this.system.cout / sust, 2),
'system.sust': 1
}])
}
}
}
async empiler(item) {
if (this.isComestible() == 'brut') {
const sust = this.system.sust + item.system.sust;
const encombrement = this.system.encombrement + item.system.encombrement;
await this.update({
"system.sust": sust,
"system.encombrement": encombrement
});
}
else {
await this.quantiteIncDec(item.system.quantite);
}
await item.delete();
}
async quantiteIncDec(nombre, options = { diminuerQuantite: true, supprimerSiZero: false }) {
const itemData = Misc.data(this);
const quantite = Number(itemData.data.quantite ?? -1);
const quantite = Number(this.system.quantite ?? -1);
if (quantite >= 0) {
const reste = Math.max(quantite + Number(nombre), 0);
if (reste == 0) {
if (options.supprimerSiZero) {
ui.notifications.notify(`${itemData.name} supprimé de votre équipement`);
ui.notifications.notify(`${this.name} supprimé de votre équipement`);
await this.delete();
}
else {
ui.notifications.notify(`Il ne vous reste plus de ${itemData.name}, vous pouvez le supprimer de votre équipement, ou trouver un moyen de vous en procurer.`);
await this.update({ "data.quantite": 0 });
ui.notifications.notify(`Il ne vous reste plus de ${this.name}, vous pouvez le supprimer de votre équipement, ou trouver un moyen de vous en procurer.`);
await this.update({ "system.quantite": 0 });
}
}
else {
await this.update({ "data.quantite": reste });
await this.update({ "system.quantite": reste });
}
}
}
/* -------------------------------------------- */
// détermine si deux équipements sont similaires: de même type, et avec les même champs hormis la quantité
isEquipementSimilaire(other) {
const itemData = Misc.data(this);
const otherData = Misc.data(other);
const tplData = Misc.templateData(this);
const otherTplData = Misc.templateData(other);
if (!this.isEquipement()) return false;
if (itemData.type != otherData.type) return false;
if (itemData.name != otherData.name) return false;
if (tplData.quantite == undefined) return false;
const differences = Object.entries(tplData).filter(([key, value]) => !['quantite', 'encTotal', 'prixTotal', 'cout'].includes(key))
.filter(([key, value]) => value != otherTplData[key]);
isInventaireEmpilable(other) {
if (!other || !this.isInventaire()) {
return [false, undefined];
}
if (this.system.quantite == undefined) {
return [false, `Impossible de regrouper des ${this.type}, ils ne sont pas empilables`];
}
else if (this.type != other.type) {
return [false, `Impossible de regrouper des ${this.type} avec des ${other.type}`];
}
else if (this.name != other.name) {
return [false, `Impossible de regrouper ${this.name} avec ${other.name}`];
}
else {
const excludedProperties = ['quantite', 'cout', 'encTotal'];
if (this.isComestible()) {
excludedProperties.push('sust', 'encombrement');
}
let differences = Object.entries(this.system)
.filter(([key, value]) => !excludedProperties.includes(key))
.filter(([key, value]) => value != other.system[key])
if (differences.length > 0) {
let message = `Impossible de regrouper les ${itemData.type} ${itemData.name}: `;
let message = `Impossible de regrouper les ${this.type} ${this.name}: `;
for (const [key, value] of differences) {
message += `<br>${key}: ${value} vs ${otherTplData[key]}`;
message += `<br>${key}: ${value} vs ${other.system[key]}`;
}
ui.notifications.info(message)
return false;
return [false, message];
}
return true;
}
return [true, undefined];
}
async proposerVente() {
console.log(this);
if (this.isConteneurNonVide()) {
ui.notifications.warn(`Votre ${this.name} n'est pas vide, pas possible de le donner ou le vendre`);
ui.notifications.warn(`Votre ${this.name} n'est pas vide, pas possible de le proposer`);
return;
}
const dialog = await DialogItemVente.create(this, (vente) => this._onProposerVente(vente))
dialog.render(true);
}
async _onProposerVente(venteData) {
venteData["properties"] = this.getProprietes();
if (venteData.isOwned) {
if (venteData.quantiteNbLots * venteData.tailleLot > venteData.quantiteMax) {
ui.notifications.warn(`Vous avez ${venteData.quantiteMax} ${venteData.item.name}, ce n'est pas suffisant pour vendre ${venteData.quantiteNbLots} de ${venteData.tailleLot}`)
await DialogItemVente.display(this, async (vente) => {
vente["properties"] = this.getProprietes();
if (vente.isOwned) {
if (vente.quantiteNbLots * vente.tailleLot > vente.quantiteMax) {
ui.notifications.warn(`Vous avez ${vente.quantiteMax} ${vente.item.name}, ce n'est pas suffisant pour vendre ${vente.quantiteNbLots} de ${vente.tailleLot}`)
return;
}
}
venteData.jsondata = JSON.stringify(venteData.item);
vente.jsondata = JSON.stringify(vente.item);
console.log(venteData);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', venteData);
console.log(vente);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente);
ChatMessage.create(RdDUtility.chatDataSetup(html));
});
}
/* -------------------------------------------- */
getProprietes() {
return this[`_${Misc.data(this).type}ChatData`]();
return this[`_${this.type}ChatData`]().filter(it => it != undefined);
}
/* -------------------------------------------- */
async postItem(modeOverride) {
console.log(this);
let chatData = duplicate(Misc.data(this));
const properties = this.getProprietes();
chatData["properties"] = properties
let chatData = duplicate(this);
chatData["properties"] = this.getProprietes();
if (this.actor) {
chatData.actor = { id: this.actor.id };
}
@ -276,262 +458,237 @@ export class RdDItem extends Item {
});
}
static propertyIfDefined(name, val, condition = (it) => true) {
return condition ? [`<b>${name}</b>: ${val}`] : [];
static propertyIfDefined(name, val, condition = true) {
return condition ? `<b>${name}</b>: ${val}` : undefined;
}
_inventaireTemplateChatData() {
return [
RdDItem.propertyIfDefined('Qualité', this.system.qualite, this.system.qualite != 0),
RdDItem.propertyIfDefined('Encombrement', this.system.encombrement)
// cout et quantité masqués
]
}
/* -------------------------------------------- */
_objetChatData() {
const tplData = Misc.templateData(this);
let properties = [].concat(
RdDItem.propertyIfDefined('Résistance', tplData.resistance, tplData.resistance),
RdDItem.propertyIfDefined('Qualité', tplData.qualite, tplData.qualite),
RdDItem.propertyIfDefined('Encombrement', tplData.encombrement),
);
return properties;
return this._inventaireTemplateChatData()
}
/* -------------------------------------------- */
_nourritureboissonChatData() {
const tplData = Misc.templateData(this);
let properties = [].concat(
RdDItem.propertyIfDefined('Sustentation', tplData.sust, tplData.sust > 0),
RdDItem.propertyIfDefined('Désaltère', tplData.desaltere, tplData.boisson),
RdDItem.propertyIfDefined('Force alcool', tplData.force, tplData.boisson && tplData.alcoolise),
RdDItem.propertyIfDefined('Exotisme', tplData.exotisme, tplData.exotisme < 0),
RdDItem.propertyIfDefined('Qualité', tplData.qualite, tplData.qualite),
RdDItem.propertyIfDefined('Encombrement', tplData.encombrement),
);
return properties;
return [
RdDItem.propertyIfDefined('Sustentation', this.system.sust, this.system.sust > 0),
RdDItem.propertyIfDefined('Désaltère', this.system.desaltere, this.system.boisson),
RdDItem.propertyIfDefined('Force alcool', this.system.force, this.system.boisson && this.system.alcoolise),
RdDItem.propertyIfDefined('Exotisme', this.system.exotisme, this.system.exotisme < 0),
...this._inventaireTemplateChatData()
]
}
/* -------------------------------------------- */
_armeChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Compétence</b>: ${tplData.competence}`,
`<b>Dommages</b>: ${tplData.dommages}`,
`<b>Force minimum</b>: ${tplData.force}`,
`<b>Resistance</b>: ${tplData.resistance}`,
`<b>Encombrement</b>: ${tplData.encombrement}`
return [
`<b>Compétence</b>: ${this.system.competence}`,
`<b>Dommages</b>: ${this.system.dommages}`,
`<b>Force minimum</b>: ${this.system.force}`,
`<b>Resistance</b>: ${this.system.resistance}`,
...this._inventaireTemplateChatData()
]
return properties;
}
/* -------------------------------------------- */
_conteneurChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Capacité</b>: ${tplData.capacite} Enc.`,
`<b>Encombrement</b>: ${tplData.encombrement}`
return [
`<b>Capacité</b>: ${this.system.capacite} Enc.`,
...this._inventaireTemplateChatData()
]
return properties;
}
/* -------------------------------------------- */
_munitionChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Encombrement</b>: ${tplData.encombrement}`
]
return properties;
return this._inventaireTemplateChatData()
}
/* -------------------------------------------- */
_armureChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Protection</b>: ${tplData.protection}`,
`<b>Détérioration</b>: ${tplData.deterioration}`,
`<b>Malus armure</b>: ${tplData.malus}`,
`<b>Encombrement</b>: ${tplData.encombrement}`
return [
`<b>Protection</b>: ${this.system.protection}`,
`<b>Détérioration</b>: ${this.system.deterioration}`,
`<b>Malus armure</b>: ${this.system.malus}`,
...this._inventaireTemplateChatData()
]
return properties;
}
/* -------------------------------------------- */
_competenceChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Catégorie</b>: ${tplData.categorie}`,
`<b>Niveau</b>: ${tplData.niveau}`,
`<b>Caractéristique par défaut</b>: ${tplData.carac_defaut}`,
`<b>XP</b>: ${tplData.xp}`
return [
`<b>Catégorie</b>: ${this.system.categorie}`,
`<b>Niveau</b>: ${this.system.niveau}`,
`<b>Caractéristique par défaut</b>: ${this.system.carac_defaut}`,
`<b>XP</b>: ${this.system.xp}`
]
return properties;
}
/* -------------------------------------------- */
_competencecreatureChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Catégorie</b>: ${tplData.categorie}`,
`<b>Niveau</b>: ${tplData.niveau}`,
`<b>Caractéristique</b>: ${tplData.carac_value}`,
`<b>XP</b>: ${tplData.xp}`
return [
`<b>Catégorie</b>: ${this.system.categorie}`,
`<b>Niveau</b>: ${this.system.niveau}`,
`<b>Caractéristique</b>: ${this.system.carac_value}`,
`<b>XP</b>: ${this.system.xp}`
]
return properties;
}
/* -------------------------------------------- */
_sortChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Draconic</b>: ${tplData.draconic}`,
`<b>Difficulté</b>: ${tplData.difficulte}`,
`<b>Case TMR</b>: ${tplData.caseTMR}`,
`<b>Points de Rêve</b>: ${tplData.ptreve}`
return [
`<b>Draconic</b>: ${this.system.draconic}`,
`<b>Difficulté</b>: ${this.system.difficulte}`,
`<b>Case TMR</b>: ${this.system.caseTMR}`,
`<b>Points de Rêve</b>: ${this.system.ptreve}`
]
return properties;
}
/* -------------------------------------------- */
_herbeChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Milieu</b>: ${tplData.milieu}`,
`<b>Rareté</b>: ${tplData.rarete}`,
`<b>Catégorie</b>: ${tplData.categorie}`,
return [
`<b>Milieu</b>: ${this.system.milieu}`,
`<b>Catégorie</b>: ${this.system.categorie}`,
...this._inventaireTemplateChatData()
]
return properties;
}
/* -------------------------------------------- */
_ingredientChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Milieu</b>: ${tplData.milieu}`,
`<b>Rareté</b>: ${tplData.rarete}`,
`<b>Catégorie</b>: ${tplData.categorie}`,
return [
`<b>Milieu</b>: ${this.system.milieu}`,
`<b>Catégorie</b>: ${this.system.categorie}`,
...this._inventaireTemplateChatData()
]
}
/* -------------------------------------------- */
_fauneChatData() {
return [
`<b>Sustentation</b>: ${this.system.sust}`,
`<b>Milieu</b>: ${this.system.milieu}`,
...this._inventaireTemplateChatData()
]
return properties;
}
/* -------------------------------------------- */
_tacheChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Caractéristique</b>: ${tplData.carac}`,
`<b>Compétence</b>: ${tplData.competence}`,
`<b>Périodicité</b>: ${tplData.periodicite}`,
`<b>Fatigue</b>: ${tplData.fatigue}`,
`<b>Difficulté</b>: ${tplData.difficulte}`
].concat([
tplData.cacher_points_de_tache ? [] :`<b>Points de Tâche</b>: ${tplData.points_de_tache}`
]).concat([
`<b>Points de Tâche atteints</b>: ${tplData.points_de_tache_courant}`]
);
return properties;
return [
`<b>Caractéristique</b>: ${this.system.carac}`,
`<b>Compétence</b>: ${this.system.competence}`,
`<b>Périodicité</b>: ${this.system.periodicite}`,
`<b>Fatigue</b>: ${this.system.fatigue}`,
`<b>Difficulté</b>: ${this.system.difficulte}`,
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}`]
}
/* -------------------------------------------- */
_livreChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Compétence</b>: ${tplData.competence}`,
`<b>Auteur</b>: ${tplData.auteur}`,
`<b>Difficulté</b>: ${tplData.difficulte}`,
`<b>Points de Tâche</b>: ${tplData.points_de_tache}`,
`<b>Encombrement</b>: ${tplData.encombrement}`
return [
`<b>Compétence</b>: ${this.system.competence}`,
`<b>Auteur</b>: ${this.system.auteur}`,
`<b>Difficulté</b>: ${this.system.difficulte}`,
RdDItem.propertyIfDefined('Points de Tâche', this.system.points_de_tache, this.system.cacher_points_de_tache),
...this._inventaireTemplateChatData()
]
return properties;
}
/* -------------------------------------------- */
_potionChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Rareté</b>: ${tplData.rarete}`,
`<b>Catégorie</b>: ${tplData.categorie}`,
`<b>Encombrement</b>: ${tplData.encombrement}`,
return [
`<b>Rareté</b>: ${this.system.rarete}`,
`<b>Catégorie</b>: ${this.system.categorie}`,
...this._inventaireTemplateChatData()
]
return properties;
}
/* -------------------------------------------- */
_queueChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Refoulement</b>: ${tplData.refoulement}`
function label(categorie) {
switch (categorie) {
case 'ideefixe': return 'Idée fixe';
case 'lancinant': return 'Désir lancinant';
}
return ''
}
return [
`<b>Refoulement</b>: ${this.system.refoulement}`,
`<b>Catégorie</b>: ${label(this.system.categorie)}`,
`<b>Affecte</b>: ${this.system.hautrevant ? 'les haut-rêvants' : 'tout le monde'}`,
]
return properties;
}
/* -------------------------------------------- */
_ombreChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Refoulement</b>: ${tplData.refoulement}`
]
return properties;
return this._queueChatData()
}
/* -------------------------------------------- */
_souffleChatData() {
const tplData = Misc.templateData(this);
let properties = [];
return properties;
return [
`<b>Affecte</b>: ${this.system.hautrevant ? 'les haut-rêvants' : 'tout le monde'}`,
];
}
/* -------------------------------------------- */
_teteChatData() {
const tplData = Misc.templateData(this);
let properties = [];
return properties;
return [
`<b>Affecte</b>: ${this.system.hautrevant ? 'les haut-rêvants' : 'tout le monde'}`,
];
}
/* -------------------------------------------- */
_tarotChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Concept</b>: ${tplData.concept}`,
`<b>Aspect</b>: ${tplData.aspect}`,
return [
RdDItem.propertyIfDefined('Carte', RdDUtility.linkCompendium(this.pack, this.id, this.name), this.pack),
`<b>Concept</b>: ${this.system.concept}`,
`<b>Aspect</b>: ${this.system.aspect}`,
]
return properties;
}
/* -------------------------------------------- */
_nombreastralChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Valeur</b>: ${tplData.value}`,
`<b>Jour</b>: ${tplData.jourlabel}`,
return [
`<b>Valeur</b>: ${this.system.value}`,
`<b>Jour</b>: ${this.system.jourlabel}`,
]
return properties;
}
/* -------------------------------------------- */
_monnaieChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Valeur en Deniers</b>: ${tplData.valeur_deniers}`,
`<b>Encombrement</b>: ${tplData.encombrement}`
]
return properties;
return this._inventaireTemplateChatData()
}
/* -------------------------------------------- */
_meditationChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Thème</b>: ${tplData.theme}`,
`<b>Compétence</b>: ${tplData.competence}`,
`<b>Support</b>: ${tplData.support}`,
`<b>Heure</b>: ${tplData.heure}`,
`<b>Purification</b>: ${tplData.purification}`,
`<b>Vêture</b>: ${tplData.veture}`,
`<b>Comportement</b>: ${tplData.comportement}`,
`<b>Case TMR</b>: ${tplData.tmr}`
return [
`<b>Thème</b>: ${this.system.theme}`,
`<b>Compétence</b>: ${this.system.competence}`,
`<b>Support</b>: ${this.system.support}`,
`<b>Heure</b>: ${this.system.heure}`,
`<b>Purification</b>: ${this.system.purification}`,
`<b>Vêture</b>: ${this.system.veture}`,
`<b>Comportement</b>: ${this.system.comportement}`,
`<b>Case TMR</b>: ${this.system.tmr}`
]
}
/* -------------------------------------------- */
_rencontreChatData() {
if (this.system.coord) {
return [
`<b>Force</b>: ${this.system.force}`,
`<b>Coordonnées</b>: ${this.system.coord}`,
]
}
return [
`<b>Force</b>: ${this.system.formule}`,
`<b>Refoulement</b>: ${this.system.refoulement}`,
RdDItem.propertyIfDefined('<b>Présent de cités</b>', '', this.system.presentCite),
]
return properties;
}
/* -------------------------------------------- */
_casetmrChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Coordonnée</b>: ${tplData.coord}`,
`<b>Spécificité</b>: ${tplData.specific}`
return [
`<b>Coordonnée</b>: ${this.system.coord}`,
`<b>Spécificité</b>: ${this.system.specific}`
]
return properties;
}
/* -------------------------------------------- */
_maladieChatData() {
const tplData = Misc.templateData(this);
let properties
if (tplData.identifie) {
properties = [
`<b>Malignité</b>: ${tplData.malignite}`,
`<b>Périodicité</b>: ${tplData.periodicite}`,
`<b>Dommages</b>: ${tplData.dommages}`
if (!this.system.identifie) {
return [`<b>Inconnue</b>`]
}
return [
`<b>Malignité</b>: ${this.system.malignite}`,
`<b>Périodicité</b>: ${this.system.periodicite}`,
`<b>Dommages</b>: ${this.system.dommages}`,
RdDItem.propertyIfDefined('<b>Remedes</b>', this.system.remedes, this.system.remedesconnus),
]
if (tplData.remedesconnus) {
properties.push(`<b>Remedes</b>: ${tplData.remedes}`)
}
} else {
properties = [
`<b>Inconnue</b>`]
}
return properties;
}
/* -------------------------------------------- */
@ -541,16 +698,13 @@ export class RdDItem extends Item {
/* -------------------------------------------- */
_gemmeChatData() {
const tplData = Misc.templateData(this);
let properties = [
`<b>Pureté</b>: ${tplData.purete}`,
`<b>Taille</b>: ${tplData.taille}`,
`<b>Inertie</b>: ${tplData.inertie}`,
`<b>Enchantabilité</b>: ${tplData.enchantabilite}`,
`<b>Prix</b>: ${tplData.cout}`,
return [
`<b>Pureté</b>: ${this.system.purete}`,
`<b>Taille</b>: ${this.system.taille}`,
`<b>Inertie</b>: ${this.system.inertie}`,
`<b>Enchantabilité</b>: ${this.system.enchantabilite}`,
...this._inventaireTemplateChatData()
]
return properties;
}
}

324
module/migrations.js Normal file
View File

@ -0,0 +1,324 @@
import { LOG_HEAD, SYSTEM_RDD } from "./constants.js";
import { Environnement } from "./environnement.js";
import { Grammar } from "./grammar.js";
class Migration {
get code() { return "sample"; }
get version() { return "0.0.0"; }
async migrate() { }
async applyItemsUpdates(computeUpdates) {
await game.actors.forEach(async (actor) => {
const actorItemUpdates = computeUpdates(actor.items);
if (actorItemUpdates.length > 0) {
console.log(
this.code,
`Applying updates on actor ${actor.name} items`,
actorItemUpdates
);
await actor.updateEmbeddedDocuments("Item", actorItemUpdates);
}
});
const itemUpdates = computeUpdates(game.items);
if (itemUpdates.length > 0) {
console.log(this.code, "Applying updates on items", itemUpdates);
await Item.updateDocuments(itemUpdates);
}
}
}
class _10_0_16_MigrationSortsReserve extends Migration {
get code() { return "creation-item-sort-reserve"; }
get version() { return "10.0.16"; }
async migrate() {
await game.actors
.filter((actor) => actor.type == "personnage")
.filter((actor) => actor.system.reve?.reserve?.list?.length ?? 0 > 0)
.forEach(async (actor) => {
const sortsReserve = actor.system.reve.reserve.list.map(this.conversionSortReserve);
console.log(`${LOG_HEAD} Migration des sorts en réserve de ${actor.name}`, sortsReserve);
await actor.createEmbeddedDocuments("Item", sortsReserve, {
renderSheet: false,
});
await actor.update({ 'system.reve.reserve': undefined })
});
}
conversionSortReserve(it) {
return {
type: 'sortreserve',
name: it.sort.name,
img: it.sort.img,
system: {
// ATTENTION, utilisation de data / _id possibles, encore présents pour les anciens sorts en réserve
sortid: it.sort._id,
draconic: it.sort.draconic,
ptreve: (it.sort.system ?? it.sort.data).ptreve_reel,
coord: it.coord,
heurecible: 'Vaisseau',
},
};
}
}
class _10_0_17_MigrationCompetenceCreature extends Migration {
get code() { return "competences-creature-parade"; }
get version() { return "10.0.17"; }
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => it.type == "competencecreature" && it.system.isparade && it.system.categorie_parade == "")
.map(it => { return { _id: it.id, "system.categorie_parade": "armes-naturelles" } }));
await this.applyItemsUpdates(items => items
.filter(it => it.type == "competencecreature" && it.system.iscombat)
.map(it => { return { _id: it.id, "system.categorie": (Grammar.includesLowerCaseNoAccent(it.name, "lancee") ? "lancer" : "melee") } })
);
}
}
class _10_0_21_VehiculeStructureResistanceMax extends Migration {
get code() { return "vehicule-structure-resistance-max"; }
get version() { return "10.0.21"; }
async migrate() {
await game.actors
.filter((actor) => actor.type == "vehicule")
.forEach(async (actor) => {
await actor.update({
'system.etat.resistance.value': actor.system.resistance,
'system.etat.resistance.max': actor.system.resistance,
'system.etat.structure.value': actor.system.structure,
'system.etat.structure.max': actor.system.structure
})
});
}
}
class _10_0_33_MigrationNomsDraconic extends Migration {
get code() { return "competences-creature-parade"; }
get version() { return "10.0.33"; }
migrationNomDraconic(ancien) {
if (typeof ancien == 'string') {
switch (ancien) {
case 'oniros': case "Voie d'Oniros": return "Voie d'Oniros";
case 'hypnos': case "Voie d'Hypnos": return "Voie d'Hypnos";
case 'narcos': case "Voie de Narcos": return "Voie de Narcos";
case 'thanatos': case "Voie de Thanatos": return "Voie de Thanatos";
}
return ancien;
}
else if (typeof ancien.name == 'string') {
return this.migrationNomDraconic(ancien.name)
}
return ancien;
}
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => ["sort", "sortreserve"].includes(it.type)
&& (typeof it.system.draconic == 'string') || (typeof it.system.draconic?.name == 'string'))
.map(it => { return { _id: it.id, "system.draconic": this.migrationNomDraconic(it.system.draconic) } }));
}
}
class _10_2_5_ArmesTirLancer extends Migration {
constructor() {
super();
this.dagues = { "system.competence": 'Dague', "system.lancer": 'Dague de jet', "system.portee_courte": 3, "system.portee_moyenne": 8, "system.portee_extreme": 15 }
this.javelot = { "system.competence": 'Lance', "system.lancer": 'Javelot', "system.portee_courte": 6, "system.portee_moyenne": 12, "system.portee_extreme": 20 }
this.fouet = { "system.competence": '', "system.lancer": 'Fouet', "system.portee_courte": 2, "system.portee_moyenne": 2, "system.portee_extreme": 3, "system.penetration": -1 }
this.arc = { "system.competence": '', "system.tir": 'Arc' }
this.arbalete = { "system.competence": '', "system.tir": 'Arbalète' }
this.fronde = { "system.competence": '', "system.tir": 'Fronde' }
this.mappings = {
'dague': { filter: it => true, updates: this.dagues },
'dague de jet': { filter: it => true, updates: this.dagues },
'javelot': { filter: it => true, updates: this.javelot },
'lance': { filter: it => it.name == 'Javeline', updates: this.javelot },
'fouet': { filter: it => true, updates: this.fouet },
'arc': { filter: it => true, updates: this.arc },
'arbalete': { filter: it => true, updates: this.arbalete },
'fronde': { filter: it => true, updates: this.fronde },
}
}
get code() { return "separation-competences-tir-lancer"; }
get version() { return "10.2.5"; }
migrateArmeTirLancer(it) {
let updates = mergeObject({ _id: it.id }, this.getMapping(it).updates);
console.log(it.name, updates);
return updates;
}
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => "arme" == it.type)
.filter(it => this.isTirLancer(it))
.filter(it => this.getMapping(it).filter(it))
.map(it => this.migrateArmeTirLancer(it)));
}
isTirLancer(it) {
return Object.keys(this.mappings).includes(this.getCompKey(it));
}
getMapping(it) {
return this.mappings[this.getCompKey(it)];
}
getCompKey(it) {
return Grammar.toLowerCaseNoAccent(it.system.competence);
}
}
class _10_2_10_DesirLancinant_IdeeFixe extends Migration {
get code() { return "desir-lancinat-idee-fixe"; }
get version() { return "10.2.10"; }
migrateQueue(it) {
let categorie = undefined
let name = it.name
if (Grammar.toLowerCaseNoAccent(name).includes('desir')) {
categorie = 'lancinant';
name = it.name.replace('Désir lancinant : ', '');
}
if (Grammar.toLowerCaseNoAccent(name).includes('idee fixe')) {
categorie = 'ideefixe';
name = it.name.replace('Idée fixe : ', '')
}
return {
_id: it.id, name: name,
'system.ideefixe': undefined,
'system.lancinant': undefined,
'system.categorie': categorie
}
}
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => ['queue', 'ombre'].includes(it.type))
.map(it => this.migrateQueue(it))
);
}
}
class _10_3_0_Inventaire extends Migration {
get code() { return "migration-equipement-inventaire"; }
get version() { return "10.3.0"; }
async migrate() {
await this.applyItemsUpdates(items => {
return this._updatesMonnaies(items)
.concat(this._updatesNonEquipe(items))
.concat(this._updatesObjets(items))
});
}
_updatesNonEquipe(items) {
return items
.filter(it => ['munition'].includes(it.type))
.map(it => { return { _id: it.id, 'system.equipe': undefined } });
}
_updatesObjets(items) {
return items
.filter(it => ['objet'].includes(it.type))
.map(it => { return { _id: it.id, 'system.resistance': undefined, 'system.equipe': undefined } });
}
_updatesMonnaies(items) {
return items
.filter(it => ['monnaie'].includes(it.type) && it.system.cout == undefined)
.map(it => { return { _id: it.id, 'system.cout': it.system.valeur_deniers / 100, 'system.valeur_deniers': undefined } });
}
}
class _10_3_0_FrequenceEnvironnement extends Migration {
get code() { return "migration-frequence-resources"; }
get version() { return "10.3.0"; }
async migrate() {
await this.applyItemsUpdates(items => items.filter(it => ['herbe', 'ingredient'].includes(it.type))
.map(it => this._updatesFrequences(it)));
}
_updatesFrequences(it) {
return {
_id: it.id,
'system.rarete': undefined,
'system.environnement': [{ milieu: it.system.milieu, rarete: it.system.rarete, frequence: Environnement.getFrequenceRarete(it.system.rarete, 'frequence') }]
}
}
}
export class Migrations {
static getMigrations() {
return [
new _10_0_16_MigrationSortsReserve(),
new _10_0_17_MigrationCompetenceCreature(),
new _10_0_21_VehiculeStructureResistanceMax(),
new _10_0_33_MigrationNomsDraconic(),
new _10_2_5_ArmesTirLancer(),
new _10_2_10_DesirLancinant_IdeeFixe(),
new _10_3_0_Inventaire(),
new _10_3_0_FrequenceEnvironnement()
];
}
constructor() {
game.settings.register(SYSTEM_RDD, "systemMigrationVersion", {
name: "System Migration Version",
scope: "world",
config: false,
type: String,
default: "0.0.0",
});
}
migrate() {
const currentVersion = game.settings.get(SYSTEM_RDD,"systemMigrationVersion");
//if (isNewerVersion(game.system.version, currentVersion)) {
if (true) { /* comment previous and uncomment here to test before upgrade */
const migrations = Migrations.getMigrations().filter(m => isNewerVersion(m.version, currentVersion));
if (migrations.length > 0) {
migrations.sort((a, b) =>
isNewerVersion(a.version, b.version)
? 1
: isNewerVersion(b.version, a.version)
? -1
: 0
);
migrations.forEach(async (m) => {
ui.notifications.info(
`Executing migration ${m.code}: version ${currentVersion} is lower than ${m.version}`
);
await m.migrate();
});
ui.notifications.info(
`Migrations done, version will change to ${game.system.version}`
);
} else {
console.log(
LOG_HEAD +
`No migration needeed, version will change to ${game.system.version}`
);
}
game.settings.set(
SYSTEM_RDD,
"systemMigrationVersion",
game.system.version
);
} else {
console.log(LOG_HEAD + `No system version changed`);
}
}
}

View File

@ -24,7 +24,7 @@ export class Misc {
}
static sum() {
return (a, b) => a + b;
return (a, b) => Number(a) + Number(b);
}
static ascending(orderFunction = x => x) {
@ -41,6 +41,11 @@ export class Misc {
return 0;
}
static typeName(type, subType) {
return subType ? game.i18n.localize(`${type.toUpperCase()}.Type${Misc.upperFirst(subType)}`)
: '';
}
/**
* Converts the value to an integer, or to 0 if undefined/null/not representing integer
* @param {*} value value to convert to an integer using parseInt
@ -68,10 +73,23 @@ export class Misc {
}
}
static indexLowercase(list) {
const obj = {};
const addToObj = (map, val) => {
const key = Grammar.toLowerCaseNoAccent(val);
if (key && !map[key]) map[key] = val
}
list.forEach(it => addToObj(obj, it))
return obj;
}
static concat(lists) {
return lists.reduce((a, b) => a.concat(b), []);
}
static classify(items, classifier = it => it.type) {
let itemsBy = {};
Misc.classifyInto(itemsBy, items, classifier);
return itemsBy;
let itemsBy = {}
Misc.classifyInto(itemsBy, items, classifier)
return itemsBy
}
static classifyFirst(items, classifier) {
@ -87,13 +105,13 @@ export class Misc {
static classifyInto(itemsBy, items, classifier = it => it.type) {
for (const item of items) {
const classification = classifier(item);
const classification = classifier(item)
let list = itemsBy[classification];
if (!list) {
list = [];
itemsBy[classification] = list;
list = []
itemsBy[classification] = list
}
list.push(item);
list.push(item)
}
}
@ -102,29 +120,11 @@ export class Misc {
}
static join(params, separator = '') {
return params.reduce((a, b) => a + separator + b);
return params?.reduce(Misc.joining(separator)) ?? '';
}
static data(it) {
if (it instanceof Actor || it instanceof Item || it instanceof Combatant) {
return it.data;
}
return it;
}
static templateData(it) {
return Misc.data(it)?.data ?? {}
}
static getEntityTypeLabel(entity) {
const documentName = entity?.documentName;
const type = entity?.data.type;
if (documentName === 'Actor' || documentName === 'Item') {
const label = CONFIG[documentName]?.typeLabels?.[type] ?? type;
return game.i18n.has(label) ? game.i18n.localize(label) : t;
}
return type;
static joining(separator = '') {
return (a, b) => a + separator + b;
}
static connectedGMOrUser(ownerId = undefined) {
@ -134,17 +134,20 @@ export class Misc {
return Misc.firstConnectedGM()?.id ?? game.user.id;
}
static getUsers() {
return game.version ? game.users : game.users.entities;
static isRollModeHiddenToPlayer() {
switch (game.settings.get("core", "rollMode")) {
case CONST.DICE_ROLL_MODES.BLIND:
case CONST.DICE_ROLL_MODES.SELF: return true;
}
return false
}
static getActiveUser(id) {
return Misc.getUsers().find(u => u.id == id && u.active);
return game.users.find(u => u.id == id && u.active);
}
static firstConnectedGM() {
return Misc.getUsers().filter(u => u.isGM && u.active).sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
return game.users.filter(u => u.isGM && u.active).sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
}
static isOwnerPlayer(actor, user=undefined) {
@ -159,12 +162,16 @@ export class Misc {
* @returns true pour un seul utilisateur: le premier GM connecté par ordre d'id
*/
static isUniqueConnectedGM() {
return game.user.id == Misc.firstConnectedGM()?.id;
return game.user.id == Misc.firstConnectedGMId();
}
static firstConnectedGMId() {
return Misc.firstConnectedGM()?.id;
}
/* -------------------------------------------- */
static findPlayer(name) {
return Misc.findFirstLike(name, Misc.getUsers(), { description: 'joueur' });
return Misc.findFirstLike(name, game.users, { description: 'joueur' });
}
/* -------------------------------------------- */

View File

@ -1,72 +1,13 @@
import { Misc } from "./misc.js"
import { RdDDice } from "./rdd-dice.js";
const poesieHautReve = [
{
reference: 'Le Ratier Bretonien',
extrait: `Le courant du Fleuve
<br>Te domine et te Porte
<br>Avant que tu te moeuves
<br>Combat le, ou il t'emporte`
},
{
reference: 'Incompatibilité, Charles Beaudelaire',
extrait: `Et lorsque par hasard une nuée errante
<br>Assombrit dans son vol le lac silencieux,
<br>On croirait voir la robe ou l'ombre transparente
<br>D'un esprit qui voyage et passe dans les cieux.`
},
{
reference: 'Au fleuve de Loire, Joachim du Bellay',
extrait: `Ô de qui la vive course
<br>Prend sa bienheureuse source,
<br>Dune argentine fontaine,
<br>Qui dune fuite lointaine,
<br>Te rends au sein fluctueux
<br>De lOcéan monstrueux`
},
{
reference: 'Denis Gerfaud',
extrait: `Et l'on peut savoir qui est le maître d'Oniros, c'est le Fleuve de l'Oubli.
Et l'on sait qui est le créateur du Fleuve de l'Oubli, c'est Hypnos et Narcos.
Mais l'on ne sait pas qui est le maître du Fleuve de l'Oubli,
sinon peut-être lui-même, ou peut-être Thanatos` },
{
reference: 'Denis Gerfaud',
extrait: `Narcos est la source du Fleuve de l'Oubli et Hypnos l'embouchure
Remonter le Fleuve est la Voie de la Nuit, la Voie du Souvenir.
Descendre le Fleuve est la Voie du Jour, la Voie de l'Oubli`
},
{
reference: 'Denis Gerfaud',
extrait: `Narcos engendre le fils dont il est la mère à l'heure du Vaisseau,
car Oniros s'embarque pour redescendre le Fleuve
vers son père Hypnos sur la Voie de l'Oubli`
},
{
reference: 'Denis Gerfaud',
extrait: `Hypnos engendre le fils dont il est la mère à l'heure du Serpent, car
tel les serpents, Oniros commence à remonter le Fleuve
sur le Voie du Souvenir vers son père Narcos`
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi se succèdent les Jours et les Ages.
<br>Les jours des Dragons sont les Ages des Hommes.`
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi parlent les sages:
&laquo;Les Dragons sont créateurs de leurs rêves, mais ils ne sont pas créateurs d'Oniros
Les Dragons ne sont pas les maîtres de leurs rêvezs, car ils ne sont pas maîtres d'Oniros.
Nul ne sait qui est le créateur des Dragons, ni qui est leur maître.
Mais l'on peut supposer qui est le maître du Rêve des Dragons, c'est Oniros&raquo;`
},
]
import { SystemCompendiums } from "./settings/system-compendiums.js";
export class Poetique {
static async getExtrait(){
return await RdDDice.rollOneOf(poesieHautReve);
static async getExtrait() {
const items = await SystemCompendiums.getItems('extrait-poetique', 'extraitpoetique')
const selected = await RdDDice.rollOneOf(items);
return {
reference: selected?.name,
extrait: selected?.system.extrait
}
}
}

View File

@ -1,44 +0,0 @@
Le courant du Fleuve
Te domine et te Porte
Avant que tu te moeuves
Combat le, ou il t'emporte
A vous qui faites ripaille
sourds aux damnés de la faim
à vous qui livrez
une inégale bataille
à ceux qui vous tendent la main
Ils sont tout près ! - Tenons fermée
<br>Cette salle, où nous les narguons.
<br>Quel bruit dehors ! Hideuse armée
<br>De vampires et de dragons !
<br>La poutre du toit descellée
<br>Ploie ainsi qu'une herbe mouillée,
<br>Et la vieille porte rouillée
<br>Tremble, à déraciner ses gonds !`),
https://www.poetica.fr/poeme-1423/guy-de-maupassant-le-sommeil-du-mandarin/
Le monde est un rêve de Dragons. Nous
ne savons pas qui sont les Dragons ni à quoi
ils ressemblent, en dépit de lantique iconographie qui les dépeint comme de gigantesques créatures ailées capables de cracher
feu et flammes.
Car parmi les humains, autre nom lui est donné,
Nom sinistre parmi tous, nom funèbre, c'est la mort!
Un ami disparu... Thanatos est passé...
Messieurs, ne crachez pas de jurons ni d'ordure
Au visage fardé de cette pauvre impure
Que déesse Famine a par un soir d'hiver,
Contrainte à relever ses jupons en plein air.

View File

@ -7,9 +7,9 @@ const matchOperationTerms = new RegExp(/@(\w*){([\w\-]+)}/i);
export class RdDAlchimie {
/* -------------------------------------------- */
static processManipulation(recetteData, actorId = undefined) {
static processManipulation(recette, actorId = undefined) {
//console.log("CALLED", recette, recette.isOwned, actorId );
let manip = recetteData.data.manipulation;
let manip = recette.system.manipulation;
let matchArray = manip.match(matchOperations);
if (matchArray) {
for (let matchStr of matchArray) {
@ -17,12 +17,12 @@ export class RdDAlchimie {
//console.log("RESULT ", result);
if (result[1] && result[2]) {
let commande = Misc.upperFirst(result[1]);
let replacement = this[`_alchimie${commande}`](recetteData, result[2], actorId);
let replacement = this[`_alchimie${commande}`](recette, result[2], actorId);
manip = manip.replace(result[0], replacement);
}
}
}
recetteData.data.manipulation_update = manip;
recette.system.manipulation_update = manip;
}
/* -------------------------------------------- */

View File

@ -15,13 +15,19 @@
// Common conf
let dialogConf = { content: html, title: "Editeur d'Astrologie", buttons: myButtons, default: "saveButton" };
let dialogOptions = { classes: ["rdddialog"], width: 600, height: 300, 'z-index': 99999 }
let dialogOptions = { classes: ["rdd-roll-dialog"], width: 600, height: 300, 'z-index': 99999 }
super(dialogConf, dialogOptions)
this.calendrier = calendrier;
this.updateData( calendrierData );
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
}
/* -------------------------------------------- */
async resetNombreAstraux() {
game.system.rdd.calendrier.resetNombreAstral();
@ -39,15 +45,4 @@
this.calendrierData = duplicate(calendrierData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
let astrologieData = this.astrologieData;
$(function () {
});
}
}

View File

@ -12,36 +12,50 @@ export class RdDAstrologieJoueur extends Dialog {
/* -------------------------------------------- */
static async create(actor, dialogConfig) {
let data = {
let dialogData = {
nombres: this.organizeNombres(actor),
dates: game.system.rdd.calendrier.getJoursSuivants(10),
etat: actor.getEtatGeneral(),
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
astrologie: RdDItemCompetence.findCompetence(actor.data.items, 'Astrologie')
astrologie: RdDItemCompetence.findCompetence(actor.items, 'Astrologie')
}
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-astrologie-joueur.html', data);
let options = { classes: ["rdddialog"], width: 600, height: 500, 'z-index': 99999 };
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-astrologie-joueur.html', dialogData);
let options = { classes: ["rdd-roll-dialog"], width: 600, height: 500, 'z-index': 99999 };
if (dialogConfig.options) {
mergeObject(options, dialogConfig.options, { overwrite: true });
}
return new RdDAstrologieJoueur(html, actor, data);
return new RdDAstrologieJoueur(html, actor, dialogData);
}
/* -------------------------------------------- */
constructor(html, actor, data) {
let myButtons = {
constructor(html, actor, dialogData) {
const dialogConf = {
title: "Nombres Astraux",
content: html,
default: "saveButton",
buttons: {
saveButton: { label: "Fermer", callback: html => this.quitDialog() }
},
};
// Get all n
// Common conf
let dialogConf = { content: html, title: "Nombres Astraux", buttons: myButtons, default: "saveButton" };
let dialogOptions = { classes: ["rdddialog"], width: 600, height: 300, 'z-index': 99999 };
const dialogOptions = { classes: ["rdd-roll-dialog"], width: 600, height: 300, 'z-index': 99999 };
super(dialogConf, dialogOptions);
this.actor = actor;
this.dataNombreAstral = duplicate(data);
this.dataNombreAstral = duplicate(dialogData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find(function () {
this.html.find("[name='diffConditions']").val(0);
});
this.html.find('[name="jet-astrologie"]').click((event) => {
this.requestJetAstrologie();
});
}
/* -------------------------------------------- */
@ -49,12 +63,12 @@ export class RdDAstrologieJoueur extends Dialog {
let itemNombres = actor.listItemsData('nombreastral');
let itemFiltered = {};
for (let item of itemNombres) {
if (itemFiltered[item.data.jourindex]) {
itemFiltered[item.data.jourindex].listValues.push(item.data.value);
if (itemFiltered[item.system.jourindex]) {
itemFiltered[item.system.jourindex].listValues.push(item.system.value);
} else {
itemFiltered[item.data.jourindex] = {
listValues: [item.data.value],
jourlabel: item.data.jourlabel
itemFiltered[item.system.jourindex] = {
listValues: [item.system.value],
jourlabel: item.system.jourlabel
}
}
}
@ -63,21 +77,21 @@ export class RdDAstrologieJoueur extends Dialog {
/* -------------------------------------------- */
requestJetAstrologie() {
let data = {
id: this.actor.data._id,
carac_vue: Misc.data(this.actor).data.carac['vue'].value,
let socketData = {
id: this.actor.id,
carac_vue: this.actor.system.carac['vue'].value,
etat: this.dataNombreAstral.etat,
astrologie: this.dataNombreAstral.astrologie,
conditions: $("#diffConditions").val(),
date: $("#joursAstrologie").val(),
conditions: this.html.find('[name="diffConditions"]').val(),
date: this.html.find('[name="joursAstrologie"]').val(),
userId: game.user.id
}
if (Misc.isUniqueConnectedGM()) {
game.system.rdd.calendrier.requestNombreAstral(data);
game.system.rdd.calendrier.requestNombreAstral(socketData);
} else {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_request_nombre_astral",
data: data
data: socketData
});
}
this.close();
@ -87,17 +101,4 @@ export class RdDAstrologieJoueur extends Dialog {
quitDialog() {
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
$(function () {
$("#diffConditions").val(0);
});
html.find('#jet-astrologie').click((event) => {
this.requestJetAstrologie();
});
}
}

View File

@ -20,7 +20,7 @@ export class RdDBonus {
static isAjustementAstrologique(rollData) {
return RdDCarac.isChance(rollData.selectedCarac) ||
rollData.selectedSort?.data.isrituel;
rollData.selectedSort?.system.isrituel;
}
/* -------------------------------------------- */
static isDefenseAttaqueFinesse(rollData) {
@ -68,23 +68,23 @@ export class RdDBonus {
}
return isCauchemar ? "cauchemar"
: rollData.dmg?.mortalite
?? rollData.arme?.data.mortalite
?? rollData.arme?.system.mortalite
?? "mortel";
}
/* -------------------------------------------- */
static _dmgArme(rollData) {
if ( rollData.arme) {
let dmgBase = rollData.arme.data.dommagesReels ?? Number(rollData.arme.data.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)
return dmgBase + Math.min(dmgBase, rollData.arme.data.magique ? rollData.arme.data.ecaille_efficacite : 0);
return dmgBase + Math.min(dmgBase, rollData.arme.system.magique ? rollData.arme.system.ecaille_efficacite : 0);
}
return 0;
}
/* -------------------------------------------- */
static _peneration(rollData) {
return parseInt(rollData.arme?.data.penetration ?? 0);
return parseInt(rollData.arme?.system.penetration ?? 0);
}
/* -------------------------------------------- */

View File

@ -8,50 +8,45 @@ export class RdDCalendrierEditeur extends Dialog {
/* -------------------------------------------- */
constructor(html, calendrier, calendrierData) {
let myButtons = {
saveButton: { label: "Enregistrer", callback: html => this.fillData() }
let dialogConf = {
content: html,
title: "Editeur de date/heure",
buttons: {
save: { label: "Enregistrer", callback: html => this.fillData() }
},
default: "save"
};
// Common conf
let dialogConf = { content: html, title: "Editeur de date/heure", buttons: myButtons, default: "saveButton" };
let dialogOptions = { classes: ["rdddialog"], width: 400, height: 300, 'z-index': 99999 }
let dialogOptions = { classes: ["rdd-dialog-calendar-editor"], width: 400, height: 'fit-content', 'z-index': 99999 }
super(dialogConf, dialogOptions)
this.calendrier = calendrier;
this.calendrierData = calendrierData; //duplicate(calendrierData);
this.calendrierData = calendrierData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("input[name='nomMois']").val(this.calendrierData.moisKey);
this.html.find("select[name='nomHeure']").val(this.calendrierData.heureKey);
this.html.find("select[name='jourMois']").val(this.calendrierData.jourMois);
this.html.find("select[name='minutesRelative']").val(calendrierData.minutesRelative);
this.html.find("select[name='annee']").val(this.calendrierData.annee);
}
/* -------------------------------------------- */
fillData( ) {
this.calendrierData.moisKey = $("#nomMois").val();
this.calendrierData.heureKey = $("#nomHeure").val();
this.calendrierData.jourMois = $("#jourMois").val();
this.calendrierData.minutesRelative = $("#minutesRelative").val();
fillData() {
this.calendrierData.annee = this.html.find("input[name='annee']").val();
this.calendrierData.moisKey = this.html.find("select[name='nomMois']").val();
this.calendrierData.heureKey = this.html.find("select[name='nomHeure']").val();
this.calendrierData.jourMois = this.html.find("select[name='jourMois']").val();
this.calendrierData.minutesRelative = this.html.find("select[name='minutesRelative']").val();
console.log("UPDATE ", this.calendrierData);
this.calendrier.saveEditeur( this.calendrierData )
this.calendrier.saveEditeur(this.calendrierData)
}
/* -------------------------------------------- */
updateData( calendrierData ) {
updateData(calendrierData) {
this.calendrierData = duplicate(calendrierData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
let calendrierData = this.calendrierData;
$(function () {
console.log(calendrierData);
$("#nomMois").val(calendrierData.moisKey);
$("#nomHeure").val(calendrierData.heureKey);
$("#jourMois").val(calendrierData.jourMois);
$("#minutesRelative").val(calendrierData.minutesRelative);
});
}
}

View File

@ -8,23 +8,24 @@ import { Grammar } from "./grammar.js";
import { RdDDice } from "./rdd-dice.js";
import { Misc } from "./misc.js";
import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { DialogChronologie } from "./dialog-chronologie.js";
/* -------------------------------------------- */
const dossierIconesHeures = 'systems/foundryvtt-reve-de-dragon/icons/heures/'
const heuresList = ["vaisseau", "sirene", "faucon", "couronne", "dragon", "epees", "lyre", "serpent", "poissonacrobate", "araignee", "roseau", "chateaudormant"];
const heuresDef = {
"vaisseau": { label: "Vaisseau", lettreFont: 'v', saison: "printemps", heure: 0, icon: 'hd01.svg' },
"sirene": { label: "Sirène", lettreFont: 'i', saison: "printemps", heure: 1, icon: 'hd02.svg' },
"faucon": { label: "Faucon", lettreFont: 'f', saison: "printemps", heure: 2, icon: 'hd03.svg' },
"couronne": { label: "Couronne", lettreFont: '', saison: "ete", heure: 3, icon: 'hd04.svg' },
"dragon": { label: "Dragon", lettreFont: 'd', saison: "ete", heure: 4, icon: 'hd05.svg' },
"epees": { label: "Epées", lettreFont: 'e', saison: "ete", heure: 5, icon: 'hd06.svg' },
"lyre": { label: "Lyre", lettreFont: 'l', saison: "automne", heure: 6, icon: 'hd07.svg' },
"serpent": { label: "Serpent", lettreFont: 's', saison: "automne", heure: 7, icon: 'hd08.svg' },
"poissonacrobate": { label: "Poisson Acrobate", lettreFont: 'p', saison: "automne", heure: 8, icon: 'hd09.svg' },
"araignee": { label: "Araignée", lettreFont: 'a', saison: "hiver", heure: 9, icon: 'hd10.svg' },
"roseau": { label: "Roseau", lettreFont: 'r', saison: "hiver", heure: 10, icon: 'hd11.svg' },
"chateaudormant": { label: "Château Dormant", lettreFont: 'c', saison: "hiver", heure: 11, icon: 'hd12.svg' }
"vaisseau": {key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "printemps", heure: 0, icon: 'hd01.svg' },
"sirene": { key: "sirene", label: "Sirène", lettreFont: 'i', saison: "printemps", heure: 1, icon: 'hd02.svg' },
"faucon": { key: "faucon", label: "Faucon", lettreFont: 'f', saison: "printemps", heure: 2, icon: 'hd03.svg' },
"couronne": { key: "couronne", label: "Couronne", lettreFont: '', saison: "ete", heure: 3, icon: 'hd04.svg' },
"dragon": { key: "dragon", label: "Dragon", lettreFont: 'd', saison: "ete", heure: 4, icon: 'hd05.svg' },
"epees": { key: "epees", label: "Epées", lettreFont: 'e', saison: "ete", heure: 5, icon: 'hd06.svg' },
"lyre": { key: "lyre", label: "Lyre", lettreFont: 'l', saison: "automne", heure: 6, icon: 'hd07.svg' },
"serpent": { key: "serpent", label: "Serpent", lettreFont: 's', saison: "automne", heure: 7, icon: 'hd08.svg' },
"poissonacrobate": { key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "automne", heure: 8, icon: 'hd09.svg' },
"araignee": { key: "araignee", label: "Araignée", lettreFont: 'a', saison: "hiver", heure: 9, icon: 'hd10.svg' },
"roseau": { key: "roseau", label: "Roseau", lettreFont: 'r', saison: "hiver", heure: 10, icon: 'hd11.svg' },
"chateaudormant": { key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "hiver", heure: 11, icon: 'hd12.svg' }
};
const saisonsDef = {
"printemps": { label: "Printemps" },
@ -42,6 +43,14 @@ const MAX_NOMBRE_ASTRAL = 12;
/* -------------------------------------------- */
export class RdDCalendrier extends Application {
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html",
popOut: false,
resizable: false
});
}
static createCalendrierPos() {
return { top: 200, left: 200 };
}
@ -51,21 +60,50 @@ export class RdDCalendrier extends Application {
return Object.values(heuresDef).find(h => h.heure == chiffre);
}
static getSigneAs(key, value) {
const heure = (typeof value == 'string' || typeof value == 'number') && Number.isInteger(Number(value))
? Number(value)
: (typeof value == 'string') ? RdDCalendrier.getChiffreFromSigne(value)
: undefined
if (heure != undefined && ['key', 'label', 'lettreFont', 'saison', 'heure', 'icon'].includes(key)) {
return RdDCalendrier.getDefSigne(heure)[key]
}
if (heure != undefined && ['webp'].includes(key)) {
return RdDCalendrier.getDefSigne(heure)['icon'].replace('svg', 'webp');
}
console.error(`Appel à getSigneAs('${key}', ${value}) avec une clé/heure incorrects`);
return value;
}
static getChiffreFromSigne(signe) {
return heuresList.indexOf(signe);
}
static getCalendrier(index) {
let calendrier = {
static createCalendrierInitial() {
return {
heureRdD: 0,
minutesRelative: 0,
indexJour: 0,
annee: 0,
moisRdD: 0,
moisLabel: heuresDef["vaisseau"].label,
jour: 0
}
}
getCalendrier(index) {
index = index ?? this.getCurrentDayIndex();
const mois = Math.floor(index / RDD_JOUR_PAR_MOIS) % RDD_MOIS_PAR_AN;
return {
heureRdD: 0, // Index dans heuresList / heuresDef[x].heure
minutesRelative: 0,
indexJour: index,
annee: Math.floor(index / (RDD_JOUR_PAR_MOIS * RDD_MOIS_PAR_AN)),
moisRdD: Math.floor(index / RDD_JOUR_PAR_MOIS) % RDD_MOIS_PAR_AN,
moisRdD: RdDCalendrier.getDefSigne(mois).heure,
moisLabel: RdDCalendrier.getDefSigne(mois).label,
jour: (index % RDD_JOUR_PAR_MOIS) // Le calendrier stocke le jour en 0-27, mais en 1-28 à l'affichage
}
calendrier.moisLabel = RdDCalendrier.getDefSigne(calendrier.moisRdD).label;
return calendrier;
}
constructor() {
@ -78,7 +116,7 @@ export class RdDCalendrier extends Application {
}
// Calendrier
this.calendrier = duplicate(game.settings.get(SYSTEM_RDD, "calendrier") ?? RdDCalendrier.getCalendrier(0));
this.calendrier = duplicate(game.settings.get(SYSTEM_RDD, "calendrier") ?? RdDCalendrier.createCalendrierInitial());
this.calendrier.annee = this.calendrier.annee ?? Math.floor((this.calendrier.moisRdD ?? 0) / RDD_MOIS_PAR_AN);
this.calendrier.moisRdD = (this.calendrier.moisRdD ?? 0) % RDD_MOIS_PAR_AN;
@ -86,34 +124,117 @@ export class RdDCalendrier extends Application {
game.settings.set(SYSTEM_RDD, "calendrier", this.calendrier);
this.listeNombreAstral = this.getListeNombreAstral();
this.rebuildListeNombreAstral(false); // Ensure always up-to-date
this.rebuildListeNombreAstral(HIDE_DICE); // Ensure always up-to-date
}
console.log(this.calendrier, this.calendrierPos, this.listeNombreAstral);
console.log('RdDCalendrier.constructor()', this.calendrier, this.calendrierPos, this.listeNombreAstral);
}
/* -------------------------------------------- */
/** @override */
async activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.updateDisplay();
this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
this.html.find('.calendar-btn-edit').click(ev => {
ev.preventDefault();
this.showCalendarEditor();
});
this.html.find('.astrologie-btn-edit').click(ev => {
ev.preventDefault();
this.showAstrologieEditor();
});
this.html.find('#calendar-move-handle').mousedown(ev => {
ev.preventDefault();
ev = ev || window.event;
let isRightMB = false;
if ("which" in ev) { // Gecko (Firefox), WebKit (Safari/Chrome) & Opera
isRightMB = ev.which == 3;
} else if ("button" in ev) { // IE, Opera
isRightMB = ev.button == 2;
}
if (!isRightMB) {
dragElement(document.getElementById("calendar-time-container"));
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
function dragElement(elmnt) {
elmnt.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.bottom = undefined
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
elmnt.onmousedown = undefined;
document.onmouseup = undefined;
document.onmousemove = undefined;
let xPos = (elmnt.offsetLeft - pos1) > window.innerWidth ? window.innerWidth - 200 : (elmnt.offsetLeft - pos1);
let yPos = (elmnt.offsetTop - pos2) > window.innerHeight - 20 ? window.innerHeight - 100 : (elmnt.offsetTop - pos2)
xPos = xPos < 0 ? 0 : xPos;
yPos = yPos < 0 ? 0 : yPos;
if (xPos != (elmnt.offsetLeft - pos1) || yPos != (elmnt.offsetTop - pos2)) {
elmnt.style.top = (yPos) + "px";
elmnt.style.left = (xPos) + "px";
}
game.system.rdd.calendrier.calendrierPos.top = yPos;
game.system.rdd.calendrier.calendrierPos.left = xPos;
if (game.user.isGM) {
game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
}
}
} else if (isRightMB) {
game.system.rdd.calendrier.calendrierPos.top = 200;
game.system.rdd.calendrier.calendrierPos.left = 200;
if (game.user.isGM) {
game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
this.setPos(game.system.rdd.calendrier.calendrierPos);
}
});
}
/* -------------------------------------------- */
getListeNombreAstral() {
return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? [];
}
/* -------------------------------------------- */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html",
popOut: false,
resizable: false
});
}
/* -------------------------------------------- */
getDateFromIndex(index) {
const date = RdDCalendrier.getCalendrier(index ?? this.getCurrentDayIndex());
return (date.jour + 1) + ' ' + RdDCalendrier.getDefSigne(date.moisRdD).label;
const dateRdD = this.getCalendrier(index);
return (dateRdD.jour + 1) + ' ' + dateRdD.moisLabel;
}
/* -------------------------------------------- */
getNumericDateFromIndex(index = undefined) {
const dateRdD = RdDCalendrier.getCalendrier(index ?? this.getCurrentDayIndex());
getDayMonthFromIndex(index = undefined) {
const dateRdD = this.getCalendrier(index);
return {
day: dateRdD.jour + 1,
month: heuresList[dateRdD.moisRdD]
@ -180,14 +301,14 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
getNombreAstral(indexDate) {
let astralData = this.getListeNombreAstral().find((nombreAstral, i) => nombreAstral.index == indexDate);
const listNombreAstral = this.getListeNombreAstral();
let astralData = listNombreAstral.find((nombreAstral, i) => nombreAstral.index == indexDate);
return astralData?.nombreAstral;
}
/* -------------------------------------------- */
async rebuildListeNombreAstral(showDice = SHOW_DICE) {
async rebuildListeNombreAstral(showDice = HIDE_DICE) {
if (Misc.isUniqueConnectedGM()) {
console.log("rebuildListeNombreAstral", showDice);
let jourCourant = this.getCurrentDayIndex();
let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
@ -199,9 +320,8 @@ export class RdDCalendrier extends Application {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
}
}
//console.log("SAVE list", newList, jourCourant);
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", newList);
this.listeNombreAstral = newList;
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
}
}
@ -223,9 +343,9 @@ export class RdDCalendrier extends Application {
checkMaladie( periode) {
for (let actor of game.actors) {
if (actor.type == 'personnage') {
let maladies = actor.filterItems( item => (item.type == 'maladie' || (item.type == 'poison' && item.data.active) ) && item.data.periodicite.toLowerCase().includes(periode) );
let maladies = actor.items.filter( item => (item.type == 'maladie' || (item.type == 'poison' && item.system.active) ) && item.system.periodicite.toLowerCase().includes(periode) );
for (let maladie of maladies) {
if ( maladie.data.identifie) {
if ( maladie.system.identifie) {
ChatMessage.create({ content: `${actor.name} souffre de ${maladie.name} (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
} else {
ChatMessage.create({ content: `${actor.name} souffre d'un mal inconnu (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
@ -264,7 +384,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async incrementerJour() {
const index = this.getCurrentDayIndex() + 1;
this.calendrier = RdDCalendrier.getCalendrier(index);
this.calendrier = this.getCalendrier(index);
await this.rebuildListeNombreAstral();
}
@ -286,18 +406,15 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
fillCalendrierData(formData = {}) {
console.log(this.calendrier);
let moisKey = heuresList[this.calendrier.moisRdD];
let heureKey = heuresList[this.calendrier.heureRdD];
console.log(moisKey, heureKey);
const mois = RdDCalendrier.getDefSigne(this.calendrier.moisRdD);
const heure = RdDCalendrier.getDefSigne(this.calendrier.heureRdD);
console.log('fillCalendrierData', this.calendrier, mois, heure);
const mois = heuresDef[moisKey];
const heure = heuresDef[heureKey];
formData.heureKey = heureKey;
formData.moisKey = moisKey;
formData.heureKey = heure.key;
formData.moisKey = mois.key;
formData.jourMois = this.calendrier.jour + 1;
formData.nomMois = mois.label; // heures et mois nommés identiques
formData.annee = this.calendrier.annee;
formData.iconMois = dossierIconesHeures + mois.icon;
formData.nomHeure = heure.label;
formData.iconHeure = dossierIconesHeures + heure.icon;
@ -320,7 +437,7 @@ export class RdDCalendrier extends Application {
if (Misc.isUniqueConnectedGM()) { // Only once
console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date);
let niveau = Number(request.astrologie.data.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
let rollData = {
caracValue: request.carac_vue,
finalLevel: niveau,
@ -428,7 +545,7 @@ export class RdDCalendrier extends Application {
function check() {
let elmnt = document.getElementById("calendar-time-container");
if (elmnt) {
elmnt.style.bottom = null;
elmnt.style.bottom = undefined;
let xPos = (pos.left) > window.innerWidth ? window.innerWidth - 200 : pos.left;
let yPos = (pos.top) > window.innerHeight - 20 ? window.innerHeight - 100 : pos.top;
elmnt.style.top = (yPos) + "px";
@ -444,9 +561,9 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
updateDisplay() {
let data = this.fillCalendrierData();
// Rebuild data
let dateHTML = `Jour ${data.jourMois} de ${data.nomMois} (${data.nomSaison})`
let calendrier = this.fillCalendrierData();
// Rebuild text du calendrier
let dateHTML = `${calendrier.jourMois} ${calendrier.nomMois} ${calendrier.annee} (${calendrier.nomSaison})`
if (game.user.isGM) {
dateHTML = dateHTML + " - NA: " + (this.getCurrentNombreAstral() ?? "indéterminé");
}
@ -454,13 +571,13 @@ export class RdDCalendrier extends Application {
handle.innerHTML = dateHTML;
}
for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
heure.innerHTML = data.nomHeure;
heure.innerHTML = calendrier.nomHeure;
}
for (const minute of document.getElementsByClassName("calendar-time-disp")) {
minute.innerHTML = `${data.minutesRelative} minutes`;
minute.innerHTML = `${calendrier.minutesRelative} minutes`;
}
for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
heureImg.src = data.iconHeure;
heureImg.src = calendrier.iconHeure;
}
}
@ -469,6 +586,7 @@ export class RdDCalendrier extends Application {
this.calendrier.minutesRelative = Number(calendrierData.minutesRelative);
this.calendrier.jour = Number(calendrierData.jourMois) - 1;
this.calendrier.moisRdD = RdDCalendrier.getChiffreFromSigne(calendrierData.moisKey);
this.calendrier.annee = Number(calendrierData.annee);
this.calendrier.heureRdD = RdDCalendrier.getChiffreFromSigne(calendrierData.heureKey);
game.settings.set(SYSTEM_RDD, "calendrier", duplicate(this.calendrier));
@ -528,97 +646,4 @@ export class RdDCalendrier extends Application {
astrologieEditeur.updateData(calendrierData);
astrologieEditeur.render(true);
}
/* -------------------------------------------- */
/** @override */
async activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen($(".gm-only"), game.user.isGM);
await this.updateDisplay();
html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
html.find('.calendar-btn-edit').click(ev => {
ev.preventDefault();
this.showCalendarEditor();
});
html.find('.astrologie-btn-edit').click(ev => {
ev.preventDefault();
this.showAstrologieEditor();
});
html.find('#calendar-move-handle').mousedown(ev => {
ev.preventDefault();
ev = ev || window.event;
let isRightMB = false;
if ("which" in ev) { // Gecko (Firefox), WebKit (Safari/Chrome) & Opera
isRightMB = ev.which == 3;
} else if ("button" in ev) { // IE, Opera
isRightMB = ev.button == 2;
}
if (!isRightMB) {
dragElement(document.getElementById("calendar-time-container"));
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
function dragElement(elmnt) {
elmnt.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.bottom = null
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
elmnt.onmousedown = null;
document.onmouseup = null;
document.onmousemove = null;
let xPos = (elmnt.offsetLeft - pos1) > window.innerWidth ? window.innerWidth - 200 : (elmnt.offsetLeft - pos1);
let yPos = (elmnt.offsetTop - pos2) > window.innerHeight - 20 ? window.innerHeight - 100 : (elmnt.offsetTop - pos2)
xPos = xPos < 0 ? 0 : xPos;
yPos = yPos < 0 ? 0 : yPos;
if (xPos != (elmnt.offsetLeft - pos1) || yPos != (elmnt.offsetTop - pos2)) {
elmnt.style.top = (yPos) + "px";
elmnt.style.left = (xPos) + "px";
}
game.system.rdd.calendrier.calendrierPos.top = yPos;
game.system.rdd.calendrier.calendrierPos.left = xPos;
if (game.user.isGM) {
game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
}
}
} else if (isRightMB) {
game.system.rdd.calendrier.calendrierPos.top = 200;
game.system.rdd.calendrier.calendrierPos.left = 200;
if (game.user.isGM) {
game.settings.set(SYSTEM_RDD, "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
this.setPos(game.system.rdd.calendrier.calendrierPos);
}
});
}
}

View File

@ -52,6 +52,11 @@ export class RdDCarac {
return selectedCarac?.label?.toLowerCase()?.match(/r(e|ê)ve(( |-)actuel)?/);
}
static isActionPhysique(selectedCarac) {
return !selectedCarac ||
selectedCarac?.label.match(/(Apparence|Force|Agilité|Dextérité|Vue|Ouïe|Odorat-Goût|Empathie|Dérobée|Mêlée|Tir|Lancer)/);
}
static isIgnoreEtatGeneral(rollData) {
const selectedCarac = rollData.selectedCarac;
return !selectedCarac ||
@ -62,7 +67,7 @@ export class RdDCarac {
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))
.reduce(Misc.sum(), 0);
const beauteSuperieur10 = Math.max((beaute ?? 10) - 10, 0);
@ -101,35 +106,35 @@ export class RdDCarac {
}
/* -------------------------------------------- */
static computeCarac(data) {
data.carac.force.value = Math.min(data.carac.force.value, parseInt(data.carac.taille.value) + 4);
static computeCarac(system) {
system.carac.force.value = Math.min(system.carac.force.value, parseInt(system.carac.taille.value) + 4);
data.carac.derobee.value = Math.floor(parseInt(((21 - data.carac.taille.value)) + parseInt(data.carac.agilite.value)) / 2);
let bonusDomKey = Math.floor((parseInt(data.carac.force.value) + parseInt(data.carac.taille.value)) / 2);
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];
data.attributs.plusdom.value = tailleData.plusdom;
system.attributs.plusdom.value = tailleData.plusdom;
data.attributs.sconst.value = RdDCarac.calculSConst(data.carac.constitution.value);
data.attributs.sust.value = tableCaracDerivee[Number(data.carac.taille.value)].sust;
system.attributs.sconst.value = RdDCarac.calculSConst(system.carac.constitution.value);
system.attributs.sust.value = tableCaracDerivee[Number(system.carac.taille.value)].sust;
data.attributs.encombrement.value = (parseInt(data.carac.force.value) + parseInt(data.carac.taille.value)) / 2;
data.carac.melee.value = Math.floor((parseInt(data.carac.force.value) + parseInt(data.carac.agilite.value)) / 2);
data.carac.tir.value = Math.floor((parseInt(data.carac.vue.value) + parseInt(data.carac.dexterite.value)) / 2);
data.carac.lancer.value = Math.floor((parseInt(data.carac.tir.value) + parseInt(data.carac.force.value)) / 2);
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);
data.sante.vie.max = Math.ceil((parseInt(data.carac.taille.value) + parseInt(data.carac.constitution.value)) / 2);
system.sante.vie.max = Math.ceil((parseInt(system.carac.taille.value) + parseInt(system.carac.constitution.value)) / 2);
data.sante.vie.value = Math.min(data.sante.vie.value, data.sante.vie.max)
data.sante.endurance.max = Math.max(parseInt(data.carac.taille.value) + parseInt(data.carac.constitution.value), parseInt(data.sante.vie.max) + parseInt(data.carac.volonte.value));
data.sante.endurance.value = Math.min(data.sante.endurance.value, data.sante.endurance.max);
data.sante.fatigue.max = data.sante.endurance.max * 2;
data.sante.fatigue.value = Math.min(data.sante.fatigue.value, data.sante.fatigue.max);
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
data.reve.reve.max = data.carac.reve.value;
data.compteurs.chance.max = data.carac.chance.value;
system.reve.reve.max = system.carac.reve.value;
system.compteurs.chance.max = system.carac.chance.value;
}

View File

@ -1,5 +1,6 @@
import { ChatUtility } from "./chat-utility.js";
import { ENTITE_INCARNE, ENTITE_NONINCARNE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { DialogSelectTarget } from "./dialog-select-target.js";
import { Grammar } from "./grammar.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
@ -9,7 +10,9 @@ import { RdDBonus } from "./rdd-bonus.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { STATUSES } from "./settings/status-effects.js";
import { Targets } from "./targets.js";
/* -------------------------------------------- */
const premierRoundInit = [
@ -66,7 +69,7 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
async finDeRound(options = { terminer: false }) {
for (let combatant of this.data.combatants) {
for (let combatant of this.combatants) {
if (combatant.actor) {
await combatant.actor.finDeRound(options);
}
@ -78,36 +81,35 @@ export class RdDCombatManager extends Combat {
/************************************************************************************/
async rollInitiative(ids, formula = undefined, messageOptions = {}) {
console.log(`${game.data.system.data.title} | Combat.rollInitiative()`, ids, formula, messageOptions);
// Structure input data
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, formula, messageOptions);
ids = typeof ids === "string" ? [ids] : ids;
const currentId = this.combatant._id;
// calculate initiative
for (let cId = 0; cId < ids.length; cId++) {
const combatant = this.combatants.get(ids[cId]);
let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, 0);
if (!formula) {
if (combatant.actor.data.type == 'creature' || combatant.actor.data.type == 'entite') {
const competence = combatant.actor.data.items.find(it => it.data.data.iscombat)
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
const competence = combatant.actor.items.find(it => it.system.iscombat)
if (competence) {
rollFormula = RdDCombatManager.formuleInitiative(2, competence.data.carac_value, competence.data.niveau, 0);
rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, 0);
}
} else {
const armeCombat = combatant.actor.data.items.find(it => it.type == 'arme' && itemData.data.equipe)
const compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.data.competence;
const competence = RdDItemCompetence.findCompetence(combatant.actor.data.items, compName);
const armeCombat = combatant.actor.itemTypes['arme'].find(it => it.system.equipe)
const compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.system.competence;
const competence = RdDItemCompetence.findCompetence(combatant.actor.items, compName);
if (competence) {
const carac = combatant.actor.data.data.carac[competence.data.defaut_carac].value;
const niveau = competence.data.niveau;
const bonusEcaille = (armeCombat?.data.magique) ? armeCombat.data.ecaille_efficacite : 0;
const carac = combatant.actor.system.carac[competence.system.defaut_carac].value;
const niveau = competence.system.niveau;
const bonusEcaille = (armeCombat?.system.magique) ? armeCombat.system.ecaille_efficacite : 0;
rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, bonusEcaille);
}
}
}
//console.log("Combatat", c);
const roll = combatant.getInitiativeRoll(rollFormula);
if ( !roll.total) {
roll.evaluate( {async: false});
if (!roll.total) {
roll.evaluate({ async: false });
}
if (roll.total <= 0) roll.total = 0.00;
console.log("Compute init for", rollFormula, roll.total, combatant);
@ -120,7 +122,7 @@ export class RdDCombatManager extends Combat {
{
speaker: {
scene: canvas.scene._id,
actor: combatant.actor ? combatant.actor._id : null,
actor: combatant.actor?._id,
token: combatant.token._id,
alias: combatant.token.name,
sound: CONFIG.sounds.dice,
@ -150,61 +152,88 @@ export class RdDCombatManager extends Combat {
}
/* -------------------------------------------- */
/** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main */
/** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main / lancer */
static listActionsArmes(armes, competences, carac) {
// Gestion des armes 1/2 mains
let actionsArme = [];
let actions = [];
for (const arme of armes) {
let action = duplicate(Misc.data(arme));
if (action.data.equipe) {
let compData = competences.map(c => Misc.data(c)).find(c => c.name == action.data.competence);
if (arme.system.equipe) {
const dommages = arme.system.dommages.toString();
const tableauDommages = dommages.includes("/") ? dommages.split("/") : [dommages, dommages];
if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) {
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)");
}
console.log(">>>>", arme)
if ((arme.system.unemain && arme.system.competence) ||
(arme.system.competence.toLowerCase().includes("corps à corps"))) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(1 main)",
dommagesReel: Number(tableauDommages[0]),
competence: arme.system.competence,
carac: carac,
competences: competences
}));
}
if (arme.system.deuxmains && arme.system.competence) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(2 mains)",
dommagesReel: Number(tableauDommages[1]),
competence: arme.system.competence.replace(" 1 main", " 2 mains"),
carac: carac,
competences: competences
}));
}
if (arme.system.lancer) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(lancer)",
dommagesReel: Number(tableauDommages[0]),
competence: arme.system.lancer,
carac: carac,
competences: competences
}));
}
if (arme.system.tir) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(tir)",
dommagesReel: Number(tableauDommages[0]),
competence: arme.system.tir,
carac: carac,
competences: competences
}));
}
}
}
return actions.sort(Misc.ascending(action => action.name + (action.system.infoMain ?? '')));
}
actionsArme.push(action);
action.action = 'attaque';
action.data.dommagesReels = Number(action.data.dommages);
action.data.niveau = compData.data.niveau;
action.data.initiative = RdDCombatManager.calculInitiative(compData.data.niveau, carac[compData.data.defaut_carac].value);
// Dupliquer les armes pouvant être à 1 main et 2 mains en patchant la compétence
if (action.data.unemain && !action.data.deuxmains) {
action.data.mainInfo = "(1m)";
} else if (!action.data.unemain && action.data.deuxmains) {
action.data.mainInfo = "(2m)";
} else if (action.data.unemain && action.data.deuxmains) {
action.data.mainInfo = "(1m)";
static $prepareAttaqueArme(infoAttaque) {
const comp = infoAttaque.competences.find(c => c.name == infoAttaque.competence);
const attaque = duplicate(infoAttaque.arme);
attaque.action = 'attaque';
attaque.system.competence = infoAttaque.competence;
attaque.system.dommagesReels = infoAttaque.dommagesReel;
attaque.system.infoMain = infoAttaque.infoMain;
attaque.system.niveau = comp.system.niveau;
attaque.system.initiative = RdDCombatManager.calculInitiative(comp.system.niveau, infoAttaque.carac[comp.system.defaut_carac].value);
return attaque;
}
const comp2m = action.data.competence.replace(" 1 main", " 2 mains"); // Replace !
const comp = Misc.data(competences.find(c => c.name == comp2m));
const arme2main = duplicate(action);
arme2main.data.mainInfo = "(2m)";
arme2main.data.niveau = comp.data.niveau;
arme2main.data.competence = comp2m;
arme2main.data.initiative = RdDCombatManager.calculInitiative(arme2main.data.niveau, carac[comp.data.defaut_carac].value);
actionsArme.push(arme2main);
const containsSlash = action.data.dommages.includes("/");
if (containsSlash) {
const tableauDegats = action.data.dommages.split("/");
action.data.dommagesReels = Number(tableauDegats[0]);
arme2main.data.dommagesReels = Number(tableauDegats[1]);
}
else{
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + action.name + " ne sont pas corrects (ie sous la forme X/Y)");
}
}
}
}
return actionsArme.sort(Misc.ascending(armeData => armeData.name + (armeData.data.mainInfo ?? '')));
static listActionsCreature(competences) {
return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(it => RdDItemCompetenceCreature.armeNaturelle(it));
}
static listActionsPossessions(actor) {
return RdDCombatManager._indexActions(actor.getPossessions().map(p =>
{
return RdDCombatManager._indexActions(actor.getPossessions().map(p => {
return {
name: p.name,
action: 'conjurer',
data: {
system: {
competence: p.name,
possessionid: p.data.data.possessionid,
possessionid: p.system.possessionid,
}
}
}));
@ -214,24 +243,22 @@ export class RdDCombatManager extends Combat {
static listActionsCombat(combatant) {
const actor = combatant.actor;
let actions = RdDCombatManager.listActionsPossessions(actor);
if (actions.length>0) {
if (actions.length > 0) {
return actions;
}
let items = actor.data.items;
if (actor.isCreature()) {
actions = actions.concat(items.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(competence => RdDItemCompetenceCreature.toActionArme(competence)));
if (actor.isCreatureEntite()) {
actions = actions.concat(RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']));
} else {
// Recupération des items 'arme'
let armes = items.filter(it => RdDItemArme.isArmeUtilisable(it))
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
//.concat(RdDItemArme.empoignade())
.concat(RdDItemArme.mainsNues());
let competences = items.filter(it => it.type == 'competence');
actions = actions.concat(RdDCombatManager.listActionsArmes(armes, competences, actor.data.data.carac));
const competences = actor.itemTypes['competence'];
actions = actions.concat(RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac));
if (actor.data.data.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', data: { initOnly: true, competence: "Draconic" } });
if (actor.system.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } });
}
}
@ -249,14 +276,14 @@ export class RdDCombatManager extends Combat {
static processPremierRoundInit() {
// Check if we have the whole init !
if (Misc.isUniqueConnectedGM() && game.combat.current.round == 1) {
let initMissing = game.combat.data.combatants.find(it => !it.initiative);
let initMissing = game.combat.combatants.find(it => !it.initiative);
if (!initMissing) { // Premier round !
for (let combatant of game.combat.data.combatants) {
for (let combatant of game.combat.combatants) {
let action = combatant.initiativeData?.arme;
//console.log("Parsed !!!", combatant, initDone, game.combat.current, arme);
if (action && action.type == "arme") {
for (let initData of premierRoundInit) {
if (Grammar.toLowerCaseNoAccentNoSpace(action.data.initpremierround).includes(initData.pattern)) {
if (Grammar.toLowerCaseNoAccentNoSpace(action.system.initpremierround).includes(initData.pattern)) {
let msg = `<h4>L'initiative de ${combatant.actor.name} a été modifiée !</h4>
<hr>
<div>
@ -327,22 +354,16 @@ export class RdDCombatManager extends Combat {
initOffset = 9;
initInfo = "Draconic"
} else {
compData = Misc.data(RdDItemCompetence.findCompetence(combatant.actor.data.items, action.data.competence));
compNiveau = compData.data.niveau;
initInfo = action.name + " / " + action.data.competence;
compData = RdDItemCompetence.findCompetence(combatant.actor.items, action.system.competence);
compNiveau = compData.system.niveau;
initInfo = action.name + " / " + action.system.competence;
if (combatant.actor.data.type == 'creature' || combatant.actor.data.type == 'entite') {
caracForInit = compData.data.carac_value;
if (compData.data.categorie == "lancer") {
initOffset = 7;
}
else {
initOffset = 5;
}
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
caracForInit = compData.system.carac_value;
} else {
caracForInit = Misc.data(combatant.actor).data.carac[compData.data.defaut_carac].value;
initOffset = RdDCombatManager._baseInitOffset(compData.data.categorie, action);
caracForInit = combatant.actor.system.carac[compData.system.defaut_carac].value;
}
initOffset = RdDCombatManager._baseInitOffset(compData.system.categorie, action);
}
let malus = combatant.actor.getEtatGeneral(); // Prise en compte état général
@ -353,6 +374,7 @@ export class RdDCombatManager extends Combat {
game.combat.rollInitiative(combatantId, rollFormula, { initInfo: initInfo });
}
/* -------------------------------------------- */
static _baseInitOffset(categorie, arme) {
if (categorie == "tir") { // Offset de principe pour les armes de jet
return 8;
@ -360,10 +382,12 @@ export class RdDCombatManager extends Combat {
if (categorie == "lancer") { // Offset de principe pour les armes de jet
return 7;
}
// Offset de principe pour les armes de jet
switch (arme.data.cac) {
case "empoignade": return 3;
case "pugilat": return 4;
switch (arme.system.cac) {
case "empoignade":
return 3;
case "pugilat":
case "naturelle":
return 4;
}
return 5;
}
@ -372,7 +396,7 @@ export class RdDCombatManager extends Combat {
static displayInitiativeMenu(html, combatantId) {
console.log("Combatant ; ", combatantId);
const combatant = game.combat.combatants.get(combatantId);
if (! (combatant?.actor) ) {
if (!(combatant?.actor)) {
ui.notifications.warn(`Le combatant ${combatant.name ?? combatantId} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return;
}
@ -384,7 +408,7 @@ export class RdDCombatManager extends Combat {
let menuItems = [];
for (let action of actions) {
menuItems.push({
name: action.data.competence,
name: action.system.competence,
icon: "<i class='fas fa-dice-d6'></i>",
callback: target => { RdDCombatManager.rollInitiativeAction(combatantId, action) }
});
@ -415,7 +439,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static onUpdateCombat(combat, change, options, userId) {
if (combat.data.round != 0 && combat.turns && combat.data.active) {
if (combat.round != 0 && combat.turns && combat.active) {
RdDCombat.combatNouveauTour(combat);
}
}
@ -447,49 +471,30 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static createUsingTarget(attacker) {
const target = RdDCombat.getTarget();
if (target == undefined) {
ui.notifications.warn((game.user.targets?.size ?? 0) > 1
? "Vous devez choisir <strong>une seule</strong> cible à attaquer!"
: "Vous devez choisir une cible à attaquer!");
}
else {
static rddCombatTarget(target, attacker) {
const defender = target?.actor;
const defenderTokenId = target?.data._id;
if ( defender.type == 'entite' && defender.data.data.definition.typeentite == ENTITE_NONINCARNE) {
ui.notifications.warn("Vous ne pouvez pas cibler une entité non incarnée !!!!");
} else {
return this.create(attacker, defender, defenderTokenId, target)
}
}
}
/* -------------------------------------------- */
static getTarget() {
if (game.user.targets && game.user.targets.size == 1) {
for (let target of game.user.targets) {
return target;
}
}
return undefined;
}
/* -------------------------------------------- */
static create(attacker, defender, defenderTokenId, target = undefined) {
const defenderTokenId = target?.id;
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static createForAttackerAndDefender(attackerId, defenderTokenId) {
static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) {
const attacker = game.actors.get(attackerId);
if (defenderTokenId) {
const defenderToken = canvas.tokens.get(defenderTokenId);
const defender = defenderToken.actor;
return RdDCombat.create(attacker, defender, defenderTokenId);
let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined;
let target = undefined
if (!defenderTokenId || !defender) {
console.warn(`RdDCombat.rddCombatForAttackerAndDefender: appel avec defenderTokenId ${defenderTokenId} incorrect, ou pas de defender correspondant`);
target = Targets.getTarget()
if (!target) {
return;
}
return RdDCombat.createUsingTarget(attacker)
defenderTokenId = target.id;
defender = target.actor;
if (!defenderTokenId || !defender) {
return;
}
}
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
@ -497,10 +502,10 @@ export class RdDCombat {
let defender = canvas.tokens.get(msg.defenderTokenId).actor;
if (Misc.isOwnerPlayerOrUniqueConnectedGM()) {
let attackerRoll = msg.attackerRoll;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : null;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
defender.encaisserDommages(attackerRoll, attacker);
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
}
@ -509,7 +514,7 @@ export class RdDCombat {
static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderTokenId);
if (defenderToken && Misc.isUniqueConnectedGM()) {
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme);
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
}
@ -536,11 +541,10 @@ export class RdDCombat {
'#echec-total-attaque',
]) {
html.on("click", button, event => {
const rddCombat = RdDCombat.createForAttackerAndDefender(
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) {
rddCombat.onEvent(button, event);
event.preventDefault();
}
@ -555,19 +559,19 @@ export class RdDCombat {
/* -------------------------------------------- */
constructor(attacker, defender, defenderTokenId, target) {
this.attacker = attacker;
this.defender = defender;
this.target = target;
this.attackerId = this.attacker.data._id;
this.defenderId = this.defender.data._id;
this.defenderTokenId = defenderTokenId;
this.attacker = attacker
this.defender = defender
this.target = target
this.attackerId = this.attacker.id
this.defenderId = this.defender.id
this.defenderTokenId = defenderTokenId
}
/* -------------------------------------------- */
async onEvent(button, event) {
const chatMessage = ChatUtility.getChatMessage(event);
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll') ;
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll');
console.log('RdDCombat', attackerRoll, defenderRoll);
const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value;
@ -686,12 +690,87 @@ export class RdDCombat {
return rollData.rolled.isSuccess;
}
/* -------------------------------------------- */
async proposerAjustementTirLancer(rollData) {
if (['tir', 'lancer'].includes(rollData.competence.system.categorie)) {
if (this.defender.isEntite([ENTITE_BLURETTE])) {
ChatMessage.create({
content: `<strong>La cible est une blurette, l'arme à distance sera perdue dans le blurêve`,
whisper: ChatMessage.getWhisperRecipients("GM")
})
}
else {
const defenderToken = canvas.tokens.get(this.defenderTokenId);
const dist = this.distance(_token, defenderToken)
const isVisible = this.isVisible(_token, defenderToken)
const portee = this._ajustementPortee(dist, rollData.arme)
const taille = this._ajustementTaille(this.defender)
const activite = this._ajustementMouvement(this.defender)
const total = [portee, taille, activite].map(it => it.diff).filter(d => !Number.isNaN(d)).reduce(Misc.sum(), 0)
ChatMessage.create({
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.html', {
rollData: rollData,
attacker: _token,
isVisible: isVisible,
defender: defenderToken,
distance: dist,
portee: portee,
taille: taille,
activite: activite,
total: total
}),
whisper: ChatMessage.getWhisperRecipients("GM")
})
}
}
}
isVisible(token, defenderToken) {
return canvas.effects.visibility.testVisibility(defenderToken.center, { object: token })
}
distance(token, defenderToken) {
return Number(canvas.grid.measureDistances([{ ray: new Ray(token.center, defenderToken.center) }], { gridSpaces: false })).toFixed(1);
}
_ajustementPortee(dist, arme) {
if (dist <= arme.system.portee_courte) return { msg: "courte", diff: 0 };
if (dist <= arme.system.portee_moyenne) return { msg: "moyenne", diff: -3 };
if (dist <= arme.system.portee_extreme) return { msg: "extrême", diff: -5 };
return { msg: "inatteignable", diff: -10 };
}
_ajustementTaille(actor) {
if (actor.isVehicule()) return { msg: "véhicule", diff: 0 }
const taille = actor.getCaracByName('TAILLE')?.value ?? 1;
if (taille <= 1) return { msg: "souris", diff: -8 };
if (taille <= 3) return { msg: "chat", diff: -4 };
if (taille <= 5) return { msg: "chien", diff: -2 };
if (taille <= 15) return { msg: "humanoïde", diff: 0 };
if (taille <= 20) return { msg: "ogre", diff: 2 };
return { msg: "gigantesque", diff: 4 };
}
_ajustementMouvement(defender) {
if (defender.getSurprise(true)) return { msg: "immobile (surprise)", diff: 0 };
if (game.combat?.combatants.find(it => it.actorId == defender.id)) return { msg: "en mouvement (combat)", diff: -4 };
return { msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3 };
}
/* -------------------------------------------- */
async attaque(competence, arme) {
if (!await this.accorderEntite('avant-attaque')) {
// const nonIncarnee = this.defender.isEntite([ENTITE_NONINCARNE])
// const blurette = this.defender.isEntite([ENTITE_BLURETTE])
// if (nonIncarnee || blurette) {
// ChatMessage.create( {
// content: `<strong>La cible est ${nonIncarnee ? 'non incarnée' : 'une blurette'}.
// Il est impossible de l'atteindre.`,
// whisper: ChatMessage.getWhisperRecipients("GM")})
// }
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return;
}
if (arme.data.cac == 'empoignade' && this.attacker.isCombatTouche()) {
if (arme.system.cac == 'empoignade' && this.attacker.isCombatTouche()) {
ChatMessage.create({
alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
@ -708,12 +787,11 @@ export class RdDCombat {
if (arme) {
this.attacker.verifierForceMin(arme);
}
await this.proposerAjustementTirLancer(rollData)
const dialog = await RdDRoll.create(this.attacker, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
options: { height: 540 }
}, {
name: 'jet-attaque',
label: 'Attaque: ' + (arme?.name ?? competence.name),
callbacks: [
@ -734,15 +812,16 @@ export class RdDCombat {
_prepareAttaque(competence, arme) {
let rollData = {
passeArme: randomID(16),
mortalite: arme?.data.mortalite,
mortalite: arme?.system.mortalite,
coupsNonMortels: false,
competence: competence,
surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
targetToken: Targets.extractTokenData(this.target),
essais: {}
};
if (this.attacker.isCreature()) {
if (this.attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData);
}
else if (arme) {
@ -751,8 +830,8 @@ export class RdDCombat {
}
else {
// sans armes: à mains nues
const niveau = competence.data.niveau;
const init = RdDCombatManager.calculInitiative(niveau, Misc.templateData(this.attacker).carac['melee'].value);
const niveau = competence.system.niveau;
const init = RdDCombatManager.calculInitiative(niveau, this.attacker.system.carac['melee'].value);
rollData.arme = RdDItemArme.mainsNues({ niveau: niveau, initiative: init });
}
return rollData;
@ -765,9 +844,9 @@ export class RdDCombat {
// force toujours, sauf empoignade
// 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
const isForce = !rollData.arme.data.empoignade;
const isFinesse = rollData.arme.data.empoignade || isMeleeDiffNegative;
const isRapide = !rollData.arme.data.empoignade && isMeleeDiffNegative && rollData.arme.data.rapide;
const isForce = !rollData.arme.system.empoignade;
const isFinesse = rollData.arme.system.empoignade || isMeleeDiffNegative;
const isRapide = !rollData.arme.system.empoignade && isMeleeDiffNegative && rollData.arme.system.rapide;
// si un seul choix possible, le prendre
if (isForce && !isFinesse && !isRapide) {
return await this.choixParticuliere(rollData, "force");
@ -802,12 +881,12 @@ export class RdDCombat {
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), this.defender.isEntite());
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
attackerRoll.show = {
cible: this.target ? this.defender.data.name : 'la cible',
cible: this.target ? this.defender.name : 'la cible',
isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
}
await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
if (!await this.accorderEntite('avant-defense')) {
if (!await this.attacker.accorder(this.defender, 'avant-defense')) {
return;
}
@ -817,13 +896,13 @@ export class RdDCombat {
}
/* -------------------------------------------- */
isPossession( attackerRoll) {
isPossession(attackerRoll) {
return attackerRoll.selectedCarac.label.toLowerCase() == 'possession';
}
/* -------------------------------------------- */
async _sendMessageDefense(attackerRoll, defenderRoll, essaisPrecedents = undefined) {
console.log("RdDCombat._sendMessageDefense", attackerRoll, defenderRoll, essaisPrecedents, " / ", this.attacker, this.target, this.attackerId, attackerRoll.competence.data.categorie);
console.log("RdDCombat._sendMessageDefense", attackerRoll, defenderRoll, essaisPrecedents, " / ", this.attacker, this.target, this.attackerId, attackerRoll.competence.system.categorie);
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
if (essaisPrecedents) {
@ -831,16 +910,16 @@ export class RdDCombat {
}
// # utilisation esquive
const corpsACorps = Misc.data(this.defender.getCompetence("Corps à corps", { onMessage: it => console.info(it, this.defender) }));
const esquives = duplicate(this.defender.getCompetences("esquive", { onMessage: it => console.info(it, this.defender) }).map(c => Misc.data(c)));
esquives.forEach(e => e.usages = e?.id ? this.defender.getItemUse(e.id) : 0);
const corpsACorps = this.defender.getCompetence("Corps à corps", { onMessage: it => console.info(it, this.defender) });
const esquives = duplicate(this.defender.getCompetences("esquive", { onMessage: it => console.info(it, this.defender) }))
esquives.forEach(e => e.system.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0);
const paramChatDefense = {
passeArme: attackerRoll.passeArme,
essais: attackerRoll.essais,
isPossession: this.isPossession(attackerRoll),
defender: Misc.data(this.defender),
attacker: Misc.data(this.attacker),
defender: this.defender,
attacker: this.attacker,
attackerId: this.attackerId,
esquives: esquives,
defenderTokenId: this.defenderTokenId,
@ -848,7 +927,7 @@ export class RdDCombat {
armes: this._filterArmesParade(this.defender, attackerRoll.competence, attackerRoll.arme),
diffLibre: attackerRoll.ajustements?.diffLibre?.value ?? 0,
attaqueParticuliere: attackerRoll.particuliere,
attaqueCategorie: attackerRoll.competence.data.categorie,
attaqueCategorie: attackerRoll.competence.system.categorie,
attaqueArme: attackerRoll.arme,
surprise: this.defender.getSurprise(true),
dmg: attackerRoll.dmg,
@ -880,8 +959,8 @@ export class RdDCombat {
// envoyer le message au destinataire
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_defense", data: {
attackerId: this.attacker?.data._id,
defenderId: this.defender?.data._id,
attackerId: this.attacker?.id,
defenderId: this.defender?.id,
defenderTokenId: this.defenderTokenId,
defenderRoll: defenderRoll,
paramChatDefense: paramChatDefense,
@ -892,13 +971,11 @@ export class RdDCombat {
/* -------------------------------------------- */
_filterArmesParade(defender, competence) {
let items = defender.data.items;
items = items.filter(it => RdDItemArme.isArmeUtilisable(it) || RdDItemCompetenceCreature.isCompetenceParade(it))
.map(Misc.data);
let items = defender.items.filter(it => RdDItemArme.isArmeUtilisable(it) || RdDItemCompetenceCreature.isCompetenceParade(it))
for (let item of items) {
item.data.nbUsage = defender.getItemUse(item._id); // Ajout du # d'utilisation ce round
item.system.nbUsage = defender.getItemUse(item.id); // Ajout du # d'utilisation ce round
}
switch (competence.data.categorie) {
switch (competence.system.categorie) {
case 'tir':
case 'lancer':
return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
@ -917,7 +994,7 @@ export class RdDCombat {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
attackerId: this.attackerId,
attacker: Misc.data(this.attacker),
attacker: this.attacker,
defenderTokenId: this.defenderTokenId,
essais: attackerRoll.essais
})
@ -930,7 +1007,7 @@ export class RdDCombat {
console.log("RdDCombat._onEchecTotal >>>", rollData);
const arme = rollData.arme;
const avecArme = !['', 'sans-armes', 'armes-naturelles'].includes(arme?.data.categorie_parade ?? '');
const avecArme = !['', 'sans-armes', 'armes-naturelles'].includes(arme?.system.categorie_parade ?? '');
const action = (rollData.attackerRoll ? (arme ? "la parade" : "l'esquive") : "l'attaque");
ChatUtility.createChatWithRollMode(this.defender.name, {
content: `<strong>Maladresse à ${action}!</strong> ` + await RdDRollTables.getMaladresse({ arme: avecArme })
@ -949,7 +1026,7 @@ export class RdDCombat {
console.log("RdDCombat.choixParticuliere >>>", rollData, choix);
if (choix != "rapidite") {
this.attacker.incDecItemUse(rollData.arme._id);
this.attacker.incDecItemUse(rollData.arme.id);
}
this.removeChatMessageActionsPasseArme(rollData.passeArme);
@ -961,20 +1038,17 @@ export class RdDCombat {
async parade(attackerRoll, armeParadeId) {
const arme = this.defender.getArmeParade(armeParadeId);
console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme);
const competence = Misc.templateData(arme)?.competence;
if (competence == undefined)
{
console.error("Pas de compétence de parade associée à ", arme) ;
const competence = arme?.system?.competence;
if (competence == undefined) {
console.error("Pas de compétence de parade associée à ", arme?.name, armeParadeId);
return;
}
let rollData = this._prepareParade(attackerRoll, arme, competence);
const dialog = await RdDRoll.create(this.defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
options: { height: 540 }
}, {
name: 'jet-parade',
label: 'Parade: ' + (arme ? arme.name : rollData.competence.name),
callbacks: [
@ -996,16 +1070,16 @@ export class RdDCombat {
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
competence: Misc.data(this.defender.getCompetence(competenceParade)),
competence: this.defender.getCompetence(competenceParade),
arme: armeParade,
surprise: this.defender.getSurprise(true),
needParadeSignificative: ReglesOptionelles.isUsing('categorieParade') && RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade),
needResist: RdDItemArme.needArmeResist(attackerRoll.arme, armeParade),
carac: Misc.templateData(this.defender).carac,
carac: this.defender.system.carac,
show: {}
};
if (this.defender.isCreature()) {
if (this.defender.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(defenderRoll);
}
@ -1045,7 +1119,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async esquive(attackerRoll, compId, compName) {
const esquive = Misc.data(this.defender.getCompetence(compId) ?? this.defender.getCompetence(compName));
const esquive = this.defender.getCompetence(compId) ?? this.defender.getCompetence(compName)
if (esquive == undefined) {
ui.notifications.error(this.defender.name + " n'a pas de compétence " + compName);
return;
@ -1054,7 +1128,8 @@ export class RdDCombat {
let rollData = this._prepareEsquive(attackerRoll, esquive);
const dialog = await RdDRoll.create(this.defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' }, {
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
name: 'jet-esquive',
label: 'Esquiver',
callbacks: [
@ -1079,11 +1154,11 @@ export class RdDCombat {
competence: competence,
surprise: this.defender.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
carac: Misc.templateData(this.defender).carac,
carac: this.defender.system.carac,
show: {}
};
if (this.defender.isCreature()) {
if (this.defender.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData);
}
return rollData;
@ -1129,11 +1204,11 @@ export class RdDCombat {
const dmg = attackerRoll.dmg.dmgArme + attackerRoll.dmg.dmgActor;
let arme = defenderRoll.arme;
let resistance = Misc.toInt(arme.data.resistance);
if (arme.data.magique) {
let resistance = Misc.toInt(arme.system.resistance);
if (arme.system.magique) {
defenderRoll.show.deteriorationArme = 'resiste'; // Par défaut
if (arme.data.resistance_magique == undefined) arme.data.resistance_magique = 0; // Quick fix
if (dmg > arme.data.resistance_magique) { // Jet uniquement si dommages supérieur à résistance magique (cf. 274)
if (arme.system.resistance_magique == undefined) arme.system.resistance_magique = 0; // Quick fix
if (dmg > arme.system.resistance_magique) { // Jet uniquement si dommages supérieur à résistance magique (cf. 274)
// Jet de résistance de l'arme de parade (p.132)
let resistRoll = await RdDResolutionTable.rollData({
caracValue: resistance,
@ -1141,11 +1216,11 @@ export class RdDCombat {
showDice: HIDE_DICE
});
if (!resistRoll.rolled.isSuccess) {
let perteResistance = (dmg - arme.data.resistance_magique)
let perteResistance = (dmg - arme.system.resistance_magique)
resistance -= perteResistance;
defenderRoll.show.deteriorationArme = resistance <= 0 ? 'brise' : 'perte';
defenderRoll.show.perteResistance = perteResistance;
this.defender.updateEmbeddedDocuments('Item', [{ _id: defenderRoll.arme._id, 'data.resistance': resistance }]);
this.defender.updateEmbeddedDocuments('Item', [{ _id: defenderRoll.arme._id, 'system.resistance': resistance }]);
}
}
} else {
@ -1161,14 +1236,14 @@ export class RdDCombat {
resistance -= dmg;
defenderRoll.show.deteriorationArme = resistance <= 0 ? 'brise' : 'perte';
defenderRoll.show.perteResistance = dmg;
this.defender.updateEmbeddedDocuments('Item', [{ _id: defenderRoll.arme._id, 'data.resistance': resistance }]);
this.defender.updateEmbeddedDocuments('Item', [{ _id: defenderRoll.arme._id, 'system.resistance': resistance }]);
}
}
// 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') {
let desarme = await RdDResolutionTable.rollData({
caracValue: this.defender.getForce(),
finalLevel: Misc.toInt(defenderRoll.competence.data.niveau) - dmg,
finalLevel: Misc.toInt(defenderRoll.competence.system.niveau) - dmg,
showDice: HIDE_DICE
});
defenderRoll.show.desarme = desarme.rolled.isEchec;
@ -1187,7 +1262,7 @@ export class RdDCombat {
defenderRoll.show.recul = 'encaisse';
} else if (rollRecul.rolled.isETotal || this._isReculCauseChute(impact)) {
defenderRoll.show.recul = 'chute';
await this.defender.setStatusEffect("EFFECT.StatusProne", true);
await this.defender.setEffect(STATUSES.StatusProne, true);
}
else {
defenderRoll.show.recul = 'recul';
@ -1211,7 +1286,7 @@ export class RdDCombat {
_computeImpactRecul(attaque) {
const taille = this.defender.getTaille();
const force = this.attacker.getForce();
const dommages = attaque.arme.data.dommagesReels ?? attaque.arme.data.dommages;
const dommages = attaque.arme.system.dommagesReels ?? attaque.arme.system.dommages;
return taille - (force + dommages);
}
@ -1229,7 +1304,7 @@ export class RdDCombat {
attackerRoll.defenderTokenId = defenderTokenId;
await this.computeRecul(defenderRoll);
this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll);
this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show);
}
else { // envoi à un GM: les joueurs n'ont pas le droit de modifier les personnages qu'ils ne possèdent pas
game.socket.emit(SYSTEM_SOCKET_ID, {
@ -1244,56 +1319,27 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
/* -------------------------------------------- */
/* retourne true si on peut continuer, false si on ne peut pas continuer */
async accorderEntite(when = 'avant-encaissement') {
if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
|| this.defender == undefined
|| !this.defender.isEntite([ENTITE_INCARNE])
|| this.defender.isEntiteAccordee(this.attacker)) {
return true;
}
let rolled = await RdDResolutionTable.roll(this.attacker.getReveActuel(), - Number(Misc.templateData(this.defender).carac.niveau.value));
let message = {
content: "Jet de points actuels de rêve à " + rolled.finalLevel + RdDResolutionTable.explain(rolled) + "<br>",
whisper: ChatMessage.getWhisperRecipients(this.attacker.name)
};
if (rolled.isSuccess) {
await this.defender.setEntiteReveAccordee(this.attacker);
message.content += this.attacker.name + " s'est accordé avec " + this.defender.name;
}
else {
message.content += this.attacker.name + " n'est pas accordé avec " + this.defender.name;
}
ChatMessage.create(message);
return rolled.isSuccess;
}
/* -------------------------------------------- */
static async displayActorCombatStatus(combat, actor) {
let data = {
let formData = {
combatId: combat._id,
alias: actor.name,
etatGeneral: actor.getEtatGeneral(),
isSonne: actor.getSonne(),
blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(),
actorId: actor.data._id,
actorId: actor.id,
isGrave: false,
isCritique: false
}
if (actor.countBlessuresNonSoigneeByName("critiques") > 0) { // Pour éviter le cumul grave + critique
data.isCritique = true;
formData.isCritique = true;
} else if (actor.countBlessuresNonSoigneeByName("graves") > 0) {
data.isGrave = true;
formData.isGrave = true;
}
ChatUtility.createChatWithRollMode(actor.name, {
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html`, data)
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html`, formData)
});
}
}

View File

@ -1,8 +1,8 @@
/* -------------------------------------------- */
import { DialogChronologie } from "./dialog-chronologie.js";
import { DialogCreateSigneDraconique } from "./dialog-create-signedraconique.js";
import { DialogStress } from "./dialog-stress.js";
import { Grammar } from "./grammar.js";
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
import { RdDCarac } from "./rdd-carac.js";
@ -13,7 +13,7 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { RdDUtility } from "./rdd-utility.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { CompendiumTableHelpers } from "./settings/system-compendiums.js";
import { TMRUtility } from "./tmr-utility.js";
const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
@ -22,54 +22,99 @@ const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
export class RdDCommands {
static init() {
if (!game.system.rdd.commands) {
const rddCommands = new RdDCommands();
rddCommands.registerCommand({ path: ["/aide"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
rddCommands.registerCommand({ path: ["/help"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
rddCommands.registerCommand({ path: ["/table", "queues"], func: (content, msg, params) => RdDRollTables.getQueue(true), descr: "Tire une Queue de Dragon" });
rddCommands.registerCommand({ path: ["/table", "ideefixe"], func: (content, msg, params) => RdDRollTables.getIdeeFixe(true), descr: "Tire une Idée fixe" });
rddCommands.registerCommand({ path: ["/table", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant(true), descr: "Tire un Désir Lancinant" });
rddCommands.registerCommand({ path: ["/table", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre(true), descr: "Tire une Ombre de Dragon" });
rddCommands.registerCommand({ path: ["/table", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR(true), descr: "Tire une Tête de Dragon pour Hauts Revants" });
rddCommands.registerCommand({ path: ["/table", "tete"], func: (content, msg, params) => RdDRollTables.getTete(true), descr: "Tire une Tête de Dragon" });
rddCommands.registerCommand({ path: ["/table", "souffle"], func: (content, msg, params) => RdDRollTables.getSouffle(true), descr: " Tire un Souffle de Dragon" });
rddCommands.registerCommand({ path: ["/table", "comp"], func: (content, msg, params) => RdDRollTables.getCompetence(true), descr: "Tire une compétence au hasard" });
rddCommands.registerCommand({ path: ["/table", "tarot"], func: (content, msg, params) => RdDRollTables.getTarot(true), descr: "Tire une carte du Tarot Draconique" });
rddCommands.registerCommand({ path: ["/meteo"], func: (content, msg, params) => rddCommands.getMeteo(msg, params), descr: "Propose une météo marine" });
rddCommands.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.getName(msg, params), descr: "Génère un nom aléatoire" });
rddCommands.registerCommand({
path: ["/tmra"], func: (content, msg, params) => rddCommands.getTMRAleatoire(msg, params),
descr: `Tire une case aléatoire des Terres médianes
<br><strong>/tmra forêt</strong> détermine une 'forêt' aléatoire
<br><strong>/tmra</strong> détermine une case aléatoire dans toutes les TMR` });
rddCommands.registerCommand({
path: ["/tmr"], func: (content, msg, params) => rddCommands.findTMR(msg, params),
descr: `Cherche où se trouve une case des Terres médianes
<br><strong>/tmr? sordide</strong> indique que la cité Sordide est en D13
<br><strong>/tmr? foret</strong> donne la liste des TMR dont le nom contient "foret" (donc, toutes les forêts)` });
rddCommands.registerCommand({
path: ["/tmrr"], func: (content, msg, params) => rddCommands.getRencontreTMR(params),
descr: `Détermine une rencontre dans un type de case
<br><strong>/tmrr foret</strong> lance un d100 et détermine la rencontre correspondante en 'forêt'
<br><strong>/tmrr forêt 47</strong> détermine la rencontre en 'forêt' pour un jet de dé de 47`
Hooks.on("chatMessage", (html, content, msg) => {
if (content[0] == '/') {
let regExp = /(\S+)/g;
let commands = content.match(regExp);
if (rddCommands.processChatCommand(commands, content, msg)) {
return false;
}
}
return true;
});
rddCommands.registerCommand({
path: ["/xp", "comp"], func: (content, msg, params) => rddCommands.getCoutXpComp(msg, params),
game.system.rdd.commands = rddCommands;
}
constructor() {
this.commandsTable = undefined;
}
_registerCommands() {
this.commandsTable = {}
this.registerCommand({ path: ["/aide"], func: (content, msg, params) => this.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
this.registerCommand({ path: ["/help"], func: (content, msg, params) => this.help(msg), descr: "Affiche l'aide pour toutes les commandes" });
this.registerCommand({ path: ["/liste", "comp"], func: (content, msg, params) => RdDRollTables.getCompetence('liste'), descr: "Affiche la liste des compétences" });
this.registerCommand({ path: ["/table", "queue"], func: (content, msg, params) => RdDRollTables.getQueue('liste'), descr: "Affiche la table des Queues de Dragon" });
this.registerCommand({ path: ["/table", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre('liste'), descr: "Affiche la table des Ombres de Thanatos" });
this.registerCommand({ path: ["/table", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR('liste'), descr: "Affiche la table des Têtes de Dragon pour Hauts Revants" });
this.registerCommand({ path: ["/table", "tete"], func: (content, msg, params) => RdDRollTables.getTete('liste'), descr: "Affiche la table des Tête de Dragon pour tous" });
this.registerCommand({ path: ["/table", "souffle"], func: (content, msg, params) => RdDRollTables.getSouffle('liste'), descr: "Affiche la table des Souffles de Dragon" });
this.registerCommand({ path: ["/table", "tarot"], func: (content, msg, params) => RdDRollTables.getTarot('liste'), descr: "Affiche la table les cartes du Tarot Draconique" });
this.registerCommand({ path: ["/table", "ideefixe"], func: (content, msg, params) => RdDRollTables.getIdeeFixe('liste'), descr: "Affiche la table des Idées fixes" });
this.registerCommand({ path: ["/table", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant('liste'), descr: "Affiche la table des Désirs Lancinants" });
this.registerCommand({
path: ["/table", "rencontre"], func: (content, msg, params) => this.tableRencontres(msg, params),
descr: `Affiche la table des Rencontres
<br><strong>/table rencontre deso</strong> affiche la table des rencontres en Désolation
<br><strong>/table rencontre mauvaise</strong> affiche la table des mauvaises rencontres`
});
this.registerCommand({ path: ["/table", "milieu"], func: (content, msg, params) => this.tableMilieu(msg, params, 'liste'), descr: "Affiche la table des ressource naturelles pour un milieu donné" });
this.registerCommand({ path: ["/tirer", "comp"], func: (content, msg, params) => RdDRollTables.getCompetence('chat'), descr: "Tire une compétence au hasard" });
this.registerCommand({ path: ["/tirer", "queue"], func: (content, msg, params) => RdDRollTables.getQueue('chat'), descr: "Tire une Queue de Dragon" });
this.registerCommand({ path: ["/tirer", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre('chat'), descr: "Tire une Ombre de Thanatos" });
this.registerCommand({ path: ["/tirer", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR('chat'), descr: "Tire une Tête de Dragon pour Hauts Revants" });
this.registerCommand({ path: ["/tirer", "tete"], func: (content, msg, params) => RdDRollTables.getTete('chat'), descr: "Tire une Tête de Dragon" });
this.registerCommand({ path: ["/tirer", "souffle"], func: (content, msg, params) => RdDRollTables.getSouffle('chat'), descr: "Tire un Souffle de Dragon" });
this.registerCommand({ path: ["/tirer", "tarot"], func: (content, msg, params) => RdDRollTables.getTarot('chat'), descr: "Tire une carte du Tarot Draconique" });
this.registerCommand({ path: ["/tirer", "ideefixe"], func: (content, msg, params) => RdDRollTables.getIdeeFixe('chat'), descr: "Tire une Idée fixe" });
this.registerCommand({ path: ["/tirer", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant('chat'), descr: "Tire un Désir Lancinant" });
this.registerCommand({ path: ["/tirer", "rencontre"], func: (content, msg, params) => this.getRencontreTMR(params), descr: `Détermine une rencontre dans les TMR (synonyme de "/tmrr")` });
this.registerCommand({ path: ["/tirer", "milieu"], func: (content, msg, params) => this.tableMilieu(msg, params, 'chat'), descr: "Effectue un tirage dans la table desressource naturelles pour un milieu donné" });
this.registerCommand({ path: ["/meteo"], func: (content, msg, params) => this.getMeteo(msg, params), descr: "Propose une météo marine" });
this.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.getName(msg, params), descr: "Génère un nom aléatoire" });
this.registerCommand({
path: ["/tmr"], func: (content, msg, params) => this.findTMR(msg, params),
descr: `Cherche où se trouve une case des Terres médianes
<br><strong>/tmr sord</strong> indique que la cité Sordide est en D13
<br><strong>/tmr foret</strong> donne la liste des TMR dont le nom contient "foret" (donc, toutes les forêts)`
});
this.registerCommand({
path: ["/tmra"], func: (content, msg, params) => this.getTMRAleatoire(msg, params),
descr: `Tire une case aléatoire des Terres médianes
<br><strong>/tmra forêt</strong> détermine une 'forêt' aléatoire
<br><strong>/tmra</strong> détermine une case aléatoire dans toutes les TMR`
});
this.registerCommand({
path: ["/tmrr"], func: (content, msg, params) => this.getRencontreTMR(params),
descr: `Détermine une rencontre dans les TMR
<br><strong>/tmrr forêt</strong> détermine une rencontre aléatoire en 'forêt'
<br><strong>/tmrr mauvaise</strong> détermine une mauvaise rencontre aléatoire
<br><strong>/tmrr for 47</strong> détermine la rencontre en 'forêt' pour un jet de dé de 47`
});
this.registerCommand({
path: ["/xp", "comp"], func: (content, msg, params) => this.getCoutXpComp(msg, params),
descr: `Détermine le coût d'expérience pour augmenter une compétence. Exemples:
<br>/xp comp -6 1: pour passer de -6 à +1
<br>/xp comp +4: pour atteindre le niveau 4 (depuis +3)`
});
rddCommands.registerCommand({
path: ["/xp", "carac"], func: (content, msg, params) => rddCommands.getCoutXpCarac(msg, params),
this.registerCommand({
path: ["/xp", "carac"], func: (content, msg, params) => this.getCoutXpCarac(msg, params),
descr: `Détermine le coût d'expérience pour augmenter une caractéristique. Exemples:
<br>/xp carac 15: coût pour atteindre 15 (depuis 14)`
});
rddCommands.registerCommand({
path: ["/rdd"], func: (content, msg, params) => rddCommands.rollRdd(msg, params),
this.registerCommand({
path: ["/rdd"], func: (content, msg, params) => this.rollRdd(msg, params),
descr: `Effectue un jet de dés dans la table de résolution. Exemples:
<br><strong>/rdd</strong> ouvre la table de résolution
<br><strong>/rdd 10 3</strong> effectue un jet 10 à +3
@ -79,15 +124,15 @@ export class RdDCommands {
<br><strong>/rdd vol déser +2</strong> effectue un jet de Volonté/Survie en désert à +2 pour les tokens sélectionnés
`
});
rddCommands.registerCommand({ path: ["/ddr"], func: (content, msg, params) => rddCommands.rollDeDraconique(msg), descr: "Lance un Dé Draconique" });
this.registerCommand({ path: ["/ddr"], func: (content, msg, params) => this.rollDeDraconique(msg), descr: "Lance un Dé Draconique" });
rddCommands.registerCommand({
this.registerCommand({
path: ["/payer"], func: (content, msg, params) => RdDUtility.afficherDemandePayer(params[0], params[1]),
descr: `Permet de payer un montant. Exemples:
descr: `Demande aux joueurs de payer un montant. Exemples:
<br><strong>/payer 5s 10d</strong> permet d'envoyer un message pour payer 5 sols et 10 deniers
<br><strong>/payer 10d</strong> permet d'envoyer un message pour payer 10 deniers`
});
rddCommands.registerCommand({
this.registerCommand({
path: ["/astro"], func: (content, msg, params) => RdDUtility.afficherHeuresChanceMalchance(Misc.join(params, ' ')),
descr: `Affiche les heures de chance et de malchance selon l'heure de naissance donnée en argument. Exemples pour l'heure de la Lyre:
<br><strong>/astro 7</strong>
@ -95,18 +140,18 @@ export class RdDCommands {
<br><strong>/astro Lyr</strong>`
});
rddCommands.registerCommand({
path: ["/signe", "+"], func: (content, msg, params) => rddCommands.creerSignesDraconiques(),
this.registerCommand({
path: ["/signe", "+"], func: (content, msg, params) => this.creerSignesDraconiques(),
descr: "Crée un signe draconique et l'ajoute aux haut-rêvants choisis."
});
rddCommands.registerCommand({
path: ["/signe", "-"], func: (content, msg, params) => rddCommands.supprimerSignesDraconiquesEphemeres(),
this.registerCommand({
path: ["/signe", "-"], func: (content, msg, params) => this.supprimerSignesDraconiquesEphemeres(),
descr: "Supprime les signes draconiques éphémères"
});
rddCommands.registerCommand({
path: ["/stress"], func: (content, msg, params) => rddCommands.distribuerStress(params),
this.registerCommand({
path: ["/stress"], func: (content, msg, params) => this.distribuerStress(params),
descr: `Distribue du stress aux personnages. Exemples:
<br><strong>/stress</strong> : Ouvre une fenêtre pour donner du stress ou de l'expérience à un ensemble de personnages
<br><strong>/stress 6</strong> : Distribue 6 points des Stress à tout les personnages joueurs, sans raison renseignée
@ -114,11 +159,10 @@ export class RdDCommands {
<br><strong>/stress 6 Glou Paulo</strong> : Distribue 6 points de Stress au personnage Paulon ou au personnage joueur Paulo, à cause d'un Glou`
});
game.system.rdd.commands = rddCommands;
}
}
constructor() {
this.commandsTable = {};
this.registerCommand({
path: ["/chrono"], func: (content, msg, params) => DialogChronologie.create(),
descr: `Enregistre une entrée de chronologie dans un article de journal`
});
}
/* -------------------------------------------- */
@ -160,37 +204,50 @@ export class RdDCommands {
processChatCommand(commandLine, content = '', msg = {}) {
// Setup new message's visibility
let rollMode = game.settings.get("core", "rollMode");
if (["gmroll", "blindroll"].includes(rollMode)) msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
if (rollMode === "blindroll") msg["blind"] = true;
if (["gmroll", "blindroll"].includes(rollMode)) {
msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
}
if (rollMode === "blindroll"){
msg["blind"] = true;
}
msg["type"] = 0;
if (!this.commandsTable) {
this._registerCommands();
}
let command = commandLine[0].toLowerCase();
if (this._isCommandHandled(command)) {
let params = commandLine.slice(1);
return this.process(command, params, content, msg);
this._processCommand(this.commandsTable, command, params, content, msg);
return true;
}
return false;
}
process(command, params, content, msg) {
return this._processCommand(this.commandsTable, command, params, content, msg);
_isCommandHandled(command){
return this.commandsTable[command] != undefined;
}
_processCommand(commandsTable, name, params, content = '', msg = {}, path = "") {
async _processCommand(commandsTable, name, params, content = '', msg = {}, path = "") {
let command = commandsTable[name];
path = path + name + " ";
if (command && command.subTable) {
if (params[0]) {
return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path)
this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path)
}
else {
this.help(msg, command.subTable);
}
return true;
}
}
if (command && command.func) {
const result = command.func(content, msg, params);
new Promise(async () => {
const result = await command.func(content, msg, params);
if (result == false) {
RdDCommands._chatAnswer(msg, command.descr);
}
});
return true;
}
return false;
@ -201,10 +258,10 @@ export class RdDCommands {
this.help(msg, undefined);
}
async help(msg, table) {
let list = []
this._buildSubTableHelp(list, table || this.commandsTable);
let commands = []
this._buildSubTableHelp(commands, table ?? this.commandsTable);
let html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/settings/dialog-aide-commands.html", { commands: list });
let html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/settings/dialog-aide-commands.html", { commands: commands });
let d = new Dialog(
{
title: "Commandes disponibles dans le tchat",
@ -212,7 +269,7 @@ export class RdDCommands {
buttons: {},
},
{
width: 600, height: 500,
width: 600, height: 600,
});
d.render(true);
@ -242,12 +299,10 @@ export class RdDCommands {
/* -------------------------------------------- */
async getRencontreTMR(params) {
if (params.length == 1 || params.length == 2) {
return TMRRencontres.rollRencontre(params[0], params[1])
return game.system.rdd.rencontresTMR.rollRencontre(params[0], params[1])
}
else {
return false;
}
}
/* -------------------------------------------- */
async rollRdd(msg, params) {
@ -299,20 +354,20 @@ export class RdDCommands {
show: { title: "Table de résolution" }
};
await RdDResolutionTable.rollData(rollData);
RdDCommands._chatAnswer(msg, await RdDResolutionTable.buildRollDataHtml(rollData));
return RdDCommands._chatAnswer(msg, await RdDResolutionTable.buildRollDataHtml(rollData));
}
/* -------------------------------------------- */
async rollDeDraconique(msg) {
let ddr = await RdDDice.rollTotal("1dr + 7");
RdDCommands._chatAnswer(msg, `Lancer d'un Dé draconique: ${ddr}`);
return RdDCommands._chatAnswer(msg, `Lancer d'un Dé draconique: ${ddr}`);
}
async getTMRAleatoire(msg, params) {
if (params.length < 2) {
let type = params[0];
const tmr = await TMRUtility.getTMRAleatoire(type ? (it => it.type == type) : (it => true));
RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`);
return RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`);
}
else {
return false;
@ -320,6 +375,7 @@ export class RdDCommands {
}
async findTMR(msg, params) {
if (params && params.length > 0) {
const search = Misc.join(params, ' ');
const found = TMRUtility.findTMR(search);
if (found?.length > 0) {
@ -327,13 +383,53 @@ export class RdDCommands {
}
return RdDCommands._chatAnswer(msg, 'Aucune TMR correspondant à ' + search);
}
return false;
}
async tableRencontres(msg, params) {
if (params && params.length > 0) {
const search = Misc.join(params, ' ');
const solvedTerrain = TMRUtility.findTMRLike(search);
if (solvedTerrain == undefined) {
return RdDCommands._chatAnswer(msg, 'Aucune TMR correspondant à ' + search);
}
return await game.system.rdd.rencontresTMR.chatTable(solvedTerrain);
}
return false;
}
async tableMilieu(msg, params, toChat) {
if (params && params.length > 0) {
const search = Misc.join(params, ' ');
const milieux = await game.system.rdd.environnement.findEnvironnementsLike(search);
if (milieux.length == 0) {
const tous = Object.values(await game.system.rdd.environnement.milieux());
return RdDCommands._chatAnswer(msg, `<strong>Aucun milieu correspondant à '${search}'.</strong>
<br>Milieux disponibles:
<br><ul class="chat-list"><li>${tous.reduce(Misc.joining('</li><li>'))}</li></ul>`);
}
if (milieux.length > 1) {
ui.notifications.warn(`<strong>Plusieurs milieux correspondent à '${search}'</strong>:
<br><ul class="chat-list"><li>${milieux.reduce(Misc.joining('</li><li>'))}</li></ul>`);
}
const tableName = `ressources en ${milieux.reduce(Misc.joining(', '))}`;
if (toChat == 'liste') {
return await game.system.rdd.environnement.searchToChatMessage(milieux, tableName);
}
else {
const row = await game.system.rdd.environnement.getRandom(milieux, tableName);
await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item');
return true;
}
}
return false;
}
/* -------------------------------------------- */
getCoutXpComp(msg, params) {
if (params && (params.length == 1 || params.length == 2)) {
let to = params.length == 1 ? Number(params[0]) : Number(params[1]);
let from = params.length == 1 ? to - 1 : Number(params[0]);
RdDCommands._chatAnswer(msg, `Coût pour passer une compétence de ${from} à ${to}: ${RdDItemCompetence.getDeltaXp(from, to)}`);
return RdDCommands._chatAnswer(msg, `Coût pour passer une compétence de ${from} à ${to}: ${RdDItemCompetence.getDeltaXp(from, to)}`);
}
else {
return false;
@ -344,7 +440,7 @@ export class RdDCommands {
getCoutXpCarac(msg, params) {
if (params && params.length == 1) {
let to = Number(params[0]);
RdDCommands._chatAnswer(msg, `Coût pour passer une caractéristique de ${to - 1} à ${to}: ${RdDCarac.getCaracXp(to)}`);
return RdDCommands._chatAnswer(msg, `Coût pour passer une caractéristique de ${to - 1} à ${to}: ${RdDCarac.getCaracXp(to)}`);
}
else {
return false;
@ -358,10 +454,9 @@ export class RdDCommands {
async supprimerSignesDraconiquesEphemeres() {
game.actors.forEach(actor => {
const ephemeres = actor.filterItems(item => Misc.data(item).type = 'signedraconique' && Misc.data(item).data.ephemere)
.map(item => item.id);
const ephemeres = actor.items.filter(item => item.type = 'signedraconique' && item.system.ephemere);
if (ephemeres.length > 0) {
actor.deleteEmbeddedDocuments("Item", ephemeres);
actor.deleteEmbeddedDocuments("Item", ephemeres.map(item => item.id));
}
});
return true;
@ -405,5 +500,6 @@ export class RdDCommands {
async getMeteo(msg, params) {
return await RdDMeteo.getMeteo();
}
}

View File

@ -1,13 +1,12 @@
import { SYSTEM_RDD } from "./constants.js";
import { Misc } from "./misc.js";
export class RddCompendiumOrganiser {
static init() {
Hooks.on('renderCompendium', async (pack, html, data) => RddCompendiumOrganiser.onRenderCompendium(pack, html, data))
Hooks.on('renderCompendium', async (pack, html, compendiumData) => RddCompendiumOrganiser.onRenderCompendium(pack, html, compendiumData))
}
static async onRenderCompendium(compendium, html, data) {
console.log('onRenderCompendium', compendium, html, data);
static async onRenderCompendium(compendium, html, compendiumData) {
console.log('onRenderCompendium', compendium, html, compendiumData);
const pack = compendium.collection
if (pack.metadata.system === SYSTEM_RDD) {
html.find('.directory-item').each((i, element) => {
@ -17,7 +16,7 @@ export class RddCompendiumOrganiser {
}
static async setEntityTypeName(pack, element) {
const label = Misc.getEntityTypeLabel(await pack.getDocument(element.dataset.documentId));
const label = RddCompendiumOrganiser.getEntityTypeLabel(await pack.getDocument(element.dataset.documentId));
RddCompendiumOrganiser.insertEntityType(element, label);
}
@ -27,4 +26,17 @@ export class RddCompendiumOrganiser {
}
}
static getEntityTypeLabel(entity) {
const documentName = entity?.documentName
const type = entity?.type
if (documentName === 'Actor' || documentName === 'Item') {
const label = CONFIG[documentName]?.typeLabels?.[type] ?? type;
if (game.i18n.has(label)) {
return game.i18n.localize(label);
}
}
return type;
}
}

56
module/rdd-confirm.js Normal file
View File

@ -0,0 +1,56 @@
import { Grammar } from "./grammar.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
export class RdDConfirm {
/* -------------------------------------------- */
static confirmer(options, autresActions) {
options.bypass = options.bypass || !(options.settingConfirmer == undefined || ReglesOptionelles.isUsing(options.settingConfirmer));
if (options.bypass) {
options.onAction();
}
else {
let buttons = {
"action": RdDConfirm._createButtonAction(options),
"cancel": RdDConfirm._createButtonCancel()
};
if (options.settingConfirmer) {
buttons = mergeObject(RdDConfirm._createButtonActionSave(options), buttons);
}
if (autresActions) {
buttons = mergeObject(autresActions, buttons);
}
const dialogDetails = {
title: options.title,
content: options.content,
default: "cancel",
buttons: buttons
};
new Dialog(dialogDetails, { width: 150 * Object.keys(buttons).length }).render(true);
}
}
static _createButtonCancel() {
return { icon: '<i class="fas fa-times"></i>', label: "Annuler" };
}
static _createButtonAction(options) {
return {
icon: '<i class="fas fa-check"></i>',
label: options.buttonLabel,
callback: () => options.onAction()
};
}
static _createButtonActionSave(options) {
return {
"actionSave": {
icon: '<i class="fas fa-user-check"></i>',
label: options.buttonLabel + "<br>et ne plus demander",
callback: () => {
ReglesOptionelles.set(options.settingConfirmer, false);
options.onAction();
}
}
};
}
}

View File

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

View File

@ -18,18 +18,19 @@ const tableGemmes = {
export class RdDGemme extends Item {
static getGemmeTypeOptionList() {
// TODO: look how to map object key-value pairs
let options = ""
for (let gemmeKey in tableGemmes) {
let gemmeData = tableGemmes[gemmeKey];
options += `<option value="${gemmeKey}">${gemmeData.label}</option>`
options += `<option value="${gemmeKey}">${tableGemmes[gemmeKey].label}</option>`
}
return options;
}
static calculDataDerivees(data) {
data.cout = (data.taille * data.purete) + data.qualite;
data.inertie = 7 - data.purete;
data.enchantabilite = data.taille - data.inertie;
static calculDataDerivees(gemme) {
gemme.system.cout = (gemme.system.taille * gemme.system.purete) + gemme.system.qualite;
gemme.system.inertie = 7 - gemme.system.purete;
gemme.system.enchantabilite = gemme.system.taille - gemme.system.inertie;
}
}

View File

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

View File

@ -2,20 +2,7 @@ import { Misc } from "./misc.js";
export class RdDHotbar {
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog for item
* Actor - open actor sheet
* Journal - open journal sheet
*/
static initDropbar( ) {
Hooks.on("hotbarDrop", async (bar, documentData, slot) => {
// Create item macro if rollable item - weapon, spell, prayer, trait, or skill
if (documentData.type == "Item") {
if (documentData.data.type != "arme" && documentData.data.type != "competence" )
return
let item = documentData.data
static async addToHotbar(item, slot) {
let command = `game.system.rdd.RdDHotbar.rollMacro("${item.name}", "${item.type}");`;
let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command));
if (!macro) {
@ -26,40 +13,35 @@ export class RdDHotbar {
command: command
}, { displaySheet: false })
}
game.user.assignHotbarMacro(macro, slot);
await game.user.assignHotbarMacro(macro, slot);
}
// Create a macro to open the actor sheet of the actor dropped on the hotbar
else if (documentData.type == "Actor") {
let actor = game.actors.get(documentData.id);
let command = `game.actors.get("${documentData.id}").sheet.render(true)`
let macro = game.macros.contents.find(m => (m.name === actor.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: actor.data.name,
type: "script",
img: actor.data.img,
command: command
}, { displaySheet: false })
game.user.assignHotbarMacro(macro, slot);
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog for item
* Actor - open actor sheet
* Journal - open journal sheet
*/
static initDropbar() {
Hooks.on("hotbarDrop", (bar, documentData, slot) => {
// Create item macro if rollable item - weapon, spell, prayer, trait, or skill
if (documentData.type == "Item") {
let item = fromUuidSync(documentData.uuid)
if (item == undefined) {
item = this.actor.items.get(documentData.uuid)
}
console.log("DROP", documentData, item)
if (!item || (item.type != "arme" && item.type != "competence")) {
return true
}
// Create a macro to open the journal sheet of the journal dropped on the hotbar
else if (documentData.type == "JournalEntry") {
let journal = game.journal.get(documentData.id);
let command = `game.journal.get("${documentData.id}").sheet.render(true)`
let macro = game.macros.contents.find(m => (m.name === journal.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: journal.data.name,
type: "script",
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp",
command: command
}, { displaySheet: false })
game.user.assignHotbarMacro(macro, slot);
this.addToHotbar(item, slot)
return false
}
}
return false;
});
return true
})
}
/** Roll macro */
@ -69,15 +51,17 @@ export class RdDHotbar {
if (speaker.token) actor = game.actors.tokens[speaker.token];
if (!actor) actor = game.actors.get(speaker.actor);
let item = Misc.data(actor?.items.find(it => it.name === itemName && it.type == itemType));
if (!item) return ui.notifications.warn(`Impossible de trouver l'objet de cette macro`);
let item = actor?.items.find(it => it.name === itemName && it.type == itemType) ?? undefined;
if (!item) {
return ui.notifications.warn(`Impossible de trouver l'objet de cette macro`);
}
// Trigger the item roll
switch (item.type) {
case "arme":
return actor.rollArme(item);
case "competence":
return actor.rollCompetence( itemName );
return actor.rollCompetence(itemName);
}
}

View File

@ -23,10 +23,9 @@ import { RdDTokenHud } from "./rdd-token-hud.js";
import { RdDCommands } from "./rdd-commands.js";
import { RdDCombatManager, RdDCombat } from "./rdd-combat.js";
import { ChatUtility } from "./chat-utility.js";
import { StatusEffects } from "./status-effects.js";
import { StatusEffects } from "./settings/status-effects.js";
import { RddCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { RdDHerbes } from "./rdd-herbes.js";
@ -35,6 +34,16 @@ import { RdDDice } from "./rdd-dice.js";
import { RdDPossession } from "./rdd-possession.js";
import { RdDSigneDraconiqueItemSheet } from "./item-signedraconique-sheet.js";
import { Misc } from "./misc.js";
import { Migrations } from './migrations.js';
import { DialogChronologie } from "./dialog-chronologie.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDRencontreItemSheet } from "./item-rencontre-sheet.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDHerbeItemSheet } from "./item-herbe-sheet.js";
import { Environnement } from "./environnement.js";
import { RdDIngredientItemSheet } from "./item-ingredient-sheet.js";
import { RdDFauneItemSheet } from "./item-faune-sheet.js";
import { RdDConteneurItemSheet } from "./item-conteneur-sheet.js";
/* -------------------------------------------- */
/* Foundry VTT Initialization */
@ -76,7 +85,7 @@ Hooks.once("init", async function () {
name: "calendrier",
scope: "world",
config: false,
default: RdDCalendrier.getCalendrier(0),
default: RdDCalendrier.createCalendrierInitial(),
type: Object
});
@ -106,6 +115,8 @@ Hooks.once("init", async function () {
default: RdDCalendrier.createCalendrierPos(),
type: Object
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "supprimer-dialogues-combat-chat", {
name: "Supprimer les dialogues de combat",
@ -147,13 +158,16 @@ Hooks.once("init", async function () {
};
/* -------------------------------------------- */
game.socket.on(SYSTEM_SOCKET_ID, sockmsg => {
game.socket.on(SYSTEM_SOCKET_ID, async (sockmsg) => {
console.log(">>>>> MSG RECV", sockmsg);
try {
RdDUtility.onSocketMessage(sockmsg);
RdDCombat.onSocketMessage(sockmsg);
ChatUtility.onSocketMessage(sockmsg);
RdDActor.onSocketMessage(sockmsg);
} catch (e) {
console.error('game.socket.on(SYSTEM_SOCKET_ID) Exception: ', sockmsg, ' => ', e)
}
});
/* -------------------------------------------- */
@ -175,19 +189,30 @@ Hooks.once("init", async function () {
Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
Items.registerSheet(SYSTEM_RDD, RdDSigneDraconiqueItemSheet, {
label: "Signe draconique",
types: ["signedraconique"],
makeDefault: true
});
RdDItemSheet.register(RdDSigneDraconiqueItemSheet);
RdDItemSheet.register(RdDRencontreItemSheet);
RdDItemSheet.register(RdDConteneurItemSheet);
RdDItemSheet.register(RdDHerbeItemSheet);
RdDItemSheet.register(RdDFauneItemSheet);
RdDItemSheet.register(RdDIngredientItemSheet);
Items.registerSheet(SYSTEM_RDD, RdDItemSheet, {
types: ["arme", "armure", "objet", "arme", "armure", "conteneur", "competence", "sort", "herbe", "ingredient", "livre", "potion", "munition", "rencontresTMR", "queue", "ombre", "souffle",
"tete", "competencecreature", "tarot", "monnaie", "nombreastral", "tache", "meditation", "casetmr", "recettealchimique", "gemme",
"musique", "chant", "danse", "jeu", "recettecuisine", "maladie", "poison", "oeuvre", "nourritureboisson", "possession"], makeDefault: true
types: [
"competence", "competencecreature",
"recettealchimique", "musique", "chant", "danse", "jeu", "recettecuisine", "oeuvre",
"objet", "arme", "armure", "livre", "potion", "munition",
"monnaie", "nourritureboisson", "gemme",
"meditation", "queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve",
"nombreastral", "tache", "maladie", "poison", "possession",
"tarot", "extraitpoetique"
], makeDefault: true
});
CONFIG.Combat.documentClass = RdDCombatManager;
// préparation des différents modules
SystemCompendiums.init();
DialogChronologie.init();
ReglesOptionelles.init();
RdDUtility.init();
RdDDice.init();
@ -199,9 +224,10 @@ Hooks.once("init", async function () {
RddCompendiumOrganiser.init();
EffetsDraconiques.init()
TMRUtility.init();
TMRRencontres.init();
RdDHotbar.initDropbar();
RdDPossession.init();
TMRRencontres.init();
Environnement.init();
});
/* -------------------------------------------- */
@ -219,21 +245,22 @@ function messageDeBienvenue() {
/* -------------------------------------------- */
// Register world usage statistics
function registerUsageCount( registerKey ) {
if ( game.user.isGM ) {
game.settings.register(registerKey, "world-key", {
function registerUsageCount(registerKey) {
if (game.user.isGM) {
game.settings.register("world", "world-key", {
name: "Unique world key",
scope: "world",
config: false,
default: "NONE",
type: String
});
let worldKey = game.settings.get(registerKey, "world-key")
if ( worldKey == undefined || worldKey == "" ) {
let worldKey = game.settings.get("world", "world-key")
if (worldKey == undefined || worldKey == "") {
worldKey = randomID(32)
game.settings.set(registerKey, "world-key", worldKey )
game.settings.set("world", "world-key", worldKey)
}
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.data.version}"`
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
$.ajax(regURL)
/* -------------------------------------------- */
}
@ -244,6 +271,9 @@ function registerUsageCount( registerKey ) {
/* -------------------------------------------- */
Hooks.once("ready", async function () {
await migrationPngWebp_1_5_34()
if (Misc.isUniqueConnectedGM()) {
new Migrations().migrate();
}
StatusEffects.onReady();
RdDHerbes.initializeHerbes();
@ -268,7 +298,7 @@ Hooks.once("ready", async function () {
}
if (Misc.isUniqueConnectedGM()) {
messageDeBienvenue();
registerUsageCount( SYSTEM_RDD );
registerUsageCount(SYSTEM_RDD);
}
});
@ -291,8 +321,8 @@ async function migrationPngWebp_1_5_34() {
await Item.updateDocuments(itemsUpdates);
await Actor.updateDocuments(actorsUpdates);
game.actors.forEach(actor => {
if (actor.data.token?.img && actor.data.token.img.match(regexOldPngJpg)) {
actor.update({ "token.img": convertImgToWebp(actor.data.token.img) });
if (actor.token?.img && actor.token.img.match(regexOldPngJpg)) {
actor.update({ "token.img": convertImgToWebp(actor.token.img) });
}
const actorItemsToUpdate = prepareDocumentsImgUpdate(actor.items);
actor.updateEmbeddedDocuments('Item', actorItemsToUpdate);
@ -313,17 +343,3 @@ async function migrationPngWebp_1_5_34() {
/* -------------------------------------------- */
Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d));
/* -------------------------------------------- */
/* Foundry VTT chat message */
/* -------------------------------------------- */
Hooks.on("chatMessage", (html, content, msg) => {
if (content[0] == '/') {
let regExp = /(\S+)/g;
let commands = content.match(regExp);
if (game.system.rdd.commands.processChatCommand(commands, content, msg)) {
return false;
}
}
return true;
});

View File

@ -3,6 +3,7 @@ import { RdDCombat } from "./rdd-combat.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { Targets } from "./targets.js";
/* -------------------------------------------- */
/* On part du principe qu'une entité démarre tjs
@ -19,23 +20,155 @@ export class RdDPossession {
/* -------------------------------------------- */
static searchPossessionFromEntite(attacker, defender) {
let poss = attacker.data.items.find(poss => poss.type == 'possession' && poss.data.data.possedeid == defender.data._id);
let poss = attacker.items.find(poss => poss.type == 'possession' && poss.system.possedeid == defender.id);
if (!poss) {
poss = defender.data.items.find(poss => poss.type == 'possession' && poss.data.data.possedeid == defender.data._id);
poss = defender.items.find(poss => poss.type == 'possession' && poss.system.possedeid == defender.id);
}
return poss && duplicate(poss) || undefined;
}
/* -------------------------------------------- */
static updateEtatPossession(possession) {
static async onAttaquePossession(target, attacker, competence, suitePossession = undefined) {
const defender = target.actor;
const fromEntite = RdDPossession.searchPossessionFromEntite(attacker, defender);
const isNouvelle = !suitePossession && ! fromEntite;
const possession = (suitePossession ?? fromEntite ?? (await RdDPossession.createPossession(attacker, defender)));
RdDPossession.$updateEtatPossession(possession)
let rollData = {
mode: "possession",
isECNIDefender: false,
competence: competence,
possession: possession,
attacker: attacker,
defender: defender,
targetToken: Targets.extractTokenData(target)
};
if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
await RdDPossession.$rollAttaquePossession(attacker, rollData, isNouvelle);
}
/* -------------------------------------------- */
static async onConjurerPossession(attacker, competence, possession) {
possession = duplicate(possession);
RdDPossession.$updateEtatPossession(possession)
let rollData = {
mode: "possession",
isECNIDefender: true,
competence: competence,
possession: possession,
attacker: attacker,
defender: game.actors.get(possession.system.possesseurid)
};
await RdDPossession.$rollAttaquePossession(attacker, rollData);
}
/* -------------------------------------------- */
static async onDefensePossession(attackerId, defenderId, possessionId) {
let attacker = game.actors.get(attackerId)
let possession = attacker?.getPossession(possessionId)
defenderId = defenderId ?? possession?.system.possesseurid ?? undefined
let defender = game.actors.get(defenderId)
possession = possession ?? defender?.getPossession(possessionId) ?? undefined;
if (!possession) {
ui.notifications.warn("Une erreur s'est produite : Aucune possession trouvée !!")
return
}
possession = duplicate(possession)
// Update for draconic roll
let rollData = {
mode: "conjuration",
isECNIDefender: defender.type == "entite",
possession: possession,
attacker: attacker,
defender: defender,
competence: defender.getDraconicOuPossession(),
selectedCarac: defender.system.carac.reve,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } }
}
rollData.competence.system.defaut_carac = 'reve-actuel'
await RdDPossession.$rollDefensePossession(defender, rollData);
}
/* -------------------------------------------- */
static async $rollAttaquePossession(attacker, rollData, isNouvelle = false) {
const dialog = await RdDRoll.create(attacker, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
name: 'jet-possession',
label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession',
callbacks: [
{ condition: r => (r.rolled.isSuccess), action: async (r) => await RdDPossession.$onRollPossession(r, true, isNouvelle) },
{ condition: r => (r.rolled.isEchec), action: async (r) => await RdDPossession.$onRollPossession(r, false, isNouvelle) },
]
});
dialog.render(true);
}
/* -------------------------------------------- */
static async $onRollPossession(rollData, isSuccess, isNouvelle = false) {
rollData.possession.isSuccess = isSuccess;
RdDPossession.$updateEtatPossession(rollData.possession);
if (isNouvelle) {
// Creer la possession sur le defenseur
rollData.defender.createEmbeddedDocuments('Item', [rollData.possession.toObject()])
}
await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html');
}
/* -------------------------------------------- */
static async $rollDefensePossession(defender, rollData) {
const dialog = await RdDRoll.create(defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html' },
{
name: 'conjurer',
label: 'Conjurer une Possession',
callbacks: [
{ action: async (r) => await RdDPossession.$onRollConjuration(r) }
]
}
);
dialog.render(true);
}
/* -------------------------------------------- */
static async $onRollConjuration(rollData) {
let actor = game.actors.get(rollData.possession.system.possedeid)
if (!rollData.rolled.isSuccess) {
if (rollData.isECNIDefender) {
rollData.possession.system.compteur--
} else {
rollData.possession.system.compteur++
}
let update = { _id: rollData.possession._id, "system.compteur": rollData.possession.system.compteur }
await actor.updateEmbeddedDocuments('Item', [update])
}
RdDPossession.$updateEtatPossession(rollData.possession)
await RdDResolutionTable.displayRollData(rollData,rollData.defender, 'chat-resultat-possession.html')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
// conjuration
actor.deleteEmbeddedDocuments("Item", [rollData.possession._id])
}
}
/* -------------------------------------------- */
static $updateEtatPossession(possession) {
possession.ptsConjuration = 0
possession.ptsPossession = 0
console.log("Possession", possession)
if (possession.data.compteur > 0) {
possession.ptsPossession = possession.data.compteur
if (possession.system.compteur > 0) {
possession.ptsPossession = possession.system.compteur
}
if (possession.data.compteur < 0) {
possession.ptsConjuration = Math.abs(possession.data.compteur)
if (possession.system.compteur < 0) {
possession.ptsConjuration = Math.abs(possession.system.compteur)
}
possession.isPosseder = false
possession.isConjurer = false
@ -47,123 +180,16 @@ export class RdDPossession {
}
}
/* -------------------------------------------- */
static async resultConjuration(rollData) {
let actor = game.actors.get(rollData.possession.data.possedeid)
if (!rollData.rolled.isSuccess) {
if (rollData.isECNIDefender) {
rollData.possession.data.compteur--
} else {
rollData.possession.data.compteur++
}
let update = { _id: rollData.possession._id, "data.compteur": rollData.possession.data.compteur }
await actor.updateEmbeddedDocuments('Item', [update])
}
this.updateEtatPossession(rollData.possession)
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
actor.deleteEmbeddedDocuments("Item", [rollData.possession._id])
}
}
/* -------------------------------------------- */
static async onDefensePossession(attackerId, defenderId, possessionId) {
let attacker = game.actors.get(attackerId)
let defender = game.actors.get(defenderId)
let possession = attacker.getPossession(possessionId) ?? defender.getPossession(possessionId) ;
if (!possession) {
ui.notifications.warn("Une erreur s'est produite : Aucune possession trouvée !!")
return
}
// Update for draconic roll
let rollData = {
mode: "conjuration",
isECNIDefender: defender.type == "entite",
possession: duplicate(possession),
attacker: attacker,
defender: defender,
competence: defender.getDraconicOuPossession(),
selectedCarac: defender.data.data.carac.reve,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } }
}
rollData.competence.data.defaut_carac = 'reve-actuel'
const dialog = await RdDRoll.create(defender, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html',
options: { height: 450 }
},
{
name: 'conjurer',
label: 'Conjurer une Possession',
callbacks: [
{ action: async r => await this.resultConjuration(r) }
]
}
);
dialog.render(true)
}
/* -------------------------------------------- */
static async onAttaquePossession(attacker, competence, possession = undefined) {
const target = RdDCombat.getTarget()
if (target == undefined) {
ui.notifications.warn((game.user.targets?.size ?? 0) > 1
? "Vous devez choisir <strong>une seule</strong> cible à posséder!"
: "Vous devez choisir une cible à posséder!");
return;
}
const defender = target.actor;
possession = duplicate(possession ?? this.searchPossessionFromEntite(attacker, defender) ??(await this.createPossession(attacker, defender)));
this.updateEtatPossession(possession)
let rollData = {
mode: "possession",
isECNIDefender: defender.type == "entite",
competence: competence,
possession: possession,
attacker: attacker,
defender: defender
};
if (attacker.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
const dialog = await RdDRoll.create(attacker, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
options: { height: 540 }
}, {
name: 'jet-possession',
label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession',
callbacks: [
{ condition: r => (r.rolled.isSuccess), action: async r => await this._onRollPossession(r, true) },
{ condition: r => (r.rolled.isEchec), action: async r => await this._onRollPossession(r, false) },
]
});
dialog.render(true)
}
/* -------------------------------------------- */
static async _onRollPossession(rollData, isSuccess) {
rollData.possession.isSuccess = isSuccess;
this.updateEtatPossession(rollData.possession);
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html');
}
/* -------------------------------------------- */
static async createPossession(attacker, defender) {
let possessionData = {
return await Item.create({
name: "Possession en cours de " + attacker.name, type: 'possession',
img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
data: { description: "", typepossession: attacker.name, possede: false, possessionid: randomID(16), possesseurid: attacker.data._id, possedeid: defender.data._id, date: 0, compteur: 0 }
}
// Creates only the possession on the personnage side
let poss = await defender.createEmbeddedDocuments('Item', [possessionData])
return duplicate(poss[0])
system: { description: "", typepossession: attacker.name, possede: false, possessionid: randomID(16), possesseurid: attacker.id, possedeid: defender.id, date: 0, compteur: 0 }
},
{
temporary: true
})
}
}

View File

@ -1,7 +1,7 @@
import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
/**
* difficultés au delà de -10
@ -14,7 +14,7 @@ const levelDown = [
{ level: -15, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 10 },
{ level: -16, score: 1, norm: 1, sign: 0, part: 0, epart: 0, etotal: 2 }
];
const levelImpossible = { score: 0, norm:0, sign: 0, part: 0, epart: 0, etotal: 1 };
const levelImpossible = { score: 0, norm: 0, sign: 0, part: 0, epart: 0, etotal: 1 };
const reussites = [
{ code: "etotal", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: -4, ptQualite: -6, quality: "Echec total", condition: (target, roll) => roll >= target.etotal && roll <= 100 },
@ -42,6 +42,44 @@ export class RdDResolutionTable {
return table;
}
/* -------------------------------------------- */
static computeChances(carac, level) {
if (level < -16) {
return levelImpossible;
}
if (level < -10) {
return levelDown.find(it => it.level == level);
}
const percentage = RdDResolutionTable.computePercentage(carac, level);
return this._computeCell(level, percentage);
}
/* -------------------------------------------- */
static _computeRow(caracValue) {
let dataRow = [
this._computeCell(-10, Math.max(Math.floor(caracValue / 4), 1)),
this._computeCell(-9, Math.max(Math.floor(caracValue / 2), 1))
]
for (var diff = -8; diff <= 22; diff++) {
dataRow[diff + 10] = this._computeCell(diff, RdDResolutionTable.computePercentage(caracValue, diff));
}
return dataRow;
}
/* -------------------------------------------- */
static _computeCell(niveau, percentage) {
return {
niveau: niveau,
score: percentage,
norm: Math.min(99, percentage),
sign: this._reussiteSignificative(percentage),
part: this._reussitePart(percentage),
epart: this._echecParticulier(percentage),
etotal: this._echecTotal(percentage)
};
}
/* -------------------------------------------- */
static getResultat(code) {
let resultat = reussites.find(r => code == r.code);
@ -51,17 +89,6 @@ export class RdDResolutionTable {
return resultat;
}
/* -------------------------------------------- */
static explain(rolled) {
let message = "<br>Jet : <strong>" + rolled.roll + "</strong> sur " + rolled.score + "% ";
if (rolled.caracValue != null && rolled.finalLevel != null) {
message += (rolled.diviseurSignificative > 1 ? `(1/${rolled.diviseurSignificative} de ` : "(")
+ rolled.caracValue + " à " + Misc.toSignedString(rolled.finalLevel) + ") ";
}
message += '<strong>' + rolled.quality + '</strong>'
return message;
}
/* -------------------------------------------- */
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
return await ChatUtility.createChatWithRollMode(actor?.userName ?? game.user.name, {
@ -82,8 +109,8 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static async roll(caracValue, finalLevel, rollData = {}){
let chances = this.computeChances(caracValue, finalLevel);
static async roll(caracValue, finalLevel, rollData = {}) {
let chances = duplicate(this.computeChances(caracValue, finalLevel));
this._updateChancesWithBonus(chances, rollData.bonus, finalLevel);
this._updateChancesFactor(chances, rollData.diviseurSignificative);
chances.showDice = rollData.showDice;
@ -95,7 +122,7 @@ export class RdDResolutionTable {
rolled.bonus = rollData.bonus;
rolled.factorHtml = Misc.getFractionHtml(rollData.diviseurSignificative);
if (ReglesOptionelles.isUsing("afficher-colonnes-reussite")){
if (ReglesOptionelles.isUsing("afficher-colonnes-reussite")) {
rolled.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll);
rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
}
@ -103,28 +130,39 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static findNiveauNecessaire(caracValue, rollValue) {
for (let cell of this.resolutionTable[caracValue]) {
if ( rollValue <= cell.norm) {
return cell.niveau;
static findNiveauNecessaire(carac, rolled) {
if (carac == 0) {
return NaN;
}
if (rolled >= carac){
const upper = Math.ceil(rolled/carac);
return 2*upper -10
}
return 16; // Dummy default
if (rolled > Math.floor(carac/2)) {
return -8
}
if (rolled > Math.floor(carac/4)) {
return -9
}
if (rolled > 1) {
return -10
}
return -11;
}
/* -------------------------------------------- */
static _updateChancesFactor(chances, diviseur) {
if (chances.level > -11 && diviseur && diviseur > 1) {
let newScore = Math.floor(chances.score / diviseur);
mergeObject(chances, this._computeCell(null, newScore), { overwrite: true });
mergeObject(chances, this._computeCell(undefined, newScore), { overwrite: true });
}
}
/* -------------------------------------------- */
static _updateChancesWithBonus(chances, bonus, finalLevel) {
if (bonus && finalLevel>-11) {
if (bonus && finalLevel > -11) {
let newScore = Number(chances.score) + bonus;
mergeObject(chances, this._computeCell(null, newScore), { overwrite: true });
mergeObject(chances, this._computeCell(undefined, newScore), { overwrite: true });
}
}
@ -142,23 +180,19 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static async rollChances(chances, diviseur, forceDiceResult = -1) {
if (forceDiceResult <= 0 || forceDiceResult > 100) {
forceDiceResult = -1;
}
chances.roll = await RdDDice.rollTotal((forceDiceResult == -1) ? "1d100" : `${forceDiceResult}`, chances);
chances.forceDiceResult = forceDiceResult <= 0 || forceDiceResult > 100 ? undefined : { total: forceDiceResult };
chances.roll = await RdDDice.rollTotal("1d100", chances);
mergeObject(chances, this.computeReussite(chances, chances.roll, diviseur), { overwrite: true });
return chances;
}
/* -------------------------------------------- */
static computeChances(caracValue, difficulte) {
if (difficulte < -16) {
return duplicate(levelImpossible);
}
if (difficulte < -10) {
return duplicate(levelDown.find(levelData => levelData.level == difficulte));
}
return duplicate(RdDResolutionTable.resolutionTable[caracValue][difficulte + 10]);
static computePercentage(carac, diff) {
if (diff < -16) return 0
if (diff < -10) return 1
if (diff == -10) return Math.max(Math.floor(carac / 4), 1)
if (diff == -9) return Math.max(Math.floor(carac / 2), 1)
return Math.max(Math.floor(carac * (diff + 10) / 2), 1);
}
/* -------------------------------------------- */
@ -166,7 +200,7 @@ export class RdDResolutionTable {
if (rollData.selectedCarac?.label.toLowerCase().includes('chance')) {
return true;
}
if (rollData.selectedSort?.data.isrituel) {
if (rollData.selectedSort?.system.isrituel) {
return true;
}
return false;
@ -215,31 +249,6 @@ export class RdDResolutionTable {
return reussite;
}
/* -------------------------------------------- */
static _computeRow(caracValue) {
let dataRow = [
this._computeCell(-10, Math.max(Math.floor(caracValue / 4), 1)),
this._computeCell(-9, Math.max(Math.floor(caracValue / 2), 1))
]
for (var diff = -8; diff <= 22; diff++) {
dataRow[diff + 10] = this._computeCell(diff, Math.max(Math.floor(caracValue * (diff + 10) / 2), 1));
}
return dataRow;
}
/* -------------------------------------------- */
static _computeCell(niveau, percentage) {
return {
niveau: niveau,
score: percentage,
norm: Math.min(99, percentage),
sign: this._reussiteSignificative(percentage),
part: this._reussitePart(percentage),
epart: this._echecParticulier(percentage),
etotal: this._echecTotal(percentage)
};
}
/* -------------------------------------------- */
static _reussiteSignificative(percentage) {
return Math.floor(percentage / 2);
@ -263,92 +272,34 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static buildHTMLResults(caracValue, levelValue) {
if (caracValue == undefined || isNaN(caracValue)) caracValue = 10;
if (levelValue == undefined || isNaN(levelValue)) levelValue = 0;
let cell = this.computeChances(caracValue, levelValue);
cell.epart = cell.epart > 99 ? 'N/A' : cell.epart;
cell.etotal = cell.etotal > 100 ? 'N/A' : cell.etotal;
cell.score = Math.min(cell.score, 99);
return `
<span class="table-proba-reussite competence-label">
Particulière: <span class="rdd-roll-part">${cell.part}</span>
- Significative: <span class="rdd-roll-sign">${cell.sign}</span>
- Réussite: <span class="rdd-roll-norm">${cell.score}</span>
- Echec Particulier: <span class="rdd-roll-epart">${cell.epart}</span>
- Echec Total: <span class="rdd-roll-etotal">${cell.etotal}</span>
</span>
`
static subTable(carac, level, delta = { carac: 2, level: 5}) {
return {
carac,
level,
minCarac: carac - (delta?.carac ?? 2),
maxCarac: carac + (delta?.carac ?? 2),
minLevel: level - (delta?.level ?? 5),
maxLevel: level + (delta?.level ?? 5)
};
}
/* -------------------------------------------- */
static buildHTMLTableExtract(caracValue, levelValue) {
return this.buildHTMLTable(caracValue, levelValue, caracValue - 2, caracValue + 2, levelValue - 5, levelValue + 5)
}
static buildHTMLTable(caracValue, levelValue, minCarac = 1, maxCarac = 21, minLevel = -10, maxLevel = 11) {
return this._buildHTMLTable(caracValue, levelValue, minCarac, maxCarac, minLevel, maxLevel)
}
/* -------------------------------------------- */
static _buildHTMLTable(caracValue, levelValue, minCarac, maxCarac, minLevel, maxLevel) {
let countColonnes = maxLevel - minLevel;
static async buildHTMLTable({ carac: carac, level: level, minCarac = 1, maxCarac = 21, minLevel = -10, maxLevel = 11 }) {
let colonnes = maxLevel - minLevel;
minCarac = Math.max(minCarac, 1);
maxCarac = Math.min(maxCarac, caracMaximumResolution);
maxCarac = Math.min(maxCarac, minCarac + 20);
minLevel = Math.max(minLevel, -10);
maxLevel = Math.max(Math.min(maxLevel, 22), minLevel + countColonnes);
let table = $("<table class='table-resolution'/>")
.append(this._buildHTMLHeader(RdDResolutionTable.resolutionTable[0], minLevel, maxLevel));
for (var rowIndex = minCarac; rowIndex <= maxCarac; rowIndex++) {
table.append(this._buildHTMLRow(RdDResolutionTable.resolutionTable[rowIndex], rowIndex, caracValue, levelValue, minLevel, maxLevel));
maxLevel = Math.max(Math.min(maxLevel, 30), minLevel + colonnes);
return await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.html', {
carac: carac,
difficulte: level,
min: minLevel,
rows: RdDResolutionTable.incrementalArray(minCarac, maxCarac),
cols: RdDResolutionTable.incrementalArray(minLevel, maxLevel)
});
}
table.append("</table>");
return table;
}
/* -------------------------------------------- */
static _buildHTMLHeader(dataRow, minLevel, maxLevel) {
let tr = $("<tr/>");
if (minLevel > -8) {
tr.append($("<th class='table-resolution-level'/>").text("-8"))
}
if (minLevel > -7) {
tr.append($("<th class='table-resolution-level'/>").text("..."));
}
for (let difficulte = minLevel; difficulte <= maxLevel; difficulte++) {
tr.append($("<th class='table-resolution-level'/>").text(Misc.toSignedString(difficulte)));
}
return tr;
}
/* -------------------------------------------- */
static _buildHTMLRow(dataRow, rowIndex, caracValue, levelValue, minLevel, maxLevel) {
let tr = $("<tr/>");
let max = maxLevel;
if (minLevel > -8) {
let score = dataRow[-8 + 10].score;
tr.append($("<td class='table-resolution-carac'/>").text(score))
}
if (minLevel > -7) {
tr.append($("<td/>"))
}
for (let difficulte = minLevel; difficulte <= max; difficulte++) {
let td = $("<td/>");
let score = dataRow[difficulte + 10].score;
if (rowIndex == caracValue && levelValue == difficulte) {
td.addClass('table-resolution-target');
} else if (difficulte == -8) {
td.addClass('table-resolution-carac');
}
tr.append(td.text(score));
}
return tr;
static incrementalArray(min, max) {
return Array.from(Array(max-min+1).keys()).map(i=>i+min)
}
}

View File

@ -1,4 +1,5 @@
import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE } from "./constants.js";
import { ENTITE_BLURETTE, ENTITE_INCARNE } from "./constants.js";
import { RdDUtility } from "./rdd-utility.js";
/**
* Extend the base Dialog entity by defining a custom window to perform roll.
@ -6,34 +7,39 @@ import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE } from "./constants.
*/
export class RdDEncaisser extends Dialog {
static async encaisser(actor) {
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-encaisser.html',
{ ajustementsEncaissement: RdDUtility.getAjustementsEncaissement() }
);
new RdDEncaisser(html, actor).render(true);
}
/* -------------------------------------------- */
constructor(html, actor) {
// Common conf
let buttons = {};
if (!actor.isEntite()){
buttons = {
let dialogConf = {
title: "Jet d'Encaissement",
content: html,
}
if (!actor.isEntite()) {
dialogConf.default = "mortel";
dialogConf.buttons = {
"mortel": { label: "Mortel", callback: html => this.performEncaisser("mortel") },
"non-mortel": { label: "Non-mortel", callback: html => this.performEncaisser("non-mortel") },
"sonne": { label: "Sonné", callback: html => this.actor.setSonne() },
};
}
else if (actor.isEntite([ENTITE_BLURETTE, ENTITE_INCARNE])){
buttons = {
"cauchemar": { label: "cauchemar", callback: html => this.performEncaisser("cauchemar") }
else if (actor.isEntite([ENTITE_BLURETTE, ENTITE_INCARNE])) {
dialogConf.default = "cauchemar"
dialogConf.buttons = {
"cauchemar": { label: "Cauchemar", callback: html => this.performEncaisser("cauchemar") }
}
}
let dialogConf = {
title: "Jet d'Encaissement",
content: html,
buttons: buttons,
default: "mortel"
}
let dialogOptions = {
classes: ["rdddialog"],
classes: ["rdd-roll-dialog"],
width: 320,
height: 240
height: 'fit-content'
}
// Select proper roll dialog template and stuff
@ -44,35 +50,29 @@ export class RdDEncaisser extends Dialog {
this.encaisserSpecial = "aucun";
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find('[name="modificateurDegats"]').val("0");
this.html.find('[name="modificateurDegats"]').change((event) => {
this.modifier = event.currentTarget.value; // Update the selected bonus/malus
});
this.html.find('[name="encaisserSpecial"]').change((event) => {
this.encaisserSpecial = event.currentTarget.value; // Update the selected bonus/malus
});
}
/* -------------------------------------------- */
performEncaisser(mortalite) {
this.actor.encaisserDommages({
dmg: {
total: Number(this.modifier),
ajustement: Number(this.modifier),
encaisserSpecial: this.encaisserSpecial,
loc: { result: 0, label: "" },
mortalite: mortalite
}
});
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
// Setup everything onload
$(function () {
$("#modificateurDegats").val("0");
});
html.find('#modificateurDegats').change((event) => {
this.modifier = event.currentTarget.value; // Update the selected bonus/malus
});
html.find('#encaisserSpecial').change((event) => {
this.encaisserSpecial = event.currentTarget.value; // Update the selected bonus/malus
});
}
}

View File

@ -13,47 +13,33 @@ export class RdDRollDialogEthylisme extends Dialog {
title: "Test d'éthylisme",
content: html,
default: "rollButton",
buttons: { "rollButton": { label: "Test d'éthylisme", callback: html => this.onButton(html) } }
buttons: { "rollButton": { label: "Test d'éthylisme", callback: html => onRoll(this.rollData) } }
};
let dialogOptions = { classes: ["rdddialog"], width: 400, height: 270, 'z-index': 99999 }
let dialogOptions = { classes: ["rdd-roll-dialog"], width: 400, height: 'fit-content', 'z-index': 99999 }
super(dialogConf, dialogOptions)
//console.log("ETH", rollData);
this.onRoll = onRoll;
this.rollData = rollData;
this.actor = actor;
}
async onButton(html) {
this.onRoll(this.rollData);
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.bringToTop();
this.html.find(".force-alcool").change((event) => {
this.rollData.forceAlcool = Misc.toInt(event.currentTarget.value);
this.updateRollResult();
});
this.html.find(".force-alcool").val(Misc.toInt(this.rollData.forceAlcool));
this.updateRollResult();
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.bringToTop(); // Ensure top level
// Get the rollData stuff
var rollData = this.rollData;
var dialog = this;
// Setup everything onload
$(function () {
$("#forceAlcool").val(Misc.toInt(rollData.forceAlcool));
dialog.updateRollResult();
});
// Update !
html.find('#forceAlcool').change((event) => {
rollData.forceAlcool = Misc.toInt(event.currentTarget.value); // Update the selected bonus/malus
dialog.updateRollResult();
});
}
async updateRollResult() {
// Mise à jour valeurs
$("#roll-param").text(this.rollData.vie + " / " + Misc.toSignedString(Number(this.rollData.etat) + Number(this.rollData.forceAlcool) + this.rollData.diffNbDoses));
$(".table-resolution").remove();
this.html.find(".roll-ethylisme").text(this.rollData.vie + " / " + Misc.toSignedString(Number(this.rollData.etat) + Number(this.rollData.forceAlcool) + this.rollData.diffNbDoses));
this.html.find(".table-resolution").remove();
}
}

View File

@ -9,12 +9,19 @@ const titleTableDeResolution = 'Table de résolution';
/* -------------------------------------------- */
export class RdDRollResolutionTable extends Dialog {
static resolutionTable = undefined;
/* -------------------------------------------- */
static async open(rollData = {}) {
static async open() {
if (RdDRollResolutionTable.resolutionTable == undefined) {
const rollData = {}
RdDRollResolutionTable._setDefaultOptions(rollData);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-resolution.html', rollData);
const dialog = new RdDRollResolutionTable(rollData, html);
dialog.render(true);
RdDRollResolutionTable.resolutionTable = new RdDRollResolutionTable(rollData, html);
RdDRollResolutionTable.resolutionTable.render(true);
}
else{
RdDRollResolutionTable.resolutionTable.bringToTop();
}
}
/* -------------------------------------------- */
@ -53,11 +60,40 @@ export class RdDRollResolutionTable extends Dialog {
'lancer-fermer': { label: 'Lancer les dés et fermer', callback: html => this.onLancerFermer() }
}
};
super(conf, { classes: ["rdddialog"], width: 800, height: 800, 'z-index': 99999 });
super(conf, { classes: ["rdd-roll-dialog"], top: 50, width: 'fit-content', height: 'fit-content', 'z-index': 99999 });
this.rollData = rollData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.bringToTop();
this.html.find("[name='diffLibre']").val(Misc.toInt(this.rollData.diffLibre));
this.html.find("[name='diffConditions']").val(Misc.toInt(this.rollData.diffConditions));
this.updateRollResult();
this.html.find('.lancer-table-resolution').click((event) => {
this.onLancer();
});
// Update !
this.html.find("[name='diffLibre']").change((event) => {
this.rollData.diffLibre = Misc.toInt(event.currentTarget.value);
this.updateRollResult();
});
this.html.find("[name='diffConditions']").change((event) => {
this.rollData.diffConditions = Misc.toInt(event.currentTarget.value);
this.updateRollResult();
});
this.html.find("[name='carac']").change((event) => {
let caracKey = event.currentTarget.value;
this.rollData.selectedCarac = this.rollData.carac[caracKey];
this.updateRollResult();
});
}
/* -------------------------------------------- */
async onLancer() {
await RdDResolutionTable.rollData(this.rollData);
@ -72,65 +108,36 @@ export class RdDRollResolutionTable extends Dialog {
await RdDResolutionTable.displayRollData(this.rollData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.bringToTop();
var dialog = this;
// Setup everything onload
function onLoad(){
$("#diffLibre").val(Misc.toInt(dialog.rollData.diffLibre));
$("#diffConditions").val(Misc.toInt(dialog.rollData.diffConditions));
dialog.updateRollResult();
}
$(function () { onLoad();});
html.find('#lancer').click((event) => {
this.onLancer();
});
// Update !
html.find('#diffLibre').change((event) => {
this.rollData.diffLibre = Misc.toInt(event.currentTarget.value);
this.updateRollResult();
});
html.find('#diffConditions').change((event) => {
this.rollData.diffConditions = Misc.toInt(event.currentTarget.value);
this.updateRollResult();
});
html.find('#carac').change((event) => {
let caracKey = event.currentTarget.value;
this.rollData.selectedCarac = this.rollData.carac[caracKey];
this.updateRollResult();
});
}
/* -------------------------------------------- */
async updateRollResult() {
let rollData = this.rollData;
rollData.caracValue = parseInt(rollData.selectedCarac.value)
rollData.finalLevel = this._computeFinalLevel(rollData);
const htmlTable = await RdDResolutionTable.buildHTMLTable({
carac: rollData.caracValue,
level: rollData.finalLevel,
maxCarac: 20,
maxLevel: 10
});
// Mise à jour valeurs
$("#carac").val(rollData.caracValue);
$("#roll-param").text(rollData.selectedCarac.value + " / " + Misc.toSignedString(rollData.finalLevel));
$(".table-resolution").remove();
$(".table-proba-reussite").remove();
$("#tableResolution").append(RdDResolutionTable.buildHTMLTable(rollData.caracValue, rollData.finalLevel));
$("#tableProbaReussite").append(RdDResolutionTable.buildHTMLResults(rollData.caracValue, rollData.finalLevel));
this.html.find("[name='carac']").val(rollData.caracValue);
this.html.find(".roll-param-resolution").text(rollData.selectedCarac.value + " / " + Misc.toSignedString(rollData.finalLevel));
this.html.find("div.placeholder-resolution").empty().append(htmlTable)
}
/* -------------------------------------------- */
_computeFinalLevel(rollData) {
const diffConditions = Misc.toInt(rollData.diffConditions);
const diffLibre = this._computeDiffLibre(rollData);
const diffLibre = Misc.toInt(rollData.diffLibre);
return diffLibre + diffConditions;
}
/* -------------------------------------------- */
_computeDiffLibre(rollData) {
return Misc.toInt(rollData.diffLibre);
async close() {
await super.close();
RdDRollResolutionTable.resolutionTable = undefined;
}
}

View File

@ -6,7 +6,7 @@ import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
/**
* Extend the base Dialog entity to select roll parameters
@ -16,48 +16,42 @@ import { ReglesOptionelles } from "./regles-optionelles.js";
export class RdDRoll extends Dialog {
/* -------------------------------------------- */
static async create(actor, rollData, dialogConfig, ...actions) {
if (actor.isRollWindowsOpened()) {
ui.notifications.warn("Vous avez déja une fenêtre de Test ouverte, il faut la fermer avant d'en ouvrir une autre.")
return;
}
actor.setRollWindowsOpened(true);
RdDRoll._ensureCorrectActions(actions);
static async create(actor, rollData, dialogConfig, action) {
RdDRoll._ensureCorrectAction(action);
RdDRoll._setDefaultOptions(actor, rollData);
const html = await renderTemplate(dialogConfig.html, rollData);
let options = { classes: ["rdddialog"], width: 600, height: 500, 'z-index': 99999 };
if (dialogConfig.options) {
mergeObject(options, dialogConfig.options, { overwrite: true })
let options = { classes: ["rdd-roll-dialog"], width: 600, height: 'fit-content', 'z-index': 99999, close: html => {} };
if (dialogConfig.close) {
options.close = dialogConfig.close;
}
return new RdDRoll(actor, rollData, html, options, actions, dialogConfig.close);
return new RdDRoll(actor, rollData, html, options, action);
}
/* -------------------------------------------- */
static _setDefaultOptions(actor, rollData) {
const actorData = Misc.data(actor);
let defaultRollData = {
alias: actor.name,
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
difficultesLibres: CONFIG.RDD.difficultesLibres,
etat: actor.getEtatGeneral(),
moral: actor.getMoralTotal(), /* La valeur du moral pour les jets de volonté */
carac: actorData.data.carac,
carac: actor.system.carac,
finalLevel: 0,
diffConditions: 0,
diffLibre: rollData.competence?.data.default_diffLibre ?? 0,
malusArmureValue: actor.getMalusArmure(),
surencMalusFlag: actor.isPersonnage() ? (actorData.data.compteurs.surenc.value < 0) : false,
surencMalusValue: actor.computeMalusSurEncombrement(),
useMalusSurenc: false,
useMoral: false, /* Est-ce que le joueur demande d'utiliser le moral ? Utile si le joueur change plusieurs fois de carac associée. */
diffLibre: rollData.competence?.system.default_diffLibre ?? 0,
perteMoralEchec: false, /* Pour l'affichage dans le chat */
use: { libre: true, conditions: true, surenc: false, encTotal: false },
use: {
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,
conditions: true,
surenc: actor.isSurenc(),
encTotal: true
},
isMalusEncombrementTotal: RdDItemCompetence.isMalusEncombrementTotal(rollData.competence),
useMalusEncTotal: false,
malusArmureValue: actor.getMalusArmure(),
surencMalusValue: actor.computeMalusSurEncombrement(),
encTotal: actor.getEncTotal(),
ajustementAstrologique: actor.ajustementAstrologique(),
surprise: actor.getSurprise(false),
@ -66,8 +60,8 @@ export class RdDRoll extends Dialog {
forceDiceResult: -1
}
// Mini patch :Ajout du rêve actuel
if ( actorData.type == "personnage") {
defaultRollData.carac["reve-actuel"] = actorData.data.reve.reve
if (actor.system.type == "personnage") {
defaultRollData.carac["reve-actuel"] = actor.system.reve.reve
}
mergeObject(rollData, defaultRollData, { recursive: true, overwrite: false });
@ -78,6 +72,7 @@ export class RdDRoll extends Dialog {
RollDataAjustements.calcul(rollData, actor);
}
/* -------------------------------------------- */
static getDiviseurSignificative(rollData) {
let facteurSign = 1;
@ -96,158 +91,133 @@ export class RdDRoll extends Dialog {
return facteurSign;
}
/* -------------------------------------------- */
static _ensureCorrectActions(actions) {
if (actions.length == 0) {
throw 'No action defined';
}
actions.forEach(action => {
static _ensureCorrectAction(action) {
if (action.callbacks == undefined) {
action.callbacks = [{ action: r => console.log(action.name, r) }];
console.warn('No callback defined for ', action.name);
action.callbacks = [{ action: r => console.warn(action.name, r) }];
}
});
}
/* -------------------------------------------- */
constructor(actor, rollData, html, options, actions, close = undefined) {
constructor(actor, rollData, html, options, action) {
let conf = {
title: actions[0].label,
title: action.label,
content: html,
buttons: {},
default: actions[0].name,
close: close
};
for (let action of actions) {
conf.buttons[action.name] = {
buttons: {
"onAction": {
label: action.label, callback: html => {
this.rollData.canClose = true;
this.onAction(action, html)
this.onAction(action)
}
}
},
default: "onAction",
close: options.close
};
}
super(conf, options);
this.actor = actor;
this.rollData = rollData;
}
close() {
if (this.rollData.canClose) {
this.actor.setRollWindowsOpened(false);
return super.close();
}
ui.notifications.info("Vous devez faire ce jet de dés!");
}
/* -------------------------------------------- */
async onAction(action, html) {
await RdDResolutionTable.rollData(this.rollData);
console.log("RdDRoll -=>", this.rollData, this.rollData.rolled);
this.actor.setRollWindowsOpened(false);
if (action.callbacks)
for (let callback of action.callbacks) {
if (callback.condition == undefined || callback.condition(this.rollData)) {
await callback.action(this.rollData);
}
}
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.bringToTop();
var dialog = this;
console.log('RdDRoll.activateListeners', this.rollData);
function onLoad() {
let rollData = dialog.rollData;
console.log(rollData);
// Update html, according to data
if (rollData.competence) {
const defaut_carac = Misc.templateData(rollData.competence).defaut_carac;
// Update html, according to rollData
if (this.rollData.competence) {
const defaut_carac = this.rollData.competence.system.defaut_carac
// Set the default carac from the competence item
rollData.selectedCarac = rollData.carac[defaut_carac];
$("#carac").val(defaut_carac);
this.rollData.selectedCarac = this.rollData.carac[defaut_carac];
this.html.find("[name='carac']").val(defaut_carac);
}
if (rollData.selectedSort) {
dialog.setSelectedSort(rollData.selectedSort);
$(".draconic").val(rollData.selectedSort.data.listIndex); // Uniquement a la selection du sort, pour permettre de changer
}
RdDItemSort.setCoutReveReel(rollData.selectedSort);
$("#diffLibre").val(Misc.toInt(rollData.diffLibre));
$("#diffConditions").val(Misc.toInt(rollData.diffConditions));
dialog.updateRollResult();
if (this.rollData.selectedSort) {
this.setSelectedSort(this.rollData.selectedSort);
this.html.find(".draconic").val(this.rollData.selectedSort.system.listIndex); // Uniquement a la selection du sort, pour permettre de changer
}
RdDItemSort.setCoutReveReel(this.rollData.selectedSort);
this.html.find("[name='diffLibre']").val(Misc.toInt(this.rollData.diffLibre));
this.html.find("[name='diffConditions']").val(Misc.toInt(this.rollData.diffConditions));
this.updateRollResult(html);
// Setup everything onload
$(function () { onLoad(); });
// Update !
html.find('#diffLibre').change((event) => {
this.html.find("[name='diffLibre']").change((event) => {
this.rollData.diffLibre = Misc.toInt(event.currentTarget.value); // Update the selected bonus/malus
this.updateRollResult();
this.updateRollResult(html);
});
html.find('#diffConditions').change((event) => {
this.html.find("[name='diffConditions']").change((event) => {
this.rollData.diffConditions = Misc.toInt(event.currentTarget.value); // Update the selected bonus/malus
this.updateRollResult();
this.updateRollResult(html);
});
html.find('#force-dice-result').change((event) => {
this.html.find("[name='force-dice-result']").change((event) => {
this.rollData.forceDiceResult = Misc.toInt(event.currentTarget.value);
});
html.find('#carac').change((event) => {
this.html.find("[name='carac']").change((event) => {
let caracKey = event.currentTarget.value;
this.rollData.selectedCarac = this.rollData.carac[caracKey]; // Update the selectedCarac
this.updateRollResult();
this.updateRollResult(html);
});
html.find('.roll-draconic').change((event) => {
this.html.find('.roll-draconic').change((event) => {
let draconicKey = Misc.toInt(event.currentTarget.value);
this.rollData.competence = this.rollData.draconicList[draconicKey]; // Update the selectedCarac
this.updateRollResult();
this.updateRollResult(html);
});
html.find('.roll-sort').change((event) => {
this.html.find('.roll-sort').change((event) => {
let sortKey = Misc.toInt(event.currentTarget.value);
this.setSelectedSort(this.rollData.sortList[sortKey]);
this.updateRollResult();
$("#diffLibre").val(this.rollData.diffLibre);
this.updateRollResult(html);
this.html.find("[name='diffLibre']").val(this.rollData.diffLibre);
});
html.find('.roll-signedraconique').change((event) => {
this.html.find('.roll-carac-competence').change((event) => {
const competence = event.currentTarget.value;
this.rollData.competence = this.rollData.competences.find(it => it.name == competence);
this.updateRollResult(html);
});
this.html.find('.roll-signedraconique').change((event) => {
let sortKey = Misc.toInt(event.currentTarget.value);
this.setSelectedSigneDraconique(this.rollData.signes[sortKey]);
this.updateRollResult();
this.updateRollResult(html);
});
html.find('#ptreve-variable').change((event) => {
this.html.find("[name='ptreve-variable']").change((event) => {
let ptreve = Misc.toInt(event.currentTarget.value);
this.rollData.selectedSort.data.ptreve_reel = ptreve;
this.rollData.selectedSort.system.ptreve_reel = ptreve;
console.log("RdDRollSelectDialog - Cout reve", ptreve);
this.updateRollResult();
this.updateRollResult(html);
});
html.find("[name='coupsNonMortels']").change((event) => {
this.html.find("[name='coupsNonMortels']").change((event) => {
this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel";
this.updateRollResult();
this.updateRollResult(html);
});
html.find('.cuisine-proportions').change((event) => {
this.html.find('.cuisine-proportions').change((event) => {
this.rollData.proportions = Number(event.currentTarget.value);
this.updateRollResult();
this.updateRollResult(html);
});
html.find('.select-by-name').change((event) => {
this.html.find('.select-by-name').change((event) => {
const attribute = event.currentTarget.attributes['name'].value;
this.rollData[attribute] = event.currentTarget.value;
this.updateRollResult();
this.updateRollResult(html);
});
html.find('.checkbox-by-name').change((event) => {
this.html.find('.checkbox-by-name').change((event) => {
const attribute = event.currentTarget.attributes['name'].value;
this.rollData[attribute] = event.currentTarget.checked;
this.updateRollResult();
this.updateRollResult(html);
});
html.find('.appel-moral').click((event) => { /* l'appel au moral, qui donne un bonus de +1 */
this.rollData.useMoral = !this.rollData.useMoral;
const appelMoral = html.find('.icon-appel-moral')[0];
const tooltip = html.find('.tooltipAppelAuMoralText')[0];
if (this.rollData.useMoral) {
this.html.find('input.use-encTotal').change((event) => {
this.rollData.use.encTotal = event.currentTarget.checked;
this.updateRollResult(html);
});
this.html.find('input.use-surenc').change((event) => {
this.rollData.use.surenc = event.currentTarget.checked;
this.updateRollResult(html);
});
this.html.find('.appel-moral').click((event) => { /* l'appel au moral, qui donne un bonus de +1 */
this.rollData.use.moral = !this.rollData.use.moral;
const appelMoral = this.html.find('.icon-appel-moral')[0];
const tooltip = this.html.find('.tooltipAppelAuMoralText')[0];
if (this.rollData.use.moral) {
if (this.rollData.moral > 0) {
tooltip.innerHTML = "Appel au moral";
appelMoral.src = "/systems/foundryvtt-reve-de-dragon/icons/moral-heureux.svg";
@ -259,47 +229,68 @@ export class RdDRoll extends Dialog {
tooltip.innerHTML = "Sans appel au moral";
appelMoral.src = "/systems/foundryvtt-reve-de-dragon/icons/moral-neutre.svg";
}
this.updateRollResult();
this.updateRollResult(html);
});
// Section Méditation
html.find('.conditionMeditation').change((event) => {
let condition = event.currentTarget.attributes['id'].value;
this.html.find('.conditionMeditation').change((event) => {
let condition = event.currentTarget.attributes['name'].value;
this.rollData.conditionMeditation[condition] = event.currentTarget.checked;
this.updateRollResult();
this.updateRollResult(html);
});
}
/* -------------------------------------------- */
close() {
if (this.rollData.canClose) {
return super.close();
}
ui.notifications.info("Vous devez faire ce jet de dés!");
}
async onAction(action) {
this.rollData.forceDiceResult = Number.parseInt(this.html.find("[name='force-dice-result']").val()) ?? -1;
await RdDResolutionTable.rollData(this.rollData);
console.log("RdDRoll -=>", this.rollData, this.rollData.rolled);
if (action.callbacks)
for (let callback of action.callbacks) {
if (callback.condition == undefined || callback.condition(this.rollData)) {
await callback.action(this.rollData);
}
}
}
async setSelectedSort(sort) {
this.rollData.selectedSort = sort; // Update the selectedCarac
this.rollData.competence = RdDItemCompetence.getVoieDraconic(this.rollData.draconicList, sort.data.draconic);
this.rollData.competence = RdDItemCompetence.getVoieDraconic(this.rollData.draconicList, sort.system.draconic);
this.rollData.bonus = RdDItemSort.getCaseBonus(sort, this.rollData.tmr.coord);
this.rollData.diffLibre = RdDItemSort.getDifficulte(sort, -7);
RdDItemSort.setCoutReveReel(sort);
const htmlSortDescription = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/partial-description-sort.html", { sort: sort });
$(".sort-ou-rituel").text(sort.data.isrituel ? "rituel" : "sort");
$(".bonus-case").text(`${this.rollData.bonus}%`);
$(".details-sort").remove();
$(".description-sort").append(htmlSortDescription);
$(".roll-draconic").val(sort.data.listIndex);
$(".div-sort-difficulte-fixe").text(Misc.toSignedString(sort.data.difficulte));
$(".div-sort-ptreve-fixe").text(sort.data.ptreve);
this.html.find(".sort-ou-rituel").text(sort.system.isrituel ? "rituel" : "sort");
this.html.find(".bonus-case").text(`${this.rollData.bonus}%`);
this.html.find(".placeholder-description-sort").children().remove();
this.html.find(".placeholder-description-sort").append(htmlSortDescription);
this.html.find(".roll-draconic").val(sort.system.listIndex);
this.html.find(".div-sort-difficulte-fixe").text(Misc.toSignedString(sort.system.difficulte));
this.html.find(".div-sort-ptreve-fixe").text(sort.system.ptreve);
const diffVariable = RdDItemSort.isDifficulteVariable(sort);
const coutVariable = RdDItemSort.isCoutVariable(sort);
HtmlUtility._showControlWhen($(".div-sort-non-rituel"), !sort.data.isrituel);
HtmlUtility._showControlWhen($(".div-sort-difficulte-var"), diffVariable);
HtmlUtility._showControlWhen($(".div-sort-difficulte-fixe"), !diffVariable);
HtmlUtility._showControlWhen($(".div-sort-ptreve-var"), coutVariable);
HtmlUtility._showControlWhen($(".div-sort-ptreve-fixe"), !coutVariable);
HtmlUtility._showControlWhen(this.html.find(".div-sort-non-rituel"), !sort.system.isrituel);
HtmlUtility._showControlWhen(this.html.find(".div-sort-difficulte-var"), diffVariable);
HtmlUtility._showControlWhen(this.html.find(".div-sort-difficulte-fixe"), !diffVariable);
HtmlUtility._showControlWhen(this.html.find(".div-sort-ptreve-var"), coutVariable);
HtmlUtility._showControlWhen(this.html.find(".div-sort-ptreve-fixe"), !coutVariable);
}
async setSelectedSigneDraconique(signe){
async setSelectedSigneDraconique(signe) {
this.rollData.signe = signe;
this.rollData.diffLibre = Misc.data(signe).data.difficulte,
this.rollData.diffLibre = signe.system.difficulte,
$(".signe-difficulte").text(Misc.toSignedString(this.rollData.diffLibre));
}
/* -------------------------------------------- */
async updateRollResult() {
async updateRollResult(html) {
let rollData = this.rollData;
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor.getBonusDegat())
@ -309,7 +300,7 @@ export class RdDRoll extends Dialog {
rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac);
let dmgText = Misc.toSignedString(rollData.dmg.total);
switch (rollData.mortalite){
switch (rollData.mortalite) {
case 'non-mortel': dmgText = `(${dmgText}) non-mortel`; break;
case 'empoignade': dmgText = `empoignade`; break;
}
@ -317,28 +308,27 @@ export class RdDRoll extends Dialog {
RollDataAjustements.calcul(rollData, this.actor);
rollData.finalLevel = this._computeFinalLevel(rollData);
HtmlUtility._showControlWhen($(".diffMoral"), rollData.ajustements.moralTotal.used);
HtmlUtility._showControlWhen($(".divAppelAuMoral"), rollData.use.appelAuMoral);
HtmlUtility._showControlWhen($("#etat-general"), !RdDCarac.isIgnoreEtatGeneral(rollData));
HtmlUtility._showControlWhen($("#ajust-astrologique"), RdDResolutionTable.isAjustementAstrologique(rollData));
const resolutionTable = await RdDResolutionTable.buildHTMLTable(RdDResolutionTable.subTable(rollData.caracValue, rollData.finalLevel))
const adjustements = await this.buildAjustements(rollData);
HtmlUtility._showControlWhen(this.html.find(".use-encTotal"), rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerivee(rollData.selectedCarac));
HtmlUtility._showControlWhen(this.html.find(".use-surenc"), rollData.ajustements.surenc.visible && RdDCarac.isActionPhysique(rollData.selectedCarac));
HtmlUtility._showControlWhen(this.html.find(".utilisation-moral"), rollData.use.appelAuMoral);
HtmlUtility._showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
HtmlUtility._showControlWhen(this.html.find(".divAppelAuMoral"), rollData.use.appelAuMoral);
// Mise à jour valeurs
$(".dialog-roll-title").text(this._getTitle(rollData));
$("[name='coupsNonMortels']").prop('checked', rollData.mortalite == 'non-mortel');
$(".dmg-arme-actor").text(dmgText);
$('.table-ajustement').remove();
$(".table-resolution").remove();
$(".table-proba-reussite").remove();
$("#tableAjustements").append(await this.buildAjustements(rollData));
$("#tableResolution").append(RdDResolutionTable.buildHTMLTableExtract(rollData.caracValue, rollData.finalLevel));
$("#tableProbaReussite").append(RdDResolutionTable.buildHTMLResults(rollData.caracValue, rollData.finalLevel));
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));
this.html.find("[name='coupsNonMortels']").prop('checked', rollData.mortalite == 'non-mortel');
this.html.find(".dmg-arme-actor").text(dmgText);
this.html.find("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable)
}
/* -------------------------------------------- */
async buildAjustements(rollData) {
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.html`, rollData);
return html;
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.html`, rollData);
}
/* -------------------------------------------- */
@ -348,31 +338,19 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */
_computeDiffCompetence(rollData) {
if (rollData.competence) {
return Misc.toInt(rollData.competence.data.niveau);
return Misc.toInt(rollData.competence.system.niveau);
}
if (rollData.draconicList) {
return Misc.toInt(rollData.competence.data.niveau);
return Misc.toInt(rollData.competence.system.niveau);
}
return 0;
}
/* -------------------------------------------- */
_computeDiffLibre(rollData) {
let diffLibre = Misc.toInt(rollData.diffLibre);
if (rollData.draconicList && rollData.selectedSort) {
return RdDItemSort.getDifficulte(rollData.selectedSort, diffLibre);
}
return diffLibre;
}
/* -------------------------------------------- */
_computeMalusArmure(rollData) {
let malusArmureValue = 0;
if (rollData.malusArmureValue && (rollData.selectedCarac.label == "Agilité" || rollData.selectedCarac.label == "Dérobée")) {
$("#addon-message").text("Malus armure appliqué : " + rollData.malusArmureValue);
malusArmureValue = rollData.malusArmureValue;
} else {
$("#addon-message").text("");
}
return malusArmureValue;
}
@ -388,7 +366,7 @@ export class RdDRoll extends Dialog {
return compName + " - " + rollData.selectedSort.name;
}
// If a weapon is there, add it in the title
const niveau = Misc.toSignedString(rollData.competence.data.niveau);
const niveau = Misc.toSignedString(rollData.competence.system.niveau)
if (compName == carac) {
// cas des créatures
return carac + " Niveau " + niveau

View File

@ -1,86 +1,109 @@
import { Grammar } from "./grammar.js";
import { CompendiumTable, CompendiumTableHelpers, SystemCompendiums } from "./settings/system-compendiums.js";
export class RdDRollTables {
/* -------------------------------------------- */
static async genericGetTableResult(tableName, toChat) {
let table = game.tables.find(table => table.name.toLowerCase() == tableName.toLowerCase())
if ( !table) {
const pack = game.packs.get("foundryvtt-reve-de-dragon.tables-diverses");
let table = RdDRollTables.getWorldTable(tableName) ?? (await RdDRollTables.getSystemTable(tableName));
const draw = await table.draw({ displayChat: toChat, rollMode: "gmroll" });
return draw.results.length > 0 ? draw.results[0] : undefined;
}
static getWorldTable(tableName) {
return game.tables.find(table => table.name.toLowerCase() == tableName.toLowerCase());
}
static async getSystemTable(tableName) {
const pack = SystemCompendiums.getPack("tables-diverses");
const index = await pack.getIndex();
const entry = index.find(e => e.name === tableName);
table = await pack.getDocument(entry._id);
}
const draw = await table.draw({ displayChat: toChat, rollMode: "gmroll"});
console.log("RdDRollTables", tableName, toChat, ":", draw);
return draw.results.length > 0 ? draw.results[0] : undefined;
return await pack.getDocument(entry._id);
}
/* -------------------------------------------- */
static async drawItemFromRollTable(tableName, toChat = false) {
const drawResult = await RdDRollTables.genericGetTableResult(tableName, toChat);
const pack = game.packs.get(drawResult.data.collection);
return await pack.getDocument(drawResult.data.resultId);
const pack = game.packs.get(drawResult.documentCollection)
return await pack.getDocument(drawResult.documentId)
}
/* -------------------------------------------- */
static async drawTextFromRollTable(tableName, toChat) {
const drawResult = await RdDRollTables.genericGetTableResult(tableName, toChat);
return drawResult.data.text;
return drawResult.text;
}
/* -------------------------------------------- */
static async getCompetence(toChat = false) {
if (toChat == 'liste') {
return await RdDRollTables.listOrRoll('competences', 'Item', ['competence'], toChat, it => 1);
}
else {
return await RdDRollTables.drawItemFromRollTable("Détermination aléatoire de compétence", toChat);
}
}
/* -------------------------------------------- */
static async getSouffle(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Souffles de Dragon", toChat);
return await RdDRollTables.listOrRoll('souffles-de-dragon', 'Item', ['souffle'], toChat);
}
/* -------------------------------------------- */
static async getQueue(toChat = false) {
let queue = await RdDRollTables.drawItemFromRollTable("Queues de dragon", toChat);
if (queue.name.toLowerCase().includes('lancinant') ) {
return await RdDRollTables.getDesirLancinant(toChat);
}
if (queue.name.toLowerCase().includes('fixe') ) {
return await RdDRollTables.getIdeeFixe(toChat);
}
return queue;
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat);
}
static async getDesirLancinant(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat);
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat,
it => it.system.frequence,
it => it.system.categorie == 'lancinant');
}
static async getIdeeFixe(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Idées fixes", toChat);
return await RdDRollTables.listOrRoll('queues-de-dragon', 'Item', ['queue'], toChat,
it => it.system.frequence,
it => it.system.categorie == 'ideefixe');
}
/* -------------------------------------------- */
static async getTeteHR(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour haut-rêvants", toChat);
return await RdDRollTables.listOrRoll('tetes-de-dragon-pour-haut-revants', 'Item', ['tete'], toChat);
}
/* -------------------------------------------- */
static async getTete(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour tous personnages", toChat);
return await RdDRollTables.listOrRoll('tetes-de-dragon-pour-tous-personnages', 'Item', ['tete'], toChat);
}
/* -------------------------------------------- */
static async getOmbre(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Ombre de Thanatos", toChat);
return await RdDRollTables.listOrRoll('ombres-de-thanatos', 'Item', ['ombre'], toChat);
}
/* -------------------------------------------- */
static async getTarot(toChat = true) {
return await RdDRollTables.drawItemFromRollTable("Tarot Draconique", toChat);
return await RdDRollTables.listOrRoll('tarot-draconique', 'Item', ['tarot'], toChat);
}
/* -------------------------------------------- */
static async getMaladresse(options = {toChat: false, arme: false}) {
static async listOrRoll(compendium, type, subTypes, toChat, itemFrequence = it => it.system.frequence, filter = it => true) {
const table = new CompendiumTable(compendium, type, subTypes);
if (toChat == 'liste') {
return await table.toChatMessage(itemFrequence, filter);
}
const row = await table.getRandom(itemFrequence, filter);
if (row) {
await CompendiumTableHelpers.tableRowToChatMessage(row, type);
}
return row;
}
/* -------------------------------------------- */
static async getMaladresse(options = { toChat: false, arme: false }) {
return await RdDRollTables.drawTextFromRollTable(
options.arme ? "Maladresse armé" : "Maladresses non armé",
options.toChat);
}
}

View File

@ -1,5 +1,4 @@
import { DialogSplitItem } from "./dialog-split-item.js";
import { Misc } from "./misc.js";
export class RdDSheetUtility {
@ -20,18 +19,24 @@ export class RdDSheetUtility {
return $(event.currentTarget)?.parents(".item");
}
static prepareItemDropParameters(destItemId, actorId, dragData, objetVersConteneur) {
const itemId = dragData.id || dragData.data._id;
static prepareItemDropParameters(destItemId, actor, dragData, objetVersConteneur) {
const item = fromUuidSync(dragData.uuid)
if (actor.canReceive(item)) {
return {
destId: destItemId,
targetActorId: actorId,
itemId: itemId,
sourceActorId: dragData.actorId,
srcId: objetVersConteneur[itemId],
onEnleverConteneur: () => { delete objetVersConteneur[itemId]; },
targetActorId: actor.id,
itemId: item.id,
sourceActorId: item.actor?.id,
srcId: objetVersConteneur[item.id],
onEnleverConteneur: () => { delete objetVersConteneur[item.id]; },
onAjouterDansConteneur: (itemId, conteneurId) => { objetVersConteneur[itemId] = conteneurId; }
}
}
else {
ui.notifications.warn(`Impossible de donner ${item.name} à ${actor.name}: ${item.type} / ${actor.type}`);
}
return undefined;
}
static async splitItem(item, actor, onSplit = () => { }) {
const dialog = await DialogSplitItem.create(item, async (item, split) => {
@ -42,12 +47,12 @@ export class RdDSheetUtility {
}
static async _onSplitItem(item, split, actor) {
if (split >= 1 && split < Misc.data(item).data.quantite) {
if (split >= 1 && split < item.system.quantite) {
await item.diminuerQuantite(split);
const itemData = duplicate(Misc.data(item));
const splitItem = duplicate(item);
// todo: ajouter dans le même conteneur?
itemData.data.quantite = split;
await actor.createEmbeddedDocuments('Item', [itemData])
splitItem.system.quantite = split;
await actor.createEmbeddedDocuments('Item', [splitItem])
}
}
}

View File

@ -1,26 +1,29 @@
import { SYSTEM_SOCKET_ID } from "./constants.js";
import { SHOW_DICE } from "./constants.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { tmrConstants } from "./tmr-constants.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDRoll } from "./rdd-roll.js";
import { Poetique } from "./poetique.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js";
import { Misc } from "./misc.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDDice } from "./rdd-dice.js";
import { STATUSES } from "./settings/status-effects.js";
import { RdDRencontre } from "./item-rencontre.js";
import { RdDCalendrier } from "./rdd-calendrier.js";
/* -------------------------------------------- */
export class RdDTMRDialog extends Dialog {
static async create(html, actor, tmrData) {
static async create(actor, tmrData) {
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', tmrData);
if (tmrData.mode != 'visu') {
// Notification au MJ
@ -55,7 +58,6 @@ export class RdDTMRDialog extends Dialog {
this.fatigueParCase = this.viewOnly || !ReglesOptionelles.isUsing("appliquer-fatigue") ? 0 : this.actor.getTMRFatigue();
this.cumulFatigue = 0;
this.loadRencontres();
this.loadSortsReserve();
this.loadCasesSpeciales();
this.allTokens = [];
this.rencontreState = 'aucune';
@ -78,12 +80,19 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
loadCasesSpeciales() {
this.casesSpeciales = this.actor.data.items.filter(item => Draconique.isCaseTMR(item));
this.casesSpeciales = this.actor.items.filter(item => Draconique.isCaseTMR(item));
}
/* -------------------------------------------- */
loadSortsReserve() {
this.sortsReserves = Misc.data(this.actor).data.reve.reserve.list;
get sortsReserve() {
return this.actor.itemTypes['sortreserve'];
}
getSortsReserve(coord) {
return this.actor.itemTypes['sortreserve'].filter(// Reserve sur une case fleuve ou normale
TMRUtility.getTMR(coord).type == 'fleuve'
? it => TMRUtility.getTMR(it.system.coord).type == 'fleuve'
: it => it.system.coord == coord
);
}
/* -------------------------------------------- */
@ -100,7 +109,7 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
_createTokens() {
if (!this.isDemiReveCache()){
if (!this.isDemiReveCache()) {
this.demiReve = this._tokenDemiReve();
this._trackToken(this.demiReve);
}
@ -117,7 +126,6 @@ export class RdDTMRDialog extends Dialog {
updateTokens() {
this._removeTokens(t => true);
this.loadRencontres();
this.loadSortsReserve();
this.loadCasesSpeciales();
this._createTokens();
}
@ -136,25 +144,24 @@ export class RdDTMRDialog extends Dialog {
return this.rencontresExistantes.map(it => this._tokenRencontre(it));
}
_getTokensSortsReserve() {
return this.sortsReserves.map(it => this._tokenSortEnReserve(it));
return this.actor.itemTypes['sortreserve'].map(it => this._tokenSortEnReserve(it));
}
/* -------------------------------------------- */
_tokenRencontre(rencontre) {
return EffetsDraconiques.rencontre.token(this.pixiTMR, rencontre, () => rencontre.coord);
return EffetsDraconiques.rencontre.token(this.pixiTMR, rencontre, () => rencontre.system.coord);
}
_tokenCaseSpeciale(casetmr) {
const caseData = Misc.data(casetmr);
const draconique = Draconique.get(caseData.data.specific);
return draconique?.token(this.pixiTMR, caseData, () => caseData.data.coord);
const caseData = casetmr;
const draconique = Draconique.get(caseData.system.specific);
return draconique?.token(this.pixiTMR, caseData, () => caseData.system.coord);
}
_tokenSortEnReserve(sortEnReserve) {
return EffetsDraconiques.sortReserve.token(this.pixiTMR, sortEnReserve.sort, () => sortEnReserve.coord);
_tokenSortEnReserve(sortReserve) {
return EffetsDraconiques.sortReserve.token(this.pixiTMR, sortReserve, () => sortReserve.system.coord);
}
_tokenDemiReve() {
const actorData = Misc.data(this.actor);
return EffetsDraconiques.demiReve.token(this.pixiTMR, actorData, () => actorData.data.reve.tmrpos.coord);
return EffetsDraconiques.demiReve.token(this.pixiTMR, this.actor, () => this.actor.system.reve.tmrpos.coord);
}
forceDemiRevePositionView() {
@ -163,7 +170,7 @@ export class RdDTMRDialog extends Dialog {
}
_getActorCoord() {
return Misc.data(this.actor).data.reve.tmrpos.coord;
return this.actor.system.reve.tmrpos.coord;
}
/* -------------------------------------------- */
@ -190,44 +197,33 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async activateListeners(html) {
super.activateListeners(html);
this.html = html;
document.getElementById("tmrrow1").insertCell(0).append(this.pixiApp.view);
if (this.viewOnly) {
html.find('.lancer-sort').remove();
html.find('.lire-signe-draconique').remove();
this.html.find('.lancer-sort').remove();
this.html.find('.lire-signe-draconique').remove();
return;
}
HtmlUtility._showControlWhen($(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility._showControlWhen($(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
HtmlUtility._showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility._showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
// Roll Sort
html.find('.lancer-sort').click((event) => {
this.html.find('.lancer-sort').click((event) => {
this.actor.rollUnSort(this._getActorCoord());
});
html.find('.lire-signe-draconique').click((event) => {
this.html.find('.lire-signe-draconique').click((event) => {
this.actor.rollLireSigneDraconique(this._getActorCoord());
});
html.find('#dir-top').click((event) => {
this.moveFromKey("top");
});
html.find('#dir-top-left').click((event) => {
this.moveFromKey("top-left");
});
html.find('#dir-top-right').click((event) => {
this.moveFromKey("top-right");
});
html.find('#dir-bottom-left').click((event) => {
this.moveFromKey("bottom-left");
});
html.find('#dir-bottom-right').click((event) => {
this.moveFromKey("bottom-right");
});
html.find('#dir-bottom').click((event) => {
this.moveFromKey("bottom");
});
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();
@ -239,75 +235,94 @@ export class RdDTMRDialog extends Dialog {
// Le reste...
this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this._getActorCoord());
await this.manageRencontre(tmr, () => {
this.postRencontre(tmr);
});
await this.manageRencontre(tmr);
}
/* -------------------------------------------- */
async updateValuesDisplay() {
if (!this.rendered) {
return;
}
const coord = this._getActorCoord();
const actorData = Misc.data(this.actor);
HtmlUtility._showControlWhen($(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(coord));
HtmlUtility._showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(coord));
let ptsreve = document.getElementById("tmr-pointsreve-value");
ptsreve.innerHTML = actorData.data.reve.reve.value;
ptsreve.innerHTML = this.actor.system.reve.reve.value;
let tmrpos = document.getElementById("tmr-pos");
if (this.isDemiReveCache()) {
tmrpos.innerHTML = '?? (' + TMRUtility.getTMRType(coord) + ')';
tmrpos.innerHTML = `?? ( ${TMRUtility.getTMRType(coord)})`;
} else {
tmrpos.innerHTML = coord + " (" + TMRUtility.getTMRLabel(coord) + ")";
tmrpos.innerHTML = `${coord} ( ${TMRUtility.getTMRLabel(coord)})`;
}
let etat = document.getElementById("tmr-etatgeneral-value");
etat.innerHTML = this.actor.getEtatGeneral();
let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = actorData.data.reve.refoulement.value;
refoulement.innerHTML = this.actor.system.reve.refoulement.value;
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
let fatigueItem = document.getElementById("tmr-fatigue-table");
//console.log("Refresh : ", actorData.data.sante.fatigue.value);
fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(actorData.data.sante.fatigue.value, actorData.data.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>";
}
}
/* -------------------------------------------- */
close() {
if ( this.actor.tmrApp ) {
async close() {
this.descenteTMR = true;
if (this.actor.tmrApp) {
this.actor.tmrApp = undefined; // Cleanup reference
if ( !this.viewOnly ) {
this.actor.setStatusEffect("EFFECT.StatusDemiReve", false);
if (!this.viewOnly) {
await this.actor.setEffect(STATUSES.StatusDemiReve, false)
this._tellToGM(this.actor.name + " a quitté les terres médianes");
}
this.actor.santeIncDec("fatigue", this.cumulFatigue).then(super.close()); // moving 1 cell costs 1 fatigue
await this.actor.santeIncDec("fatigue", this.cumulFatigue)
}
await super.close();
}
/* -------------------------------------------- */
async onActionRencontre(action, tmr) {
switch (action) {
case 'derober':
await this.derober();
return;
case 'refouler':
await this.refouler();
break;
case 'maitriser':
await this.maitriserRencontre();
break;
case 'ignorer':
await this.ignorerRencontre();
break;
}
await this.postRencontre(tmr);
}
async derober() {
await this.actor.addTMRRencontre(this.currentRencontre);
console.log("-> derober", this.currentRencontre);
await this.actor.addTMRRencontre(this.currentRencontre);
this._tellToGM(this.actor.name + " s'est dérobé et quitte les TMR.");
this.close();
}
/* -------------------------------------------- */
async refouler() {
this._tellToGM(this.actor.name + " a refoulé : " + this.currentRencontre.name);
console.log("-> refouler", this.currentRencontre);
await this.actor.ajouterRefoulement(this.currentRencontre.system.refoulement, `${this.currentRencontre.system.genre == 'f' ? 'une' : 'un'} ${this.currentRencontre.name}`);
await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary
await this.actor.ajouterRefoulement(this.currentRencontre.refoulement ?? 1);
this.updateTokens();
console.log("-> refouler", this.currentRencontre)
this.updateValuesDisplay();
this.nettoyerRencontre();
}
/* -------------------------------------------- */
async ignorerRencontre() {
this._tellToGM(this.actor.name + " a ignoré : " + this.currentRencontre.name);
console.log("-> ignorer", this.currentRencontre);
this._tellToGM(this.actor.name + " a ignoré: " + this.currentRencontre.name);
await this.actor.deleteTMRRencontreAtPosition(); // Remove the stored rencontre if necessary
this.updateTokens();
this.updateValuesDisplay();
@ -315,15 +330,22 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
colorierZoneRencontre(listCoordTMR) {
// garder la trace de l'état en cours
setRencontreState(state, listCoordTMR) {
this.rencontreState = state;
this.$marquerCasesTMR(listCoordTMR ?? []);
}
/* -------------------------------------------- */
$marquerCasesTMR(listCoordTMR) {
this.currentRencontre.graphics = []; // Keep track of rectangles to delete it
this.currentRencontre.locList = duplicate(listCoordTMR); // And track of allowed location
for (let coordTMR of listCoordTMR) {
let rect = this._getCaseRectangleCoord(coordTMR);
var rectDraw = new PIXI.Graphics();
rectDraw.beginFill(0xFFFF00, 0.3);
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);
rectDraw.lineStyle(5, 0xff0000);
// draw a rectangle
rectDraw.drawRect(rect.x, rect.y, rect.w, rect.h);
this.pixiApp.stage.addChild(rectDraw);
@ -331,29 +353,6 @@ export class RdDTMRDialog extends Dialog {
}
}
/* -------------------------------------------- */
// garder la trace de l'état en cours
setStateRencontre(state) {
this.rencontreState = state;
}
/* -------------------------------------------- */
async choisirCasePortee(coord, portee) {
if (this.actor.isTMRCache())
{
return;
}
// Récupère la liste des cases à portées
let locList = TMRUtility.getTMRPortee(coord, portee);
this.colorierZoneRencontre(locList);
}
/* -------------------------------------------- */
async choisirCaseType(type) {
const locList = TMRUtility.filterTMR(it => it.type == type).map(it => it.coord);
this.colorierZoneRencontre(locList);
}
/* -------------------------------------------- */
checkQuitterTMR() {
@ -378,15 +377,15 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async quitterLesTMRInconscient() {
if (this.currentRencontre?.isPersistant) {
await this.refouler();
}
this.close();
}
/* -------------------------------------------- */
async maitriserRencontre() {
this.actor.deleteTMRRencontreAtPosition();
console.log("-> maitriser", this.currentRencontre);
await this.actor.deleteTMRRencontreAtPosition();
this.updateTokens();
let rencontreData = {
@ -397,7 +396,7 @@ export class RdDTMRDialog extends Dialog {
rencontre: this.currentRencontre,
nbRounds: 1,
canClose: false,
selectedCarac: {label: "reve-actuel"},
selectedCarac: { label: "reve-actuel" },
tmr: TMRUtility.getTMR(this._getActorCoord())
}
@ -406,8 +405,6 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _tentativeMaitrise(rencData) {
console.log("-> matriser", rencData);
rencData.reve = this.actor.getReveActuel();
rencData.etat = this.actor.getEtatGeneral();
@ -417,27 +414,35 @@ export class RdDTMRDialog extends Dialog {
? this._rollPresentCite(rencData)
: await RdDResolutionTable.roll(rencData.reve, RollDataAjustements.sum(rencData.ajustements));
let postProcess = await TMRRencontres.gererRencontre(this, rencData);
const result = rencData.rolled.isSuccess
? rencData.rencontre.system.succes
: rencData.rencontre.system.echec;
await RdDRencontre.appliquer(result.effets, this, rencData);
rencData.poesie = { extrait: result.poesie, reference: result.reference };
rencData.message = this.formatMessageRencontre(rencData, result.message);
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.html`, rencData)
});
if (postProcess) {
/** Gère les rencontres avec du post-processing (passeur, messagers, tourbillons, ...) */
await postProcess(this, rencData);
}
else {
this.currentRencontre = undefined;
}
this.updateValuesDisplay();
if (this.checkQuitterTMR()) {
return;
}
else if (rencData.rolled.isEchec && rencData.rencontre.isPersistant) {
if (this.rencontreState == 'persistant') {
this._nouvelleTentativeMaitrise(rencData);
}
else if (!this.isRencontreDeplacement()) {
this.nettoyerRencontre();
}
}
_nouvelleTentativeMaitrise(rencData) {
setTimeout(() => {
// TODO: remplacer par une boucle while(this.currentRencontre) ?
rencData.nbRounds++;
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
@ -445,13 +450,31 @@ export class RdDTMRDialog extends Dialog {
this._tentativeMaitrise(rencData);
this._deleteTmrMessages(rencData.actor, rencData.nbRounds);
}, 2000);
this.rencontreState == 'normal';
}
formatMessageRencontre(rencData, template) {
let messageDuree = ''
if (rencData.nbRounds > 1) {
if (rencData.rolled.isSuccess) {
messageDuree = ` Au total, vous avez passé ${rencData.nbRounds} rounds à vous battre!`;
}
else {
messageDuree = ` Vous avez passé ${rencData.nbRounds} rounds à lutter!`;
}
}
try {
const compiled = Handlebars.compile(template);
return compiled(rencData) + messageDuree ;
} catch (error) {
return template + messageDuree ;
}
}
/* -------------------------------------------- */
_rollPresentCite(rencontreData) {
let rolled = RdDResolutionTable.computeChances(rencontreData.reve, 0);
mergeObject(rolled, { caracValue: rencontreData.reve, finalLevel: 0, roll: rolled.score });
_rollPresentCite(rencData) {
let rolled = RdDResolutionTable.computeChances(rencData.reve, 0);
mergeObject(rolled, { caracValue: rencData.reve, finalLevel: 0, roll: rolled.score });
RdDResolutionTable.succesRequis(rolled);
return rolled;
}
@ -486,44 +509,49 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async manageRencontre(tmr, postRencontre) {
async manageRencontre(tmr) {
if (this.viewOnly) {
return;
}
this.descenteTMR = false;
this.currentRencontre = undefined;
if (this._presentCite(tmr, postRencontre)) {
if (this._presentCite(tmr)) {
return;
}
let rencontre = await this._jetDeRencontre(tmr);
if (rencontre) { // Manages it
if (rencontre.rencontre) rencontre = rencontre.rencontre; // Manage stored rencontres
console.log("manageRencontre", rencontre);
this.currentRencontre = duplicate(rencontre);
let dialog = new RdDTMRRencontreDialog("", this, this.currentRencontre, postRencontre);
dialog.render(true);
this.currentRencontre = await this._jetDeRencontre(tmr);
if (this.currentRencontre) {
if (this.rencontresExistantes.find(it => it.id == this.currentRencontre.id)){
// rencontre en attente suite à dérobade
await this.maitriserRencontre();
}
else {
postRencontre();
let dialog = new RdDTMRRencontreDialog(this, this.currentRencontre, tmr);
dialog.render(true);
}
}
else {
this.postRencontre(tmr);
}
}
/* -------------------------------------------- */
_presentCite(tmr, postRencontre) {
_presentCite(tmr) {
const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord));
if (presentCite) {
this.minimize();
const caseData = Misc.data(presentCite);
EffetsDraconiques.presentCites.choisirUnPresent(caseData, (type => this._utiliserPresentCite(presentCite, type, tmr, postRencontre)));
const caseData = presentCite;
EffetsDraconiques.presentCites.choisirUnPresent(caseData, (present => this._utiliserPresentCite(presentCite, present, tmr)));
}
return presentCite;
}
/* -------------------------------------------- */
async _utiliserPresentCite(presentCite, typeRencontre, tmr, postRencontre) {
this.currentRencontre = TMRRencontres.getRencontre(typeRencontre);
await TMRRencontres.evaluerForceRencontre(this.currentRencontre);
async _utiliserPresentCite(presentCite, present, tmr) {
this.currentRencontre = present.clone({
'system.force': await RdDDice.rollTotal(present.system.formule),
'system.coord': tmr.coord
}, {save: false});
await EffetsDraconiques.presentCites.ouvrirLePresent(this.actor, presentCite);
this.removeToken(tmr, presentCite);
@ -540,42 +568,38 @@ export class RdDTMRDialog extends Dialog {
await this._tentativeMaitrise(rencontreData);
this.maximize();
postRencontre();
this.postRencontre(tmr);
}
/* -------------------------------------------- */
async _jetDeRencontre(tmr) {
let rencontre = this.rencontresExistantes.find(prev => prev.coord == tmr.coord);
let rencontre = this.lookupRencontreExistente(tmr);
if (rencontre) {
return rencontre;
return game.system.rdd.rencontresTMR.calculRencontre(rencontre, tmr);
}
let locTMR = (this.isDemiReveCache()
? Misc.upperFirst(tmr.type) + " ??"
? TMRUtility.getTMRType(tmr.coord) + " ??"
: tmr.label + " (" + tmr.coord + ")");
let myRoll = await RdDDice.rollTotal("1dt");
if (TMRUtility.isForceRencontre() || myRoll == 7) {
let myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE });
if (myRoll == 7) {
this._tellToUser(myRoll + ": Rencontre en " + locTMR);
return await this.rencontreTMRRoll(tmr, this.actor.isRencontreSpeciale());
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
} else {
this._tellToUser(myRoll + ": Pas de rencontre en " + locTMR);
}
}
/* -------------------------------------------- */
async rencontreTMRRoll(tmr, isMauvaise = false) {
let rencontre = TMRUtility.utiliseForceRencontre() ??
(isMauvaise
? await TMRRencontres.getMauvaiseRencontre()
: await TMRRencontres.getRencontreAleatoire(tmr.type));
rencontre.coord = tmr.coord;
rencontre.date = game.system.rdd.calendrier.getDateFromIndex();
rencontre.heure = game.system.rdd.calendrier.getCurrentHeure();
return rencontre;
lookupRencontreExistente(tmr) {
return this.rencontresExistantes.find(it => it.system.coord == tmr.coord)
?? this.rencontresExistantes.find(it => it.system.coord == "");
}
/* -------------------------------------------- */
async manageTmrInnaccessible(tmr) {
if (!tmr) {
return await this.actor.reinsertionAleatoire('Sortie de carte');
}
const caseTmrInnaccessible = this.casesSpeciales.find(c => EffetsDraconiques.isInnaccessible(c, tmr.coord));
if (caseTmrInnaccessible) {
return await this.actor.reinsertionAleatoire(caseTmrInnaccessible.name);
@ -596,7 +620,7 @@ export class RdDTMRDialog extends Dialog {
maitrise: { verbe: 'maîtriser', action: 'Maîtriser le fleuve' }
}
rollData.double = EffetsDraconiques.isDoubleResistanceFleuve(this.actor) ? true : undefined,
rollData.competence.data.defaut_carac = 'reve-actuel';
rollData.competence.system.defaut_carac = 'reve-actuel';
await this._rollMaitriseCaseHumide(rollData);
}
}
@ -608,7 +632,6 @@ export class RdDTMRDialog extends Dialog {
async _resultatMaitriseCaseHumide(rollData) {
await this.souffleSiEchecTotal(rollData);
this.toclose = rollData.rolled.isEchec;
if (rollData.rolled.isSuccess && rollData.double) {
rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements };
rollData.double = undefined;
@ -621,7 +644,7 @@ export class RdDTMRDialog extends Dialog {
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData)
});
if (rollData.rolled.isEchec) {
this.close();
await this.close();
}
}
@ -635,16 +658,16 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
isCaseHumide(tmr) {
if (!(TMRUtility.isCaseHumide(tmr) || this.isCaseHumideAdditionelle(tmr))) {
return undefined;
return false;
}
if (this.isCaseMaitrisee(tmr.coord)) {
ChatMessage.create({
content: tmr.label + ": cette case humide est déja maitrisée grâce à votre Tête <strong>Quête des Eaux</strong>",
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return undefined;
return false;
}
return -7;
return true;
}
/* -------------------------------------------- */
@ -721,7 +744,7 @@ export class RdDTMRDialog extends Dialog {
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } },
maitrise: { verbe: 'conquérir', action: options.action }
};
rollData.competence.data.defaut_carac = 'reve-actuel';
rollData.competence.system.defaut_carac = 'reve-actuel';
await this._maitriserTMR(rollData, r => this._onResultatConquerir(r, options));
}
@ -731,8 +754,6 @@ export class RdDTMRDialog extends Dialog {
if (rollData.rolled.isETotal) {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false });
}
this.toclose = rollData.rolled.isEchec;
rollData.poesie = await Poetique.getExtrait();
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
@ -754,7 +775,6 @@ export class RdDTMRDialog extends Dialog {
const dialog = await RdDRoll.create(this.actor, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html',
options: { height: 420 },
close: html => { this.maximize(); } // Re-display TMR
},
{
@ -777,9 +797,8 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async declencheSortEnReserve(coord) {
let sortsEnCoord = TMRUtility.getSortsReserve(this.sortsReserves, coord);
if (sortsEnCoord.length > 0) {
let sorts = this.getSortsReserve(coord);
if (sorts.length > 0) {
if (EffetsDraconiques.isSortReserveImpossible(this.actor)) {
ui.notifications.error("Une queue ou un souffle vous empèche de déclencher de sort!");
return;
@ -787,8 +806,8 @@ export class RdDTMRDialog extends Dialog {
if (!EffetsDraconiques.isUrgenceDraconique(this.actor) &&
(EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord))) {
let msg = "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête <strong>Reserve en Sécurité</strong> ou <strong>Réserve Exensible</strong>, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher : <ul>";
for (let sortReserve of sortsEnCoord) {
msg += "<li><a class='chat-card-button' id='sort-reserve' data-actor-id='" + this.actor._id + "' data-tmr-coord='" + coord + "' data-sort-id='" + sortReserve.sort._id + "'>" + sortReserve.sort.name + "</a></li>";
for (let sort of sorts) {
msg += `<li><a class="chat-card-button declencher-sort-reserve" data-actor-id="${this.actor.id}" data-tmr-coord="${coord}" data-sort-id='${sort.id}">${sort.name}</a></li>`;
}
msg += "</ol>";
ChatMessage.create({
@ -797,33 +816,36 @@ export class RdDTMRDialog extends Dialog {
});
return;
}
await this.processSortReserve(sortsEnCoord[0]);
await this.processSortReserve(sorts[0]);
}
}
/* -------------------------------------------- */
lancerSortEnReserve(coord, sortId) {
let sortEnCoord = TMRUtility.getSortsReserve(this.sortsReserves, coord);
let sortReserve = sortEnCoord.find(sortReserve => sortReserve.sort._id == sortId);
if (sortReserve) {
this.processSortReserve(sortReserve);
let sorts = this.getSortsReserve(coord);
let sort = sorts.find(it => it.id == sortId);
if (sort) {
this.processSortReserve(sort);
} else {
ChatMessage.create({
content: "Une erreur est survenue : impossible de récupérer le sort en réserve demandé.",
whisper: ChatMessage.getWhisperRecipients(game.user.name)
content:
"Une erreur est survenue : impossible de récupérer le sort en réserve demandé.",
whisper: ChatMessage.getWhisperRecipients(game.user.name),
});
}
}
/* -------------------------------------------- */
async processSortReserve(sortReserve) {
await this.actor.deleteSortReserve(sortReserve);
//this.updateSortReserve();
console.log("declencheSortEnReserve", sortReserve)
this._tellToUserAndGM(`Vous avez déclenché le sort en réserve <strong> ${sortReserve.sort.name}</strong>
avec ${sortReserve.sort.data.ptreve_reel} points de Rêve
en ${sortReserve.coord} (${TMRUtility.getTMRLabel(sortReserve.coord)})
`);
await this.actor.deleteEmbeddedDocuments('Item', [sortReserve.id]);
console.log("declencheSortEnReserve", sortReserve);
const heureCible = RdDCalendrier.getSigneAs('label', sortReserve.system.heurecible);
this._tellToUserAndGM(`Vous avez déclenché
${sortReserve.system.echectotal ? "<strong>l'échec total!</strong>" : "le sort"}
en réserve <strong>${sortReserve.name}</strong>
avec ${sortReserve.system.ptreve} points de Rêve
en ${sortReserve.system.coord} (${TMRUtility.getTMRLabel(sortReserve.system.coord)}).
L'heure ciblée est ${heureCible}`);
this.close();
}
@ -875,7 +897,6 @@ export class RdDTMRDialog extends Dialog {
if (this.viewOnly) {
return;
}
let clickOddq = RdDTMRDialog._computeEventOddq(event.data.originalEvent);
await this._onClickTMRPos(clickOddq); // Vérifier l'état des compteurs reve/fatigue/vie
}
@ -890,14 +911,14 @@ export class RdDTMRDialog extends Dialog {
if (this.isDemiReveCache()) {
if (this.isTerreAttache(targetCoord)
|| (this.isCaseHumide(currentCoord) && this.isCaseHumide(targetCoord))
|| deplacementType == 'changeur')
{
|| this.isConnaissanceFleuve(currentCoord, targetCoord)
|| deplacementType == 'changeur') {
// déplacement possible
await this.actor.montreTMR();
await this.actor.setTMRVisible(true);
this.demiReve = this._tokenDemiReve();
this._trackToken(this.demiReve);
}
else
{
else {
ui.notifications.error(`Vous ne connaissez plus votre position dans les TMR.
Vous devez utiliser les boutons de direction pour vous déplacer.
Une fois que vous aurez retrouvé votre demi-rêve, demandez au gardien de vérifier et rendre les TMR visibles.
@ -906,17 +927,15 @@ export class RdDTMRDialog extends Dialog {
}
}
switch (deplacementType){
switch (deplacementType) {
case 'normal':
case 'changeur':
case 'passeur':
await this._deplacerDemiReve(targetCoord, deplacementType);
break;
case 'messager':
await this._messagerDemiReve(targetCoord);
break;
case 'changeur':
case 'passeur':
await this._deplacerDemiReve(targetCoord, deplacementType);
break;
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");
console.log("STATUS :", this.rencontreState, this.currentRencontre);
@ -927,19 +946,23 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
_calculDeplacement(targetCoord, currentCoord, fromOddq, toOddq) {
const isInArea = this.rencontreState == 'aucune'
? (this.isTerreAttache(targetCoord) || this.isConnaissanceFleuve(currentCoord, targetCoord) || TMRUtility.distanceOddq(fromOddq, toOddq) <= 1)
: this.currentRencontre?.locList.find(coord => coord == targetCoord) ?? false
if (isInArea) {
switch (this.rencontreState) {
case 'aucune': return 'normal';
case 'passeur': case 'changeur': case 'messager': return this.rencontreState;
if (this.isRencontreDeplacement()) {
if (this.currentRencontre?.locList?.find(coord => coord == targetCoord)) {
return this.rencontreState;
}
}
else {
if (this.isTerreAttache(targetCoord) || this.isConnaissanceFleuve(currentCoord, targetCoord) || TMRUtility.distanceOddq(fromOddq, toOddq) <= 1) {
return 'normal'
}
}
return 'erreur';
}
isRencontreDeplacement() {
return ['passeur', 'changeur', 'messager'].includes(this.rencontreState);
}
/* -------------------------------------------- */
async _messagerDemiReve(targetCoord) {
/*
@ -979,7 +1002,7 @@ export class RdDTMRDialog extends Dialog {
this.actor.notifyRefreshTMR();
if (deplacementType == 'normal') { // Pas de rencontres après un saut de type passeur/changeur/...
await this.manageRencontre(tmr, () => this.postRencontre(tmr));
await this.manageRencontre(tmr);
}
else {
await this.postRencontre(tmr);
@ -998,13 +1021,14 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async postRencontre(tmr) {
if (!(this.viewOnly || this.currentRencontre)) {
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.purifierPeriple(tmr);
await this.conquerirTMR(tmr);
await this.validerVisite(tmr);
await this.declencheSortEnReserve(tmr.coord);
await this.actor.checkSoufflePeage(tmr);
// TODO: vérifier que la méthode s'arrête en cas de non-maîtrise
if (!this.descenteTMR) await this.manageCaseHumide(tmr);
if (!this.descenteTMR) await this.conquerirCiteFermee(tmr);
if (!this.descenteTMR) await this.purifierPeriple(tmr);
if (!this.descenteTMR) await this.conquerirTMR(tmr);
if (!this.descenteTMR) await this.validerVisite(tmr);
if (!this.descenteTMR) await this.declencheSortEnReserve(tmr.coord);
if (!this.descenteTMR) await this.actor.checkSoufflePeage(tmr);
}
}
@ -1023,7 +1047,7 @@ export class RdDTMRDialog extends Dialog {
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;
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 };
}

View File

@ -2,44 +2,39 @@
export class RdDTMRRencontreDialog extends Dialog {
/* -------------------------------------------- */
constructor(html, tmrApp, rencontre, postRencontre) {
constructor(tmrApp, rencontre, tmr) {
const dialogConf = {
title: "Rencontre en TMR!",
content: "Vous rencontrez un " + rencontre.name + " de force " + rencontre.force + "<br>",
content: "Vous rencontrez un " + rencontre.name + " de force " + rencontre.system.force + "<br>",
buttons: {
derober: { icon: '<i class="fas fa-check"></i>', label: "Se dérober", callback: () => { this.onButtonFuir(() => tmrApp.derober()); } },
refouler: { icon: '<i class="fas fa-check"></i>', label: "Refouler", callback: () => this.onButtonAction(() => tmrApp.refouler()) },
maitiser: { icon: '<i class="fas fa-check"></i>', label: "Maîtriser", callback: () => this.onButtonAction(() => tmrApp.maitriserRencontre()) }
derober: { icon: '<i class="fas fa-check"></i>', label: "Se dérober", callback: () => this.onButtonAction('derober') },
maitiser: { icon: '<i class="fas fa-check"></i>', label: "Maîtriser", callback: () => this.onButtonAction('maitriser') }
},
default: "derober"
};
if (rencontre.ignorer) {
dialogConf.buttons.ignorer = { icon: '<i class="fas fa-check"></i>', label: "Ignorer", callback: () => this.onButtonAction(() => tmrApp.ignorerRencontre()) }
};
}
if ((rencontre.system.refoulement ?? 0) == 0) {
dialogConf.buttons.ignorer = { icon: '<i class="fas fa-check"></i>', label: "Ignorer", callback: () => this.onButtonAction('ignorer') }
}
else {
dialogConf.buttons.refouler = { icon: '<i class="fas fa-check"></i>', label: "Refouler", callback: () => this.onButtonAction('refouler') }
}
const dialogOptions = {
classes: ["tmrrencdialog"],
width: 320, height: 240,
width: 320, height: 'fit-content',
'z-index': 50
}
super(dialogConf, dialogOptions);
this.toClose = false;
this.rencontreData = duplicate(rencontre);
this.postRencontre = postRencontre;
this.tmr = tmr;
this.tmrApp = tmrApp;
this.tmrApp.minimize();
}
async onButtonAction(action) {
this.toClose = true;
await action();
this.postRencontre();
}
async onButtonFuir(action) {
this.toClose = true;
await action();
this.tmrApp.onActionRencontre(action, this.tmr)
}
/* -------------------------------------------- */

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