Compare commits

...

180 Commits
v1.3 ... master

Author SHA1 Message Date
c515e9bc66 Merge branch 'tyrcho-master-patch-22717' into 'master'
typos

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!269
2021-06-07 22:01:32 +00:00
b2e7a8a6e1 typos 2021-06-07 21:41:04 +00:00
09045c0d53 Blocage astro #198, Synchro horloge #197, gérer pièces #196 2021-06-01 21:05:45 +02:00
4c4e1e8419 Fix combat et messages 2021-06-01 07:52:47 +02:00
66bcc51102 Merge branch 'master-fix-combat' into 'master'
Fix: regression combat

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!262
2021-06-01 04:34:22 +00:00
32844bbf3c increment version 1.3.62 2021-06-01 00:20:24 +02:00
21d6f14b68 Fix: regression combat
Notifier tous les postes clients des données de passe d'arme
2021-06-01 00:18:41 +02:00
87a0bece5e Cosmetic changes 2021-05-26 17:45:52 +02:00
e0f83e9be2 Fix TMR 2021-05-26 16:41:18 +02:00
01c1978ccd Fix pour montée TMR en double 2021-05-24 09:36:09 +02:00
47ae157781 Fix pour montée TMR en double 2021-05-24 09:35:49 +02:00
0c092b9099 Fix for Misc.js 2021-05-22 17:51:30 +02:00
a6d1a84b13 Fix combat avec MJ 2021-05-22 08:40:00 +02:00
82e8954d5c Merge branch 'master-fix-defense-193' into 'master'
#193: utiliser isElectedUser pour les défenses

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!249
2021-05-22 06:39:14 +00:00
249850558b #193: utiliser isElectedUser pour les défenses
Misc.isElectedUser renvoit true pour un seul MJ connecté,
ou le joueur connecté s'il n'y a pas de MJ.

Ca veut dire que sans MJ, les messages de combat ne marcheront
peut-être pas, mais pour tous les autres cas, ça marchera correctement,
vu que ce sera toujours un MJ qui postera le message de défense au
joueur (connecté ou pas) et au MJ
2021-05-22 02:04:25 +02:00
45b10f2c6b Fix sur defense 2021-05-18 10:14:57 +02:00
d39dac3bf9 Merge branch 'master-fix' into 'master'
Fix: pas de messages de défense

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!240
2021-05-18 08:14:18 +00:00
1f5e8c084b Fix +dom Bandersnatch 2021-05-18 00:36:07 +02:00
0864721bc5 Fix: pas de messages de défense
Dans certains cas, il n'y avait plus de message pour que le personnage
attaqué se défense
2021-05-17 23:18:03 +02:00
4c5685c20b Update release 2021-05-14 08:46:38 +02:00
a618200417 Merge branch 'master-fix' into 'master'
Fix rencontre Rêve de Dragon

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!237
2021-05-12 04:45:17 +00:00
62eb6482d0 Fix rencontre Rêve de Dragon
Le minimum était devenu 14
2021-05-12 00:06:53 +02:00
9b8ea03067 Merge branch 'master-fix' into 'master'
Fix jet récupération

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!229
2021-05-08 05:41:14 +00:00
8090d0280d Fix jet récupération
le +7 n'est pas justifié, sinon on ne fait que des rêves
de dragon
2021-05-08 00:27:50 +02:00
c31202fbd3 Merge branch 'master-misc' into 'master'
Utiliser des polices pour les dés

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!223
2021-05-01 21:50:58 +00:00
146c8d184c Utiliser des polices pour les dés
Afin de ne pas avoir de soucis de couleurs
2021-05-01 15:53:26 +02:00
c251727dea Aide tchat dans un dialogue 2021-05-01 13:00:49 +02:00
19f5e2af59 Fix jet éthylisme sur vie 2021-05-01 13:00:42 +02:00
8b2f02d4b0 Sync versions 2021-04-30 23:11:06 +02:00
03b88a269a Merge branch 'master-des' into 'master'
Un dé pour les heures draconiques

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!219
2021-04-30 06:13:46 +00:00
32d016b9ad Dé des heures
inutile, donc indispensable

 /roll 1dh
2021-04-30 00:51:38 +02:00
a0c1d063f1 Fix dé dragon sans bords pour tous
Pas uniquement pour le système rdd
2021-04-30 00:51:38 +02:00
1e5446fcde Merge branch 'master-des' into 'master'
Jolis dés draconiques

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!218
2021-04-29 06:25:38 +00:00
f76e0db046 Jolis dés draconiques 2021-04-29 03:59:16 +02:00
b7c816129b Merge branch 'master-blessures' into 'master'
Amélioration des blessures #173

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!216
2021-04-28 07:42:18 +00:00
c786757db0 Fix: oubli correction tmr multiples 2021-04-28 00:43:06 +02:00
78d4b48aae Amélioration des blessures #173
# Conflicts:
#	templates/chat-resultat-encaissement.html
2021-04-28 00:33:44 +02:00
0a9e8e0ac6 Amélioration présentation blessures #173 2021-04-28 00:33:44 +02:00
5443cb52bb Merge branch 'master-rappel-regles' into 'master'
Amélioration, rappel-des-regles

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!215
2021-04-27 07:34:02 +00:00
b232a8fa0c typofix 2021-04-27 00:36:01 +02:00
a68aa29b53 Extraction calcul chances 2021-04-26 23:45:43 +02:00
ccb9cd73f4 Amélioration, rappel-des-regles 2021-04-26 23:44:39 +02:00
723d1e6b99 Fix #187 2021-04-26 00:07:36 +02:00
40f99da8ac Merge branch 'master-fix-187' into 'master'
Fix: recul sur encaissement créature #187

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!213
2021-04-25 22:06:21 +00:00
76865d24d2 Fix: recul sur encaissement créature #187 2021-04-25 14:50:44 +02:00
020824af7e Reset des nombre astraux 2021-04-25 09:43:26 +02:00
cc8f432746 Merge branch 'master' of gitlab.com:LeRatierBretonnien/foundryvtt-reve-de-dragon 2021-04-24 09:31:41 +02:00
6435ce70da Fix sauvegarde TMR 2021-04-24 09:31:29 +02:00
db9cbc693a Merge branch 'master-fix' into 'master'
Fix: après dérobade, impossible ouvrir fiche

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!208
2021-04-18 06:39:12 +00:00
b202352979 Fix: après dérobade, impossible ouvrir fiche 2021-04-17 21:03:59 +02:00
7ddedea204 Updat release 2021-04-17 09:19:15 +02:00
2bca036d53 Merge branch 'master-fix' into 'master'
Empêcher doublons sur tête/souffle #175

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!206
2021-04-17 07:18:31 +00:00
8f08f95dbf Updat release 2021-04-17 09:08:09 +02:00
b5581ff9fb Ajout de tokens de créatures 2021-04-17 00:04:25 +02:00
e06ae1937a Empêcher doublons sur tête/souffle #175
Lors de l'ajout de la tête présents des cités, le présent de chaque
cité était ajouté par tous les joueurs connectés qui traitaient le hook
2021-04-16 23:33:09 +02:00
0f7e1ef553 Merge branch 'master-fix-compendium' into 'master'
Fix compendium voyageurs

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!201
2021-04-16 07:14:57 +00:00
51afd9020c Fix compendium voyageurs
- nom de token Coureur des Rues
- don de haut rêve pour le Cuisinier Haut-Rêvant
- tokens maintenant neutres
2021-04-15 20:00:14 +02:00
1bc5e06147 Merge branch 'master-fix-comp-base' into 'master'
Fix categorieCompetences.base

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!196
2021-04-09 08:29:51 +00:00
7f3a575349 Fix categorieCompetences.base
au lieu de niveau/level
2021-04-09 09:41:57 +02:00
c26ba66722 #182 - Décompte XP 2021-04-09 08:46:46 +02:00
cb105dd311 Merge branch 'master' of gitlab.com:LeRatierBretonnien/foundryvtt-reve-de-dragon 2021-04-09 08:09:20 +02:00
76c88e530e #182 - Décompte XP 2021-04-09 08:09:04 +02:00
03839397ce Merge branch 'master-fix-182' into 'master'
Fix xp des compétences tronc #182

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!195
2021-04-09 06:05:05 +00:00
74ddc8893a Fix xp des compétences tronc #182
La méthode splice retourne les éléments supprimées, et non pas
le tableau après suppression

# Conflicts:
#	module/item-competence.js
#	module/misc.js
2021-04-09 01:12:02 +02:00
790aa950c3 Message explicite HR 2021-04-04 09:03:28 +02:00
4672a3a7d6 Message explicite HR 2021-04-04 08:58:23 +02:00
2a8ab5e1ce Merge branch 'master-demi-surprise' into 'master'
Demi-surprise: Réussite normale => échec #181

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!190
2021-04-04 06:50:37 +00:00
4aaa72ed1a Erreur si draconic sans être haut-rêvant 2021-04-04 00:16:49 +02:00
f087b61c27 Demi-surprise: Réussite normale => échec #181
En demi-surprise, les résultats "réussite normaux" sont
maintenant présenté comme des réussite insuffisantes
2021-04-03 23:59:47 +02:00
96c6936c3e Fix: message pour non haut-rêvant 2021-04-03 23:59:47 +02:00
7d27d7d9f9 Fix sur commande astro 2021-04-03 23:01:04 +02:00
0d7ba3be69 Merge branch 'master' of gitlab.com:LeRatierBretonnien/foundryvtt-reve-de-dragon 2021-04-03 18:53:21 +02:00
8670456d9a Increment version 2021-04-03 18:53:09 +02:00
b2c7ff4927 Merge branch 'master-177' into 'master'
Fix: #177 arme creature incorrecte

Closes #177

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!189
2021-04-03 16:52:42 +00:00
680fbee090 Fix: #177 arme creature incorrecte 2021-04-03 13:05:10 +02:00
5c6a01b9f4 Push pour mini-fixes 2021-04-03 08:39:31 +02:00
d45f5d625a #179 - Fix reve-actuel 2021-04-03 08:32:27 +02:00
2b1a9151d7 Gestion état sonné correct 2021-04-03 08:18:08 +02:00
3335c41c27 Merge branch 'master-improve' into 'master'
Draconic pour les haut-rêvants - HUD

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!188
2021-04-03 06:16:18 +00:00
329ae475d9 Correction message échec total 2021-04-03 03:05:44 +02:00
72f8c83caf Draconic que pour les Haut rêvant #174 2021-04-03 03:04:48 +02:00
d353b6ccaf Fix regression liste tâches 2021-04-02 22:06:58 +02:00
a122a2e421 Merge branch 'master-hautrevant' into 'master'
Limiter le draconic aux Haut rêvant - #174

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!185
2021-04-02 06:45:10 +00:00
2465af4413 réparation icones 2021-04-01 23:02:03 +02:00
817c5d30ad Les haut-rêvants ont le don de haut rêve 2021-04-01 23:02:02 +02:00
87440cf7f9 Limiter le draconic aux Haut rêvant - #174 2021-04-01 23:02:02 +02:00
965e45cead Merge branch 'master-fixes' into 'master'
Fix détermination nombre astral

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!184
2021-04-01 05:41:36 +00:00
ac8610cd6c Fix détermination nombre astral
fixé, déplacé dans les savoirs

# Conflicts:
#	module/rdd-calendrier.js
#	templates/actor-sheet.html
2021-04-01 01:12:16 +02:00
08cf1f49e1 Fix Chanter la chanson 2021-04-01 01:12:15 +02:00
4925cee756 Fix changement jour 2021-04-01 01:12:15 +02:00
43acbfb443 Simplification calcul de difficulté alchimie 2021-04-01 01:12:15 +02:00
9a6fb1a850 Fix reférence de recette de cuisine 2021-04-01 01:12:15 +02:00
42fb0c0b5e Merge branch 'master-fixes' into 'master'
Création d'oeuvres

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!182
2021-03-31 06:40:56 +00:00
fa2b125459 Création d'oeuvres
# Conflicts:
#	module/actor-sheet.js
2021-03-31 01:34:52 +02:00
e8d0748688 Fix tooltip périple 2021-03-29 23:57:37 +02:00
2972504640 Fix caracXP, chance, /payer 2021-03-27 14:55:32 +01:00
3582fa9c8a Merge branch 'master-fixes' into 'master'
Fix expérience carac

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!179
2021-03-27 13:54:53 +00:00
d5277137dd Fix dépense chance sur appel à la chance 2021-03-27 14:41:12 +01:00
dae852a1e0 Fix expérience carac
=> await manquant
=> plusieurs whisper recipients: pas supporté
=> manque lowercase sur recherche de carac par nom
2021-03-27 14:41:12 +01:00
1ea6b66483 Fix chance 2021-03-26 07:42:58 +01:00
d35f3ce5a2 Merge branch 'master-improve' into 'master'
Fix jet de moral/chance

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!178
2021-03-26 06:42:14 +00:00
bf9e74ef7c Ne pas afficher la description d'un sort manqué
Pas d'affichage pour un sort mis en réserve non plus
2021-03-26 01:16:53 +01:00
fd73b2c7af Fix récupération chance 2021-03-26 01:16:52 +01:00
3aa13511cd Fix jet de moral
L'affichage du message ne fonctionnait plus
2021-03-26 01:16:52 +01:00
94cebbb9e5 Ajout edition nombre astral 2021-03-25 18:05:42 +01:00
2a1495927f Merge branch 'master' of gitlab.com:LeRatierBretonnien/foundryvtt-reve-de-dragon 2021-03-25 17:53:30 +01:00
8d42871988 Fix competence base 2021-03-25 17:52:02 +01:00
868ed01723 Merge branch 'master-improve' into 'master'
Fix /rdd [carac]

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!177
2021-03-25 16:36:49 +00:00
65c90c5bd4 Fix /rdd [carac]
- en cas de carac exact (reve), choisir cette caractéristique
(pas de message par rapport à reve-actuel)
- support /rdd odorat-gout
2021-03-25 10:27:33 +01:00
c16101428a Add 2021-03-25 07:45:32 +01:00
77d5646497 Permet les macros spéciales (SF) 2021-03-25 07:34:19 +01:00
1c0c775de3 Merge branch 'master-improve' into 'master'
Jets de masse

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!174
2021-03-25 06:32:16 +00:00
e8a59b56a4 Fix: privilégier le nom exact 2021-03-25 01:58:41 +01:00
39f6307058 Version 1.3.38 2021-03-25 00:16:53 +01:00
a532a989d6 Jets de masse
Quelques exemples:

- `/rdd dexterite bricolage -2`
- `/rdd vue survie en sous-sol -2`
- `/rdd vue désert 0`
- `/rdd vue vigi -3`
- `/rdd vol vigi 0` : Volonté Vigilance
- `/rdd chance-actuelle 0`
- `/rdd reve-actuel -8`

Attention:
- `/rdd vue vig 0`

 => Navigation et Vigilance correspondent, c'est Navigation qui est
pris (premier dans l'ordre alphabétique), avec un message.
2021-03-25 00:15:08 +01:00
a802307dac Update release 2021-03-24 11:10:17 +01:00
d95a0ebfc1 Merge branch 'master-prepare-080' into 'master'
Preparation access documentData

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!173
2021-03-22 19:20:02 +00:00
25d7a447a8 Preparation access documentData
Ajout de méthode Misc.data pour accéder aux data des Actor/Item
Dans le cas où on est sur un Actor/Item, retourne le document
(noeud data)

Dans les autres cas, retourne l'objet lkui même (donc, le document)

Du coup, on devrait pouvoir facilement changer en 0.8.0
2021-03-22 20:10:54 +01:00
2b6d1d8de1 Merge branch 'master-fixes' into 'master'
Fix: recalcul des nombres astraux

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!172
2021-03-20 17:42:29 +00:00
de922e2605 Version 1.3.36 2021-03-20 14:44:27 +01:00
dbad358d7a Fix: recalcul des nombres astraux 2021-03-20 14:36:53 +01:00
218f38e044 Cleanup: réduction de duplicates 2021-03-20 14:36:53 +01:00
441259efb6 Cleanup compendium et monnaies 2021-03-20 14:36:53 +01:00
c99ac0675a Sync version 2021-03-20 10:38:51 +01:00
d5fbca4778 Merge branch 'master-fixes' into 'master'
Reports de la branche 0.8.0 non liés à Foundry

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!171
2021-03-19 12:24:23 +00:00
c509e23513 Ajout Périple & urgence draconique
# Conflicts:
#	module/tmr-utility.js
2021-03-19 00:02:48 +01:00
50c336cda7 Ajout de souffle dans certains cas
- conquete de cité échouée
2021-03-19 00:02:48 +01:00
34e12cd701 Suppression de queues TMR finies 2021-03-19 00:02:48 +01:00
bbd38bd618 Extrait méthode Actor.getDemiReve 2021-03-19 00:02:48 +01:00
3f4d52487d Refactor: extrait méthodes communes
- suppression de casetmr lors de visite/conquete
- suppression de toutes les cases TMRs liées
- notification lors de suppression d'une casetmr pour enlever la queue

# Conflicts:
#	module/actor.js
2021-03-19 00:02:48 +01:00
944dd103d2 Ajout commande idée fixe/désir lancinant 2021-03-19 00:02:48 +01:00
e71c03abb0 Fix erreur en cas d'attaque sans cible 2021-03-19 00:02:48 +01:00
17d863482d simplification/organisation RdDActorSheet.getData 2021-03-19 00:02:48 +01:00
dcc450db63 Fix tooltip rencontre dans les TMR 2021-03-19 00:02:47 +01:00
6025f5d1de Merge branch 'master' of gitlab.com:LeRatierBretonnien/foundryvtt-reve-de-dragon 2021-03-18 17:08:47 +01:00
0b898b1ee7 Sync fix/version 2021-03-18 17:08:34 +01:00
1ad457f25f Merge branch 'master-fixes' into 'master'
Stress transformé avec les compétences

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!170
2021-03-18 06:24:02 +00:00
5e8c01935c Stress transformé avec les compétences
Pour ne pas avoir à naviguer d'onglet pour répartir le stress
transformé
2021-03-18 01:15:09 +01:00
0b7ec59822 utilisation de class au lieu d'id 2021-03-18 01:15:09 +01:00
a44904ebc0 Fix moral sur dissolution 2021-03-18 01:15:09 +01:00
a86ac0ffa2 Merge branch 'master-fixes' into 'master'
Moral sur les oeuvres

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!169
2021-03-17 07:38:29 +00:00
0b3b1c66bd Posé par terre est maintenant global
- accessible à tous les joueurs
- lié au token des personnages

Ceci permet aux joueurs d'utiliser ce "véhicule" pour échanger des
objets
2021-03-17 01:26:43 +01:00
f26ae24d13 Nettoyage feuilles acteurs
- template partial pour les compétences
- séparation des options/calculs/données
- extraction de méthodes pour les calculs (total xp, total carac, ...)

- déplacement de code de RdDUtility vers RdDItemCompetence / RdDCarac
2021-03-17 01:21:37 +01:00
5207df0223 Supprime feuille inutilisée 2021-03-17 01:04:37 +01:00
10ddcf031e Rename data to formData 2021-03-17 01:04:02 +01:00
830f4fde9a Suppression for="Categorie" dans html
Aucun impact, pas de référence trouvée à cet attribut
2021-03-16 22:34:33 +01:00
98de1a6922 Moral sur les oeuvres
Simplification de l'appel au moral pour les oeuvres

Ajout de la possibilité d'utiliser le moral sur les oeuvres

Affichage du moral/appel selon la caractéristique
2021-03-16 22:34:33 +01:00
dab371578d Ajout hover sur boutons des acteurs 2021-03-16 22:34:33 +01:00
c58c9636d2 Merge branch 'master-fixes' into 'master'
Afficher le jet d'endurance pour les personnages

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!168
2021-03-15 07:42:18 +00:00
7f811ae249 Version 1.3.33 2021-03-15 00:12:43 +01:00
396f73ec22 Fix appel au moral
Maintenant géré pour les cas autres qu'un jet de compétence pur:

- attaque/défense
- tâches
- carac pure
- arts
- jeu

Message à la 2ème personne, la 3eme est utilisée juste pour le "titre"
des messages
2021-03-15 00:11:13 +01:00
1de15d0b32 Fix: cas de xp/niveau vide
Après avoir enlevé l'xp par erreur, elle est gardée vide, mieux vaut
la mettre à 0 pour pouvoir l'augmenter par la suite
2021-03-15 00:11:13 +01:00
57ec6403bb Autoformat 2021-03-15 00:11:13 +01:00
372eb6d334 Afficher le jet d'endurance pour les personnages
Ne pas afficher pour les acteur qui ne sont pas affectés à un joueur
2021-03-15 00:11:13 +01:00
bc45ffa7bb Minor fixes 2021-03-14 20:29:21 +01:00
b5e2f892a5 Merge branch 'master-fixes' into 'master'
Plein de petits fixes

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!167
2021-03-14 19:20:52 +00:00
c28e8d4c9b Ajuster réussite/échec en demi-surprise 2021-03-14 19:19:37 +01:00
658e4f45aa Attaque en finesse/rapidité pour créatures 2021-03-14 19:06:43 +01:00
fdb1aec36a Pas de parade avec arme non-équipée 2021-03-14 19:06:03 +01:00
4a76221f07 Choisir un GM connecté
Pour le cas où 2 GM sont définis, éviter de passer par un GM inactif
2021-03-14 19:05:42 +01:00
300419cbad Amélioration usage armes
Décompter uniquemlent si le jet n'est pas une particulière

Sur attaque particulière, décompter si le choix n'est pas une attaque
en rapidité
2021-03-14 19:04:35 +01:00
620b34443d Ne pas afficher Utilisations 0 2021-03-14 16:07:44 +01:00
432098f9c5 Couleurs du calendrier
De nuit, les boutons noirs sur fond gris sur fond sombre
étaient difficiles à voir

séparation de class css pour affichage/position du bouton
séparation du nombre de minutes/heure à positionner
2021-03-14 16:02:31 +01:00
e1b10bc489 Message si un combatant n'a pas d'actor
Par exemple, après suppression d'un acteur, les combatants
correspondant aux tokens n'ont plus d'acteur.
2021-03-14 14:23:26 +01:00
b4281afba8 Corrections noms de tokens
Plusieurs animaux avaient un token nommé Quileurbist
2021-03-14 13:10:12 +01:00
ffd7b0b0c1 /tmra renvoie une tmr aléatoire 2021-03-14 12:54:12 +01:00
c281551603 Fix combat mains nues 2021-03-12 23:19:34 +01:00
5e9f1cd8a6 Increase version 2021-03-11 12:10:26 +01:00
9245c7da44 Ajout portrait des voyageurs 2021-03-10 22:30:37 +01:00
1d69e1dc5a Fix #167 2021-03-09 23:28:21 +01:00
ec0e1ac257 Ajout des points de voyage 2021-03-08 16:43:00 +01:00
aec5d6f239 Fix moral 2021-03-06 10:16:57 +01:00
e09bdadba7 Merge branch 'bugFixAppelAuMoral' into 'master'
Bugfix Appel au moral.

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!164
2021-03-06 09:09:58 +00:00
7e252cf6ae Bugfix Appel au moral. 2021-03-05 22:38:25 +01:00
38adf22e82 Sync head 2021-02-28 08:37:42 +01:00
da7d67b3d7 Merge branch 'v1.3-fixes' into 'master'
V1.3 fixes

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!161
2021-02-28 07:34:44 +00:00
948f707340 fix Grammar 2021-02-28 02:01:23 +01:00
cfe99ec188 #166 Supprimer seulement le sort déclenché 2021-02-28 02:01:15 +01:00
d85396db0f fix typos 2021-02-28 02:01:07 +01:00
950e050051 Merge branch 'v1.3' into 'master'
V1.3

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!159
2021-02-27 22:10:43 +00:00
159 changed files with 3199 additions and 2838 deletions

BIN
fonts/goudyacc.woff Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
icons/creatures/dong_t.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
icons/heures/de-heures.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
icons/heures/hd01.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
icons/heures/hd02.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
icons/heures/hd03.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
icons/heures/hd04.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
icons/heures/hd05.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
icons/heures/hd06.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
icons/heures/hd07.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
icons/heures/hd08.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
icons/heures/hd09.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
icons/heures/hd10.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
icons/heures/hd11.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
icons/heures/hd12.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
icons/tmr/gift.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

BIN
icons/tmr/pelerin.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
icons/tmr/scroll.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
icons/tmr/wave.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -7,6 +7,7 @@
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 {
@ -25,41 +26,25 @@ export class RdDActorCreatureSheet extends RdDActorSheet {
/* -------------------------------------------- */
getData() {
let data = super.getData();
console.log("Creature : ", data);
data.itemsByType = {};
for (const item of data.items) {
let list = data.itemsByType[item.type];
if (!list) {
list = [];
data.itemsByType[item.type] = list;
}
list.push(item);
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(),
}
formData.calc.surEncombrementMessage = (formData.data.compteurs.surenc.value < 0) ? "Sur-Encombrement!" : "";
formData.options.isGM = game.user.isGM;
// Compute current carac sum
let sum = 0;
Object.values(data.data.carac).forEach(carac => { if (!carac.derivee) { sum += parseInt(carac.value) } });
data.data.caracSum = sum;
formData.data.competencecreature = formData.itemsByType["competencecreature"];
data.data.carac.taille.isTaille = true; // To avoid button link;
data.data.blessures.resume = this.actor.computeResumeBlessure(data.data.blessures);
RdDUtility.filterItemsPerTypeForSheet(formData);
RdDUtility.buildArbreDeConteneur(this, formData);
data.data.isGM = game.user.isGM;
console.log("Creature : ", this.objetVersConteneur, formData);
data.data.competencecreature = data.itemsByType["competencecreature"];
this.actor.computeEncombrementTotalEtMalusArmure();
RdDUtility.filterItemsPerTypeForSheet(data);
RdDUtility.buildArbreDeConteneur(this, data);
data.data.encTotal = this.actor.encTotal;
data.data.isGM = game.user.isGM;
console.log("Creature : ", this.objetVersConteneur, data);
return data;
return formData;
}
/* -------------------------------------------- */

View File

@ -4,6 +4,7 @@
*/
import { HtmlUtility } from "./html-utility.js";
import { Misc } from "./misc.js";
/* -------------------------------------------- */
export class RdDActorEntiteSheet extends ActorSheet {
@ -29,24 +30,18 @@ export class RdDActorEntiteSheet extends ActorSheet {
}
/* -------------------------------------------- */
getData() {
let data = super.getData();
async getData() {
let formData = super.getData();
data.itemsByType = {};
for (const item of data.items) {
let list = data.itemsByType[item.type];
if (!list) {
list = [];
data.itemsByType[item.type] = list;
}
list.push(item);
}
formData.itemsByType = Misc.classify(formData.items);
data.data.carac.taille.isTaille = true; // To avoid button link;
data.data.competencecreature = data.itemsByType["competencecreature"];
data.data.isGM = game.user.isGM;
formData.options.isGM = game.user.isGM;
return data;
formData.data.carac.taille.isTaille = true; // To avoid button link;
formData.data.competencecreature = formData.itemsByType["competencecreature"];
return formData;
}
/* -------------------------------------------- */

View File

@ -10,6 +10,7 @@ import { RdDItemCompetence } from "./item-competence.js";
import { RdDBonus } from "./rdd-bonus.js";
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
/* -------------------------------------------- */
export class RdDActorSheet extends ActorSheet {
@ -31,120 +32,115 @@ export class RdDActorSheet extends ActorSheet {
}
/* -------------------------------------------- */
getData() {
let data = super.getData();
if ( data.actor.type == 'creature' || data.actor.type == 'humanoide') return data; // Shortcut
async getData() {
let formData = super.getData();
// -------------- version 0.7.9
// let formData = {
// cssClass: this.entity.owner ? "editable" : "locked",
// editable: this.isEditable,
// entity: duplicate(this.entity.data),
// limited: this.entity.limited,
// options: this.options,
// owner: this.entity.owner,
// title: this.title
// }
// // Entity data
// formData.actor = formData.entity;
// formData.data = formData.entity.data;
data.data.editCaracComp = this.options.editCaracComp;
data.data.showCompNiveauBase = this.options.showCompNiveauBase;
data.data.montrerArchetype = this.options.montrerArchetype;
// // Owned items
// formData.items = formData.actor.items;
// formData.items.sort((a, b) => (a.sort || 0) - (b.sort || 0));
data.itemsByType = Misc.classify(data.items);
formData.itemsByType = Misc.classify(formData.items);
RdDUtility.filterItemsPerTypeForSheet(formData);
// Competence per category
data.data.comptageArchetype = RdDUtility.getLimitesArchetypes();
data.data.competenceXPTotal = 0;
data.competenceByCategory = Misc.classify(
data.itemsByType.competence,
item => item.data.categorie,
item => {
let archetypeKey = (item.data.niveau_archetype < 0) ? 0 : item.data.niveau_archetype;
if (data.data.comptageArchetype[archetypeKey] == undefined) {
data.data.comptageArchetype[archetypeKey] = { "niveau": archetypeKey, "nombreMax": 0, "nombre": 0};
}
data.data.comptageArchetype[archetypeKey].nombre = (data.data.comptageArchetype[archetypeKey]?.nombre??0) + 1; //Comptage archetype
item.data.xpNext = RdDItemCompetence.getCompetenceNextXp(item.data.niveau);
item.data.isLevelUp = item.data.xp >= item.data.xpNext; // Flag de niveau à MAJ
//this.actor.checkCompetenceXP(item.name); // Petite vérification experience
item.data.showCompetence = !data.data.showCompNiveauBase || (Number(item.data.niveau) != Number(RdDUtility.getLevelCategory(item.data.categorie)));
// Ignorer les compétences 'troncs' à ce stade
data.data.competenceXPTotal += RdDItemCompetence.computeCompetenceXPCost(item);
return item;
});
data.data.competenceXPTotal -= RdDItemCompetence.computeEconomieCompetenceTroncXP(data.itemsByType.competence);
formData.options.isGM = game.user.isGM;
// Compute current carac sum
let sum = 0;
for (let caracName in data.data.carac) {
let currentCarac = data.data.carac[caracName];
if (!currentCarac.derivee) {
sum += parseInt(currentCarac.value);
}
currentCarac.xpNext = RdDUtility.getCaracNextXp(currentCarac.value);
currentCarac.isLevelUp = (currentCarac.xp >= currentCarac.xpNext);
}
sum += (data.data.beaute >= 0) ? (data.data.beaute - 10) : 0;
data.data.caracSum = sum;
// la taille est la taille: on ne peut pas l'utiliser pour un jet
formData.data.carac.taille.isTaille = true;
// Force empty arme, at least for Esquive
if (data.itemsByType.arme == undefined) data.itemsByType.arme = [];
for (const arme of data.itemsByType.arme) {
arme.data.niveau = 0; // Per default, TODO to be fixed
for (const melee of data.competenceByCategory.melee) {
if (melee.name == arme.data.competence)
arme.data.niveau = melee.data.niveau
}
for (const tir of data.competenceByCategory.tir) {
if (tir.name == arme.data.competence)
arme.data.niveau = tir.data.niveau
}
for (const lancer of data.competenceByCategory.lancer) {
if (lancer.name == arme.data.competence)
arme.data.niveau = lancer.data.niveau
}
}
if (this.actor.data.type == 'creature') return formData; // Shortcut
// To avoid armour and so on...
data.data.combat = duplicate(RdDUtility.checkNull(data.itemsByType['arme']));
data.data.combat = RdDCombatManager.finalizeArmeList(data.data.combat, data.itemsByType.competence, data.data.carac);
formData.competenceByCategory = Misc.classify(formData.data.competences, it => it.data.categorie);
data.esquive = { name: "Esquive", niveau: data.competenceByCategory?.melee.find(it => it.name == 'Esquive')?.data.niveau ?? -6};
let corpsACorps = data.competenceByCategory?.melee.find(it => it.name == 'Corps à corps');
if (corpsACorps) {
let cc_init = RdDCombatManager.calculInitiative(corpsACorps.data.niveau, data.data.carac['melee'].value);
data.data.combat.push(RdDItemArme.mainsNues({ niveau: corpsACorps.data.niveau, initiative: cc_init }));
}
this.armesList = duplicate(data.data.combat);
formData.calc = {
comptageArchetype: RdDItemCompetence.computeResumeArchetype(formData.data.competences),
competenceXPTotal: RdDItemCompetence.computeTotalXP(formData.data.competences),
caracTotal: RdDCarac.computeTotal(formData.data.carac, formData.data.beaute),
// Mise à jour de l'encombrement total et du prix de l'équipement
encTotal: await this.actor.computeEncombrementTotalEtMalusArmure(),
prixTotalEquipement: await this.actor.computePrixTotalEquipement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
fatigue: {
malus: RdDUtility.calculMalusFatigue(formData.data.sante.fatigue.value, formData.data.sante.endurance.max),
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(formData.data.sante.fatigue.value, formData.data.sante.endurance.max).html() + "</table>"
},
resumeBlessures: this.actor.computeResumeBlessure(formData.data.blessures),
};
formData.calc.surEncombrementMessage = (formData.data.compteurs.surenc.value < 0) ? "Sur-Encombrement!" : "";
data.data.carac.taille.isTaille = true; // To avoid button link;
data.data.compteurs.chance.isChance = true;
data.data.blessures.resume = this.actor.computeResumeBlessure(data.data.blessures);
formData.data.competences.forEach(item => {
item.visible = !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(item);
RdDItemCompetence.levelUp(item);
});
Object.values(formData.data.carac).forEach(c => {
RdDCarac.levelUp(c);
});
// toujours avoir une liste d'armes (pour mettre esquive et corps à corps)
formData.data.combat = duplicate(formData.itemsByType.arme ?? []);
RdDItemArme.computeNiveauArmes(formData.data.combat, formData.data.competences);
RdDItemArme.ajoutCorpsACorps(formData.data.combat, formData.data.competences, formData.data.carac );
formData.esquive = RdDItemCompetence.getEsquive(formData.data.competences);
formData.data.combat = RdDCombatManager.finalizeArmeList(formData.data.combat, formData.itemsByType.competence, formData.data.carac);
this.armesList = formData.data.combat;
// Mise à jour de l'encombrement total et du prix de l'équipement
this.actor.computeEncombrementTotalEtMalusArmure();
this.actor.computePrixTotalEquipement();
// Common data
data.data.competenceByCategory = data.competenceByCategory;
data.data.encTotal = this.actor.encTotal;
data.data.prixTotalEquipement = this.actor.prixTotalEquipement;
data.data.surprise = RdDBonus.find(this.actor.getSurprise(false)).descr;
data.data.isGM = game.user.isGM;
data.ajustementsConditions = CONFIG.RDD.ajustementsConditions;
data.difficultesLibres = CONFIG.RDD.difficultesLibres;
formData.data.competenceByCategory = formData.competenceByCategory;
formData.data.isGM = game.user.isGM;
formData.ajustementsConditions = CONFIG.RDD.ajustementsConditions;
formData.difficultesLibres = CONFIG.RDD.difficultesLibres;
// low is normal, this the base used to compute the grid.
data.data.fatigue = {
malus: RdDUtility.calculMalusFatigue(data.data.sante.fatigue.value, data.data.sante.endurance.max),
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(data.data.sante.fatigue.value, data.data.sante.endurance.max).html() + "</table>"
formData.data.fatigue = {
malus: RdDUtility.calculMalusFatigue(formData.data.sante.fatigue.value, formData.data.sante.endurance.max),
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(formData.data.sante.fatigue.value, formData.data.sante.endurance.max).html() + "</table>"
}
RdDUtility.filterItemsPerTypeForSheet(data);
data.data.sortReserve = data.data.reve.reserve.list;
data.data.rencontres = duplicate(data.data.reve.rencontre.list);
data.data.caseSpeciales = data.itemsByType['casetmr'];
RdDUtility.buildArbreDeConteneur(this, data);
data.data.surEncombrementMessage = (data.data.compteurs.surenc.value < 0) ? "Sur-Encombrement!" : "";
data.data.vehiculesList = this.actor.buildVehiculesList();
data.data.monturesList = this.actor.buildMonturesList();
data.data.suivantsList = this.actor.buildSuivantsList();
return data;
formData.hautreve = {
sortsReserve: formData.data.reve.reserve.list,
rencontres: duplicate(formData.data.reve.rencontre.list),
casesTmr: formData.itemsByType.casetmr
}
RdDUtility.buildArbreDeConteneur(this, formData);
formData.subacteurs = {
vehicules: this.actor.listeVehicules(),
montures: this.actor.listeMontures(),
suivants: this.actor.listeSuivants()
}
if (this.actor.getBestDraconic().data.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 _onDrop(event) {
let toSuper = await RdDUtility.processItemDropEvent(this, event);
if ( toSuper) {
if (toSuper) {
super._onDrop(event);
}
}
@ -156,16 +152,16 @@ export class RdDActorSheet extends ActorSheet {
/* -------------------------------------------- */
async creerObjet() {
let itemType = $("#creer-equipement").val();
let itemType = $(".item-type").val();
await this.actor.createOwnedItem({ name: 'Nouveau ' + itemType, type: itemType }, { renderSheet: true });
}
/* -------------------------------------------- */
async selectObjetType() {
let itemType = ["objet", "arme", "armure", "conteneur", "herbe", "ingredient", "livre", "potion", "munition", "monnaie"];
let options = '<span class="competence-label">Selectionnez le type d\'équipement</span><select id="creer-equipement">';
for (let typeName of itemType) {
options += '<option value="' + typeName + '">' + typeName + '</option>'
let typeObjets = ["objet", "arme", "armure", "conteneur", "herbe", "ingredient", "livre", "potion", "munition", "monnaie"];
let options = `<span class="competence-label">Selectionnez le type d'équipement</span><select class="item-type">`;
for (let typeName of typeObjets) {
options += `<option value="${typeName}">${typeName}</option>`
}
options += '</select>';
let d = new Dialog({
@ -182,6 +178,27 @@ export class RdDActorSheet extends ActorSheet {
d.render(true);
}
/* -------------------------------------------- */
async selectTypeOeuvre() {
let typeOeuvres = ["oeuvre", "recettecuisine", "musique", "chant", "danse", "jeu" ];
let options = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
for (let typeName of typeOeuvres) {
options += `<option value="${typeName}">${typeName}</option>`
}
options += '</select>';
let d = new Dialog({
title: "Créer une oeuvre",
content: options,
buttons: {
one: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'oeuvre",
callback: () => this.creerObjet()
}
}
});
d.render(true);
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
@ -229,12 +246,15 @@ export class RdDActorSheet extends ActorSheet {
ev.preventDefault();
}
});
html.find('#creer-tache').click(ev => {
html.find('.creer-tache').click(ev => {
this.createEmptyTache();
});
html.find('#creer-un-objet').click(ev => {
html.find('.creer-un-objet').click(ev => {
this.selectObjetType();
});
html.find('.creer-une-oeuvre').click(ev => {
this.selectTypeOeuvre();
});
html.find('#nettoyer-conteneurs').click(ev => {
this.actor.nettoyerConteneurs();
});
@ -275,11 +295,11 @@ export class RdDActorSheet extends ActorSheet {
this.actor.rollCarac(caracName.toLowerCase());
});
html.find('#chance-actuelle').click((event) => {
html.find('.chance-actuelle').click((event) => {
this.actor.rollCarac('chance-actuelle');
});
html.find('#chance-appel').click((event) => {
html.find('.chance-appel').click((event) => {
this.actor.rollAppelChance();
});
@ -335,8 +355,8 @@ export class RdDActorSheet extends ActorSheet {
html.find('.subacteur-label a').click((event) => {
const li = $(event.currentTarget).parents(".item");
let actorId = li.data('actor-id');
let actor = game.actors.get( actorId) ;
if ( actor ) {
let actor = game.actors.get(actorId);
if (actor) {
actor.sheet.render(true);
}
});
@ -461,9 +481,7 @@ export class RdDActorSheet extends ActorSheet {
// On pts de reve change
html.find('.pointsreve-value').change((event) => {
let reveValue = event.currentTarget.value;
let reve = duplicate(this.actor.data.data.reve.reve);
reve.value = reveValue;
this.actor.update({ "data.reve.reve": reve });
this.actor.update({ "data.reve.reve.value": reveValue });
});
// On seuil de reve change
@ -473,7 +491,7 @@ export class RdDActorSheet extends ActorSheet {
});
html.find('#attribut-protection-edit').change((event) => {
this.actor.updateProtectionValue(event.currentTarget.attributes.name.value, parseInt(event.target.value));
this.actor.updateAttributeValue(event.currentTarget.attributes.name.value, parseInt(event.target.value));
});
// On stress change
@ -485,19 +503,19 @@ export class RdDActorSheet extends ActorSheet {
html.find('#ethylisme').change((event) => {
this.actor.setEthylisme(parseInt(event.target.value));
});
html.find('#stress-test').click((event) => {
html.find('.stress-test').click((event) => {
this.actor.transformerStress();
this.render(true);
});
html.find('#moral-malheureux').click((event) => {
html.find('.moral-malheureux').click((event) => {
this.actor.jetDeMoral('malheureuse');
this.render(true);
});
html.find('#moral-neutre').click((event) => {
html.find('.moral-neutre').click((event) => {
this.actor.jetDeMoral('neutre');
this.render(true);
});
html.find('#moral-heureux').click((event) => {
html.find('.moral-heureux').click((event) => {
this.actor.jetDeMoral('heureuse');
this.render(true);
});

View File

@ -33,21 +33,24 @@ export class RdDActorVehiculeSheet extends ActorSheet {
}
/* -------------------------------------------- */
getData() {
let data = super.getData();
async getData() {
let formData = super.getData();
data.itemsByType = Misc.classify(data.items);
formData.itemsByType = Misc.classify(formData.items);
RdDUtility.filterItemsPerTypeForSheet(data);
RdDUtility.buildArbreDeConteneur(this, data);
RdDUtility.filterItemsPerTypeForSheet(formData);
RdDUtility.buildArbreDeConteneur(this, formData);
this.actor.computeEncombrementTotalEtMalusArmure();
data.data.isGM = game.user.isGM;
data.data.surEncombrementMessage = (this.encTotal > data.capacite_encombrement) ? "Sur-Encombrement!" : "";
formData.options.isGM = game.user.isGM;
console.log("DATA", data);
formData.calc ={
encTotal: await this.actor.computeEncombrementTotalEtMalusArmure(),
}
formData.calc.surEncombrementMessage = formData.calc.encTotal > formData.data.capacite_encombrement ? "Sur-Encombrement!" : "",
return data;
console.log("DATA", formData);
return formData;
}
/* -------------------------------------------- */

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
import { Misc } from "./misc.js";
/**
* Class providing helper methods to get the list of users, and
@ -7,32 +8,27 @@ export class ChatUtility {
/* -------------------------------------------- */
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.part, sockmsg.gmId);
case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.part);
}
}
/* -------------------------------------------- */
static onRemoveMessages(part, gmId) {
if (game.user._id == gmId) {
static onRemoveMessages(part) {
if (Misc.isElectedUser()) {
const toDelete = game.messages.filter(it => it.data.content.includes(part));
toDelete.forEach(it => it.delete());
}
}
}
/* -------------------------------------------- */
static removeChatMessageContaining(part) {
const gmId = game.user.isGM ? game.user._id : game.users.entities.find(u => u.isGM)?.id;
if (!gmId || game.user.isGM) {
ChatUtility.onRemoveMessages(part, game.user._id);
if (Misc.isElectedUser()) {
ChatUtility.onRemoveMessages(part);
}
else {
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_delete_chat_message", data: {
part:part,
gmId: gmId,
}});
msg: "msg_delete_chat_message", data: { part: part }
});
}
}

1
module/constants.js Normal file
View File

@ -0,0 +1 @@
export const SYSTEM_RDD = "foundryvtt-reve-de-dragon";

View File

@ -1,27 +0,0 @@
import { RdDDice } from "./rdd-dice.js";
export class DeDraconique extends Roll{
static async ddr(rollMode=undefined) {
let ddr = new DeDraconique().evaluate();
await RdDDice.show(ddr, rollMode);
return ddr;
}
constructor(){
super("1d8x8 - 0")
}
evaluate() {
super.evaluate();
const rerolls = Math.ceil(this.total / 8);
this.terms[this.terms.length - 1] = rerolls;
this.results[this.results.length - 1] = rerolls;
this._total -= rerolls;
return this;
}
async render(chatOptions) {
return super.render(chatOptions)
}
}

View File

@ -26,7 +26,7 @@ export class Grammar {
/* -------------------------------------------- */
static articleDetermine(genre) {
switch (toLowerCaseNoAccent(genre)) {
switch (Grammar.toLowerCaseNoAccent(genre)) {
case 'f': case 'feminin': return 'la';
case 'p': case 'mp': case 'fp': case 'pluriel': return 'les';
default:
@ -35,8 +35,8 @@ export class Grammar {
}
/* -------------------------------------------- */
static articleIndétermine(genre) {
switch (toLowerCaseNoAccent(genre)) {
static articleIndetermine(genre) {
switch (Grammar.toLowerCaseNoAccent(genre)) {
case 'f': case 'feminin': return 'une';
case 'p': case 'fp': case 'mp': case 'pluriel': return 'des';
case 'n': case 'neutre': return 'du'
@ -58,7 +58,7 @@ export class Grammar {
* @param {...any} mots
*/
static accord(genre, ...mots) {
switch (toLowerCaseNoAccent(genre)) {
switch (Grammar.toLowerCaseNoAccent(genre)) {
default:
case 'n': case 'neutre':
case 'm': case 'masculin': return mots[0];

View File

@ -1,4 +1,6 @@
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
const nomCategorieParade = {
"sans-armes": "Sans arme / armes naturelles",
@ -16,20 +18,33 @@ const nomCategorieParade = {
/* -------------------------------------------- */
export class RdDItemArme extends Item {
static isArme(item) {
return (item.type == 'competencecreature' && item.data.iscombat) || item.type == 'arme';
static isArme(itemData) {
itemData = Misc.data(itemData);
return (itemData.type == 'competencecreature' && itemData.data.iscombat) || itemData.type == 'arme';
}
/* -------------------------------------------- */
static getArmeData(item) {
switch (item ? item.data.type : '') {
case 'arme': return item.data;
static getArmeData(armeData) {
armeData = Misc.data(armeData);
switch (armeData ? armeData.type : '') {
case 'arme': return armeData;
case 'competencecreature':
return RdDItemCompetenceCreature.toArme(item.data);
return RdDItemCompetenceCreature.toArme(armeData);
}
return RdDItemArme.mainsNues();
}
static computeNiveauArmes(armes, competences) {
for (const arme of armes) {
arme.data.niveau = RdDItemArme.niveauCompetenceArme(arme, competences);
}
}
static niveauCompetenceArme(arme, competences) {
const compArme = competences.find(it => it.name == arme.data.competence);
return compArme?.data.niveau ?? -8;
}
/* -------------------------------------------- */
static getNomCategorieParade(arme) {
const categorie = arme?.data ? RdDItemArme.getCategorieParade(arme) : arme;
@ -38,7 +53,7 @@ export class RdDItemArme extends Item {
/* -------------------------------------------- */
static needArmeResist(armeAttaque, armeParade) {
if (!armeAttaque || !armeParade){
if (!armeAttaque || !armeParade) {
return false;
}
// Epées parant une arme de bois (cf. page 115 ), une résistance est nécessaire
@ -49,21 +64,22 @@ export class RdDItemArme extends Item {
}
/* -------------------------------------------- */
static getCategorieParade(arme) {
if (arme.data.categorie_parade) {
return arme.data.categorie_parade;
static getCategorieParade(armeData) {
armeData = Misc.data(armeData);
if (armeData.data.categorie_parade) {
return armeData.data.categorie_parade;
}
// pour compatibilité avec des personnages existants
if (arme.type == 'competencecreature' || arme.data.categorie == 'creature' ) {
return arme.data.categorie_parade || (arme.data.isparade ? 'sans-armes' : '');
if (armeData.type == 'competencecreature' || armeData.data.categorie == 'creature') {
return armeData.data.categorie_parade || (armeData.data.isparade ? 'sans-armes' : '');
}
if (!arme.type.match(/arme|competencecreature/)) {
if (!armeData.type.match(/arme|competencecreature/)) {
return '';
}
if (arme.data.competence == undefined) {
if (armeData.data.competence == undefined) {
return 'competencecreature';
}
let compname = arme.data.competence.toLowerCase();
let compname = armeData.data.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';
@ -72,9 +88,9 @@ export class RdDItemArme extends Item {
if (compname.match('bouclier')) return 'boucliers';
if (compname.match('masse')) return 'masses';
if (compname.match('epée') || compname.match('épée')) {
if (arme.name.toLowerCase().match(/(gnome)/))
if (armeData.name.toLowerCase().match(/(gnome)/))
return 'epees-courtes';
if (arme.name.toLowerCase().match(/((e|é)pée dragone|esparlongue|demi-dragonne)/))
if (armeData.name.toLowerCase().match(/((e|é)pée dragone|esparlongue|demi-dragonne)/))
return 'epees-longues';
return 'epees-lourdes';
}
@ -86,7 +102,7 @@ export class RdDItemArme extends Item {
/* -------------------------------------------- */
static needParadeSignificative(armeAttaque, armeParade) {
if (!armeAttaque || !armeParade){
if (!armeAttaque || !armeParade) {
return false;
}
// categories d'armes à la parade (cf. page 115 )
@ -119,36 +135,44 @@ export class RdDItemArme extends Item {
}
/* -------------------------------------------- */
static armeUneOuDeuxMains(arme, aUneMain) {
if (arme) {
arme.data.unemain = arme.data.unemain || !arme.data.deuxmains;
const uneOuDeuxMains = arme.data.unemain && arme.data.deuxmains;
const containsSlash = !Number.isInteger(arme.data.dommages) && arme.data.dommages.includes("/");
static armeUneOuDeuxMains(armeData, aUneMain) {
armeData = Misc.data(armeData);
if (armeData) {
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 (containsSlash) { // Sanity check
arme = duplicate(arme);
armeData = duplicate(armeData);
const tableauDegats = arme.data.dommages.split("/");
const tableauDegats = armeData.data.dommages.split("/");
if (aUneMain)
arme.data.dommagesReels = Number(tableauDegats[0]);
armeData.data.dommagesReels = Number(tableauDegats[0]);
else // 2 mains
arme.data.dommagesReels = Number(tableauDegats[1]);
armeData.data.dommagesReels = Number(tableauDegats[1]);
}
else {
arme.data.dommagesReels = Number(arme.data.dommages);
armeData.data.dommagesReels = Number(armeData.data.dommages);
}
if (uneOuDeuxMains != containsSlash) {
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)");
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + armeData.name + " ne sont pas corrects (ie sous la forme X/Y)");
}
}
return arme;
return armeData;
}
static isArmeUtilisable(item) {
return item.type == 'arme' && (item.data.resistance > 0 || item.data.portee_courte>0);
static isArmeUtilisable(itemData) {
itemData = Misc.data(itemData);
return itemData.type == 'arme' && itemData.data.equipe && (itemData.data.resistance > 0 || itemData.data.portee_courte > 0);
}
static mainsNues(actorData={}) {
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 }));
}
static mainsNues(actorData = {}) {
const mainsNues = {
name: 'Mains nues',
data: {
@ -163,7 +187,7 @@ export class RdDItemArme extends Item {
}
};
if (actorData) {
mergeObject( mainsNues.data, actorData, {overwrite:false});
mergeObject(mainsNues.data, actorData, { overwrite: false });
}
return mainsNues
}

View File

@ -1,9 +1,45 @@
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
const competenceTroncs = [["Esquive", "Dague", "Corps à corps"],
["Epée à 1 main", "Epée à 2 mains", "Hache à 1 main", "Hache à 2 mains", "Lance", "Masse à 1 main", "Masse à 2 mains"]];
const competence_xp_par_niveau = [5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20, 30, 30, 40, 40, 60, 60, 100, 100, 100, 100, 100, 100, 100, 100, 100];
const competence_niveau_max = competence_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 }
];
/* -------------------------------------------- */
const categorieCompetences = {
"generale": { base: "-4", label: "Générales" },
"particuliere": { base: "-8", label: "Particulières" },
"specialisee": { base: "-11", label: "Spécialisées" },
"connaissance": { base: "-11", label: "Connaissances" },
"draconic": { base: "-11", label: "Draconics" },
"melee": { base: "-6", label: "Mêlée" },
"tir": { base: "-8", label: "Tir" },
"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 };
@ -20,11 +56,29 @@ const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item {
static actorCompendium(actorType) {
return compendiumCompetences[actorType];
}
static getCategorieCompetences() {
return categorieCompetences;
}
static getNiveauBase(category) {
return categorieCompetences[category].base;
}
static getLabelCategorie(category) {
return categorieCompetences[category].label;
}
static getEsquive(competences) {
return { name: 'Esquive', niveau: RdDItemCompetence.findCompetence(competences, 'Esquive')?.data.niveau ?? -6 };
}
/* -------------------------------------------- */
static isCompetenceArme(competence) {
switch (competence.data.categorie) {
case 'melee':
return competence.name.toLowerCase() != 'esquive';
return competence.name != 'Esquive';
case 'tir':
case 'lancer':
return true;
@ -55,11 +109,49 @@ export class RdDItemCompetence extends Item {
}
return false;
}
/* -------------------------------------------- */
static computeTotalXP(competences) {
const total = competences.map(c => RdDItemCompetence.computeXP(c))
.reduce((a, b) => a + b, 0);
const economieTronc = RdDItemCompetence.computeEconomieXPTronc(competences);
return total - economieTronc;
}
/* -------------------------------------------- */
static computeXP(competence) {
const factor = competence.name.includes('Thanatos') ? 2 : 1; // Thanatos compte double !
const xpNiveau = RdDItemCompetence.computeDeltaXP(competence.data.base, competence.data.niveau ?? competence.data.base);
const xp = competence.data.xp ?? 0;
const xpSort = competence.data.xp_sort ?? 0;
return factor * (xpNiveau + xp) + xpSort;
}
/* -------------------------------------------- */
static computeEconomieXPTronc(competences) {
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)))
.sort(Misc.ascending())
.splice(0, list.length-1) // prendre toutes les valeurs sauf l'une des plus élevées
.reduce(Misc.sum(), 0)
).reduce(Misc.sum(), 0);
}
/* -------------------------------------------- */
static computeDeltaXP(from, to) {
RdDItemCompetence._valideNiveau(from);
RdDItemCompetence._valideNiveau(to);
return competence_xp_cumul[to] - competence_xp_cumul[from];
}
/* -------------------------------------------- */
static computeCompetenceXPCost(competence) {
let xp = RdDItemCompetence.getDeltaXp(competence.data.base, competence.data.niveau ?? competence.data.base);
xp += competence.data.xp ?? 0;
if ( competence.name.includes('Thanatos') ) xp *= 2; /// Thanatos compte double !
if (competence.name.includes('Thanatos')) xp *= 2; /// Thanatos compte double !
xp += competence.data.xp_sort ?? 0;
return xp;
}
@ -69,19 +161,45 @@ export class RdDItemCompetence extends Item {
let economie = 0;
for (let troncList of competenceTroncs) {
let list = troncList.map(name => RdDItemCompetence.findCompetence(competences, name))
.sort( (c1, c2) => c2.data.niveau - c1.data.niveau); // tri du plus haut au plus bas
list.splice(0,1); // ignorer la plus élevée
.sort((c1, c2) => c2.data.niveau - c1.data.niveau); // tri du plus haut au plus bas
list.splice(0, 1); // ignorer la plus élevée
list.forEach(c => {
economie += RdDItemCompetence.getDeltaXp(c.data.base, Math.min(c.data.niveau, 0) );
economie += RdDItemCompetence.getDeltaXp(c.data.base, Math.min(c.data.niveau, 0));
});
}
return economie;
}
static levelUp(itemData) {
itemData.data.xpNext = RdDItemCompetence.getCompetenceNextXp(itemData.data.niveau);
itemData.data.isLevelUp = itemData.data.xp >= itemData.data.xpNext;
}
static isVisible(itemData) {
return Number(itemData.data.niveau) != RdDItemCompetence.getNiveauBase(itemData.data.categorie);
}
/* -------------------------------------------- */
static isNiveauBase(itemData) {
return Number(itemData.data.niveau) == RdDItemCompetence.getNiveauBase(itemData.data.categorie);
}
/* -------------------------------------------- */
static findCompetence(list, name) {
name = name.toLowerCase();
return list.find(item => item.name.toLowerCase() == name && (item.type == "competence" || item.type == "competencecreature"))
name = Grammar.toLowerCaseNoAccent(name);
const competences = list.filter(it => Grammar.toLowerCaseNoAccent(it.name).includes(name) && (it.type == "competence" || it.type == "competencecreature"));
if (competences.length == 0) {
return undefined;
}
let competence = competences.find(it => Grammar.toLowerCaseNoAccent(it.name) == name);
if (competence) {
return competence;
}
if (competences.length>1) {
const names = competences.map(it => it.name).reduce((a, b) => `${a}<br>${b}`);
ui.notifications.info(`Plusieurs compétences possibles:<br>${names}<br>La première sera choisie: ${competences[0].name}`);
}
return competences[0];
}
/* -------------------------------------------- */
@ -103,10 +221,26 @@ export class RdDItemCompetence extends Item {
}
/* -------------------------------------------- */
static _valideNiveau(niveau){
static _valideNiveau(niveau) {
if (niveau < -11 || niveau > competence_niveau_max) {
console.warn("Niveau en dehors des niveaux de compétences: [-11, " + competence_niveau_max + "]", niveau)
console.warn(`Niveau ${niveau} en dehors des niveaux de compétences: [-11, ${competence_niveau_max} ]`);
}
}
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const archetype = RdDItemCompetence.getLimitesArchetypes();
competences.forEach(it => {
let niveau = Math.max(0, it.data.niveau_archetype);
archetype[niveau] = archetype[niveau] ?? { "niveau": niveau, "nombreMax": 0, "nombre": 0 };
archetype[niveau].nombre = (archetype[niveau]?.nombre ?? 0) + 1;
});
return archetype;
}
/* -------------------------------------------- */
static getLimitesArchetypes() {
return duplicate(limitesArchetypes);
}
}

View File

@ -1,3 +1,5 @@
import { Misc } from "./misc.js";
/* -------------------------------------------- */
export class RdDItemCompetenceCreature extends Item {
@ -15,31 +17,34 @@ export class RdDItemCompetenceCreature extends Item {
}
/* -------------------------------------------- */
static toArme(item) {
if (RdDItemCompetenceCreature.isCompetenceAttaque(item)) {
let arme = { name: item.name, data: duplicate(item.data) };
static toArme(itemData) {
if (RdDItemCompetenceCreature.isCompetenceAttaque(itemData)) {
let arme = duplicate(Misc.data(itemData));
mergeObject(arme.data,
{
competence: item.name,
competence: arme.name,
resistance: 100,
equipe: true,
dommagesReels: arme.data.dommages,
penetration: 0,
force: 0,
rapide: true
});
return arme;
}
console.error("RdDItemCompetenceCreature.toArme(", item, ") : impossible de transformer l'Item en arme");
console.error("RdDItemCompetenceCreature.toArme(", itemData, ") : impossible de transformer l'Item en arme");
return undefined;
}
/* -------------------------------------------- */
static isCompetenceAttaque(item) {
return item.type == 'competencecreature' && item.data.iscombat;
static isCompetenceAttaque(itemData) {
itemData = Misc.data(itemData);
return itemData.type == 'competencecreature' && itemData.data.iscombat;
}
/* -------------------------------------------- */
static isCompetenceParade(item) {
return item.type == 'competencecreature' && item.data.isparade;
static isCompetenceParade(itemData) {
itemData = Misc.data(itemData);
return itemData.type == 'competencecreature' && itemData.data.isparade;
}
}

40
module/item-monnaie.js Normal file
View File

@ -0,0 +1,40 @@
const monnaiesData = [
{
_id: randomID(16), 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: "" }
},
{
_id: randomID(16), 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: "" }
},
{
_id: randomID(16), 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: "" }
},
{
_id: randomID(16), 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: "" }
}
]
export class Monnaie {
static monnaiesData() {
return monnaiesData;
}
static filtrerMonnaies(items) {
return items.filter(it => it.type == 'monnaie');
}
static monnaiesManquantes(items) {
const valeurs = Monnaie.filtrerMonnaies(items)
.map(it => it.data.valeur_deniers)
return duplicate(monnaiesData.filter(monnaie => !valeurs.find(v => v != monnaie.data.valeur_deniers)));
}
}

View File

@ -1,3 +1,4 @@
import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js";
/* -------------------------------------------- */
@ -7,7 +8,8 @@ export class RdDItem extends Item {
async postItem() {
console.log(this);
const properties = this[`_${this.data.type}ChatData`]();
let chatData = duplicate(this.data);
const itemData = Misc.data(this);
let chatData = duplicate(itemData);
chatData["properties"] = properties
//Check if the posted item should have availability/pay buttons
@ -49,17 +51,17 @@ export class RdDItem extends Item {
{
if (this.isOwned)
{
if (this.data.data.quantite == 0)
if (itemData.data.quantite == 0)
dialogResult[0] = -1
else if (this.data.data.quantite < dialogResult[0])
else if (itemData.data.quantite < dialogResult[0])
{
dialogResult[0] = this.data.data.quantite;
dialogResult[0] = itemData.data.quantite;
ui.notifications.notify(`Impossible de poster plus que ce que vous avez. La quantité à été réduite à ${dialogResult[0]}.`)
this.update({"data.quantite" : 0})
}
else {
ui.notifications.notify(`Quantité réduite par ${dialogResult[0]}.`)
this.update({"data.quantite" : this.data.data.quantite - dialogResult[0]})
this.update({"data.quantite" : itemData.data.quantite - dialogResult[0]})
}
}
}
@ -84,7 +86,7 @@ export class RdDItem extends Item {
chatData.jsondata = JSON.stringify(
{
compendium : "postedItem",
payload: this.data,
payload: itemData,
});
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-item.html', chatData).then(html => {
@ -95,217 +97,217 @@ export class RdDItem extends Item {
/* -------------------------------------------- */
_objetChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Encombrement</b>: ${data.encombrement}`
`<b>Encombrement</b>: ${rddData.encombrement}`
]
return properties;
}
/* -------------------------------------------- */
_armeChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Compétence</b>: ${data.competence}`,
`<b>Dommages</b>: ${data.dommages}`,
`<b>Force minimum</b>: ${data.force}`,
`<b>Resistance</b>: ${data.resistance}`,
`<b>Encombrement</b>: ${data.encombrement}`
`<b>Compétence</b>: ${rddData.competence}`,
`<b>Dommages</b>: ${rddData.dommages}`,
`<b>Force minimum</b>: ${rddData.force}`,
`<b>Resistance</b>: ${rddData.resistance}`,
`<b>Encombrement</b>: ${rddData.encombrement}`
]
return properties;
}
/* -------------------------------------------- */
_conteneurChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Capacité</b>: ${data.capacite} Enc.`,
`<b>Encombrement</b>: ${data.encombrement}`
`<b>Capacité</b>: ${rddData.capacite} Enc.`,
`<b>Encombrement</b>: ${rddData.encombrement}`
]
return properties;
}
/* -------------------------------------------- */
_munitionChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Encombrement</b>: ${data.encombrement}`
`<b>Encombrement</b>: ${rddData.encombrement}`
]
return properties;
}
/* -------------------------------------------- */
_armureChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Protection</b>: ${data.protection}`,
`<b>Détérioration</b>: ${data.deterioration}`,
`<b>Malus armure</b>: ${data.malus}`,
`<b>Encombrement</b>: ${data.encombrement}`
`<b>Protection</b>: ${rddData.protection}`,
`<b>Détérioration</b>: ${rddData.deterioration}`,
`<b>Malus armure</b>: ${rddData.malus}`,
`<b>Encombrement</b>: ${rddData.encombrement}`
]
return properties;
}
/* -------------------------------------------- */
_competenceChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Catégorie</b>: ${data.categorie}`,
`<b>Niveau</b>: ${data.niveau}`,
`<b>Caractéristique par défaut</b>: ${data.carac_defaut}`,
`<b>XP</b>: ${data.xp}`
`<b>Catégorie</b>: ${rddData.categorie}`,
`<b>Niveau</b>: ${rddData.niveau}`,
`<b>Caractéristique par défaut</b>: ${rddData.carac_defaut}`,
`<b>XP</b>: ${rddData.xp}`
]
return properties;
}
/* -------------------------------------------- */
_competencecreatureChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Catégorie</b>: ${data.categorie}`,
`<b>Niveau</b>: ${data.niveau}`,
`<b>Caractéristique</b>: ${data.carac_value}`,
`<b>XP</b>: ${data.xp}`
`<b>Catégorie</b>: ${rddData.categorie}`,
`<b>Niveau</b>: ${rddData.niveau}`,
`<b>Caractéristique</b>: ${rddData.carac_value}`,
`<b>XP</b>: ${rddData.xp}`
]
return properties;
}
/* -------------------------------------------- */
_sortChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Draconic</b>: ${data.draconic}`,
`<b>Difficulté</b>: ${data.difficulte}`,
`<b>Case TMR</b>: ${data.caseTMR}`,
`<b>Points de Rêve</b>: ${data.ptreve}`
`<b>Draconic</b>: ${rddData.draconic}`,
`<b>Difficulté</b>: ${rddData.difficulte}`,
`<b>Case TMR</b>: ${rddData.caseTMR}`,
`<b>Points de Rêve</b>: ${rddData.ptreve}`
]
return properties;
}
/* -------------------------------------------- */
_herbeChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Milieu</b>: ${data.milieu}`,
`<b>Rareté</b>: ${data.rarete}`,
`<b>Catégorie</b>: ${data.categorie}`,
`<b>Milieu</b>: ${rddData.milieu}`,
`<b>Rareté</b>: ${rddData.rarete}`,
`<b>Catégorie</b>: ${rddData.categorie}`,
]
return properties;
}
/* -------------------------------------------- */
_ingredientChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Milieu</b>: ${data.milieu}`,
`<b>Rareté</b>: ${data.rarete}`,
`<b>Catégorie</b>: ${data.categorie}`,
`<b>Milieu</b>: ${rddData.milieu}`,
`<b>Rareté</b>: ${rddData.rarete}`,
`<b>Catégorie</b>: ${rddData.categorie}`,
]
return properties;
}
/* -------------------------------------------- */
_tacheChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Caractéristique</b>: ${data.carac}`,
`<b>Compétence</b>: ${data.competence}`,
`<b>Périodicité</b>: ${data.periodicite}`,
`<b>Fatigue</b>: ${data.fatigue}`,
`<b>Difficulté</b>: ${data.difficulte}`,
`<b>Points de Tâche</b>: ${data.points_de_tache}`,
`<b>Points de Tâche atteints</b>: ${data.points_de_tache_courant}`
`<b>Caractéristique</b>: ${rddData.carac}`,
`<b>Compétence</b>: ${rddData.competence}`,
`<b>Périodicité</b>: ${rddData.periodicite}`,
`<b>Fatigue</b>: ${rddData.fatigue}`,
`<b>Difficulté</b>: ${rddData.difficulte}`,
`<b>Points de Tâche</b>: ${rddData.points_de_tache}`,
`<b>Points de Tâche atteints</b>: ${rddData.points_de_tache_courant}`
]
return properties;
}
/* -------------------------------------------- */
_livreChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Compétence</b>: ${data.competence}`,
`<b>Auteur</b>: ${data.auteur}`,
`<b>Difficulté</b>: ${data.difficulte}`,
`<b>Points de Tâche</b>: ${data.points_de_tache}`,
`<b>Encombrement</b>: ${data.encombrement}`
`<b>Compétence</b>: ${rddData.competence}`,
`<b>Auteur</b>: ${rddData.auteur}`,
`<b>Difficulté</b>: ${rddData.difficulte}`,
`<b>Points de Tâche</b>: ${rddData.points_de_tache}`,
`<b>Encombrement</b>: ${rddData.encombrement}`
]
return properties;
}
/* -------------------------------------------- */
_potionChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Rareté</b>: ${data.rarete}`,
`<b>Catégorie</b>: ${data.categorie}`,
`<b>Encombrement</b>: ${data.encombrement}`,
`<b>Rareté</b>: ${rddData.rarete}`,
`<b>Catégorie</b>: ${rddData.categorie}`,
`<b>Encombrement</b>: ${rddData.encombrement}`,
]
return properties;
}
/* -------------------------------------------- */
_queueChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Refoulement</b>: ${data.refoulement}`
`<b>Refoulement</b>: ${rddData.refoulement}`
]
return properties;
}
/* -------------------------------------------- */
_ombreChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Refoulement</b>: ${data.refoulement}`
`<b>Refoulement</b>: ${rddData.refoulement}`
]
return properties;
}
/* -------------------------------------------- */
_souffleChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [];
return properties;
}
/* -------------------------------------------- */
_teteChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [];
return properties;
}
/* -------------------------------------------- */
_tarotChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Concept</b>: ${data.concept}`,
`<b>Aspect</b>: ${data.aspect}`,
`<b>Concept</b>: ${rddData.concept}`,
`<b>Aspect</b>: ${rddData.aspect}`,
]
return properties;
}
/* -------------------------------------------- */
_nombreastralChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Valeur</b>: ${data.value}`,
`<b>Jour</b>: ${data.jourlabel}`,
`<b>Valeur</b>: ${rddData.value}`,
`<b>Jour</b>: ${rddData.jourlabel}`,
]
return properties;
}
/* -------------------------------------------- */
_monnaieChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Valeur en Deniers</b>: ${data.valeur_deniers}`,
`<b>Encombrement</b>: ${data.encombrement}`
`<b>Valeur en Deniers</b>: ${rddData.valeur_deniers}`,
`<b>Encombrement</b>: ${rddData.encombrement}`
]
return properties;
}
/* -------------------------------------------- */
_meditationChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Thème</b>: ${data.theme}`,
`<b>Compétence</b>: ${data.competence}`,
`<b>Support</b>: ${data.support}`,
`<b>Heure</b>: ${data.heure}`,
`<b>Purification</b>: ${data.purification}`,
`<b>Vêture</b>: ${data.veture}`,
`<b>Comportement</b>: ${data.comportement}`,
`<b>Case TMR</b>: ${data.tmr}`
`<b>Thème</b>: ${rddData.theme}`,
`<b>Compétence</b>: ${rddData.competence}`,
`<b>Support</b>: ${rddData.support}`,
`<b>Heure</b>: ${rddData.heure}`,
`<b>Purification</b>: ${rddData.purification}`,
`<b>Vêture</b>: ${rddData.veture}`,
`<b>Comportement</b>: ${rddData.comportement}`,
`<b>Case TMR</b>: ${rddData.tmr}`
]
return properties;
}
/* -------------------------------------------- */
_casetmrChatData() {
const data = duplicate(this.data.data);
const rddData = Misc.data(this).data;
let properties = [
`<b>Coordonnée</b>: ${data.coord}`,
`<b>Spécificité</b>: ${data.specific}`
`<b>Coordonnée</b>: ${rddData.coord}`,
`<b>Spécificité</b>: ${rddData.specific}`
]
return properties;
}

View File

@ -3,6 +3,7 @@ import { RdDUtility } from "./rdd-utility.js";
import { RdDItem } from "./item-rdd.js";
import { RdDAlchimie } from "./rdd-alchimie.js";
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
/**
* Extend the basic ItemSheet with some very simple modifications
@ -30,7 +31,7 @@ export class RdDItemSheet extends ItemSheet {
{
class: "post",
icon: "fas fa-comment",
onclick: ev => new RdDItem(this.item.data).postItem()
onclick: ev => new RdDItem(Misc.data(this.item)).postItem()
})
return buttons
}
@ -47,26 +48,26 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */
async getData() {
let data = super.getData();
data.categorieCompetences = RdDUtility.getCategorieCompetences();
if ( data.item.type == 'tache' || data.item.type == 'livre' || data.item.type == 'meditation' || data.item.type == 'oeuvre') {
data.caracList = duplicate(game.system.model.Actor.personnage.carac);
data.competences = await RdDUtility.loadCompendiumNames( 'foundryvtt-reve-de-dragon.competences' );
let formData = super.getData();
formData.categorieCompetences = RdDItemCompetence.getCategorieCompetences();
if ( formData.item.type == 'tache' || formData.item.type == 'livre' || formData.item.type == 'meditation' || formData.item.type == 'oeuvre') {
formData.caracList = duplicate(game.system.model.Actor.personnage.carac);
formData.competences = await RdDUtility.loadCompendiumNames( 'foundryvtt-reve-de-dragon.competences' );
}
if (data.item.type == 'arme') {
data.competences = await RdDUtility.loadCompendium( 'foundryvtt-reve-de-dragon.competences', it => RdDItemCompetence.isCompetenceArme(it));
if (formData.item.type == 'arme') {
formData.competences = await RdDUtility.loadCompendium( 'foundryvtt-reve-de-dragon.competences', it => RdDItemCompetence.isCompetenceArme(it));
}
if ( data.item.type == 'recettealchimique' ) {
RdDAlchimie.processManipulation(data.item, this.actor && this.actor._id );
if ( formData.item.type == 'recettealchimique' ) {
RdDAlchimie.processManipulation(formData.item, this.actor && this.actor._id );
}
if ( this.actor ) {
data.isOwned = true;
data.actorId = this.actor._id;
formData.isOwned = true;
formData.actorId = this.actor._id;
}
data.bonusCaseList = RdDItemSort.getBonusCaseList(data, true);
data.isGM = game.user.isGM; // Pour verrouiller certaines éditions
formData.bonusCaseList = RdDItemSort.getBonusCaseList(formData, true);
formData.isGM = game.user.isGM; // Pour verrouiller certaines éditions
return data;
return formData;
}
/* -------------------------------------------- */
@ -101,7 +102,7 @@ export class RdDItemSheet extends ItemSheet {
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 de trouver un acteur pour réaliser cette tâche alchimique.");
}
});
@ -111,7 +112,7 @@ export class RdDItemSheet extends ItemSheet {
async _onClickSelectCategorie(event) {
event.preventDefault();
let level = RdDUtility.getLevelCategory(event.currentTarget.value);
let level = RdDItemCompetence.getNiveauBase(event.currentTarget.value);
this.object.data.data.base = level;
$("#base").val( level );
}

View File

@ -6,26 +6,42 @@
export class Misc {
static isFunction(v) {
return v && {}.toString.call(v) === '[object Function]';
}
}
static upperFirst(text) {
return text.charAt(0).toUpperCase() + text.slice(1);
}
static toSignedString(number){
static toSignedString(number) {
const value = parseInt(number)
const isPositiveNumber = value != NaN && value > 0;
return isPositiveNumber ? "+"+number : number
return isPositiveNumber ? "+" + number : number
}
static sum() {
return (a, b) => a + b;
}
static ascending(orderFunction = x=>x) {
return (a, b) => Misc.sortingBy(orderFunction(a), orderFunction(b));
}
static descending(orderFunction = x=>x) {
return (a, b) => Misc.sortingBy(orderFunction(b), orderFunction(a));
}
static sortingBy(a, b) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
}
/**
* 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
*/
static toInt(value)
{
if (value == undefined)
{
static toInt(value) {
if (value == undefined) {
return 0;
}
const parsed = parseInt(value);
@ -47,6 +63,17 @@ export class Misc {
return itemsBy;
}
static classifyFirst(items, classifier) {
let itemsBy = {};
for (const item of items) {
const classification = classifier(item);
if (!itemsBy[classification]) {
itemsBy[classification] = item;
}
}
return itemsBy;
}
static classifyInto(itemsBy, items, classifier = it => it.type, transform = it => it) {
for (const item of items) {
const classification = classifier(item);
@ -67,4 +94,36 @@ export class Misc {
return [...new Set(array)];
}
static actorData(actor) {
return Misc.data(actor);
}
static itemData(item) {
return Misc.data(item);
}
static data(it) {
if (it instanceof Item) {
return it.data;
}
if (it instanceof Actor) {
return it.data;
}
return it;
}
static templateData(it) {
return Misc.data(it)?.data ?? {}
}
static connectedGMOrUser(ownerId = undefined) {
if (ownerId && game.user.id == ownerId){
return ownerId;
}
return (game.user.isGM ? game.user.id : game.users.entities.find(u => u.isGM && u.active)?.id) ?? game.user.id;
}
static isElectedUser() {
return game.user.id == Misc.connectedGMOrUser();
}
}

View File

@ -50,7 +50,7 @@ const poesieHautReve = [
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi se cuccèdent les Jours et les Ages.
extrait: `Ainsi se succèdent les Jours et les Ages.
<br>Les jours des Dragons sont les Ages des Hommes.`
},
{

44
module/poetique.txt Normal file
View File

@ -0,0 +1,44 @@
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

@ -49,20 +49,13 @@ export class RdDAlchimie {
}
/* -------------------------------------------- */
static getDifficulte( aspects ) {
let aspectsArray = aspects.split('-');
let diff = 0;
let nbDifferent = 0;
let aspectsHash = {}
for (let colconst of aspectsArray) {
if ( aspectsHash[colconst] ){ // Deja present, augmente difficulté de 1
diff -= 1;
} else {
nbDifferent++;
aspectsHash[colconst] = colconst; // Keep track
}
static getDifficulte(aspects) {
let elements = aspects.split('-');
let composantes = elements.length;
let distincts = Object.keys(Misc.classifyFirst(elements, it => it)).length;
if (distincts == 1) {
composantes--;
}
diff = diff - ((nbDifferent>1) ? nbDifferent : 0); // Ca doit marcher ....
return Math.min(0, diff); // Pour être sur
return Math.min(0, -composantes);
}
}

View File

@ -9,6 +9,7 @@ export class RdDAstrologieEditeur extends Dialog {
constructor(html, calendrier, calendrierData) {
let myButtons = {
resetButton: { label: "Re-tirer les nombres astraux", callback: html => this.resetNombreAstraux() },
saveButton: { label: "Fermer", callback: html => this.fillData() }
};
@ -21,6 +22,14 @@ export class RdDAstrologieEditeur extends Dialog {
this.updateData( calendrierData );
}
/* -------------------------------------------- */
resetNombreAstraux() {
game.system.rdd.calendrier.resetNombreAstral();
game.system.rdd.calendrier.rebuildListeNombreAstral();
game.system.rdd.calendrier.showAstrologieEditor();
}
/* -------------------------------------------- */
fillData( ) {
}

View File

@ -1,4 +1,5 @@
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
/**
@ -61,7 +62,7 @@ export class RdDAstrologieJoueur extends Dialog {
/* -------------------------------------------- */
requestJetAstrologie( ) {
let data = { id: this.actor.data._id,
carac_vue: this.actor.data.data.carac['vue'].value,
carac_vue: Misc.data(this.actor).data.carac['vue'].value,
etat: this.dataNombreAstral.etat,
astrologie: this.dataNombreAstral.astrologie,
conditions: $("#diffConditions").val(),

View File

@ -8,56 +8,58 @@ import { Grammar } from "./grammar.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: 'S', saison: "printemps", heure: 1, icon: 'hd02.svg' },
"faucon": { label: "Faucon", lettreFont: 'f', saison: "printemps", heure: 2, icon: 'hd03.svg' },
"couronne": { label: "Couronne", lettreFont: 'C', 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' }
};
const saisonsDef = { "printemps": { label: "Printemps"},
"ete": { label: "Eté"},
"automne": { label: "Automne"},
"hiver": { label: "Hiver"}
};
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' }
};
const saisonsDef = {
"printemps": { label: "Printemps" },
"ete": { label: "Eté" },
"automne": { label: "Automne" },
"hiver": { label: "Hiver" }
};
const RDD_JOUR_PAR_MOIS = 28;
const MAX_NOMBRE_ASTRAL = 12;
/* -------------------------------------------- */
export class RdDCalendrier extends Application {
/* -------------------------------------------- */
/* -------------------------------------------- */
async initCalendrier() {
// Calendrier
this.calendrier = duplicate(game.settings.get("foundryvtt-reve-de-dragon", "calendrier"));
console.log("CALENDRIER", this.calendrier);
if ( this.calendrier == undefined || this.calendrier.moisRdD == undefined) {
this.calendrier.heureRdD = 0; // Index dans heuresList
if (this.calendrier == undefined || this.calendrier.moisRdD == undefined) {
this.calendrier.heureRdD = 0; // Index dans heuresList
this.calendrier.minutesRelative = 0;
this.calendrier.moisRdD = 0; // Index dans heuresList
this.calendrier.jour = 0;
if ( game.user.isGM) { // Uniquement si GM
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", this.calendrier );
this.calendrier.moisRdD = 0; // Index dans heuresList
this.calendrier.jour = 0;
if (game.user.isGM) { // Uniquement si GM
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", this.calendrier);
}
}
// position
this.calendrierPos = duplicate(game.settings.get("foundryvtt-reve-de-dragon", "calendrier-pos"));
if ( this.calendrierPos == undefined || this.calendrierPos.top == undefined) {
this.calendrierPos.top = 200;
this.calendrierPos.left = 200;
if ( game.user.isGM) { // Uniquement si GM
game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", this.calendrierPos );
if (this.calendrierPos == undefined || this.calendrierPos.top == undefined) {
this.calendrierPos.top = 200;
this.calendrierPos.left = 200;
if (game.user.isGM) { // Uniquement si GM
game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", this.calendrierPos);
}
}
// nombre astral
if ( game.user.isGM) {
if (game.user.isGM) {
this.listeNombreAstral = this._loadListNombreAstral();
this.rebuildListeNombreAstral(); // Ensure always up-to-date
}
@ -79,11 +81,11 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
getDateFromIndex( index = undefined ) {
if ( !index) index = this.getCurrentDayIndex();
getDateFromIndex(index = undefined) {
if (!index) index = this.getCurrentDayIndex();
let month = Math.floor(index / 28);
let day = (index - (month*28)) + 1;
return day+" "+heuresList[month];
let day = (index - (month * 28)) + 1;
return day + " " + heuresList[month];
}
/* -------------------------------------------- */
@ -92,15 +94,15 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
getCurrentDayIndex( ) {
getCurrentDayIndex() {
return (this.calendrier.moisRdD * 28) + this.calendrier.jour;
}
/* -------------------------------------------- */
getJoursSuivants( num) {
getJoursSuivants(num) {
let jours = [];
let index = this.getCurrentDayIndex();
for (let i=0; i<num; i++) {
for (let i = 0; i < num; i++) {
jours[i] = { label: this.getDateFromIndex(index), index: index };
index += 1;
}
@ -123,21 +125,27 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
getNombreAstral( indexDate ) {
getNombreAstral(indexDate) {
const liste = this.listeNombreAstral ?? this._loadListNombreAstral();
let astralData = liste.find( (nombreAstral, i) => nombreAstral.index == indexDate );
if (! astralData?.nombreAstral ) {
let astralData = liste.find((nombreAstral, i) => nombreAstral.index == indexDate);
if (!astralData?.nombreAstral) {
this.rebuildListeNombreAstral();
astralData = liste.find( (nombreAstral, i) => nombreAstral.index == indexDate );
astralData = liste.find((nombreAstral, i) => nombreAstral.index == indexDate);
}
return astralData?.nombreAstral ?? "N/A";
}
/* -------------------------------------------- */
resetNombreAstral( ) {
this.listeNombreAstral = [];
game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral);
}
/* -------------------------------------------- */
rebuildListeNombreAstral() {
let jourCourant = this.getCurrentDayIndex();
let jourFin = jourCourant + 12;
let newList = [0,1,2,3,4,5,6,7,8,9,10,11].map( i => this.ajouterNombreAstral(jourCourant + i));
let newList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map(i => this.ajouterNombreAstral(jourCourant + i));
if (this.listeNombreAstral) {
for (const na of this.listeNombreAstral) {
if (na && na.index >= jourCourant && na.index < jourFin) {
@ -145,34 +153,49 @@ export class RdDCalendrier extends Application {
}
}
}
game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral );
this.listeNombreAstral = newList;
game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral);
}
/* -------------------------------------------- */
incrementTime(minute = 0) {
this.calendrier.minutesRelative += minute;
if (this.calendrier.minutesRelative >= 120 ) {
onCalendarButton(ev) {
ev.preventDefault();
const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
if (calendarAvance) {
this.incrementTime(Number(calendarAvance.value));
}
else if (calendarSet) {
this.positionnerHeure(Number(calendarSet.value));
}
this.updateDisplay();
}
/* -------------------------------------------- */
incrementTime(minutes = 0) {
this.calendrier.minutesRelative += minutes;
if (this.calendrier.minutesRelative >= 120) {
this.calendrier.minutesRelative -= 120;
this.calendrier.heureRdD += 1;
}
if ( this.calendrier.heureRdD > 11 ) {
if (this.calendrier.heureRdD > 11) {
this.calendrier.heureRdD -= 12;
this.incrementerJour();
}
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier) );
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier));
// Notification aux joueurs
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_sync_time",
data: duplicate(this.calendrier)
} );
});
}
/* -------------------------------------------- */
incrementerJour( ) {
incrementerJour() {
this.calendrier.jour += 1;
if ( this.calendrier.jour >= RDD_JOUR_PAR_MOIS) {
if (this.calendrier.jour >= RDD_JOUR_PAR_MOIS) {
this.calendrier.jour -= RDD_JOUR_PAR_MOIS;
if ( this.calendrier.jour <= 0)
if (this.calendrier.jour <= 0)
this.calendrier.jour = 0;
this.calendrier.moisRdD += 1;
// Reconstruire les nombres astraux
@ -181,97 +204,95 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
syncPlayerTime( calendrier ) {
syncPlayerTime(calendrier) {
this.calendrier = duplicate(calendrier); // Local copy update
this.updateDisplay(); // Then update
this.updateDisplay();
}
/* -------------------------------------------- */
positionnerHeure( indexHeure ) {
if ( indexHeure <= this.calendrier.heureRdD )
positionnerHeure(indexHeure) {
if (indexHeure <= this.calendrier.heureRdD)
this.incrementerJour();
this.calendrier.heureRdD = indexHeure;
this.calendrier.minutesRelative = 0;
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier) );
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier));
}
/* -------------------------------------------- */
fillCalendrierData( data = {} ) {
let moisKey = heuresList[this.calendrier.moisRdD];
fillCalendrierData(formData = {}) {
let moisKey = heuresList[this.calendrier.moisRdD];
let heureKey = heuresList[this.calendrier.heureRdD];
const mois = heuresDef[moisKey];
const heure = heuresDef[heureKey];
//console.log(moisKey, heureKey);
data.heureKey = heureKey;
data.moisKey = moisKey;
data.jourMois = this.calendrier.jour + 1;
data.nomMois = mois.label; // heures et mois nommés identiques
data.iconMois = dossierIconesHeures + mois.icon;
data.nomHeure = heure.label;
data.iconHeure = dossierIconesHeures + heure.icon;
data.nomSaison = saisonsDef[mois.saison].label;
data.minutesRelative = this.calendrier.minutesRelative;
data.isGM = game.user.isGM;
return data;
formData.heureKey = heureKey;
formData.moisKey = moisKey;
formData.jourMois = this.calendrier.jour + 1;
formData.nomMois = mois.label; // heures et mois nommés identiques
formData.iconMois = dossierIconesHeures + mois.icon;
formData.nomHeure = heure.label;
formData.iconHeure = dossierIconesHeures + heure.icon;
formData.nomSaison = saisonsDef[mois.saison].label;
formData.heureRdD = this.calendrier.heureRdD;
formData.minutesRelative = this.calendrier.minutesRelative;
formData.isGM = game.user.isGM;
return formData;
}
/* -------------------------------------------- */
getLectureAstrologieDifficulte( dateIndex ) {
getLectureAstrologieDifficulte(dateIndex) {
let indexNow = this.getCurrentDayIndex();
let diffDay = dateIndex - indexNow;
return - Math.floor(diffDay / 2);
}
/* -------------------------------------------- */
async requestNombreAstral( request) {
if ( game.user.isGM) { // Only GM
console.log( request );
let jourDiff = this.getLectureAstrologieDifficulte( request.date);
async requestNombreAstral(request) {
if (game.user.isGM) { // Only GM
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 rolled = await RdDResolutionTable.rollData({
let rollData= {
caracValue: request.carac_vue,
finalLevel: niveau,
showDice: false});
let nbAstral = this.getNombreAstral( request.date );
let nbAstralFaux = nbAstral;
showDice: false
};
await RdDResolutionTable.rollData(rollData);
let nbAstral = this.getNombreAstral(request.date);
request.rolled = rollData.rolled;
request.isValid = true;
request.rolled = rolled;
if ( !rolled .isSuccess ) {
if (!request.rolled.isSuccess) {
request.isValid = false;
while ( nbAstralFaux == nbAstral ) {
nbAstralFaux = new Roll("1d12").roll().total;
}
nbAstral = nbAstralFaux;
let nbAstralFaux = new Roll("1d11").evaluate().total;
nbAstral = nbAstral==nbAstralFaux ? 12 : nbAstralFaux;
// Mise à jour des nombres astraux du joueur
let astralData = this.listeNombreAstral.find( (nombreAstral, i) => nombreAstral.index == request.date );
astralData.valeursFausses.push( {actorId: request.id, nombreAstral: nbAstralFaux});
game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral );
let astralData = this.listeNombreAstral.find((nombreAstral, i) => nombreAstral.index == request.date);
astralData.valeursFausses.push({ actorId: request.id, nombreAstral: nbAstralFaux });
game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral);
}
request.nbAstral = nbAstral;
if ( game.user.isGM) {
RdDUtility.responseNombreAstral( request );
} else {
if (game.user.isGM) {
RdDUtility.responseNombreAstral(request);
} else {
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_response_nombre_astral",
data: request
} );
});
}
}
}
/* -------------------------------------------- */
getAjustementAstrologique(heureNaissance, name='inconnu')
{
getAjustementAstrologique(heureNaissance, name = 'inconnu') {
let heure = Grammar.toLowerCaseNoAccent(heureNaissance);
if (heure && heuresDef[heure]) {
let hn = heuresDef[heure].heure;
let chiffreAstral = this.getCurrentNombreAstral();
let heureCourante = this.calendrier.heureRdD;
let ecartChance = (hn + chiffreAstral - heureCourante)%12;
switch (ecartChance)
{
let ecartChance = (hn + chiffreAstral - heureCourante) % 12;
switch (ecartChance) {
case 0: return 4;
case 4: case 8: return 2;
case 6: return -4;
@ -286,12 +307,12 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
getData() {
let data = super.getData();
let formData = super.getData();
this.fillCalendrierData(data);
this.fillCalendrierData(formData);
this.setPos( this.calendrierPos );
return data;
this.setPos(this.calendrierPos);
return formData;
}
/* -------------------------------------------- */
@ -301,8 +322,8 @@ export class RdDCalendrier extends Application {
let elmnt = document.getElementById("calendar-time-container");
if (elmnt) {
elmnt.style.bottom = null;
let xPos = (pos.left) > window.innerWidth ? window.innerWidth-200 : pos.left;
let yPos = (pos.top) > window.innerHeight-20 ? window.innerHeight-100 : pos.top;
let xPos = (pos.left) > window.innerWidth ? window.innerWidth - 200 : pos.left;
let yPos = (pos.top) > window.innerHeight - 20 ? window.innerHeight - 100 : pos.top;
elmnt.style.top = (yPos) + "px";
elmnt.style.left = (xPos) + "px";
resolve();
@ -316,125 +337,101 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
updateDisplay() {
let data = this.fillCalendrierData( );
// Rebuild data
let data = this.fillCalendrierData();
// Rebuild data
let dateHTML = `Jour ${data.jourMois} de ${data.nomMois} (${data.nomSaison})`;
if (game.user.isGM) {
dateHTML = dateHTML + " - NA: "+this.getCurrentNombreAstral();
dateHTML = dateHTML + " - NA: " + this.getCurrentNombreAstral();
}
for (let handle of document.getElementsByClassName("calendar-date-rdd")) {
handle.innerHTML = dateHTML;
}
for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
heure.innerHTML = data.nomHeure;
}
for (const minute of document.getElementsByClassName("calendar-time-disp")) {
minute.innerHTML = `${data.minutesRelative} minutes`;
}
for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
heureImg.src = data.iconHeure;
}
document.getElementById("calendar--move-handle").innerHTML = dateHTML;
document.getElementById("calendar-heure-texte").innerHTML = `${data.nomHeure}`;
document.getElementById("calendar-time").innerHTML = `${data.minutesRelative} min.`;
document.getElementById("calendar-heure-img").src = data.iconHeure;
}
/* -------------------------------------------- */
saveEditeur( calendrierData ) {
saveEditeur(calendrierData) {
this.calendrier.minutesRelative = Number(calendrierData.minutesRelative);
this.calendrier.jour = Number(calendrierData.jourMois) - 1;
this.calendrier.moisRdD = heuresList.findIndex(mois => mois === calendrierData.moisKey);
this.calendrier.heureRdD = heuresList.findIndex(heure => heure === calendrierData.heureKey);; // Index dans heuresList
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier) );
this.calendrier.jour = Number(calendrierData.jourMois) - 1;
this.calendrier.moisRdD = heuresList.findIndex(mois => mois === calendrierData.moisKey);
this.calendrier.heureRdD = heuresList.findIndex(heure => heure === calendrierData.heureKey);; // Index dans heuresList
game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier));
this.rebuildListeNombreAstral();
this.updateDisplay();
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_sync_time",
data: duplicate(this.calendrier)
});
}
/* -------------------------------------------- */
async showCalendarEditor() {
let calendrierData = duplicate( this.fillCalendrierData( ) );
if ( this.editeur == undefined ) {
let calendrierData = duplicate(this.fillCalendrierData());
if (this.editeur == undefined) {
calendrierData.jourMoisOptions = Array(28).fill().map((item, index) => 1 + index);
calendrierData.heuresOptions = [0, 1];
calendrierData.minutesOptions = Array(120).fill().map((item, index) => 0 + index);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData );
this.editeur = new RdDCalendrierEditeur(html, this, calendrierData )
calendrierData.heuresOptions = [0, 1];
calendrierData.minutesOptions = Array(120).fill().map((item, index) => 0 + index);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData);
this.editeur = new RdDCalendrierEditeur(html, this, calendrierData)
}
this.editeur.updateData( calendrierData );
this.editeur.updateData(calendrierData);
this.editeur.render(true);
}
/* -------------------------------------------- */
async showAstrologieEditor() {
let calendrierData = duplicate( this.fillCalendrierData( ) );
let astrologieArray = [];
for (let astralData of this.listeNombreAstral ) {
astralData.humanDate = this.getDateFromIndex( astralData.index );
let calendrierData = duplicate(this.fillCalendrierData());
let astrologieArray = [];
for (let astralData of this.listeNombreAstral) {
astralData.humanDate = this.getDateFromIndex(astralData.index);
for (let vf of astralData.valeursFausses) {
let actor = game.actors.get( vf.actorId);
console.log(vf.actorId, actor );
let actor = game.actors.get(vf.actorId);
console.log(vf.actorId, actor);
vf.actorName = (actor) ? actor.name : "Inconnu";
}
astrologieArray.push( duplicate(astralData ) );
astrologieArray.push(duplicate(astralData));
}
//console.log("ASTRO", astrologieArray);
calendrierData.astrologieData = astrologieArray;
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-astrologie-template.html', calendrierData );
let astrologieEditeur = new RdDAstrologieEditeur(html, this, calendrierData )
astrologieEditeur.updateData( calendrierData );
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-astrologie-template.html', calendrierData);
let astrologieEditeur = new RdDAstrologieEditeur(html, this, calendrierData)
astrologieEditeur.updateData(calendrierData);
astrologieEditeur.render(true);
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen($(".gm-only"), game.user.isGM);
this.updateDisplay();
html.find('#calendar-btn-1min').click(ev => {
ev.preventDefault();
this.incrementTime(1);
this.updateDisplay();
});
html.find('#calendar-btn-5min').click(ev => {
ev.preventDefault();
this.incrementTime(5);
this.updateDisplay();
});
html.find('#calendar-btn-10min').click(ev => {
ev.preventDefault();
this.incrementTime(10);
this.updateDisplay();
});
html.find('#calendar-btn-20min').click(ev => {
ev.preventDefault();
this.incrementTime(20);
this.updateDisplay();
});
html.find('#calendar-btn-30min').click(ev => {
ev.preventDefault();
this.incrementTime(30);
this.updateDisplay();
});
html.find('#calendar-btn-1heure').click(ev => {
ev.preventDefault();
this.incrementTime(120);
this.updateDisplay();
});
html.find('#calendar-btn-vaisseau').click(ev => {
ev.preventDefault();
this.positionnerHeure(0); // 0 -> vaisseau
this.updateDisplay();
});
html.find('#calendar-btn-lyre').click(ev => {
ev.preventDefault();
this.positionnerHeure(6); // 6 -> lyre
this.updateDisplay();
});
html.find('#calendar-btn-edit').click(ev => {
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 => {
html.find('.astrologie-btn-edit').click(ev => {
ev.preventDefault();
this.showAstrologieEditor();
});
html.find('#calendar--move-handle').mousedown(ev => {
html.find('#calendar-move-handle').mousedown(ev => {
ev.preventDefault();
ev = ev || window.event;
let isRightMB = false;
@ -479,23 +476,27 @@ export class RdDCalendrier extends Application {
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)
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)){
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.top = yPos;
game.system.rdd.calendrier.calendrierPos.left = xPos;
game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos) );
if (game.user.isGM) {
game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
}
}
} else if(isRightMB){
game.system.rdd.calendrier.calendrierPos.top = 200;
} else if (isRightMB) {
game.system.rdd.calendrier.calendrierPos.top = 200;
game.system.rdd.calendrier.calendrierPos.left = 200;
game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos) );
if (game.user.isGM) {
game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
}
this.setPos(game.system.rdd.calendrier.calendrierPos);
}
});

View File

@ -1,7 +1,66 @@
import { Grammar } from "./grammar.js";
const tableCaracDerivee = {
// xp: coût pour passer du niveau inférieur à ce niveau
1: { xp: 3, poids: "moins de 1kg", plusdom: -5, sconst: 0.5, sust: 0.1 },
2: { xp: 3, poids: "1-5", plusdom: -4, sconst: 0.5, sust: 0.3 },
3: { xp: 4, poids: "6-10", plusdom: -3, sconst: 1, sust: 0.5, beaute: 'hideux' },
4: { xp: 4, poids: "11-20", plusdom: -3, sconst: 1, sust: 1, beaute: 'repoussant' },
5: { xp: 5, poids: "21-30", plusdom: -2, sconst: 1, sust: 1, beaute: 'franchement très laid' },
6: { xp: 5, poids: "31-40", plusdom: -1, sconst: 2, sust: 2, beaute: 'laid' },
7: { xp: 6, poids: "41-50", plusdom: -1, sconst: 2, sust: 2, beaute: 'très désavantagé' },
8: { xp: 6, poids: "51-60", plusdom: 0, sconst: 2, sust: 2, beaute: 'désavantagé' },
9: { xp: 7, poids: "61-65", plusdom: 0, sconst: 3, sust: 2, beaute: 'pas terrible' },
10: { xp: 7, poids: "66-70", plusdom: 0, sconst: 3, sust: 3, beaute: 'commun' },
11: { xp: 8, poids: "71-75", plusdom: 0, sconst: 3, sust: 3, beaute: 'pas mal' },
12: { xp: 8, poids: "76-80", plusdom: +1, sconst: 4, sust: 3, beaute: 'avantagé' },
13: { xp: 9, poids: "81-90", plusdom: +1, sconst: 4, sust: 3, beaute: 'mignon' },
14: { xp: 9, poids: "91-100", plusdom: +2, sconst: 4, sust: 4, beaute: 'beau' },
15: { xp: 10, poids: "101-110", plusdom: +2, sconst: 5, sust: 4, beaute: 'très beau' },
16: { xp: 20, poids: "111-120", plusdom: +3, sconst: 5, sust: 4, beaute: 'éblouissant' },
17: { xp: 30, poids: "121-131", plusdom: +3, sconst: 5, sust: 5 },
18: { xp: 40, poids: "131-141", plusdom: +4, sconst: 6, sust: 5 },
19: { xp: 50, poids: "141-150", plusdom: +4, sconst: 6, sust: 5 },
20: { xp: 60, poids: "151-160", plusdom: +4, sconst: 6, sust: 6 },
21: { xp: 70, poids: "161-180", plusdom: +5, sconst: 7, sust: 6 },
22: { xp: 80, poids: "181-200", plusdom: +5, sconst: 7, sust: 7 },
23: { xp: 90, poids: "201-300", plusdom: +6, sconst: 7, sust: 8 },
24: { xp: 100, poids: "301-400", plusdom: +6, sconst: 8, sust: 9 },
25: { xp: 110, poids: "401-500", plusdom: +7, sconst: 8, sust: 10 },
26: { xp: 120, poids: "501-600", plusdom: +7, sconst: 8, sust: 11 },
27: { xp: 130, poids: "601-700", plusdom: +8, sconst: 9, sust: 12 },
28: { xp: 140, poids: "701-800", plusdom: +8, sconst: 9, sust: 13 },
29: { xp: 150, poids: "801-900", plusdom: +9, sconst: 9, sust: 14 },
30: { xp: 160, poids: "901-1000", plusdom: +9, sconst: 10, sust: 15 },
31: { xp: 170, poids: "1001-1500", plusdom: +10, sconst: 10, sust: 16 },
32: { xp: 180, poids: "1501-2000", plusdom: +11, sconst: 10, sust: 17 }
};
export class RdDCarac {
/* -------------------------------------------- */
static findCarac(carac, name) {
name = Grammar.toLowerCaseNoAccent(name);
const pairs = Object.entries(carac)
.filter(([key, value]) => key.includes(name) || Grammar.toLowerCaseNoAccent(value.label).includes(name));
let c = pairs.find(([key, value]) => key == name || Grammar.toLowerCaseNoAccent(value.label) == name);
if (c) {
return c[1];
}
pairs.sort((a, b) => a[0].length- b[0].length);
if (pairs.length > 0) {
c = pairs[0][1];
if (pairs.length > 1) {
const labels = pairs.map(pair => pair[1].label).reduce((a, b) => `${a}<br>${b}`);
ui.notifications.info(`Plusieurs caractéristiques possibles:<br>${labels}<br>La première sera choisie: ${c.label}.`);
}
return c;
}
return undefined;
}
static isAgiliteOuDerivee(selectedCarac) {
return selectedCarac?.label.match(/(Agilité|Dérobée)/);
}
@ -21,6 +80,37 @@ export class RdDCarac {
(RdDCarac.isReve(selectedCarac) && !competence);
}
static computeTotal(carac, beaute = undefined) {
const total = Object.values(carac).filter(c => !c.derivee)
.map(it => parseInt(it.value))
.reduce((a, b) => a + b, 0);
const beauteSuperieur10 = Math.max((beaute ?? 10) - 10, 0);
return total + beauteSuperieur10;
}
static levelUp(it) {
it.xpNext = RdDCarac.getCaracNextXp(it.value);
it.isLevelUp = (it.xp >= it.xpNext);
}
/* -------------------------------------------- */
static calculSConst(constitution) {
return Number(tableCaracDerivee[Number(constitution)].sconst);
}
/* -------------------------------------------- */
static getCaracNextXp(value) {
const nextValue = Number(value) + 1;
// xp est le coût pour atteindre cette valeur, on regarde donc le coût de la valeur+1
return RdDCarac.getCaracXp(nextValue);
}
static getCaracXp(targetValue) {
return tableCaracDerivee[targetValue]?.xp ?? 200;
}
/**
* Lappel à la chance nest possible que pour recommencer les jets dactions physiques :
* tous les jets de combat, de FORCE, dAGILITÉ, de DEXTÉRITÉ, de Dérobée, dAPPARENCE,
@ -29,4 +119,38 @@ export class RdDCarac {
static isActionPhysique(selectedCarac) {
return Grammar.toLowerCaseNoAccent(selectedCarac?.label).match(/(apparence|force|agilite|dexterite|vue|ouie|odorat|empathie|melee|tir|lancer|derobee)/);
}
/* -------------------------------------------- */
static computeCarac(data) {
data.carac.force.value = Math.min(data.carac.force.value, parseInt(data.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);
bonusDomKey = Math.min(Math.max(bonusDomKey, 0), 32); // Clamp de securite
let tailleData = tableCaracDerivee[bonusDomKey];
data.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;
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);
data.sante.vie.max = Math.ceil((parseInt(data.carac.taille.value) + parseInt(data.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);
//Compteurs
data.reve.reve.max = data.carac.reve.value;
data.compteurs.chance.max = data.carac.chance.value;
}
}

View File

@ -50,7 +50,12 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
cleanSonne() {
for (let combatant of this.data.combatants) {
combatant.actor.verifierSonneRound(this.current.round);
if (combatant.actor) {
combatant.actor.verifierSonneRound(this.current.round);
}
else {
ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur!`)
}
}
}
@ -74,28 +79,30 @@ export class RdDCombatManager extends Combat {
//if (!c) return results;
let rollFormula = formula; // Init per default
console.log("RR :", rollFormula);
if (!rollFormula) {
let armeCombat, competence;
if (c.actor.data.type == 'creature' || c.actor.data.type == 'entite') {
for (const competenceItem of c.actor.data.items) {
if (competenceItem.data.iscombat) {
competence = duplicate(competenceItem);
for (const competenceItemData of c.actor.data.items) {
if (competenceItemData.data.iscombat) {
competence = duplicate(competenceItemData);
}
}
rollFormula = "2+( (" + RdDCombatManager.calculInitiative(competence.data.niveau, competence.data.carac_value) + ")/100)";
} else {
for (const item of c.actor.data.items) {
if (item.type == "arme" && item.data.equipe) {
armeCombat = duplicate(item);
for (const itemData of c.actor.data.items) {
if (itemData.type == "arme" && itemData.data.equipe) {
armeCombat = duplicate(itemData);
}
}
let compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.data.competence;
competence = RdDItemCompetence.findCompetence(c.actor.data.items, compName);
let bonusEcaille = (armeCombat && armeCombat.data.magique) ? armeCombat.data.ecaille_efficacite : 0;
rollFormula = "2+( (" + RdDCombatManager.calculInitiative(competence.data.niveau, c.actor.data.data.carac[competence.data.defaut_carac].value, bonusEcaille) + ")/100)";
rollFormula = "2+( (" + RdDCombatManager.calculInitiative(competence.data.niveau, Misc.data(c.actor).data.carac[competence.data.defaut_carac].value, bonusEcaille) + ")/100)";
}
}
//console.log("Combatat", c);
console.log("RR :", rollFormula);
const roll = super._getInitiativeRoll(c, rollFormula);
if (roll.total <= 0) roll.total = 0.00;
console.log("Compute init for", rollFormula, roll.total);
@ -171,10 +178,14 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
static buildListeActionsCombat(combatant) {
const actor = combatant.actor; // Easy access
let items = actor.data.items;
if (combatant.actor == undefined) {
ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return [];
}
const actorData = Misc.data(combatant.actor);
let items = combatant.actor.data.items;
let actions = []
if (actor.isCreature()) {
if (combatant.actor.isCreature()) {
actions = actions.concat(items.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(competence => RdDItemCompetenceCreature.toArme(competence)));
} else {
@ -184,9 +195,11 @@ export class RdDCombatManager extends Combat {
.concat(RdDItemArme.mainsNues());
let competences = items.filter(it => it.type == 'competence');
actions = actions.concat(RdDCombatManager.finalizeArmeList(armes, competences, actor.data.data.carac));
actions = actions.concat(RdDCombatManager.finalizeArmeList(armes, competences, actorData.data.carac));
actions.push({ name: "Draconic", data: { initOnly: true, competence: "Draconic" } });
if (actorData.data.attributs.hautrevant.value) {
actions.push({ name: "Draconic", data: { initOnly: true, competence: "Draconic" } });
}
}
actions.push({ name: "Autre action", data: { initOnly: true, competence: "Autre action" } });
@ -251,17 +264,20 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
static rollInitiativeCompetence(combatantId, arme) {
const combatant = game.combat.getCombatant(combatantId);
const actor = combatant.actor;
if (combatant.actor == undefined) {
ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return [];
}
let initInfo = "";
let initOffset = 0;
let caracForInit = 0;
let compNiveau = 0;
let competence = { name: "Aucune" };
if (actor.getSurprise() == "totale") {
if (combatant.actor.getSurprise() == "totale") {
initOffset = -1; // To force 0
initInfo = "Surprise Totale"
} else if (actor.getSurprise() == "demi") {
} else if (combatant.actor.getSurprise() == "demi") {
initOffset = 0;
initInfo = "Demi Surprise"
} else if (arme.name == "Autre action") {
@ -276,13 +292,13 @@ export class RdDCombatManager extends Combat {
compNiveau = competence.data.niveau;
initInfo = arme.name + " / " + arme.data.competence;
if (actor.data.type == 'creature' || actor.data.type == 'entite') {
if (combatant.actor.data.type == 'creature' || combatant.actor.data.type == 'entite') {
caracForInit = competence.data.carac_value;
if (competence.data.categorie == "lancer") {
initOffset = 5;
}
} else {
caracForInit = actor.data.data.carac[competence.data.defaut_carac].value;
caracForInit = Misc.data(combatant.actor).data.carac[competence.data.defaut_carac].value;
if (competence.data.categorie == "lancer") { // Offset de principe pour les armes de jet
initOffset = 4;
}
@ -294,7 +310,7 @@ export class RdDCombatManager extends Combat {
}
}
}
let malus = actor.getEtatGeneral(); // Prise en compte état général
let malus = combatant.actor.getEtatGeneral(); // Prise en compte état général
// Cas des créatures et entités vs personnages
let rollFormula = initOffset + "+ ( (" + RdDCombatManager.calculInitiative(compNiveau, caracForInit) + " + " + malus + ") /100)";
// Garder la trace de l'arme/compétence utilisée pour l'iniative
@ -348,6 +364,8 @@ export class RdDCombat {
return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense":
return RdDCombat.onMsgDefense(sockmsg.data);
case "msg_combat_passearme":
return RdDCombat.onMsgPasseArme(sockmsg.data);
}
}
@ -381,9 +399,8 @@ export class RdDCombat {
/* -------------------------------------------- */
static combatNouveauTour(combat) {
let turn = combat.turns.find(t => t.tokenId == combat.current.tokenId);
if (game.user.isGM) {
// seul le GM notifie le status
if (Misc.isElectedUser()) {
let turn = combat.turns.find(t => t.tokenId == combat.current.tokenId);
this.displayActorCombatStatus(combat, turn.actor);
// TODO Playaudio for player??
}
@ -402,9 +419,11 @@ export class RdDCombat {
? "Vous devez choisir <strong>une seule</strong> cible à attaquer!"
: "Vous devez choisir une cible à attaquer!");
}
const defender = target?.actor;
const defenderTokenId = target?.data._id;
return this.create(attacker, defender, defenderTokenId, target)
else {
const defender = target?.actor;
const defenderTokenId = target?.data._id;
return this.create(attacker, defender, defenderTokenId, target)
}
}
/* -------------------------------------------- */
@ -417,9 +436,31 @@ export class RdDCombat {
return undefined;
}
static messagePasseArme(data) {
game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: "msg_combat_passearme", data: data });
RdDCombat.onMsgPasseArme(data);
}
static onMsgPasseArme(data) {
switch (data.actionPasseArme) {
case "store-attaque":
game.system.rdd.combatStore.attaques[data.id] = data.rollData;
break;
case "store-defense":
game.system.rdd.combatStore.defenses[data.id] = data.rollData;
break;
case "delete-attaque":
delete game.system.rdd.combatStore.attaques[data.id];
break;
case "delete-defense":
delete game.system.rdd.combatStore.defenses[data.id];
break;
}
}
/* -------------------------------------------- */
static _storeAttaque(attackerId, attackerRoll) {
game.system.rdd.combatStore.attaques[attackerId] = duplicate(attackerRoll);
RdDCombat.messagePasseArme({ actionPasseArme: "store-attaque", id: attackerId, rollData: attackerRoll });
}
/* -------------------------------------------- */
@ -429,12 +470,12 @@ export class RdDCombat {
/* -------------------------------------------- */
static _deleteAttaque(attackerId) {
delete game.system.rdd.combatStore.attaques[attackerId];
RdDCombat.messagePasseArme({ actionPasseArme: "delete-attaque", id: attackerId });
}
/* -------------------------------------------- */
static _storeDefense(defenderRoll) {
game.system.rdd.combatStore.defenses[defenderRoll.passeArme] = duplicate(defenderRoll);
static _storeDefense(passeArme, defenderRoll) {
RdDCombat.messagePasseArme({ actionPasseArme: "store-defense", id: passeArme, rollData: defenderRoll });
}
/* -------------------------------------------- */
@ -444,7 +485,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static _deleteDefense(passeArme) {
delete game.system.rdd.combatStore.defenses[passeArme];
RdDCombat.messagePasseArme({ actionPasseArme: "delete-defense", id: passeArme });
}
/* -------------------------------------------- */
@ -466,32 +507,30 @@ export class RdDCombat {
/* -------------------------------------------- */
static onMsgEncaisser(data) {
let attackerRoll = RdDCombat._getAttaque(data.attackerId); // Retrieve the rolldata from the store
if (game.user.id === data.gmId) { // Seul le GM effectue l'encaissement sur la fiche
if (Misc.isElectedUser()) {
let attackerRoll = RdDCombat._getAttaque(data.attackerId); // Retrieve the rolldata from the store
let attacker = data.attackerId ? game.actors.get(data.attackerId) : null;
let defender = canvas.tokens.get(data.defenderTokenId).actor;
defender.encaisserDommages(attackerRoll, attacker);
RdDCombat._deleteDefense(attackerRoll.passeArme);
RdDCombat._deleteAttaque(data.attackerId);
}
RdDCombat._deleteDefense(attackerRoll.passeArme);
RdDCombat._deleteAttaque(data.attackerId);
}
/* -------------------------------------------- */
static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderTokenId);
if (defenderToken) {
if (!game.user.isGM && !game.user.character) { // vérification / sanity check
ui.notifications.error("Le joueur " + game.user.name + " n'est connecté à aucun personnage. Impossible de continuer.");
return;
}
if ((game.user.isGM && !defenderToken.actor.hasPlayerOwner) || (defenderToken.actor.hasPlayerOwner && (game.user.character._id == defenderToken.actor.data._id))) {
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
if (!game.user.isGM && !game.user.character) { // vérification / sanity check
ui.notifications.error("Le joueur " + game.user.name + " n'est connecté à aucun personnage. Impossible de continuer.");
return;
}
if (defenderToken && Misc.isElectedUser()) {
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
if (rddCombat) {
const defenderRoll = msg.defenderRoll;
RdDCombat._storeAttaque(msg.attackerId, defenderRoll.attackerRoll);
RdDCombat._storeDefense(defenderRoll);
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
rddCombat.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
rddCombat._chatMessageDefense(msg.paramChatDefense);
}
@ -522,9 +561,11 @@ export class RdDCombat {
const rddCombat = RdDCombat.createForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) {
rddCombat.onEvent(button, event);
event.preventDefault();
rddCombat.onEvent(button, event);
event.preventDefault();
}
});
}
html.on("click", '#chat-jet-vie', event => {
@ -638,6 +679,7 @@ export class RdDCombat {
static isEchec(rollData) {
switch (rollData.ajustements.surprise.used) {
case 'totale': return true;
case 'demi': return !rollData.rolled.isSign;
}
return rollData.rolled.isEchec;
}
@ -645,7 +687,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static isEchecTotal(rollData) {
if (!rollData.attackerRoll && rollData.ajustements.surprise.used) {
return rollData.rolled.isEchec;
return rollData.rolled.isEchec && rollData.rolled.code != 'notSign';
}
return rollData.rolled.isETotal;
}
@ -662,6 +704,7 @@ export class RdDCombat {
static isReussite(rollData) {
switch (rollData.ajustements.surprise.used) {
case 'totale': return false;
case 'demi': return rollData.rolled.isSign;
}
return rollData.rolled.isSuccess;
}
@ -674,8 +717,9 @@ export class RdDCombat {
let rollData = this._prepareAttaque(competence, arme);
console.log("RdDCombat.attaque >>>", rollData);
this.attacker.incItemUse(arme._id); // Usage
this.attacker.verifierForceMin(arme);
if (arme) {
this.attacker.verifierForceMin(arme);
}
const dialog = await RdDRoll.create(this.attacker, rollData,
{
@ -686,7 +730,9 @@ export class RdDCombat {
label: 'Attaque: ' + (arme?.name ?? competence.name),
callbacks: [
this.attacker.createCallbackExperience(),
this.attacker.createCallbackAppelAuMoral(),
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: r => arme && !RdDCombat.isParticuliere(r), action: r => this.attacker.incDecItemUse(arme._id) },
{ condition: r => (RdDCombat.isReussite(r) && !RdDCombat.isParticuliere(r)), action: r => this._onAttaqueNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onAttaqueParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onAttaqueEchec(r) },
@ -706,7 +752,6 @@ export class RdDCombat {
surpriseDefenseur: this.defender.getSurprise(true),
essais: {}
};
rollData.diviseurSignificative = this._getDiviseurSignificative(rollData);
if (this.attacker.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData);
@ -725,10 +770,9 @@ export class RdDCombat {
/* -------------------------------------------- */
async _onAttaqueParticuliere(rollData) {
RdDCombat._storeAttaque(this.attackerId, rollData);
this.attacker.decItemUse( rollData.arme._id ); // Usage décrémenté sur particulière
// Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum
const isMeleeDiffNegative = rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0;
const isMeleeDiffNegative = (rollData.competence.type == 'competencecreature' || rollData.selectedCarac.label == "Mêlée") && rollData.diffLibre < 0;
ChatMessage.create({
alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
@ -751,7 +795,7 @@ export class RdDCombat {
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
// Save rollData for defender
RdDCombat._storeAttaque(this.attackerId, attackerRoll);
RdDCombat._storeDefense(defenderRoll)
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
attackerRoll.show = {
cible: this.target ? this.defender.data.name : 'la cible',
@ -802,8 +846,7 @@ export class RdDCombat {
dmg: attackerRoll.dmg,
};
const selfMessage = essaisPrecedents != undefined;
if (!selfMessage && (!game.user.isGM || this.defender.hasPlayerOwner)) {
if (!Misc.isElectedUser()) {
this._socketSendMessageDefense(paramChatDefense, defenderRoll);
}
else {
@ -840,7 +883,7 @@ export class RdDCombat {
/* -------------------------------------------- */
_filterArmesParade(defender, competence) {
let items = defender.data.items;
items = items.filter(item => RdDItemArme.isArmeUtilisable(item) || RdDItemCompetenceCreature.isCompetenceParade(item));
items = 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
}
@ -896,6 +939,10 @@ export class RdDCombat {
async choixParticuliere(rollData, choix) {
console.log("RdDCombat.choixParticuliere >>>", rollData, choix);
if (choix != "rapidite") {
this.attacker.incDecItemUse(rollData.arme._id);
}
this.removeChatMessageActionsPasseArme(rollData.passeArme);
rollData.particuliere = choix;
await this._onAttaqueNormale(rollData);
@ -906,7 +953,6 @@ export class RdDCombat {
let arme = this.defender.getArmeParade(armeParadeId);
console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme);
this.defender.incItemUse(armeParadeId); // Usage
let rollData = this._prepareParade(attackerRoll, arme);
@ -919,7 +965,9 @@ export class RdDCombat {
label: 'Parade: ' + (arme ? arme.name : rollData.competence.name),
callbacks: [
this.defender.createCallbackExperience(),
this.defender.createCallbackAppelAuMoral(),
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: r => !RdDCombat.isParticuliere(r), action: r => this.defender.incDecItemUse(armeParadeId) },
{ condition: RdDCombat.isReussite, action: r => this._onParadeNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onParadeParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onParadeEchec(r) },
@ -933,7 +981,7 @@ export class RdDCombat {
const compName = armeParade.data.competence;
const armeAttaque = attackerRoll.arme;
let rollData = {
let defenderRoll = {
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
@ -945,31 +993,12 @@ export class RdDCombat {
carac: this.defender.data.data.carac,
show: {}
};
rollData.diviseurSignificative = this._getDiviseurSignificative(rollData);
if (this.defender.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData);
RdDItemCompetenceCreature.setRollDataCreature(defenderRoll);
}
return rollData;
}
/* -------------------------------------------- */
_getDiviseurSignificative(defenderRoll) {
let facteurSign = 1;
if (defenderRoll.surprise == 'demi') {
facteurSign *= 2;
}
if (defenderRoll.needParadeSignificative) {
facteurSign *= 2;
}
if (RdDBonus.isDefenseAttaqueFinesse(defenderRoll)) {
facteurSign *= 2;
}
if (!ReglesOptionelles.isUsing('tripleSignificative')) {
facteurSign = Math.min(facteurSign, 4);
}
return facteurSign;
return defenderRoll;
}
/* -------------------------------------------- */
@ -1002,7 +1031,7 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true });
RdDCombat._storeDefense(defenderRoll);
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
}
/* -------------------------------------------- */
@ -1014,7 +1043,6 @@ export class RdDCombat {
}
console.log("RdDCombat.esquive >>>", attackerRoll, esquive);
let rollData = this._prepareEsquive(attackerRoll, esquive);
this.defender.incItemUse(esquive._id); // Usage
const dialog = await RdDRoll.create(this.defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html' }, {
@ -1022,6 +1050,8 @@ export class RdDCombat {
label: 'Esquiver',
callbacks: [
this.defender.createCallbackExperience(),
this.defender.createCallbackAppelAuMoral(),
{ condition: r => !RdDCombat.isParticuliere(r), action: r => this.defender.incDecItemUse(esquive._id) },
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: RdDCombat.isReussite, action: r => this._onEsquiveNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onEsquiveParticuliere(r) },
@ -1043,7 +1073,6 @@ export class RdDCombat {
carac: this.defender.data.data.carac,
show: {}
};
rollData.diviseurSignificative = this._getDiviseurSignificative(rollData);
if (this.defender.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData);
@ -1074,7 +1103,7 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(defenderRoll.passeArme);
this._sendMessageDefense(defenderRoll.attackerRoll, defenderRoll, { defense: true })
RdDCombat._storeDefense(defenderRoll);
RdDCombat._storeDefense(defenderRoll.passeArme, defenderRoll);
}
/* -------------------------------------------- */
@ -1176,7 +1205,7 @@ export class RdDCombat {
_computeImpactRecul(attaque) {
const taille = this.defender.getTaille();
const force = this.attacker.getForce();
const dommages = attaque.arme.data.dommagesReels;
const dommages = attaque.arme.data.dommagesReels ?? attaque.arme.data.dommages;
return taille - (force + dommages);
}
@ -1194,7 +1223,7 @@ export class RdDCombat {
this._onEchecTotal(defenderRoll);
}
if (game.user.isGM) { // Current user is the GM -> direct access
if (Misc.isElectedUser()) {
attackerRoll.attackerId = this.attackerId;
attackerRoll.defenderTokenId = defenderTokenId;

View File

@ -1,8 +1,8 @@
/* -------------------------------------------- */
import { DeDraconique } from "./de-draconique.js";
import { RdDItemCompetence } from "./item-competence.js";
import { Misc } from "./misc.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDNameGen } from "./rdd-namegen.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
@ -10,9 +10,9 @@ 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 { TMRType, TMRUtility } from "./tmr-utility.js";
import { TMRUtility } from "./tmr-utility.js";
const rddRollNumeric = /(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
const rddRollNumeric = /$(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
/* -------------------------------------------- */
export class RdDCommands {
@ -23,6 +23,8 @@ export class 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" });
@ -32,7 +34,7 @@ export class RdDCommands {
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),
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` });
@ -61,9 +63,11 @@ export class RdDCommands {
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
<br><strong>/rdd 10 +2</strong> effectue un jet 10 à +2
<br><strong>/rdd 15 -2</strong> effectue un jet 15 à -2
<br><strong>/rdd 15 0 s</strong> effectue un jet 15 à 0, avec significative requise`
<br><strong>/rdd 15 0 s</strong> effectue un jet 15 à 0, avec significative requise
<br><strong>/rdd Vue Vigilance -2</strong> effectue un jet de Vue/Vigilance à -2 pour les tokens sélectionnés
<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" });
@ -161,11 +165,25 @@ export class RdDCommands {
}
/* -------------------------------------------- */
help(msg, table = undefined) {
async help(msg) {
this.help(msg, undefined);
}
async help(msg, table) {
let list = []
this._buildSubTableHelp(list, table || this.commandsTable);
const messageAide = list.reduce((a, b) => a + '</li><li class="list-item">' + b);
RdDCommands._chatAnswer(msg, `Commandes disponibles<ul class="alterne-list"><li class="list-item">${messageAide}</li></ul>`);
let html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/settings/dialog-aide-commands.html", { commands: list });
let d = new Dialog(
{
title: "Commandes disponibles dans le tchat",
content: html,
buttons: {},
},
{
width: 600, height: 500,
});
d.render(true);
}
/* -------------------------------------------- */
@ -214,6 +232,27 @@ export class RdDCommands {
await this.rollRdDNumeric(msg, carac, diff, significative);
return;
}
let actors = canvas.tokens.controlled.map(it => it.actor).filter(it => it);
if (actors && actors.length > 0) {
let length = params.length;
let diff = Number(params[length - 1]);
if (Number.isInteger(Number(diff))) {
length--;
}
else {
diff = 0;
}
const caracName = params[0];
const compName = length > 1 ? params.slice(1, length).reduce((a, b) => `${a} ${b}`) : undefined;
for (let actor of actors) {
await actor.rollCaracCompetence(caracName, compName, diff);
}
return;
}
else {
ui.notifications.warn("Sélectionnez au moins un personnage pour lancer les dés")
}
}
}
@ -232,7 +271,7 @@ export class RdDCommands {
/* -------------------------------------------- */
async rollDeDraconique(msg) {
let ddr = new DeDraconique().evaluate();
let ddr = new Roll("1dr + 7").evaluate();
ddr.showDice = true;
await RdDDice.showDiceSoNice(ddr);
RdDCommands._chatAnswer(msg, `Lancer d'un Dé draconique: ${ddr.total}`);
@ -241,7 +280,7 @@ export class RdDCommands {
getTMRAleatoire(msg, params) {
if (params.length < 2) {
let type = params[0];
const tmr = TMRUtility.getTMRAleatoire(it => it.type == type);
const tmr = TMRUtility.getTMRAleatoire(type ? (it => it.type == type) : (it => true));
RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`);
}
else {
@ -266,7 +305,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}: ${RdDUtility.getCaracXp(to)}`);
RdDCommands._chatAnswer(msg, `Coût pour passer une caractéristique de ${to - 1} à ${to}: ${RdDCarac.getCaracXp(to)}`);
}
else {
return false;

View File

@ -1,10 +1,140 @@
import { ChatUtility } from "./chat-utility.js";
import { SYSTEM_RDD } from "./constants.js";
import { Misc } from "./misc.js";
function img(src) {
return `<img src="${src}" class="dice-img" />`
}
function iconHeure(heure) {
if (heure < 10) {
heure = '0' + heure;
}
return `systems/foundryvtt-reve-de-dragon/icons/heures/hd${heure}.webp`
}
const imagesHeures = [1, 2, 3, 4, 5, 6, 7, 9, 9, 10, 11, 12].map(it => iconHeure(it));
const imgSigneDragon = img(imagesHeures[4]);
/** De7 pour les jets de rencontre */
export class De7 extends Die {
/** @override */
static DENOMINATION = "7";
static diceSoNiceData(system) {
return {
type: "d7",
font: "HeuresDraconiques",
fontScale : 0.7,
labels: ['1', '2', '3', '4', '5', '6', 'd', '0'],
system: system
}
}
constructor(termData) {
termData.faces = 8;
super(termData);
}
evaluate() {
super.evaluate();
this.explode("x=8");
return this;
}
get total() {
return this.values.filter(it => it != 8).reduce(Misc.sum(), 0);
}
static getResultLabel(result) {
switch (result) {
case 7: return imgSigneDragon;
}
return result;
}
}
/** DeDraconique pour le D8 sans limite avec 8=>0 */
export class DeDraconique extends Die {
static DENOMINATION = "r";
static diceSoNiceData(system) {
return {
type: "dr",
font: "HeuresDraconiques",
fontScale : 0.7,
labels: ['1', '2', '3', '4', '5', '6', 'd', '0'],
system: system
}
}
constructor(termData) {
termData.faces = 8;
super(termData);
}
evaluate() {
super.evaluate();
this.explode("x=7");
return this;
}
get total() {
return this.values.filter(it => it != 8).reduce(Misc.sum(), 0);
}
static getResultLabel(result) {
switch (result) {
case 7: return imgSigneDragon;
case 8: return 0;
}
return result;
}
}
/** De 12 avec les heures */
export class DeHeure extends Die {
/** @override */
static DENOMINATION = "h";
static diceSoNiceData(system) {
return {
type: "dh",
font: "HeuresDraconiques",
labels: ['v', 'i', 'f', 'o', 'd', 'e', 'l', 's', 'p', 'a', 'r', 'c'],
system: system
}
}
constructor(termData) {
termData.faces = 12;
super(termData);
}
static getResultLabel(result) {
return img(imagesHeures[result-1]);
}
}
export class RdDDice {
static init() {
CONFIG.Dice.terms[De7.DENOMINATION] = De7;
CONFIG.Dice.terms[DeDraconique.DENOMINATION] = DeDraconique;
CONFIG.Dice.terms[DeHeure.DENOMINATION] = DeHeure;
}
static diceSoNiceReady(dice3d) {
for (const system of Object.keys(dice3d.DiceFactory.systems)) {
dice3d.addDicePreset(De7.diceSoNiceData(system));
dice3d.addDicePreset(DeDraconique.diceSoNiceData(system));
dice3d.addDicePreset(DeHeure.diceSoNiceData(system));
}
}
/* -------------------------------------------- */
static async show(roll, rollMode = undefined) {
if (roll.showDice || game.settings.get("foundryvtt-reve-de-dragon", "dice-so-nice") == true) {
if (roll.showDice || game.settings.get(SYSTEM_RDD, "dice-so-nice") == true) {
await this.showDiceSoNice(roll, rollMode);
}
return roll;

View File

@ -22,13 +22,13 @@ 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 { RdDItemCompetence } from "./item-competence.js";
import { StatusEffects } from "./status-effects.js";
import { RddCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { RdDDice } from "./rdd-dice.js";
/* -------------------------------------------- */
/* Foundry VTT Initialization */
@ -46,7 +46,8 @@ Hooks.once("init", async function () {
game.system.rdd = {
TMRUtility,
RdDUtility,
RdDHotbar
RdDHotbar,
RdDResolutionTable
}
/* -------------------------------------------- */
@ -153,6 +154,7 @@ Hooks.once("init", async function () {
CONFIG.Combat.entityClass = RdDCombatManager;
// préparation des différents modules
RdDDice.init();
RdDCommands.init();
RdDCombat.init();
RdDCombatManager.init(),
@ -211,7 +213,12 @@ Hooks.once("ready", function () {
});
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* Dice-so-nice ready */
/* -------------------------------------------- */
Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d));
/* -------------------------------------------- */
/* Foundry VTT chat message */
/* -------------------------------------------- */
Hooks.on("chatMessage", (html, content, msg) => {
if (content[0] == '/') {

View File

@ -51,10 +51,8 @@ const reussites = [
{ code: "error", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: 0, ptQualite: 0, quality: "Jet de dés invalide", condition: (target, roll) => (roll <= 0 || roll > 100) }
];
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
/* -------------------------------------------- */
const reussiteSignificative = reussites.find(r => r.code == "sign");
const reussiteNormale = reussites.find(r => r.code == "norm");
const echecNormal = reussites.find(r => r.code == "echec");
const caracMaximumResolution = 60;
/* -------------------------------------------- */
export class RdDResolutionTable {
@ -115,7 +113,7 @@ export class RdDResolutionTable {
this._updateChancesFactor(chances, diviseur);
chances.showDice = showDice;
let rolled = await this.rollChances(chances);
let rolled = await this.rollChances(chances, diviseur);
rolled.caracValue = caracValue;
rolled.finalLevel = finalLevel;
rolled.bonus = bonus;
@ -150,12 +148,12 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static async rollChances(chances) {
static async rollChances(chances, diviseur) {
let myRoll = new Roll("1d100").roll();
myRoll.showDice = chances.showDice;
await RdDDice.show(myRoll);
chances.roll = myRoll.total;
mergeObject(chances, this.computeReussite(chances, chances.roll), { overwrite: true });
mergeObject(chances, this.computeReussite(chances, chances.roll, diviseur), { overwrite: true });
return chances;
}
@ -167,7 +165,8 @@ export class RdDResolutionTable {
if (difficulte < -10) {
return duplicate(levelDown.find(levelData => levelData.level == difficulte));
}
return duplicate(RdDResolutionTable.resolutionTable[caracValue][difficulte + 10]);
const chances = RdDResolutionTable.resolutionTable[caracValue][difficulte + 10];
return chances ? duplicate(chances) : RdDResolutionTable._computeCell(difficulte, RdDResolutionTable.computeChances(caracValue, difficulte));
}
/* -------------------------------------------- */
@ -216,22 +215,36 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static computeReussite(chances, roll) {
return reussites.find(x => x.condition(chances, roll));
static computeReussite(chances, roll, diviseur) {
const reussite = reussites.find(x => x.condition(chances, roll));
if (diviseur > 1 && reussite.code == 'norm') {
return reussiteInsuffisante;
}
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));
let dataRow = [];
for (var diff = -10; diff <= 22; diff++) {
dataRow[diff + 10] = this._computeCell(diff, RdDResolutionTable._computePercentage(caracValue, diff));
}
return dataRow;
}
static _computePercentage(caracValue, diff) {
if (diff <-10) {
return 1;
}
if (diff == -10){
return Math.max(Math.floor(caracValue / 4), 1);
}
if (diff == -9) {
return Math.max(Math.floor(caracValue / 2), 1)
}
return Math.max(Math.floor(caracValue * (diff + 10) / 2), 1);
}
/* -------------------------------------------- */
static _computeCell(niveau, percentage) {
return {
@ -269,8 +282,8 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static buildHTMLResults(caracValue, levelValue) {
if ( caracValue == undefined || isNaN(caracValue )) caracValue = 10;
if ( levelValue == undefined || isNaN(levelValue )) levelValue = 0;
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;
@ -278,14 +291,14 @@ export class RdDResolutionTable {
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>
`
<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>
`
}
/* -------------------------------------------- */

View File

@ -7,6 +7,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";
/**
* Extend the base Dialog entity to select roll parameters
@ -18,7 +19,7 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */
static async create(actor, rollData, dialogConfig, ...actions) {
if (actor.isRollWindowsOpened() ) {
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;
}
@ -38,37 +39,58 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */
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: actor.data.data.carac,
carac: actorData.data.carac,
finalLevel: 0,
diffConditions: 0,
diffLibre: rollData.competence?.data.default_diffLibre ?? 0,
malusArmureValue: actor.getMalusArmure(),
surencMalusFlag: actor.isPersonnage() ? (actor.data.data.compteurs.surenc.value < 0) : false,
surencMalusFlag: actor.isPersonnage() ? (actorData.data.compteurs.surenc.value < 0) : false,
surencMalusValue: actor.getSurenc(),
useMalusSurenc: false,
appelAuMoralPossible : false, /* Est-ce que l'appel au moral est possible ? Variable utisé pour l'affichage ou non de la ligne concernant le moral */
appelAuMoralDemander :false, /* Est-ce que le joueur demande d'utiliser le moral ? Utile si le joueur change plusieurs fois de carac associée. */
jetEchouerMoralDiminuer : false, /* Pour l'affichage dans le chat */
use: { libre: true, conditions: true, surenc: false, encTotal: false, appelAuMoral : false /* Le jet se fait ou non en utilisant l'appel au moral */},
useMoral: false, /* Est-ce que le joueur demande d'utiliser le moral ? Utile si le joueur change plusieurs fois de carac associée. */
perteMoralEchec: false, /* Pour l'affichage dans le chat */
use: { libre: true, conditions: true, surenc: false, encTotal: false },
isMalusEncombrementTotal: RdDItemCompetence.isMalusEncombrementTotal(rollData.competence),
useMalusEncTotal: false,
encTotal: actor.getEncTotal(),
ajustementAstrologique: actor.ajustementAstrologique(),
surprise: actor.getSurprise(false),
canClose: true
}
};
mergeObject(rollData, defaultRollData, { recursive: true, overwrite: false });
if (rollData.forceCarac) {
rollData.carac = rollData.forceCarac;
}
rollData.diviseurSignificative = RdDRoll.getDiviseurSignificative(rollData);
RollDataAjustements.calcul(rollData, actor);
}
/* -------------------------------------------- */
static getDiviseurSignificative(rollData) {
let facteurSign = 1;
if (rollData.surprise == 'demi') {
facteurSign *= 2;
}
if (rollData.needParadeSignificative) {
facteurSign *= 2;
}
if (RdDBonus.isDefenseAttaqueFinesse(rollData)) {
facteurSign *= 2;
}
if (!ReglesOptionelles.isUsing('tripleSignificative')) {
facteurSign = Math.min(facteurSign, 4);
}
return facteurSign;
}
/* -------------------------------------------- */
static _ensureCorrectActions(actions) {
@ -120,11 +142,10 @@ export class RdDRoll extends Dialog {
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)) {
callback.action(this.rollData);
await callback.action(this.rollData);
}
}
}
@ -206,27 +227,22 @@ export class RdDRoll extends Dialog {
this.rollData.useMalusEncTotal = event.currentTarget.checked;
this.updateRollResult();
});
html.find('#iconeSmile').change((event) => {
console.log("iconeSmile");
console.log(html.find('.iconeSmile'));
html.find('.imgAppelAuMoral').click((event) => { /* l'appel au moral, qui donne un bonus de +1 */
this.rollData.useMoral = !this.rollData.useMoral;
if (this.rollData.useMoral) {
if (this.rollData.moral > 0) {
html.find('.imgAppelAuMoral')[0].src = "/systems/foundryvtt-reve-de-dragon/icons/moral-heureux.svg";
html.find('.tooltipAppelAuMoralText')[0].innerHTML = "Appel au moral";
} else {
html.find('.imgAppelAuMoral')[0].src = "/systems/foundryvtt-reve-de-dragon/icons/moral-malheureux.svg";
html.find('.tooltipAppelAuMoralText')[0].innerHTML = "Appel à l'énergie du désespoir";
}
} else {
html.find('.imgAppelAuMoral')[0].src = "/systems/foundryvtt-reve-de-dragon/icons/moral-neutre.svg";
html.find('.tooltipAppelAuMoralText')[0].innerHTML = "Sans appel au moral";
}
this.updateRollResult();
});
html.find('#iconeSmile').click((event) => { /* l'appel au moral, qui donne un bonus de +1 */
this.rollData.appelAuMoralDemander = ! this.rollData.appelAuMoralDemander;
if ( this.rollData.appelAuMoralDemander ) {
if ( this.rollData.moral > 0 ) {
html.find('#iconeSmile')[0].src = "/systems/foundryvtt-reve-de-dragon/icons/moral-heureux.svg";
html.find('#tooltipAppelAuMoralText')[0].innerHTML = "Appel au moral";
} else {
html.find('#iconeSmile')[0].src = "/systems/foundryvtt-reve-de-dragon/icons/moral-malheureux.svg";
html.find('#tooltipAppelAuMoralText')[0].innerHTML = "Appel à l'énergie du désespoir";
}
} else {
html.find('#iconeSmile')[0].src = "/systems/foundryvtt-reve-de-dragon/icons/moral-neutre.svg";
html.find('#tooltipAppelAuMoralText')[0].innerHTML = "Sans appel au moral";
}
this.updateRollResult();
});
// Section Méditation
html.find('.conditionMeditation').change((event) => {
let condition = event.currentTarget.attributes['id'].value;
@ -242,6 +258,7 @@ export class RdDRoll extends Dialog {
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor.getBonusDegat());
rollData.caracValue = parseInt(rollData.selectedCarac.value);
rollData.coupsNonMortels = (rollData.attackerRoll?.dmg.mortalite ?? rollData.dmg.mortalite) == 'non-mortel';
rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac);
let dmgText = Misc.toSignedString(rollData.dmg.total);
if (rollData.coupsNonMortels) {
@ -253,20 +270,11 @@ export class RdDRoll extends Dialog {
HtmlUtility._showControlWhen($("#div-sort-ptreve"), RdDItemSort.isCoutVariable(rollData.selectedSort))
}
if ( ! RdDCarac.isActionPhysique(rollData.selectedCarac || ! actor.isPersonnage() ) ) {
rollData.appelAuMoralPossible = false;
rollData.use.appelAuMoral = false;
} else {
rollData.appelAuMoralPossible = true;
rollData.use.appelAuMoral = rollData.appelAuMoralDemander;
}
RollDataAjustements.calcul(rollData, this.actor);
rollData.finalLevel = this._computeFinalLevel(rollData);
HtmlUtility._showControlWhen($(".diffMoral"), rollData.ajustements.moralTotal.used);
HtmlUtility._showControlWhen($("#divAppelAuMoral"), rollData.appelAuMoralPossible );
HtmlUtility._showControlWhen($(".divAppelAuMoral"), rollData.use.appelAuMoral);
HtmlUtility._showControlWhen($("#etat-general"), !RdDCarac.isIgnoreEtatGeneral(rollData.selectedCarac, rollData.competence));
HtmlUtility._showControlWhen($("#ajust-astrologique"), RdDResolutionTable.isAjustementAstrologique(rollData));

View File

@ -48,14 +48,22 @@ export class RdDRollTables {
static async getQueue(toChat = false) {
let queue = await RdDRollTables.drawItemFromRollTable("Queues de dragon", toChat);
if (queue.name.toLowerCase().includes('lancinant') ) {
queue = await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat);
return await RdDRollTables.getDesirLancinant(toChat);
}
if (queue.name.toLowerCase().includes('fixe') ) {
queue = await RdDRollTables.drawItemFromRollTable("Idées fixes", toChat);
return await RdDRollTables.getIdeeFixe(toChat);
}
return queue;
}
static async getDesirLancinant(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat);
}
static async getIdeeFixe(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Idées fixes", toChat);
}
/* -------------------------------------------- */
static async getTeteHR(toChat = false) {
return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour haut-rêvants", toChat);

View File

@ -11,6 +11,7 @@ import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
/* -------------------------------------------- */
export class RdDTMRDialog extends Dialog {
@ -72,11 +73,11 @@ export class RdDTMRDialog extends Dialog {
}
loadSortsReserve() {
this.sortsReserves = duplicate(this.actor.data.data.reve.reserve.list);
this.sortsReserves = Misc.data(this.actor).data.reve.reserve.list;
}
loadRencontres() {
this.rencontresExistantes = duplicate(this.actor.getTMRRencontres()).list;
this.rencontresExistantes = this.actor.getTMRRencontres();
}
/* -------------------------------------------- */
@ -108,6 +109,11 @@ export class RdDTMRDialog extends Dialog {
this._createTokens();
}
removeToken(tmr, casetmr) {
this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id);
this.updateTokens()
}
/* -------------------------------------------- */
_getTokensCasesTmr() {
return this.casesSpeciales.map(c => this._tokenCaseSpeciale(c)).filter(token => token);
@ -131,7 +137,7 @@ export class RdDTMRDialog extends Dialog {
return EffetsDraconiques.sortReserve.token(this.pixiTMR, sortEnReserve.sort, () => sortEnReserve.coord);
}
_tokenDemiReve() {
return EffetsDraconiques.demiReve.token(this.pixiTMR, this.actor, () => this.actor.data.data.reve.tmrpos.coord);
return EffetsDraconiques.demiReve.token(this.pixiTMR, this.actor, () => Misc.data(this.actor).data.reve.tmrpos.coord);
}
_updateDemiReve() {
@ -142,7 +148,7 @@ export class RdDTMRDialog extends Dialog {
async activateListeners(html) {
super.activateListeners(html);
document.getElementById("tmrrow1").insertCell(1).append(this.pixiApp.view);
document.getElementById("tmrrow1").insertCell(0).append(this.pixiApp.view);
if (this.viewOnly) {
html.find('#lancer-sort').remove();
@ -150,7 +156,7 @@ export class RdDTMRDialog extends Dialog {
else {
// Roll Sort
html.find('#lancer-sort').click((event) => {
this.actor.rollUnSort(this.actor.data.data.reve.tmrpos.coord);
this.actor.rollUnSort(Misc.data(this.actor).data.reve.tmrpos.coord);
});
}
if (this.viewOnly) {
@ -165,44 +171,43 @@ export class RdDTMRDialog extends Dialog {
// Le reste...
this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord);
let tmr = TMRUtility.getTMR(Misc.data(this.actor).data.reve.tmrpos.coord);
await this.manageRencontre(tmr, () => {
this.postRencontre(tmr);
this.actor.displayTMRQueueSouffleInformation();
});
}
/* -------------------------------------------- */
updateValuesDisplay() {
let ptsreve = document.getElementById("tmr-pointsreve-value");
ptsreve.innerHTML = this.actor.data.data.reve.reve.value;
const actorData = Misc.data(this.actor);
ptsreve.innerHTML = actorData.data.reve.reve.value;
let tmrpos = document.getElementById("tmr-pos");
let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord);
tmrpos.innerHTML = this.actor.data.data.reve.tmrpos.coord + " (" + tmr.label + ")";
let tmr = TMRUtility.getTMR(actorData.data.reve.tmrpos.coord);
tmrpos.innerHTML = actorData.data.reve.tmrpos.coord + " (" + tmr.label + ")";
let etat = document.getElementById("tmr-etatgeneral-value");
etat.innerHTML = this.actor.getEtatGeneral();
let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = this.actor.data.data.reve.refoulement.value;
refoulement.innerHTML = actorData.data.reve.refoulement.value;
let fatigueItem = document.getElementById("tmr-fatigue-table");
//console.log("Refresh : ", this.actor.data.data.sante.fatigue.value);
fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.actor.data.data.sante.fatigue.value, this.actor.data.data.sante.endurance.max).html() + "</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>";
}
/* -------------------------------------------- */
close() {
this.actor.santeIncDec("fatigue", this.cumulFatigue).then(super.close()); // moving 1 cell costs 1 fatigue
this.actor.tmrApp = undefined; // Cleanup reference
this.actor.setStatusDemiReve(false);
if (!this.viewOnly) {
this.actor.setStatusDemiReve(false);
this._tellToGM(this.actor.name + " a quitté les terres médianes");
}
}
/* -------------------------------------------- */
async derober() {
await this.actor.addTMRRencontre(this.currentRencontre);
@ -286,6 +291,7 @@ export class RdDTMRDialog extends Dialog {
return false;
}
/* -------------------------------------------- */
async quitterLesTMRInconscient() {
if (this.currentRencontre?.isPersistant) {
await this.refouler();
@ -306,7 +312,7 @@ export class RdDTMRDialog extends Dialog {
rencontre: this.currentRencontre,
nbRounds: 1,
canClose: false,
tmr: TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord)
tmr: TMRUtility.getTMR(Misc.data(this.actor).data.reve.tmrpos.coord)
}
await this._tentativeMaitrise(rencontreData);
@ -354,6 +360,7 @@ export class RdDTMRDialog extends Dialog {
}
}
/* -------------------------------------------- */
_rollPresentCite(rencontreData) {
let rolled = RdDResolutionTable.computeChances(rencontreData.reve, 0);
mergeObject(rolled, { caracValue: rencontreData.reve, finalLevel: 0, roll: rolled.score });
@ -385,6 +392,11 @@ export class RdDTMRDialog extends Dialog {
ChatMessage.create({ content: message, user: game.user._id, whisper: ChatMessage.getWhisperRecipients("GM") });
}
/* -------------------------------------------- */
_tellToUserAndGM(message) {
ChatMessage.create({ content: message, user: game.user._id, whisper: [game.user._id].concat(ChatMessage.getWhisperRecipients("GM")) });
}
/* -------------------------------------------- */
async manageRencontre(tmr, postRencontre) {
if (this.viewOnly) {
@ -409,6 +421,7 @@ export class RdDTMRDialog extends Dialog {
}
}
/* -------------------------------------------- */
_presentCite(tmr, postRencontre) {
const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord));
if (presentCite) {
@ -418,6 +431,7 @@ export class RdDTMRDialog extends Dialog {
return presentCite;
}
/* -------------------------------------------- */
async _utiliserPresentCite(presentCite, typeRencontre, tmr, postRencontre) {
this.currentRencontre = TMRRencontres.getRencontre(typeRencontre);
await TMRRencontres.evaluerForceRencontre(this.currentRencontre);
@ -498,9 +512,7 @@ export class RdDTMRDialog extends Dialog {
}
async _resultatMaitriseCaseHumide(rollData) {
if (rollData.rolled.isETotal) {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false });
}
await this.souffleSiEchecTotal(rollData);
this.toclose = rollData.rolled.isEchec;
if (rollData.rolled.isSuccess && rollData.double) {
rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements };
@ -518,6 +530,12 @@ export class RdDTMRDialog extends Dialog {
}
}
async souffleSiEchecTotal(rollData) {
if (rollData.rolled.isETotal) {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false });
}
}
/* -------------------------------------------- */
isCaseHumide(tmr) {
if (!(TMRUtility.isCaseHumide(tmr) || this.isCaseHumideAdditionelle(tmr))) {
@ -558,16 +576,29 @@ export class RdDTMRDialog extends Dialog {
await this._conquerir(tmr, {
difficulte: -9,
action: 'Conquérir la cité',
onConqueteReussie: r => EffetsDraconiques.fermetureCites.onConquete(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)),
onConqueteEchec: r => this.close(),
onConqueteReussie: r => EffetsDraconiques.fermetureCites.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)),
onConqueteEchec: r => {
this.souffleSiEchecTotal(rollData);
this.close()
},
canClose: false
});
}
}
removeToken(tmr, casetmr) {
this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id);
this.updateTokens()
/* -------------------------------------------- */
async purifierPeriple(tmr) {
if (EffetsDraconiques.periple.find(this.casesSpeciales, tmr.coord)) {
await this._conquerir(tmr, {
difficulte: EffetsDraconiques.periple.getDifficulte(tmr),
action: 'Purifier ' + TMRUtility.getTMRDescr(tmr.coord),
onConqueteReussie: r => EffetsDraconiques.periple.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)),
onConqueteEchec: r => {
this.souffleSiEchecTotal(rollData);
this.close()
},
canClose: false
});
}
}
/* -------------------------------------------- */
@ -576,8 +607,8 @@ export class RdDTMRDialog extends Dialog {
await this._conquerir(tmr, {
difficulte: -7,
action: 'Conquérir',
onConqueteReussie: r => EffetsDraconiques.conquete.onConquete(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)),
onConqueteEchec: r => { },
onConqueteReussie: r => EffetsDraconiques.conquete.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)),
onConqueteEchec: r => this.close(),
canClose: false
});
}
@ -639,23 +670,25 @@ export class RdDTMRDialog extends Dialog {
dialog.render(true);
}
async validerPelerinage(tmr) {
await EffetsDraconiques.pelerinage.onFinPelerinage(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr));
async validerVisite(tmr) {
await EffetsDraconiques.pelerinage.onVisiteSupprimer(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr));
await EffetsDraconiques.urgenceDraconique.onVisiteSupprimer(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr));
}
/* -------------------------------------------- */
async declencheSortEnReserve(coord) {
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coord);
if (sortReserveList.length > 0) {
if (EffetsDraconiques.isSortImpossible(this.actor)) {
let sortsEnCoord = TMRUtility.getSortsReserve(this.sortsReserves, coord);
if (sortsEnCoord.length > 0) {
if (EffetsDraconiques.isSortReserveImpossible(this.actor)) {
ui.notifications.error("Une queue ou un souffle vous empèche de déclencher de sort!");
return;
}
if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord)) {
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 sortReserveList) {
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>";
}
msg += "</ol>";
@ -663,17 +696,16 @@ export class RdDTMRDialog extends Dialog {
content: msg,
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
} else {
await this.processSortReserve(sortReserveList[0]);
return;
}
await this.processSortReserve(sortsEnCoord[0]);
}
}
/* -------------------------------------------- */
lancerSortEnReserve(coord, sortId) {
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coord);
let sortReserve = sortReserveList.find(sortReserve => sortReserve.sort._id == sortId);
//console.log("SORT RESA", sortReserveList, coordTMR, sortId, sortReserve);
let sortEnCoord = TMRUtility.getSortsReserve(this.sortsReserves, coord);
let sortReserve = sortEnCoord.find(sortReserve => sortReserve.sort._id == sortId);
if (sortReserve) {
this.processSortReserve(sortReserve);
} else {
@ -689,7 +721,7 @@ export class RdDTMRDialog extends Dialog {
await this.actor.deleteSortReserve(sortReserve);
//this.updateSortReserve();
console.log("declencheSortEnReserve", sortReserve)
this._tellToGM(`Vous avez déclenché le sort en réserve <strong> ${sortReserve.sort.name}</strong>
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)})
`);
@ -753,7 +785,7 @@ export class RdDTMRDialog extends Dialog {
}
async _onClickTMRPos(eventPos) {
let currentPos = TMRUtility.convertToCellPos(this.actor.data.data.reve.tmrpos.coord);
let currentPos = TMRUtility.convertToCellPos(Misc.data(this.actor).data.reve.tmrpos.coord);
console.log("deplacerDemiReve >>>>", currentPos, eventPos);
@ -819,7 +851,7 @@ export class RdDTMRDialog extends Dialog {
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_tmr_move", data: {
actorId: this.actor.data._id,
tmrPos: this.actor.data.data.reve.tmrpos
tmrPos: Misc.data(this.actor).data.reve.tmrpos
}
});
@ -835,8 +867,9 @@ export class RdDTMRDialog extends Dialog {
if (!(this.viewOnly || this.currentRencontre)) {
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.purifierPeriple(tmr);
await this.conquerirTMR(tmr);
await this.validerPelerinage(tmr);
await this.validerVisite(tmr);
await this.declencheSortEnReserve(tmr.coord);
await this.actor.checkSoufflePeage(tmr);
}

View File

@ -1,43 +1,10 @@
/* Common useful functions shared between objects */
import { RdDRollTables } from "./rdd-rolltables.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDCombat, RdDCombatManager } from "./rdd-combat.js";
import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDCombat } from "./rdd-combat.js";
import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js";
/* -------------------------------------------- */
const categorieCompetences = {
"generale": { level: "-4", label: "Générales" },
"particuliere": { level: "-8", label: "Particulières" },
"specialisee": { level: "-11", label: "Spécialisées" },
"connaissance": { level: "-11", label: "Connaissances" },
"draconic": { level: "-11", label: "Draconics" },
"melee": { level: "-6", label: "Mêlée" },
"tir": { level: "-8", label: "Tir" },
"lancer": { level: "-8", label: "Lancer" }
}
/* -------------------------------------------- */
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 }
];
/* -------------------------------------------- */
// This table starts at 0 -> niveau -10
const carac_array = ["taille", "apparence", "constitution", "force", "agilite", "dexterite", "vue", "ouie", "odoratgout", "volonte", "intellect", "empathie", "reve", "chance", "melee", "tir", "lancer", "derobee"];
@ -45,42 +12,6 @@ const difficultesLibres = [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10];
const ajustementsConditions = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6, +7, +8, +9, +10];
const ajustementsEncaissement = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, +13, +14, +15, +16, +17, +18, +19, +20, +21, +22, +23, +24, +25];
const tableCaracDerivee = {
// xp: coût pour passer du niveau inférieur à ce niveau
1: { xp: 3, poids: "moins de 1kg", plusdom: -5, sconst: 0.5, sust: 0.1 },
2: { xp: 3, poids: "1-5", plusdom: -4, sconst: 0.5, sust: 0.3 },
3: { xp: 4, poids: "6-10", plusdom: -3, sconst: 1, sust: 0.5, beaute: 'hideux' },
4: { xp: 4, poids: "11-20", plusdom: -3, sconst: 1, sust: 1, beaute: 'repoussant' },
5: { xp: 5, poids: "21-30", plusdom: -2, sconst: 1, sust: 1, beaute: 'franchement très laid' },
6: { xp: 5, poids: "31-40", plusdom: -1, sconst: 2, sust: 2, beaute: 'laid' },
7: { xp: 6, poids: "41-50", plusdom: -1, sconst: 2, sust: 2, beaute: 'très désavantagé' },
8: { xp: 6, poids: "51-60", plusdom: 0, sconst: 2, sust: 2, beaute: 'désavantagé' },
9: { xp: 7, poids: "61-65", plusdom: 0, sconst: 3, sust: 2, beaute: 'pas terrible' },
10: { xp: 7, poids: "66-70", plusdom: 0, sconst: 3, sust: 3, beaute: 'commun' },
11: { xp: 8, poids: "71-75", plusdom: 0, sconst: 3, sust: 3, beaute: 'pas mal' },
12: { xp: 8, poids: "76-80", plusdom: +1, sconst: 4, sust: 3, beaute: 'avantagé' },
13: { xp: 9, poids: "81-90", plusdom: +1, sconst: 4, sust: 3, beaute: 'mignon' },
14: { xp: 9, poids: "91-100", plusdom: +2, sconst: 4, sust: 4, beaute: 'beau' },
15: { xp: 10, poids: "101-110", plusdom: +2, sconst: 5, sust: 4, beaute: 'très beau' },
16: { xp: 20, poids: "111-120", plusdom: +3, sconst: 5, sust: 4, beaute: 'éblouissant' },
17: { xp: 30, poids: "121-131", plusdom: +3, sconst: 5, sust: 5 },
18: { xp: 40, poids: "131-141", plusdom: +4, sconst: 6, sust: 5 },
19: { xp: 50, poids: "141-150", plusdom: +4, sconst: 6, sust: 5 },
20: { xp: 60, poids: "151-160", plusdom: +4, sconst: 6, sust: 6 },
21: { xp: 70, poids: "161-180", plusdom: +5, sconst: 7, sust: 6 },
22: { xp: 80, poids: "181-200", plusdom: +5, sconst: 7, sust: 7 },
23: { xp: 90, poids: "201-300", plusdom: +6, sconst: 7, sust: 8 },
24: { xp: 100, poids: "301-400", plusdom: +6, sconst: 8, sust: 9 },
25: { xp: 110, poids: "401-500", plusdom: +7, sconst: 8, sust: 10 },
26: { xp: 120, poids: "501-600", plusdom: +7, sconst: 8, sust: 11 },
27: { xp: 130, poids: "601-700", plusdom: +8, sconst: 9, sust: 12 },
28: { xp: 140, poids: "701-800", plusdom: +8, sconst: 9, sust: 13 },
29: { xp: 150, poids: "801-900", plusdom: +9, sconst: 9, sust: 14 },
30: { xp: 160, poids: "901-1000", plusdom: +9, sconst: 10, sust: 15 },
31: { xp: 170, poids: "1001-1500", plusdom: +10, sconst: 10, sust: 16 },
32: { xp: 180, poids: "1501-2000", plusdom: +11, sconst: 10, sust: 17 }
}
/* -------------------------------------------- */
function _buildAllSegmentsFatigue(max) {
const cycle = [5, 2, 4, 1, 3, 0];
@ -170,6 +101,8 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/actor-vehicule-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet-competence-partial.html',
'systems/foundryvtt-reve-de-dragon/templates/actor-liste-blessures-partial.html',
'systems/foundryvtt-reve-de-dragon/templates/actor-blessure-partial.html',
//Items
'systems/foundryvtt-reve-de-dragon/templates/item-competence-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/item-competencecreature-sheet.html',
@ -260,10 +193,6 @@ export class RdDUtility {
return loadTemplates(templatePaths);
}
/* -------------------------------------------- */
static getLimitesArchetypes() {
return duplicate(limitesArchetypes);
}
/* -------------------------------------------- */
static checkNull(items) {
@ -296,32 +225,40 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static filterItemsPerTypeForSheet(data) {
data.data.materiel = this.checkNull(data.itemsByType['objet']);
data.data.conteneurs = this.checkNull(data.itemsByType['conteneur']);
data.data.armes = this.checkNull(data.itemsByType['arme']);
data.data.armures = this.checkNull(data.itemsByType['armure']);
data.data.livres = this.checkNull(data.itemsByType['livre']);
data.data.potions = this.checkNull(data.itemsByType['potion']);
data.data.ingredients = this.checkNull(data.itemsByType['ingredient']);
data.data.munitions = this.checkNull(data.itemsByType['munition']);
data.data.herbes = this.checkNull(data.itemsByType['herbe']);
data.data.sorts = this.checkNull(data.itemsByType['sort']);
data.data.queues = this.checkNull(data.itemsByType['queue']);
data.data.souffles = this.checkNull(data.itemsByType['souffle']);
data.data.ombres = this.checkNull(data.itemsByType['ombre']);
data.data.tetes = this.checkNull(data.itemsByType['tete']);
data.data.taches = this.checkNull(data.itemsByType['tache']);
data.data.monnaie = this.checkNull(data.itemsByType['monnaie']);
data.data.meditations = this.checkNull(data.itemsByType['meditation']);
data.data.chants = this.checkNull(data.itemsByType['chant']);
data.data.danses = this.checkNull(data.itemsByType['danse']);
data.data.musiques = this.checkNull(data.itemsByType['musique']);
data.data.oeuvres = this.checkNull(data.itemsByType['oeuvre']);
data.data.jeux = this.checkNull(data.itemsByType['jeu']);
data.data.recettescuisine = this.checkNull(data.itemsByType['recettecuisine']);
data.data.recettesAlchimiques = this.checkNull(data.itemsByType['recettealchimique']);
data.data.objets = data.data.conteneurs.concat(data.data.materiel).concat(data.data.armes).concat(data.data.armures).concat(data.data.munitions).concat(data.data.livres).concat(data.data.potions).concat(data.data.herbes).concat(data.data.ingredients);
static filterItemsPerTypeForSheet(formData) {
formData.data.materiel = this.checkNull(formData.itemsByType['objet']);
formData.data.conteneurs = this.checkNull(formData.itemsByType['conteneur']);
formData.data.armes = this.checkNull(formData.itemsByType['arme']);
formData.data.armures = this.checkNull(formData.itemsByType['armure']);
formData.data.livres = this.checkNull(formData.itemsByType['livre']);
formData.data.potions = this.checkNull(formData.itemsByType['potion']);
formData.data.ingredients = this.checkNull(formData.itemsByType['ingredient']);
formData.data.munitions = this.checkNull(formData.itemsByType['munition']);
formData.data.herbes = this.checkNull(formData.itemsByType['herbe']);
formData.data.sorts = this.checkNull(formData.itemsByType['sort']);
formData.data.queues = this.checkNull(formData.itemsByType['queue']);
formData.data.souffles = this.checkNull(formData.itemsByType['souffle']);
formData.data.ombres = this.checkNull(formData.itemsByType['ombre']);
formData.data.tetes = this.checkNull(formData.itemsByType['tete']);
formData.data.taches = this.checkNull(formData.itemsByType['tache']);
formData.data.monnaie = this.checkNull(formData.itemsByType['monnaie']);
formData.data.meditations = this.checkNull(formData.itemsByType['meditation']);
formData.data.chants = this.checkNull(formData.itemsByType['chant']);
formData.data.danses = this.checkNull(formData.itemsByType['danse']);
formData.data.musiques = this.checkNull(formData.itemsByType['musique']);
formData.data.oeuvres = this.checkNull(formData.itemsByType['oeuvre']);
formData.data.jeux = this.checkNull(formData.itemsByType['jeu']);
formData.data.recettescuisine = this.checkNull(formData.itemsByType['recettecuisine']);
formData.data.recettesAlchimiques = this.checkNull(formData.itemsByType['recettealchimique']);
formData.data.objets = formData.data.conteneurs.concat(formData.data.materiel)
.concat(formData.data.armes)
.concat(formData.data.armures)
.concat(formData.data.munitions)
.concat(formData.data.livres)
.concat(formData.data.potions)
.concat(formData.data.herbes)
.concat(formData.data.ingredients);
formData.data.competences = (formData.itemsByType.competence??[]).concat(formData.itemsByType.competencecreature??[]);
}
/* -------------------------------------------- */
@ -403,15 +340,6 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static getCategorieCompetences() {
return categorieCompetences;
}
static getLevelCategory(category) {
return categorieCompetences[category].level;
}
static getLabelCategory(category) {
return categorieCompetences[category].label;
}
static getCaracArray() {
return carac_array;
}
@ -429,53 +357,6 @@ export class RdDUtility {
return definitionsBlessures;
}
/* -------------------------------------------- */
static getCaracNextXp(value) {
const nextValue = Number(value) + 1;
// xp est le coût pour atteindre cette valeur, on regarde donc le coût de la valeur+1
return RdDUtility.getCaracXp(nextValue);
}
static getCaracXp(targetValue) {
return tableCaracDerivee[targetValue]?.xp ?? 200;
}
/* -------------------------------------------- */
static computeCarac(data) {
data.carac.force.value = Math.min(data.carac.force.value, parseInt(data.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);
bonusDomKey = Math.min(Math.max(bonusDomKey, 0), 32); // Clamp de securite
let tailleData = tableCaracDerivee[bonusDomKey];
data.attributs.plusdom.value = tailleData.plusdom;
data.attributs.sconst.value = RdDUtility.calculSConst(data.carac.constitution.value);
data.attributs.sust.value = tableCaracDerivee[Number(data.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);
data.sante.vie.max = Math.ceil((parseInt(data.carac.taille.value) + parseInt(data.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);
//Compteurs
data.reve.reve.max = data.carac.reve.value;
data.compteurs.chance.max = data.carac.chance.value;
}
static calculSConst(constitution) {
return Number(tableCaracDerivee[Number(constitution)].sconst);
}
/* -------------------------------------------- */
static getSegmentsFatigue(maxEnd) {
maxEnd = Math.max(maxEnd, 1);
@ -606,8 +487,10 @@ export class RdDUtility {
/* -------------------------------------------- */
static async loadCompendium(compendium, filter = item => true) {
if (!compendium){
return [];
}
let compendiumItems = await RdDUtility.loadCompendiumNames(compendium);
const pack = game.packs.get(compendium);
let list = [];
for (let compendiumItem of compendiumItems) {
@ -672,7 +555,10 @@ export class RdDUtility {
// Gestion du bouton payer
html.on("click", '#payer-button', event => {
let sumdenier = event.currentTarget.attributes['data-somme-denier'].value;
let quantite = event.currentTarget.attributes['data-quantite'].value;
let quantite = 1;
if ( event.currentTarget.attributes['data-quantite'] ) {
quantite = event.currentTarget.attributes['data-quantite'].value;
}
let jsondata = event.currentTarget.attributes['data-jsondata']
let objData
if (jsondata) {
@ -814,15 +700,19 @@ export class RdDUtility {
/* -------------------------------------------- */
static afficherHeuresChanceMalchance(heureNaissance) {
if (heureNaissance) {
let ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance);
ChatMessage.create({
content: `A l'heure ${game.system.rdd.calendrier.getCurrentHeure()}, le modificateur de Chance/Malchance pour l'heure de naissance ${heureNaissance} est de : ${ajustement}.`,
whisper: ChatMessage.getWhisperRecipients("MJ")
});
}
else {
ui.notifications.warn("Pas d'heure de naissance selectionnée")
if ( game.user.isGM) {
if (heureNaissance) {
let ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance);
ChatMessage.create({
content: `A l'heure ${game.system.rdd.calendrier.getCurrentHeure()}, le modificateur de Chance/Malchance pour l'heure de naissance ${heureNaissance} est de : ${ajustement}.`,
whisper: ChatMessage.getWhisperRecipients("GM")
});
}
else {
ui.notifications.warn("Pas d'heure de naissance selectionnée")
}
} else {
ui.notifications.warn("Vous n'avez pas accès à cette commande")
}
}

View File

@ -50,14 +50,14 @@ export class ReglesOptionelles extends FormApplication {
}
getData() {
let data = super.getData();
data.regles = listeReglesOptionelles.map(it => {
let formData = super.getData();
formData.regles = listeReglesOptionelles.map(it => {
let r = duplicate(it);
r.id = ReglesOptionelles._getIdRegle(r.name);
r.active = ReglesOptionelles.isUsing(r.name);
return r;
})
return data;
return formData;
}
static isUsing(name) {

View File

@ -18,9 +18,9 @@ import { ReglesOptionelles } from "./regles-optionelles.js";
*/
export const referenceAjustements = {
competence: {
isUsed: (rollData, actor) => rollData.competence,
getLabel: (rollData, actor) => rollData.competence?.name,
getValue: (rollData, actor) => rollData.competence?.data?.niveau,
isUsed: (rollData, actor) => Misc.data(rollData.competence),
getLabel: (rollData, actor) => Misc.data(rollData.competence)?.name,
getValue: (rollData, actor) => Misc.data(rollData.competence)?.data?.niveau,
},
meditation: {
isUsed: (rollData, actor) => rollData.meditation,
@ -32,7 +32,7 @@ export const referenceAjustements = {
getLabel: (rollData, actor) => rollData.selectedSort?.name ?? rollData.attackerRoll ? 'Imposée' : 'Libre',
getValue: (rollData, actor) => rollData.selectedSort
? RdDItemSort.getDifficulte(rollData.selectedSort, rollData.diffLibre)
: rollData.diffLibre ?? rollData.competence?.data.default_diffLibre ?? 0
: rollData.diffLibre ?? Misc.data(rollData.competence)?.data.default_diffLibre ?? 0
},
diffConditions: {
isUsed: (rollData, actor) => rollData.diffConditions != undefined,
@ -73,8 +73,8 @@ export const referenceAjustements = {
getValue: (rollData, actor) => actor.getSurenc()
},
moral: {
isVisible: (rollData, actor) => RdDCarac.isActionPhysique(rollData.selectedCarac),
isUsed: (rollData, actor) => rollData.use?.appelAuMoral,
isVisible: (rollData, actor) => actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac) && rollData.useMoral,
isUsed: (rollData, actor) => rollData.useMoral,
getLabel: (rollData, actor) => 'Appel au moral',
getValue: (rollData, actor) => 1
},
@ -94,10 +94,10 @@ export const referenceAjustements = {
getDescr: (rollData, actor) => rollData.diviseurSignificative > 1 ? `Facteur significative <span class="rdd-diviseur">&times;${Misc.getFractionHtml(rollData.diviseurSignificative)}</span>` : ''
},
isEcaille: {
isVisible: (rollData, actor) => rollData.arme?.data.magique && Number(rollData.arme?.data.ecaille_efficacite) > 0,
isUsed: (rollData, actor) => rollData.arme?.data.magique && Number(rollData.arme?.data.ecaille_efficacite) > 0,
isVisible: (rollData, actor) => Misc.data(rollData.arme)?.data.magique && Number(Misc.data(rollData.arme)?.data.ecaille_efficacite) > 0,
isUsed: (rollData, actor) => Misc.data(rollData.arme)?.data.magique && Number(Misc.data(rollData.arme)?.data.ecaille_efficacite) > 0,
getLabel: (rollData, actor) => "Ecaille d'Efficacité: ",
getValue: (rollData, actor) => (rollData.arme?.data.magique && Number(rollData.arme.data.ecaille_efficacite) > 0) ? rollData.arme.data.ecaille_efficacite : 0,
getValue: (rollData, actor) => Math.max(Number(Misc.data(rollData.arme)?.data.ecaille_efficacite), 0),
},
finesse: {
isUsed: (rollData, actor) => RdDBonus.isDefenseAttaqueFinesse(rollData),

View File

@ -103,9 +103,9 @@ class StatusEffectsSettings extends FormApplication {
}
getData() {
let data = super.getData();
data.effects = CONFIG.RDD.allEffects;
return data;
let formData = super.getData();
formData.effects = CONFIG.RDD.allEffects;
return formData;
}
activateListeners(html) {

View File

@ -1,4 +1,3 @@
import { DeDraconique } from "./de-draconique.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { TMRUtility } from "./tmr-utility.js";
@ -269,7 +268,7 @@ const rencontresStandard = [
{ code: "reflet", name: "Reflet d'ancien Rêve", type: "reflet", genre: "m", force: "2d6", isPersistant: true },
{ code: "tbblanc", name: "Tourbillon blanc", type: "tbblanc", genre: "m", force: "2d6", isPersistant: true },
{ code: "tbnoir", name: "Tourbillon noir", type: "tbnoir", genre: "m", force: "2d8", isPersistant: true },
{ code: "rdd", name: "Rêve de Dragon", type: "rdd", genre: "m", force: "1ddr + 7", refoulement: 2, quitterTMR: true }
{ code: "rdd", name: "Rêve de Dragon", type: "rdd", genre: "m", force: "1dr + 7", refoulement: 2, quitterTMR: true }
];
const rencontresPresentCite = [
@ -381,13 +380,7 @@ export class TMRRencontres {
/* -------------------------------------------- */
static async evaluerForceRencontre(rencontre) {
if (TMRRencontres.isReveDeDragon(rencontre)) {
const ddr = await DeDraconique.ddr("selfroll")
rencontre.force = 7 + ddr.total;
}
else {
rencontre.force = new Roll(rencontre.force).evaluate().total;
}
rencontre.force = new Roll(rencontre.force).evaluate().total;
return rencontre.force;
}

View File

@ -210,8 +210,8 @@ const TMRMapping = {
export const TMRType = {
cite: { name: "cité", genre: "f" },
sanctuaire: { name: "sanctuaire" },
plaines: { name: "plaines", genre: "p" },
sanctuaire: { name: "sanctuaire", genre: 'm' },
plaines: { name: "plaines", genre: "fp" },
pont: { name: "pont", genre: "m" },
collines: { name: "collines", genre: "p" },
foret: { name: "forêt", genre: "f" },
@ -332,7 +332,12 @@ export class TMRUtility {
}
static getTMRLabel(coord) {
return TMRMapping[coord]?.label ?? (coord+": case inconnue");
return TMRMapping[coord]?.label ?? (coord + ": case inconnue");
}
static getTMRDescr(coord) {
const tmr = TMRMapping[coord];
return Grammar.articleDetermine(tmr.genre) + ' ' + tmr.label;
}
static isCaseHumide(tmr) {
@ -384,7 +389,7 @@ export class TMRUtility {
currentPos.x = currentPos.x + direction.x;
currentPos.y = currentPos.y + direction.y;
if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Sortie de carte ! Ré-insertion aléatoire
coord = TMRUtility.getTMR(TMRUtility.convertToTMRCoord(currentPos));
coord = TMRUtility.getTMR(TMRUtility.convertToTMRCoord(currentPos));
} else {
coord = await actor.reinsertionAleatoire('Sortie de carte');
}
@ -427,7 +432,7 @@ export class TMRUtility {
}
/* -------------------------------------------- */
static getSortReserveList(reserveList, coord) {
static getSortsReserve(reserveList, coord) {
// TODO : Gérer les têtes spéciales réserve!
let tmrDescr = this.getTMR(coord);
//console.log("Sort réserve : ", tmrDescr);
@ -435,7 +440,7 @@ export class TMRUtility {
return reserveList.filter(it => TMRUtility.getTMR(it.coord).type == 'fleuve');
}
// Reserve sur un case "normale"
return reserveList.filter(it => it.coord == coord);
return reserveList.filter(it => it.coord == coord);
}
/* -------------------------------------------- */
@ -450,9 +455,8 @@ export class TMRUtility {
for (let dy = -portee; dy <= portee; dy++) { // Loop thru lines
const currentPos = { x: centerPos.x + dx, y: centerPos.y + dy };
if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Coordinate is valie
let posPicNow = this.computeRealPictureCoordinates(currentPos, tmrConstants);
let dist = Math.sqrt(Math.pow(posPicNow.x - posPic.x, 2) + Math.pow(posPicNow.y - posPic.y, 2)) / tmrConstants.cellw;
if (dist < portee + 0.5) {
let dist = this.distancePosTMR(centerPos, currentPos);
if (dist <= portee) {
caseList.push(this.convertToTMRCoord(currentPos)); // Inside the area
}
}
@ -460,5 +464,21 @@ export class TMRUtility {
}
return caseList;
}
static distanceTMR(coord1, coord2) {
let pos1 = this.convertToCellPos(coord1);
let pos2 = this.convertToCellPos(coord2);
return this.distancePosTMR(pos1, pos2);
}
static distancePosTMR(pos1, pos2) {
const dx = pos2.x - pos1.x;
const dy = pos2.y - pos1.y;
const abs_dx = Math.abs(dx);
const abs_dy = Math.abs(dy);
const distance = Math.sign(dx) == Math.sign(dy) ? Math.max(abs_dx, abs_dy) : (abs_dx + abs_dy);
return distance;
}
}

View File

@ -35,11 +35,8 @@ export class Conquete extends Draconique {
await this.createCaseTmr(actor, 'Conquête: ' + conquete.label, conquete, queue._id);
}
async onConquete(actor, tmr, onRemoveToken) {
let existants = actor.data.items.filter(it => this.isCase(it, tmr.coord));
for (let casetmr of existants) {
await actor.deleteOwnedItem(casetmr.data.sourceid);
onRemoveToken(tmr, casetmr);
}
async onActorDeleteCaseTmr(actor, casetmr) {
await actor.deleteOwnedItem(casetmr.data.sourceid);
}
}

View File

@ -24,10 +24,6 @@ export class Desorientation extends Draconique {
return Object.keys(TMRType).filter(it => !dejaDesorientes.includes(it));
}
async onActorDeleteOwned(actor, souffle) {
await this._supprimerCasesTmr(actor, souffle);
}
code() { return 'desorientation' }
tooltip(linkData) { return `Désorientation, cette case n'existe plus !` }
img() { return 'icons/svg/explosion.svg' }
@ -51,11 +47,5 @@ export class Desorientation extends Draconique {
}
}
async _supprimerCasesTmr(actor, souffle) {
let caseTmrs = actor.data.items.filter(it => it.data.sourceId == souffle._id);
for (let casetmr of caseTmrs) {
await actor.deleteOwnedItem(casetmr._id);
}
}
}

View File

@ -7,8 +7,7 @@ const registeredEffects = [
/**
* Définition des informations d'une "draconique" (queue, ombre, tête, souffle) qui influence les TMR
*/
export class Draconique
{
export class Draconique {
static isCaseTMR(element) { return element.type == 'casetmr'; }
static isQueueDragon(element) { return element.type == 'queue' || element.type == 'ombre'; }
static isSouffleDragon(element) { return element.type == 'souffle'; }
@ -28,6 +27,7 @@ export class Draconique
static all() {
return Object.values(registeredEffects);
}
static get(code) {
return registeredEffects[code];
}
@ -56,10 +56,11 @@ export class Draconique
}
async onActorDeleteOwned(actor, item) {
let caseTmrs = actor.data.items.filter(it => this.isCase(it) && it.data.sourceid == item._id);
for (let casetmr of caseTmrs) {
await actor.deleteOwnedItem(casetmr._id);
}
this.deleteCasesTmr(actor, item);
return false;
}
async onActorDeleteCaseTmr(actor, casetmr) {
return false;
}
/**
@ -102,7 +103,7 @@ export class Draconique
if (this.img()) {
return pixiTMR.sprite(this.code());
}
else{
else {
return pixiTMR.circle()
}
}
@ -120,11 +121,25 @@ export class Draconique
return list.find(c => this.isCase(c, coord));
}
async createCaseTmr(actor, label, tmr, sourceId=undefined) {
async createCaseTmr(actor, label, tmr, sourceId = undefined) {
await actor.createOwnedItem({
name: label, type: 'casetmr', img: this.img(), _id: randomID(16),
data: { coord: tmr.coord, specific: this.code(), sourceid:sourceId }
data: { coord: tmr.coord, specific: this.code(), sourceid: sourceId }
});
}
async deleteCasesTmr(actor, draconique) {
let caseTmrs = actor.data.items.filter(it => this.isCase(it) && it.data.sourceid == draconique._id);
for (let casetmr of caseTmrs) {
await actor.deleteOwnedItem(casetmr._id);
}
}
async onVisiteSupprimer(actor, tmr, onRemoveToken) {
let existants = actor.data.items.filter(it => this.isCase(it, tmr.coord));
for (let casetmr of existants) {
await actor.deleteOwnedItem(casetmr._id);
onRemoveToken(tmr, casetmr);
}
}
}

View File

@ -14,6 +14,9 @@ import { PresentCites } from "./present-cites.js";
import { Desorientation } from "./desorientation.js";
import { Conquete } from "./conquete.js";
import { Pelerinage } from "./pelerinage.js";
import { Periple } from "./periple.js";
import { UrgenceDraconique } from "./urgence-draconique.js";
import { Misc } from "../misc.js";
export class EffetsDraconiques {
@ -32,6 +35,8 @@ export class EffetsDraconiques {
static desorientation = new Desorientation();
static conquete = new Conquete();
static pelerinage = new Pelerinage();
static periple = new Periple();
static urgenceDraconique = new UrgenceDraconique();
static init() {
Draconique.register(EffetsDraconiques.carteTmr);
@ -49,6 +54,8 @@ export class EffetsDraconiques {
Draconique.register(EffetsDraconiques.desorientation);
Draconique.register(EffetsDraconiques.conquete);
Draconique.register(EffetsDraconiques.pelerinage);
Draconique.register(EffetsDraconiques.periple);
Draconique.register(EffetsDraconiques.urgenceDraconique);
}
/* -------------------------------------------- */
@ -86,87 +93,100 @@ export class EffetsDraconiques {
return EffetsDraconiques.presentCites.isCase(caseTMR, coord);
}
/* -------------------------------------------- */
static isMauvaiseRencontre(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueSouffle(it) && it.name.toLowerCase().includes('mauvaise rencontre'));
static isMauvaiseRencontre(item) {
return EffetsDraconiques.isMatching(item, it => Draconique.isQueueSouffle(it) && it.name.toLowerCase().includes('mauvaise rencontre'));
}
static isMonteeLaborieuse(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueSouffle(it) && it.name.toLowerCase().includes('montée laborieuse'));
static isMonteeLaborieuse(item) {
return EffetsDraconiques.isMatching(item, it => Draconique.isQueueSouffle(it) && it.name.toLowerCase().includes('montée laborieuse'));
}
/* -------------------------------------------- */
static isFermetureCite(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.fermetureCites.match(it));
static isFermetureCite(item) {
return EffetsDraconiques.isMatching(item, it => EffetsDraconiques.fermetureCites.match(it));
}
static isPontImpraticable(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.pontImpraticable.match(it));
static isPontImpraticable(item) {
return EffetsDraconiques.isMatching(item, it => EffetsDraconiques.pontImpraticable.match(it));
}
static isDoubleResistanceFleuve(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase().includes('résistance du fleuve'));
static isDoubleResistanceFleuve(item) {
return EffetsDraconiques.isMatching(item, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase().includes('résistance du fleuve'));
}
static isPeage(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase().includes('péage'));
static isPeage(item) {
return EffetsDraconiques.isMatching(item, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase().includes('péage'));
}
static isPeriple(element) {
// TODO
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && ir.name.toLowerCase() == 'périple');
static isPeriple(item) {
return EffetsDraconiques.isMatching(item, it => EffetsDraconiques.periple.match(it));
}
static isDesorientation(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.desorientation.match(it)); // TODO
static isDesorientation(item) {
return EffetsDraconiques.isMatching(item, it => EffetsDraconiques.desorientation.match(it)); // TODO
}
/* -------------------------------------------- */
static isSortImpossible(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.conquete.match(it) || EffetsDraconiques.pelerinage.match(it));
static isSortImpossible(item) {
return EffetsDraconiques.isMatching(item, it =>
EffetsDraconiques.conquete.match(it) ||
EffetsDraconiques.periple.match(it) ||
EffetsDraconiques.urgenceDraconique.match(it) ||
EffetsDraconiques.pelerinage.match(it)
);
}
static isConquete(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.conquete.match(it));
static isSortReserveImpossible(item) {
return EffetsDraconiques.isMatching(item, it =>
EffetsDraconiques.conquete.match(it) ||
EffetsDraconiques.periple.match(it) ||
EffetsDraconiques.pelerinage.match(it)
);
}
static isPelerinage(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.pelerinage.match(it));
static isConquete(item) {
return EffetsDraconiques.isMatching(item, it => EffetsDraconiques.conquete.match(it));
}
static countInertieDraconique(element) {
return EffetsDraconiques.count(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase().includes('inertie draconique'));
static isPelerinage(item) {
return EffetsDraconiques.isMatching(item, it => EffetsDraconiques.pelerinage.match(it));
}
static isUrgenceDraconique(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase() == 'urgence draconique');
static countInertieDraconique(item) {
return EffetsDraconiques.count(item, it => Draconique.isQueueDragon(it) && it.name.toLowerCase().includes('inertie draconique'));
}
static isUrgenceDraconique(item) {
return EffetsDraconiques.isMatching(item, it => EffetsDraconiques.urgenceDraconique.match(it));
}
/* -------------------------------------------- */
static isDonDoubleReve(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name == 'Don de double-rêve');
static isDonDoubleReve(item) {
return EffetsDraconiques.isMatching(item, it => Draconique.isTeteDragon(it) && it.name == 'Don de double-rêve');
}
static isConnaissanceFleuve(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes('connaissance du fleuve'));
static isConnaissanceFleuve(item) {
return EffetsDraconiques.isMatching(item, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes('connaissance du fleuve'));
}
static isReserveEnSecurite(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes(' en sécurité'));
static isReserveEnSecurite(item) {
return EffetsDraconiques.isMatching(item, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes(' en sécurité'));
}
static isDeplacementAccelere(element) {
return EffetsDraconiques.isMatching(element, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes(' déplacement accéléré'));
static isDeplacementAccelere(item) {
item = Misc.data(item);
return EffetsDraconiques.isMatching(item, it => Draconique.isTeteDragon(it) && it.name.toLowerCase().includes(' déplacement accéléré'));
}
static isMatching(element, matcher) {
return EffetsDraconiques.toItems(element).find(matcher);
static isMatching(item, matcher) {
return EffetsDraconiques.toItems(item).find(matcher);
}
static count(element, matcher) {
return EffetsDraconiques.toItems(element).filter(matcher).length;
static count(item, matcher) {
return EffetsDraconiques.toItems(item).filter(matcher).length;
}
static toItems(element) {
return (element?.entity === 'Actor') ? element.data.items : (element?.entity === 'Item') ? [element] : [];
static toItems(item) {
return (item?.entity === 'Actor') ? item.data.items : (item?.entity === 'Item') ? [Misc.data(item)] : [];
}
}

View File

@ -35,11 +35,4 @@ export class FermetureCites extends Draconique {
await this.createCaseTmr(actor, 'Fermeture: ' + tmr.label, tmr, souffle._id);
}
}
async onConquete(actor, tmr, onRemoveToken) {
const citeFermee = actor.data.items.find(it => this.isCase(it, tmr.coord));
await actor.deleteOwnedItem(citeFermee._id);
onRemoveToken(tmr, citeFermee);
}
}

View File

@ -31,12 +31,8 @@ export class Pelerinage extends Draconique {
});
}
async onFinPelerinage(actor, tmr, onRemoveToken) {
const pelerinages = actor.data.items.filter(it => this.isCase(it, tmr.coord));
for (let p of pelerinages){
await actor.deleteOwnedItem(p.data.sourceid);
onRemoveToken(tmr, p);
}
async onActorDeleteCaseTmr(actor, casetmr) {
await actor.deleteOwnedItem(casetmr.data.sourceid);
}
}

44
module/tmr/periple.js Normal file
View File

@ -0,0 +1,44 @@
import { Grammar } from "../grammar.js";
import { tmrColors, tmrConstants, tmrTokenZIndex, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class Periple extends Draconique {
constructor() {
super();
}
type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('periple'); }
manualMessage() { return false }
async onActorCreateOwned(actor, souffle) {
let terrain = new Roll("1d2").evaluate().total == 1 ? 'sanctuaire' : 'necropole';
let tmrs = TMRUtility.getListTMR(terrain);
for (let tmr of tmrs) {
await this.createCaseTmr(actor, 'Périple: ' + tmr.label, tmr, souffle._id);
}
}
code() { return 'periple' }
tooltip(linkData) { return `Votre Périple passe par ${this.tmrLabel(linkData)}` }
img() { return 'icons/svg/acid.svg' }
createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(), {
zIndex: tmrTokenZIndex.conquete,
alpha: 1,
color: tmrColors.souffle,
taille: tmrConstants.twoThird,
decallage: tmrConstants.right
});
}
getDifficulte(tmr) {
switch (tmr.type) {
case 'sanctuaire': return -3;
case 'necropole': return -5;
}
return 0;
}
}

View File

@ -13,7 +13,7 @@ export class Rencontre extends Draconique {
async onActorCreateOwned(actor, item) { }
code() { return 'rencontre' }
tooltip(linkData) { return `${linkData.name} de force ${linkData.force}` }
tooltip(linkData) { return `${linkData.rencontre.name} de force ${linkData.rencontre.force}` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/heures/hd06.svg' }
createSprite(pixiTMR) {

View File

@ -0,0 +1,55 @@
import { ChatUtility } from "../chat-utility.js";
import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js";
import { RdDRollTables } from "../rdd-rolltables.js";
import { tmrColors, tmrConstants, tmrTokenZIndex, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class UrgenceDraconique extends Draconique {
constructor() {
super();
}
type() { return 'queue' }
match(item) { return Draconique.isQueueDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('urgence draconique'); }
manualMessage() { return false }
async onActorCreateOwned(actor, queue) {
let coordSortsReserve = (actor.data.data.reve.reserve?.list.map(it => it.coord)) ?? [];
if (coordSortsReserve.length == 0) {
// La queue se transforme en idée fixe
let ideeFixe = await RdDRollTables.getIdeeFixe();
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: `En l'absence de sorts en réserve, l'urgence draconique de ${actor.name} se transforme en ${queue.name}`
});
await actor.createOwnedItem(ideeFixe);
await actor.deleteOwnedItem(queue._id);
return;
}
else {
let demiReve = actor.getDemiReve();
coordSortsReserve.sort((a, b) => TMRUtility.distanceTMR(a, demiReve) - TMRUtility.distanceTMR(b, demiReve));
let tmr = TMRUtility.getTMR(coordSortsReserve[0]);
await this.createCaseTmr(actor, 'Urgence draconique: ' + tmr.label, tmr, queue._id);
}
}
async onActorDeleteCaseTmr(actor, casetmr) {
await actor.deleteOwnedItem(casetmr.data.sourceid);
}
code() { return 'urgence' }
tooltip(linkData) { return `Urgence draconique!` }
img() { return 'icons/svg/hazard.svg' }
createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
zIndex: tmrTokenZIndex.conquete,
color: tmrColors.queues,
taille: tmrConstants.full,
decallage: { x: 2, y: 0 }
});
}
}

File diff suppressed because one or more lines are too long

View File

@ -34,7 +34,6 @@
{"_id":"CMtQM06J3BZsHHxH","name":"Sandales","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.1,"equipe":false,"resistance":0,"qualite":0,"cout":0.3},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/sandales.webp","effects":[]}
{"_id":"CQSxJv1mgmIeMCbM","name":"Grappin","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.5,"equipe":false,"resistance":0,"qualite":0,"cout":2},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/grappin.webp","effects":[]}
{"_id":"D5Z3FaUv91B8eCOP","name":"Obyssum vert","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"ingredient","data":{"description":"<p>Poudre verd&acirc;tre apparaissant sur les tiges de certains roseaux.</p>\n<p>VUE/Alchimie &agrave; -2</p>","niveau":0,"encombrement":0.001,"base":0,"quantite":1,"milieu":"Lieux humides","rarete":"","categorie":"Alchimie","cout":0.05},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/l_obyssum_vert.webp","effects":[]}
{"_id":"ESU3IRLnBrFznFa3","name":"Dragons (pièces d'or)","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.01,"equipe":false,"resistance":0,"qualite":0,"cout":10},"flags":{},"img":"icons/commodities/currency/coins-plain-stack-gold.webp","effects":[]}
{"_id":"ElweMV283IUpqaik","name":"Sable-Poudre","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"potion","data":{"description":"<p>Granul&eacute;s. Poudre blanche.</p>","quantite":1,"encombrement":0.1,"rarete":"","categorie":"Alchimie","cout":2},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/sable_poudre.webp","effects":[]}
{"_id":"Eospy1EFNlhgOyXc","name":"Lacet de cuir (1 m)","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.01,"equipe":false,"resistance":0,"qualite":0,"cout":0.06},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/lacet.webp","effects":[]}
{"_id":"F0hcXfGaaYKQ0229","name":"Narcos, voie des Sortilèges","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"livre","data":{"description":"<p>Ce tome imposant, ouvertement destin&eacute; aux haut-r&ecirc;vants, r&eacute;v&egrave;le que la voie de Narcos ne poss&egrave;de pas que des rituels, mais &eacute;galement des sortil&egrave;ges. En saisir le sens demande toutefois un minimum de +4 en voie de Narcos. Il permet de comprendre le principe des sorts de transformation et d&rsquo;envisager la synth&egrave;se de <em>Fl&egrave;che de feu</em>,<em> Dague de force</em>, <em>Dragonne lame</em> et <em>Gourdindragon</em>. Sans son assimilation pr&eacute;alable, la synth&egrave;se de ces sorts est totalement inenvisageable. Sa difficult&eacute; de lecture est de -6, son assimilation requiert 28 points de t&acirc;che, p&eacute;riodicit&eacute; 1 heure.</p>","auteur":"Segamor le Transformiste","quantite":1,"difficulte":-6,"points_de_tache":28,"encombrement":0,"xp":"","cout":0,"competence":""},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_narcos.png","effects":[]}
@ -78,7 +77,6 @@
{"_id":"PrnJrG50u1UPdlJN","name":"Liqueur de Bagdol","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"potion","data":{"description":"<p>Fluide. Liquide noir et odorant.</p>","quantite":1,"encombrement":0.1,"rarete":"","categorie":"Alchimie","cout":0.5},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/liqueur_de_bagdol.webp","effects":[]}
{"_id":"PuuPn6WGfU8uBAyb","name":"Robe de soie","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0,"equipe":false,"resistance":0,"qualite":0,"cout":10},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/robe_soie.webp","effects":[]}
{"name":"Bâton","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"arme","data":{"categorie_parade":"","description":"","quantite":1,"encombrement":2,"equipe":false,"dommages":"1","penetration":0,"force":"9","resistance":8,"competence":"Masse à 2 mains","cout":0.5,"portee_courte":0,"magique":false,"ecaille_efficacite":null,"resistance_magique":null,"portee_moyenne":0,"portee_extreme":0,"rapide":false,"deuxmains":true,"unemain":false,"initpremierround":"baton"},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/baton.webp","effects":[],"_id":"Qh4Tp7lZ6wLnX4w0"}
{"_id":"RC1co7jmHMDqlJGy","name":"Deniers (pièces d'étain)","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.005,"equipe":false,"resistance":0,"qualite":0,"cout":0.01},"flags":{},"img":"icons/commodities/currency/coins-assorted-mix-platinum.webp","effects":[]}
{"_id":"RGdDQ3yJYMkSuA5G","name":"Provisions cuites (1 sust)","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"<p>pain, fromage, viande s&eacute;ch&eacute;e...</p>","quantite":1,"encombrement":0.1,"equipe":false,"resistance":0,"qualite":0,"cout":0.02},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/provision_cuite.webp","effects":[]}
{"_id":"RKr1ZhTvC6poiNa1","name":"Gros Clou","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.1,"equipe":false,"resistance":0,"qualite":0,"cout":0.05},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/gros_clou.webp","effects":[]}
{"_id":"RNxCQWMDy06uQ8uj","name":"Ecuelle de fer","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.1,"equipe":false,"resistance":0,"qualite":0,"cout":0.15},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/ecuelle_fer.webp","effects":[]}
@ -93,7 +91,6 @@
{"_id":"Sm28dG9isppoQzPQ","name":"Bas de lin","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0,"equipe":false,"resistance":0,"qualite":0,"cout":0.3},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/bas_lin.webp","effects":[]}
{"_id":"SrV0r5hnGdKeSIHR","name":"Cuillère de bois","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.03,"equipe":false,"resistance":0,"qualite":0,"cout":0.03},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/cuillere_bois.webp","effects":[]}
{"_id":"SsnGNjTekvB50uWa","name":"Chapeau de cuir souple","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.08,"equipe":false,"resistance":0,"qualite":0,"cout":0.5},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/chapeau_cuir.webp","effects":[]}
{"_id":"T9UiLcJonuHmGNwq","name":"Sols (pièces d'argent)","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.005,"equipe":false,"resistance":0,"qualite":0,"cout":1},"flags":{},"img":"icons/commodities/currency/coins-assorted-mix-silver.webp","effects":[]}
{"name":"Hache de bataille","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"arme","data":{"categorie_parade":"haches","description":"","quantite":1,"encombrement":2,"equipe":false,"dommages":"3/4","penetration":0,"force":"12/11","resistance":8,"competence":"Hache à 1 main","cout":15,"portee_courte":0,"magique":false,"ecaille_efficacite":0,"resistance_magique":0,"portee_moyenne":0,"portee_extreme":0,"rapide":false,"deuxmains":true,"unemain":true,"initpremierround":"hachebataille"},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/hache_bataille.webp","effects":[],"_id":"TKsUXJq9w7ezcFGQ"}
{"_id":"TY6Ft8a6WfxD6pD9","name":"Bobineau de fil","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.01,"equipe":false,"resistance":0,"qualite":0,"cout":0.1},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/bobineau.webp","effects":[]}
{"_id":"UDmq6CY3NsttcHe4","name":"Peigne en corne","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.04,"equipe":false,"resistance":0,"qualite":0,"cout":0.4},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/peigne.webp","effects":[]}
@ -120,12 +117,14 @@
{"_id":"Z0ij7qpoYeWMVocP","name":"Ceinturon de cuir","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"conteneur","data":{"description":"","capacite":6,"encombrement":0.1,"equipe":false,"qualite":0,"contenu":[],"cout":0.5},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/ceinturon.webp","effects":[]}
{"_id":"ZLda3pfrbiKucSea","name":"Cornebouffe","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"objet","data":{"description":null,"quantite":1,"encombrement":null,"equipe":false,"resistance":0,"qualite":0,"cout":2},"flags":{"core":{"sourceId":"Item.yXOePj4twuchMblc"}},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/cornebouffe.webp","effects":[]}
{"_id":"a3Wj2WNKFrzqRGVG","name":"Chemise de soie","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0,"equipe":false,"resistance":0,"qualite":0,"cout":6},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/chemise_soie.webp","effects":[]}
{"name":"Or (10 sols)","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"monnaie","data":{"quantite":0,"valeur_deniers":1000,"encombrement":0.01,"description":""},"flags":{"pick-up-stix":{"pick-up-stix":{"owner":"3ajJ5ZIa9bdcKUZQ"}}},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp","effects":[],"_id":"aOgha34ew68iyMnM"}
{"_id":"b0f08L5CDeFIMluC","name":"Cuir Souple","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"armure","data":{"description":"<p>Même épaisseur que nos modernes blousons de cuir. Pourpoint ou cotte de cuir souple + culottes de cuir souple + bottes de cuir souple.</p>\n<p>&nbsp;</p>","quantite":1,"encombrement":0,"equipe":false,"protection":2,"deterioration":0,"malus":0,"cout":6},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_souple.webp","effects":[]}
{"_id":"bA0JDA7awoWhu0vO","name":"Teinture d'Erozonne","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"potion","data":{"description":"<p>Fluide.&nbsp;</p>\n<p>Liquide ros&acirc;tre.</p>","quantite":1,"encombrement":0.1,"rarete":"","categorie":"Alchimie","cout":2},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/teinture_erozonne.webp","effects":[]}
{"_id":"beQ9d4QQwZDQl5NA","name":"Flûte à bec","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.09,"equipe":false,"resistance":0,"qualite":0,"cout":1},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/flute_bec.webp","effects":[]}
{"_id":"bgkEBYUEFLvAaeVf","name":"Luth, viole","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":1,"equipe":false,"resistance":0,"qualite":0,"cout":7},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/luth.webp","effects":[]}
{"_id":"bxDITKRhXiyvLhMz","name":"Candique","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"ingredient","data":{"description":"<p>Poudre blanche apparaissant sous l&rsquo;&eacute;corce de nombreux arbres,</p>\n<p>VUE/Alchimie &agrave; 0.</p>","niveau":0,"encombrement":0.001,"base":0,"quantite":1,"milieu":"Forêts","rarete":"","categorie":"Alchimie","cout":0.02},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/candique.webp","effects":[]}
{"_id":"cVZbnh5cYxBx6P5b","name":"Burin, gouge, ciseau","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.2,"equipe":false,"resistance":0,"qualite":0,"cout":0.3},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/gouge.webp","effects":[]}
{"name":"Bronze (10 deniers)","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"monnaie","data":{"quantite":6,"valeur_deniers":10,"encombrement":0.01,"description":""},"flags":{"pick-up-stix":{"pick-up-stix":{"owner":"3ajJ5ZIa9bdcKUZQ"}}},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp","effects":[],"_id":"caw96HJsdScvPk7g"}
{"_id":"ckKnviu9SHvWgya0","name":"Bougie de cire (2 heures)","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.1,"equipe":false,"resistance":0,"qualite":0,"cout":0.05},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/bougie.webp","effects":[]}
{"_id":"cobfvOmFpti5lJuK","name":"Chemise de lin","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0,"equipe":false,"resistance":0,"qualite":0,"cout":0.3},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/chemise_lin.webp","effects":[]}
{"_id":"dBR6KXvfmjjIcwsc","name":"Pilon en marbre","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.1,"equipe":false,"resistance":0,"qualite":0,"cout":0.2},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/pilon.webp","effects":[]}
@ -183,6 +182,7 @@
{"name":"Sang","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"munition","data":{"description":"<p>1 mesure (20cl) de sang.</p>","quantite":1,"encombrement":0.1,"equipe":false,"qualite":0,"cout":0},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/liquides/liquide_sang.webp","effects":[],"_id":"slusKo2nVCtFwDkN"}
{"_id":"snupUovwaPAe46aD","name":"Fiole en grès (20 cl)","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"conteneur","data":{"description":"","capacite":0.1,"encombrement":0.1,"equipe":false,"qualite":0,"contenu":[],"cout":0.1},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/fiole_gres.webp","effects":[]}
{"_id":"szOThadvQvFcS79R","name":"Cuir Epais","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"armure","data":{"description":"<p>Cuir très épais comme le cuir de botte. Pectoral de cuir épais + jupon de bandes ou de tresses de cuir ou cuissards de cuir épais sur culottes de cuir souple + bottes dures + casque de cuir.</p>\n<p>&nbsp;</p>","quantite":1,"encombrement":2,"equipe":false,"protection":3,"deterioration":0,"malus":-1,"cout":10},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_epais.webp","effects":[]}
{"name":"Etain (1 denier)","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"monnaie","data":{"quantite":4,"valeur_deniers":1,"encombrement":0.01,"description":""},"flags":{"pick-up-stix":{"pick-up-stix":{"owner":"3ajJ5ZIa9bdcKUZQ"}}},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp","effects":[],"_id":"t1sIGhFtmxjAIfWv"}
{"_id":"tBFt4h3jqINsOxLI","name":"Outre (2 litres)","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"conteneur","data":{"description":"","capacite":1,"encombrement":0.08,"equipe":false,"qualite":0,"contenu":[],"cout":0.2},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/outre.webp","effects":[]}
{"name":"Marteau","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"arme","data":{"categorie_parade":"","description":"","quantite":1,"encombrement":0.3,"equipe":false,"dommages":"2","penetration":0,"force":"7","resistance":8,"competence":"Masse à 1 main","cout":1,"portee_courte":0,"magique":false,"ecaille_efficacite":0,"resistance_magique":0,"portee_moyenne":0,"portee_extreme":0,"rapide":false,"deuxmains":false,"unemain":false,"initpremierround":"masse"},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/marteau.webp","effects":[],"_id":"tMWzePiuMtiCQnAU"}
{"_id":"tY3shj5FA8nwMgxX","name":"Vin","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"munition","data":{"description":"<p>1 mesure (20cl) de vin.</p>","quantite":1,"encombrement":0.1,"equipe":false,"qualite":0,"cout":0},"flags":{"core":{"sourceId":"Item.QNNWTG5yqQKmcpJ7"}},"img":"systems/foundryvtt-reve-de-dragon/icons/liquides/liquide_vin.webp","effects":[]}
@ -203,6 +203,7 @@
{"_id":"yILNvELKbsz2OOln","name":"Ecritoire","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":1,"equipe":false,"resistance":0,"qualite":0,"cout":1},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/ecritoire.webp","effects":[]}
{"_id":"yO9Vx7tqF8qbZoYw","name":"Besace de cuir","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"conteneur","data":{"description":"","capacite":10,"encombrement":0.2,"equipe":false,"qualite":0,"contenu":[],"cout":0.5},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/besace.webp","effects":[]}
{"_id":"z3xiBzZBZXlaRVzZ","name":"Le Grand Iris","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"livre","data":{"description":"<p>Cette judicieuse r&eacute;flexion sur les sorts d&rsquo;illusion visuelle d&rsquo;Hypnos ne peut &ecirc;tre assimil&eacute;e que si l&rsquo;on poss&egrave;de au minimum z&eacute;ro en voie d&rsquo;Hypnos. Il conf&egrave;re un bonus de synth&egrave;se de +2 et de 12 points de sorts aux trois yeux d&rsquo;Hypnos : Invisibilit&eacute;, Transfiguration, M&eacute;tamorphose. Sa difficult&eacute; de lecture est de -3, son assimilation requiert 16 points de t&acirc;che, p&eacute;riodicit&eacute; une heure.</p>","auteur":"Khrachtchoum le Problémeux","quantite":1,"difficulte":-3,"points_de_tache":16,"encombrement":0,"xp":"0","cout":0,"competence":""},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.png","effects":[]}
{"name":"Argent (1 sol)","permission":{"default":0,"Q4cUvqxCxMoTJXDL":3},"type":"monnaie","data":{"quantite":17,"valeur_deniers":100,"encombrement":0.01,"description":""},"flags":{"pick-up-stix":{"pick-up-stix":{"owner":"3ajJ5ZIa9bdcKUZQ"}}},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp","effects":[],"_id":"z8nkjovgrEj2d8kD"}
{"_id":"zQWlnUsd8bPySujd","name":"Aiguille à coudre","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"objet","data":{"description":"","quantite":1,"encombrement":0.01,"equipe":false,"resistance":0,"qualite":0,"cout":0.1},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/aiguille.webp","effects":[]}
{"_id":"zYI8mDiysWtmsSyy","name":"Carquois","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"conteneur","data":{"description":"","capacite":2,"encombrement":0.1,"equipe":false,"qualite":0,"cout":0.5},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/carquois.webp","effects":[]}
{"_id":"zlDa1vwmls6Uf4pt","name":"Bourse de cuir","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"conteneur","data":{"description":"","capacite":0.5,"encombrement":0.01,"equipe":false,"qualite":0,"contenu":[],"cout":0.1},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/objets/bourse.webp","effects":[]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,81 @@
des centaines de graines emport<72>es par le vent
des chemin<69>es de f<>es
des cristaux de neige <20>tincelants au soleil
des feux follets dans la nuit
des fumeroles s'<27>chappant de fissures dans le sol
des gr<67>lons de la taille d'un oeuf de pigeon
des lichens <20> l'assaut d'une souche
des mirages sur l'horizon
des nuages accroch<63>es aux flancs d'une montagne
des nu<6E>es dans le ciel nocturne
des plumes duveteuses accroch<63>es dans les foug<75>res
des roches sculpt<70>es par l'<27>rosion
des signes comme grav<61>s <20> m<>me la pierre <20>voquant la langue des Dragons
des silhouettes impr<70>cises dans la brume
des sons de fl<66>tes provenant du sous bois
des traces de fossiles dans une roche
des uages noirs qui moutonnent juste avant la pluie
des veinures aux reflets m<>talliques dans la roche
des voiles d'aurores bor<6F>ales tombant dans le ciel nocturne
des <20>clairs z<>brant le ciel <20> l'horizon
l'entrelacs des branches d'un arbre mill<6C>naire
l'<27>coulement d'une chute l'eau
l'<27>cume sur les vagues sal<61>es
la brillance d'<27>toiles align<67>es
la coloration verte des flammes
la course hypnotique des balles d'un jongleur
la formation de givre sur une <20>tendue d'eau
la lueur du cr<63>puscule sur les cimes <20> l'horizon
la lune rouge sang
la ros<6F>e dans une toile d'araign<67>e
la teinte rouge de la lune <20> travers les nuages
le faisceau d'ondes caus<75> par un insecte aquatique
le mouvement de grains de sable pouss<73>s par le vent
le mouvement de vagues battant le rivage
le mouvement r<>gulier des pales d'un moulin <20> vent
le rythme de l'eau qui emporte les aubes d'un moulin
le soleil masqu<71> par un passage de nuages
le tableau abstrait de t<>ches sur le sol
les cicatrices et boutons du visage d'un malade
les colorations violac<61>es et oranges du ciel matinal
les figures de la rouille sur un vieux casque
les filaments d'une fleur carnivore enla<6C>ant un insecte
les rayons du soleil couchant
les reflets d'eau <20> travers les plantes aquatiques
les remous cascadant d<>un torrent tels un liquide en <20>bullition dans une marmite de gigant
les restes d'un mur antique
les vagues du vent dans les herbes hautes
un arbre mort fendu par la foudre
un arc-en-ciel double
un arc-en-ciel tr<74>s lumineux
un cadavre d'animal inconnu
un dragon de nuages prenant son envol
un empilement de pierres
un enfant sautant dans les flaques d'eau
un fant<6E>me de poussi<73>re <20>tincellant au soleil
un geyser projetant eau et vapeur <20> la face des Dragons
un manche d'outil poli par l'usure
un moustique gorg<72> de sang
un nid d'oiseau inconnu
un nuage d'insectes assombrissant le ciel
un panache de fum<75>e volcaniques s'<27>levant <20> l'horizon
un parterre de fleurs
un prairie brumeuse
un rideau de pluie <20>clair<69> d'un rai de lumi<6D>re diffus
un tourbillon dans l'eau
un tourbillon de poussi<73>re
un vol d'oiseaux migrateurs align<67>s tra<72>ant des lettres dans le ciel
une braise attis<69>e par le vent
une coloration bleut<75>e de la lune
une concr<63>tion rocheuse <20>voquant une cascade fig<69>e
une fine couche de terre craquel<65>e par le soleil
une forme animale <20>voqu<71>e par les courbes d'une <20>corce
une gerbe d'<27>tincelles <20>chappant du feu
une mante religieuse d<>vorrant son male
une mue de l<>zard
une nu<6E>e chaotique d'oiseaux tourbillonnant dans le vent
une ond<6E>e illumin<69>e tombant tel un voile
une phosphorescence dans l'eau
une tornade dans le lointain
une <20>toile filante
une <20>trange disposition d'ossements sur le sol

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