Compare commits

..

34 Commits

Author SHA1 Message Date
54df875451 Merge pull request 'v10.7.20 - la cuirasse de Sémolosse' (#662) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #662
2023-07-12 08:09:06 +02:00
253a1bd433 v10.7.20 - la cuirasse de Sémolosse
- correction de méthodes qui filtrent les items
  - recherche de cases TMR
  - recherche de tâches de lecture
  - recherche d'armure (pour le malus armure)
  - recherche de potions
2023-07-12 00:46:46 +02:00
e58d88fab6 Fix: filterItem ne marchait plus sans type 2023-07-12 00:39:21 +02:00
41d2404de2 Merge pull request 'v10.7.20 - la poigne de Sémolosse' (#655) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #655
2023-06-19 15:36:32 +02:00
631ee0b801 v10.7.20 - La poigne de Sémolosse 2023-06-15 02:02:00 +02:00
ed9c574cd2 Fin d'empoignade
En cours de round en atteignant 2 points d'empoignade, on peut
uniquement entraîner au sol.

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

Seul le propriétaire de l'empoigneur peut tenter d'empêcher
la libération du défenseur, de projeter au sol, ou de faire perdre
de l'endurance.
2023-06-15 01:03:33 +02:00
40f2ac8714 Correction malus de taille empoignade 2023-06-15 00:30:11 +02:00
804fa3b784 Fix empoignade
- les items d'empoignade sont ajoutés par le MJ
- les caractéristiques du défenseur sont utilisées pour la défense
- la difficulté d'attaque est imposée au défenseur
- les particulières sont en finesse (p133)
2023-06-15 00:30:11 +02:00
d35e47824d Merge pull request 'v10.7.19 - les fantômes de Semolosse' (#653) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #653
2023-06-14 08:27:49 +02:00
37d3fa5bc5 Version 10.7.19 - les fantômes de Semolosse 2023-06-14 02:10:59 +02:00
8a12eb865c Migration des compendiums 2023-06-14 02:10:59 +02:00
5baa94b3f0 Gestion des difficultés de Possession
- gestion de la difficulté imposée sur la défense
- gestion des particulières en attaque considérées en finesse
- utilisation du rêve actuel pour les personnages
2023-06-14 02:10:59 +02:00
37c2b6432d Catégories des compétences de créatures
Les créatures peuvent avoir des compétences d'armes (lancer,
mêlée, tir, armes naturelles), de parade, de possession, et générales.

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

Ne pas essayer d'acheter autrement car on aurait des données d'item
non structurées, et donc ça ne fonctionnerait pas.
2023-06-14 02:10:59 +02:00
d82a543860 Merge pull request 'v10.7.18 - le repos de Semolosse' (#651) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #651
2023-06-08 06:51:42 +02:00
2e76961ba7 Correction rendu changelog en ligne
# Conflicts:
#	changelog.md
2023-06-08 01:48:05 +02:00
a9f50bbc5e v10.7.18 2023-06-08 01:40:58 +02:00
8ba3476d7b Fix: la date des blessures ne marchait plus
la liste des types (pour aider à la saisie) est une idée mitigée

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

Saisie de tous les types du template.
2023-06-08 01:34:29 +02:00
4e8f6e8872 Merge pull request 'v10.7.17 - Le doigt du destin de Sémolosse' (#649) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #649
2023-06-05 20:14:00 +02:00
727701bdcd v10.7.17 2023-06-05 20:01:40 +02:00
dcc0f0acfd Fix: validation encaissement MJ 2023-06-05 19:58:57 +02:00
61eee66ebe Merge pull request 'v10.7.16 - La morsure de Sémolosse' (#648) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #648
2023-06-04 20:37:17 +02:00
c75d10f69b v10.7.16 - La morsure de Sémolosse 2023-06-04 02:41:06 +02:00
333bb051c1 Correction de liens
Quelques liens pointaient sur les icones du bazaar de forge-vtt
2023-06-04 02:36:40 +02:00
1bf247db33 Fix affichage des "objets" dans les commerces
La confusion entre les Item "objets" et le champ formData.objets
rendait les Item "objets" indisponibles (par exemple, les vêtements)
2023-06-04 02:36:40 +02:00
49fc2c9b0a Max release = v10 2023-05-29 13:32:22 +02:00
9013376096 Merge pull request 'v10.7.14 - l'expérience de Sémolosse' (#646) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #646
2023-05-29 07:46:20 +02:00
972459a08d Version 10.7.14 2023-05-28 22:05:36 +02:00
1607629365 Tri des listes d'items 2023-05-28 22:05:09 +02:00
8f7efdad87 Utilisation de la dateReel du calendrier 2023-05-28 22:04:03 +02:00
2dbe0dea4a Refonte du journal d'expérience
Reprise du journal d'expérience pour:
- afficher ancienne/nouvelle valeur
- la valeur du changement
- si c'est manuel / automatique
- identifier les dépenses de stress
- identifier les augmentations de compétences
- les changements des compteurs
2023-05-28 22:03:57 +02:00
5fc455fbad Ajout des acteurs accordés aux entités 2023-05-28 22:01:35 +02:00
8a7e4d3a9e Merge pull request 'Version 10.7.13 - L'armure de Semolosse' (#644) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10
Reviewed-on: #644
2023-05-25 20:35:39 +02:00
82 changed files with 2277 additions and 2904 deletions

4
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

@ -1,17 +1,20 @@
import { RdDBaseActorReveSheet } from "./base-actor-reve-sheet.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { RdDUtility } from "./rdd-utility.js";
export class RdDActorEntiteSheet extends RdDBaseActorReveSheet {
export class RdDActorEntiteSheet extends RdDActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(RdDBaseActorReveSheet.defaultOptions, {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html",
width: 640, height: 720,
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
});
}
async getData() {
let formData = await super.getData();
formData.resonances = this.actor.system.sante.resonnance.actors.map(actorId => game.actors.get(actorId))

View File

@ -7,30 +7,33 @@ import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js";
import { RdDBaseActorReveSheet } from "./actor/base-actor-reve-sheet.js";
import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js";
import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
import { ChatUtility } from "./chat-utility.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDActorSheet extends RdDBaseActorReveSheet {
export class RdDActorSheet extends RdDBaseActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(RdDBaseActorReveSheet.defaultOptions, {
RdDUtility.initAfficheContenu();
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueArchetype: false,
vueDetaillee: false
});
}
@ -52,8 +55,7 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "",
malusArmure: this.actor.getMalusArmure()
surEncombrementMessage: this.actor.getMessageSurEncombrement(),
})
this.timerRecherche = undefined;
@ -77,12 +79,9 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
});
// toujours avoir une liste d'armes (pour mettre esquive et corps à corps)
const actor = this.actor;
formData.combat = duplicate(formData.armes);
formData.combat = duplicate(formData.armes ?? []);
RdDItemArme.computeNiveauArmes(formData.combat, formData.competences);
formData.combat.push(RdDItemArme.mainsNues(actor));
formData.combat.push(RdDItemArme.empoignade(actor));
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac);
formData.esquives = this.actor.getCompetences("Esquive");
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
formData.empoignades = this.actor.getEmpoignades();
@ -111,28 +110,20 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
return formData;
}
/* -------------------------------------------- */
/** @override */
/* -------------------------------------------- */ /** @override */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.sheet-possession-attack').click(async event => {
const poss = RdDSheetUtility.getItem(event, this.actor)
this.actor.conjurerPossession(poss)
})
this.html.find('.subacteur-label a').click(async event => {
let actorId = RdDSheetUtility.getEventItemData(event, 'actor-id');
let actor = game.actors.get(actorId);
if (actor) {
actor.sheet.render(true);
}
this.html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.actionPrincipale(this.actor, async () => this.render())
});
this.html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const actorId = li.data("actor-id");
@ -162,6 +153,18 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
this.actor.updateCompteurValue("experience", parseInt(event.target.value));
});
this.html.find('.encaisser-direct').click(async event => {
this.actor.encaisser();
})
this.html.find('.sheet-possession-attack').click(async event => {
const poss = RdDSheetUtility.getItem(event, this.actor)
this.actor.conjurerPossession(poss)
})
this.html.find('.remise-a-neuf').click(async event => {
if (game.user.isGM) {
this.actor.remiseANeuf();
}
});
this.html.find('.creer-tache').click(async event => {
this.createEmptyTache();
});
@ -171,7 +174,9 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
this.html.find('.creer-blessure-legere').click(async event => RdDItemBlessure.createBlessure(this.actor, 2));
this.html.find('.creer-blessure-grave').click(async event => RdDItemBlessure.createBlessure(this.actor, 4));
this.html.find('.creer-blessure-critique').click(async event => RdDItemBlessure.createBlessure(this.actor, 6));
this.html.find('.creer-une-oeuvre').click(async event => this.selectTypeOeuvreToCreate());
this.html.find('.creer-une-oeuvre').click(async event => {
this.selectTypeOeuvreToCreate();
});
this.html.find('.blessure-premierssoins-done').change(async event => {
const blessure = this.getBlessure(event);
@ -196,6 +201,11 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
});
// Roll Carac
this.html.find('.carac-label a').click(async event => {
let caracName = event.currentTarget.attributes.name.value;
this.actor.rollCarac(caracName.toLowerCase());
});
this.html.find('.chance-actuelle').click(async event => {
this.actor.rollCarac('chance-actuelle');
});
@ -204,10 +214,14 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
this.actor.rollAppelChance();
});
// Roll Skill
this.html.find('[name="jet-astrologie"]').click(async event => {
this.actor.astrologieNombresAstraux();
});
// Roll Skill
this.html.find('a.competence-label').click(async event => {
this.actor.rollCompetence(RdDSheetUtility.getItemId(event));
});
this.html.find('.tache-label a').click(async event => {
this.actor.rollTache(RdDSheetUtility.getItemId(event));
});
@ -232,6 +246,13 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
this.html.find('.recettecuisine-label a').click(async event => {
this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event));
});
this.html.find('.subacteur-label a').click(async event => {
let actorId = RdDSheetUtility.getEventItemData(event, 'actor-id');
let actor = game.actors.get(actorId);
if (actor) {
actor.sheet.render(true);
}
});
// Boutons spéciaux MJs
this.html.find('.forcer-tmr-aleatoire').click(async event => {
@ -266,12 +287,35 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
}
});
// Display TMR
this.html.find('.visu-tmr').click(async event => { this.actor.displayTMR("visu") })
this.html.find('.monte-tmr').click(async event => { this.actor.displayTMR("normal") })
this.html.find('.monte-tmr-rapide').click(async event => { this.actor.displayTMR("rapide") })
// Display TMR, visualisation
this.html.find('.visu-tmr').click(async event => {
this.actor.displayTMR("visu");
});
this.html.find('.repos').click(async event => { await this.actor.repos() })
// Display TMR, normal
this.html.find('.monte-tmr').click(async event => {
this.actor.displayTMR("normal");
});
// Display TMR, fast
this.html.find('.monte-tmr-rapide').click(async event => {
this.actor.displayTMR("rapide");
});
this.html.find('.repos').click(async event => {
await this.actor.repos();
});
this.html.find('.delete-active-effect').click(async event => {
if (game.user.isGM) {
let effect = this.html.find(event.currentTarget).parents(".active-effect").data('effect');
this.actor.removeEffect(effect);
}
});
this.html.find('.enlever-tous-effets').click(async event => {
if (game.user.isGM) {
await this.actor.removeEffects();
}
});
this.html.find('.carac-xp-augmenter').click(async event => {
let caracName = event.currentTarget.name.replace("augmenter.", "");
this.actor.updateCaracXPAuto(caracName);
@ -285,39 +329,47 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
if (this.options.vueDetaillee) {
// On carac change
this.html.find('.carac-value').change(async event => {
let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
this.actor.updateCarac(caracName, parseInt(event.target.value));
});
this.html.find('input.carac-xp').change(async event => {
let caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", "");
this.actor.updateCaracXP(caracName, parseInt(event.target.value));
});
// On competence change
this.html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
//console.log("Competence changed :", compName);
this.actor.updateCompetence(compName, parseInt(event.target.value));
});
// On competence xp change
this.html.find('input.competence-xp').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXP(compName, parseInt(event.target.value));
});
// On competence xp change
this.html.find('input.competence-xp-sort').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value));
});
this.html.find('.toggle-archetype').click(async event => {
this.options.vueArchetype = !this.options.vueArchetype;
this.render(true);
});
// On competence archetype change
this.html.find('.competence-archetype').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceArchetype(compName, parseInt(event.target.value));
});
}
this.html.find('.nouvelle-incarnation').click(async event => {
this.actor.nouvelleIncarnation();
});
this.html.find('.show-hide-competences').click(async event => {
this.options.showCompNiveauBase = !this.options.showCompNiveauBase;
this.render(true);
});
this.html.find('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
});
// On pts de reve change
this.html.find('.pointsreve-value').change(async event => {
let reveValue = event.currentTarget.value;
@ -330,6 +382,11 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
this.actor.setPointsDeSeuil(event.currentTarget.value);
});
// On stress change
this.html.find('.compteur-edit').change(async event => {
let fieldName = event.currentTarget.attributes.name.value;
this.actor.updateCompteurValue(fieldName, parseInt(event.target.value));
});
this.html.find('.stress-test').click(async event => {
this.actor.transformerStress();
@ -351,7 +408,7 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
this.actor.jetVie();
});
this.html.find('.jet-endurance').click(async event => {
await this.jetEndurance();
this.actor.jetEndurance();
});
this.html.find('.vie-plus').click(async event => {
@ -360,6 +417,12 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
this.html.find('.vie-moins').click(async event => {
this.actor.santeIncDec("vie", -1);
});
this.html.find('.endurance-plus').click(async event => {
this.actor.santeIncDec("endurance", 1);
});
this.html.find('.endurance-moins').click(async event => {
this.actor.santeIncDec("endurance", -1);
});
this.html.find('.ptreve-actuel-plus').click(async event => {
this.actor.reveActuelIncDec(1);
});
@ -374,16 +437,6 @@ export class RdDActorSheet extends RdDBaseActorReveSheet {
});
}
async jetEndurance() {
const endurance = this.actor.getEnduranceActuelle()
const result = await this.actor.jetEndurance(endurance);
ChatMessage.create({
content: `Jet d'Endurance : ${result.jetEndurance} / ${endurance}
<br>${this.actor.name} a ${result.sonne ? 'échoué' : 'réussi'} son Jet d'Endurance ${result.sonne ? 'et devient Sonné' : ''}`,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.actor.name)
});
}
getBlessure(event) {
const itemId = this.html.find(event.currentTarget).parents(".item-blessure").data('item-id');
const blessure = this.actor.getItem(itemId, 'blessure');

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -16,10 +16,13 @@ export class RdDBaseActorSheet extends ActorSheet {
/** @override */
static get defaultOptions() {
RdDUtility.initAfficheContenu();
return mergeObject(ActorSheet.defaultOptions, {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false
});
}
@ -28,7 +31,7 @@ export class RdDBaseActorSheet extends ActorSheet {
async getData() {
Monnaie.validerMonnaies(this.actor.itemTypes['monnaie']);
this.actor.computeEtatGeneral();
this.actor.recompute();
let formData = {
title: this.title,
id: this.actor.id,
@ -132,13 +135,6 @@ export class RdDBaseActorSheet extends ActorSheet {
super.activateListeners(html);
this.html = html;
if (!this.options.editable) return;
this.html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.actionPrincipale(this.actor, async () => this.render())
});
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(this.getItemId(event));
this.render(true);
@ -171,26 +167,6 @@ export class RdDBaseActorSheet extends ActorSheet {
this.html.find('.nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs();
});
this.html.find('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
});
if (this.options.vueDetaillee) {
// On carac change
this.html.find('.carac-value').change(async event => {
let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
this.actor.updateCarac(caracName, parseInt(event.target.value));
});
// On competence change
this.html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
//console.log("Competence changed :", compName);
this.actor.updateCompetence(compName, parseInt(event.target.value));
});
}
}
_rechercherKeyup(event) {

View File

@ -1,8 +1,6 @@
import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Monnaie } from "../item-monnaie.js";
import { TYPES } from "../item.js";
import { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js";
import { RdDConfirm } from "../rdd-confirm.js";
@ -11,33 +9,6 @@ import { SystemCompendiums } from "../settings/system-compendiums.js";
import { APP_ASTROLOGIE_REFRESH } from "../sommeil/app-astrologie.js";
export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
static _findCaracByName(carac, name) {
name = Grammar.toLowerCaseNoAccent(name);
switch (name) {
case 'reve-actuel': case 'reve actuel':
return carac.reve;
case 'chance-actuelle': case 'chance actuelle':
return carac.chance;
}
const caracList = Object.entries(carac);
let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique' });
if (!entry || entry.length == 0) {
entry = Misc.findFirstLike(name, caracList, { mapper: it => it[1].label, description: 'caractéristique' });
}
return entry && entry.length > 0 ? carac[entry[0]] : undefined;
}
getCaracByName(name) {
switch (Grammar.toLowerCaseNoAccent(name)) {
case 'reve-actuel': case 'reve actuel':
return this.getCaracReveActuel();
case 'chance-actuelle': case 'chance-actuelle':
return this.getCaracChanceActuelle();
}
return RdDBaseActor._findCaracByName(this.system.carac, name);
}
static getDefaultImg(itemType) {
return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
@ -79,13 +50,7 @@ export class RdDBaseActor extends Actor {
static onRemoteActorCall(callData, userId) {
if (userId == game.user.id) {
let actor = game.actors.get(callData?.actorId);
if (callData.tokenId) {
let token = canvas.tokens.placeables.find(t => t.id == callData.tokenId)
if (token) {
actor = token.actor
}
}
const actor = game.actors.get(callData?.actorId);
if (Misc.isOwnerPlayerOrUniqueConnectedGM(actor)) { // Seul le joueur choisi effectue l'appel: le joueur courant si propriétaire de l'actor, ou le MJ sinon
const args = callData.args;
console.info(`RdDBaseActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDBaseActor.${callData.method}(`, ...args, ')');
@ -140,39 +105,11 @@ export class RdDBaseActor extends Actor {
super(docData, context);
}
/* -------------------------------------------- */
async _preCreate(data, options, user) {
await super._preCreate(data, options, user);
// Configure prototype token settings
const prototypeToken = {};
if (this.type === "personnage") Object.assign(prototypeToken, {
sight: { enabled: true }, actorLink: true, disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
});
this.updateSource({ prototypeToken });
}
/* -------------------------------------------- */
prepareData() {
super.prepareData()
this.prepareActorData()
this.cleanupConteneurs()
this.computeEtatGeneral()
this.computeEncTotal()
}
async prepareActorData() { }
async computeEtatGeneral() { }
/* -------------------------------------------- */
findPlayer() {
return game.users.players.find(player => player.active && player.character?.id == this.id);
}
isCreatureEntite() { return this.isCreature() || this.isEntite() }
isCreature() { return false }
isEntite(typeentite = []) { return false }
isVehicule() { return false }
isPersonnage() { return false }
isCreatureEntite() { return this.type == 'creature' || this.type == 'entite'; }
isCreature() { return this.type == 'creature'; }
isEntite() { return this.type == 'entite'; }
isPersonnage() { return this.type == 'personnage'; }
isVehicule() { return this.type == 'vehicule'; }
getItem(id, type = undefined) {
const item = this.items.get(id);
if (type == undefined || (item?.type == type)) {
@ -182,14 +119,16 @@ export class RdDBaseActor extends Actor {
}
listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
filterItems(filter, type = undefined) { return (type ? this.itemTypes[type] : this.items)?.filter(filter) ?? []; }
filterItems(filter, type = undefined) { return (type ? this.itemTypes[type] : this.items)?.filter(filter); }
findItemLike(idOrName, type) {
return this.getItem(idOrName, type)
?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
}
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
getEncombrementMax() { return 0 }
recompute() { }
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
@ -205,10 +144,9 @@ export class RdDBaseActor extends Actor {
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
}
async creerObjetParMJ(object) {
async creerObjetParMJ(object){
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'creerObjetParMJ',
args: [object]
@ -218,16 +156,6 @@ export class RdDBaseActor extends Actor {
await this.createEmbeddedDocuments('Item', [object])
}
/* -------------------------------------------- */
async cleanupConteneurs() {
let updates = this.itemTypes['conteneur']
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
if (updates.length > 0) {
await this.updateEmbeddedDocuments("Item", updates)
}
}
/* -------------------------------------------- */
getFortune() {
return Monnaie.getFortune(this.itemTypes['monnaie']);
@ -292,7 +220,6 @@ export class RdDBaseActor extends Actor {
if (fromActorId && !game.user.isGM) {
RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(),
tokenId: this.token?.id,
actorId: this.id,
method: 'ajouterSols', args: [sols, fromActorId]
});
@ -335,10 +262,10 @@ export class RdDBaseActor extends Actor {
const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
if (!itemVendu) {
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !` : `Impossible de retrouver: ${achat.vente.item.name} !`);
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !`: `Impossible de retrouver: ${achat.vente.item.name} !`);
return;
}
if (vendeur && !vendeur.verifierQuantite(itemVendu, quantite)) {
if (vendeur && !this.verifierQuantite(itemVendu, quantite)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
return
}
@ -391,7 +318,7 @@ export class RdDBaseActor extends Actor {
}
verifierQuantite(item, quantiteDemande) {
const disponible = this.getQuantiteDisponible(item);
const disponible = item?.getQuantite();
return disponible == undefined || disponible >= quantiteDemande;
}
@ -440,6 +367,14 @@ export class RdDBaseActor extends Actor {
}
/* -------------------------------------------- */
computeMalusSurEncombrement() {
return 0;
}
getEncombrementMax() {
return 0;
}
async computeEncTotal() {
if (!this.pack) {
this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0);
@ -448,10 +383,6 @@ export class RdDBaseActor extends Actor {
return 0;
}
getEncTotal() {
return Math.floor(this.encTotal ?? 0);
}
async createItem(type, name = undefined) {
if (!name) {
name = 'Nouveau ' + Misc.typeName('Item', type);
@ -516,33 +447,62 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
conteneurPeutContenir(dest, moved) {
conteneurPeutContenir(dest, item) {
if (!dest) {
return true;
}
if (!dest.isConteneur()) {
return false;
}
if (moved.isConteneurContenu(dest)) {
ui.notifications.warn(`Impossible de déplacer un conteneur parent (${moved.name}) dans un de ses contenus ${dest.name} !`);
return false;
const destData = dest
if (this._isConteneurContenu(item, dest)) {
ui.notifications.warn(`Impossible de déplacer un conteneur parent (${item.name}) dans un de ses contenus ${destData.name} !`);
return false; // Loop detected !
}
// Calculer le total actuel des contenus
const encContenu = dest.getEncContenu();
const newEnc = moved.getEncTotal(); // Calculer le total actuel du nouvel objet
const placeDisponible = Math.roundDecimals(dest.system.capacite - encContenu - newEnc, 4)
let encContenu = this.getRecursiveEnc(dest) - Number(destData.system.encombrement);
let newEnc = this.getRecursiveEnc(item); // Calculer le total actuel du nouvel objet
// Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
if (placeDisponible < 0) {
if (Number(destData.system.capacite) < encContenu + newEnc) {
ui.notifications.warn(
`Le conteneur ${dest.name} a une capacité de ${dest.system.capacite}, et contient déjà ${encContenu}.
Impossible d'y ranger: ${moved.name} d'encombrement ${newEnc}!`);
`Le conteneur ${dest.name} a une capacité de ${destData.system.capacite}, et contient déjà ${encContenu}.
Impossible d'y ranger: ${item.name} d'encombrement ${newEnc}!`);
return false;
}
return true;
}
/* -------------------------------------------- */
_isConteneurContenu(item, conteneur) {
if (item?.isConteneur()) { // Si c'est un conteneur, il faut vérifier qu'on ne le déplace pas vers un sous-conteneur lui appartenant
for (let id of item.system.contenu) {
let subObjet = this.getItem(id);
if (subObjet?.id == conteneur.id) {
return true; // Loop detected !
}
if (subObjet?.isConteneur()) {
return this._isConteneurContenu(subObjet, conteneur);
}
}
}
return false;
}
/* -------------------------------------------- */
getRecursiveEnc(objet) {
if (!objet) {
return 0;
}
const tplData = objet.system;
if (objet.type != 'conteneur') {
return Number(tplData.encombrement) * Number(tplData.quantite);
}
const encContenus = tplData.contenu.map(idContenu => this.getRecursiveEnc(this.getItem(idContenu)));
return encContenus.reduce(Misc.sum(), 0)
+ Number(tplData.encombrement) /* TODO? Number(tplData.quantite) -- on pourrait avoir plusieurs conteneurs...*/
}
/* -------------------------------------------- */
/** Ajoute un item dans un conteneur, sur la base
* de leurs ID */
@ -680,20 +640,5 @@ export class RdDBaseActor extends Actor {
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
}
actionImpossible(action) {
ui.notifications.info(`${this.name} ne peut pas faire cette action: ${action}`)
}
async roll() { this.actionImpossible("jet de caractéristiques") }
async jetEthylisme() { this.actionImpossible("jet d'éthylisme") }
async rollAppelChance() { this.actionImpossible("appel à la chance") }
async jetDeMoral() { this.actionImpossible("jet de moral") }
async actionPrincipale(item, onActionItem = async () => { }) {
switch (item.type) {
case TYPES.conteneur: return await item.sheet.render(true);
}
return undefined
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -114,7 +114,7 @@ export class DialogChronologie extends Dialog {
heure: RdDTimestamp.definition(this.html.find("form.rdddialogchrono :input[name='chronologie.heure']").val()),
minute: this.html.find("form.rdddialogchrono :input[name='chronologie.minute']").val(),
},
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val().replace('T', ' ')
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val()
}
}

View File

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

View File

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

View File

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

View File

@ -8,18 +8,18 @@ const xp_par_niveau = [5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20,
const niveau_max = xp_par_niveau.length - 10;
/* -------------------------------------------- */
const limitesArchetypes = [
{ niveau: 0, nombreMax: 100 },
{ niveau: 1, nombreMax: 10 },
{ niveau: 2, nombreMax: 9 },
{ niveau: 3, nombreMax: 8 },
{ niveau: 4, nombreMax: 7 },
{ niveau: 5, nombreMax: 6 },
{ niveau: 6, nombreMax: 5 },
{ niveau: 7, nombreMax: 4 },
{ niveau: 8, nombreMax: 3 },
{ niveau: 9, nombreMax: 2 },
{ niveau: 10, nombreMax: 1 },
{ niveau: 11, nombreMax: 1 },
{ "niveau": 0, "nombreMax": 100, "reste": 100 },
{ "niveau": 1, "nombreMax": 10, "reste": 10 },
{ "niveau": 2, "nombreMax": 9, "reste": 9 },
{ "niveau": 3, "nombreMax": 8, "reste": 8 },
{ "niveau": 4, "nombreMax": 7, "reste": 7 },
{ "niveau": 5, "nombreMax": 6, "reste": 6 },
{ "niveau": 6, "nombreMax": 5, "reste": 5 },
{ "niveau": 7, "nombreMax": 4, "reste": 4 },
{ "niveau": 8, "nombreMax": 3, "reste": 3 },
{ "niveau": 9, "nombreMax": 2, "reste": 2 },
{ "niveau": 10, "nombreMax": 1, "reste": 1 },
{ "niveau": 11, "nombreMax": 1, "reste": 1 }
];
/* -------------------------------------------- */
@ -79,9 +79,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static isCompetenceArme(competence) {
if (competence.isCompetence() && !competence.isCorpsACorps() && !competence.isEsquive()) {
if (competence.isCompetence()) {
switch (competence.system.categorie) {
case 'melee':
return !Grammar.toLowerCaseNoAccent(competence.name).includes('esquive');
case 'tir':
case 'lancer':
return true;
@ -92,10 +93,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static isArmeUneMain(competence) {
return competence.isCompetenceArme() && competence.name.toLowerCase().includes("1 main");
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("1 main");
}
static isArme2Main(competence) {
return competence.isCompetenceArme() && competence.name.toLowerCase().includes("2 main");
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("2 main");
}
static isThanatos(competence) {
@ -258,17 +259,13 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const computed = duplicate(limitesArchetypes);
computed.forEach(it => { it.nombre = 0; it.reste = it.nombreMax; });
competences.map(it => Math.max(0, it.system.niveau_archetype))
.filter(n => n > 0)
.forEach(n => {
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0, nombre: 0 };
computed[n].reste--;
computed[n].nombre++;
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0 };
computed[n].reste = computed[n].reste - 1;
});
return computed.filter(it => it.niveau > 0);
return computed.filter(it => it.reste > 0 && it.niveau > 0);
}
/* -------------------------------------------- */

View File

@ -1,5 +1,5 @@
import { TYPES } from "./item.js";
import { RdDItem, TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.js";
const categories = {
@ -55,20 +55,6 @@ export class RdDItemCompetenceCreature extends Item {
}
/* -------------------------------------------- */
static isCompetenceAttaque(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
case "lancer":
case "naturelle":
case "possession":
return true
}
}
return undefined
}
static getCategorieAttaque(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
@ -77,7 +63,6 @@ export class RdDItemCompetenceCreature extends Item {
case "lancer":
case "naturelle":
case "possession":
case "parade":
return item.system.categorie
}
}

View File

@ -5,7 +5,7 @@ import { RdDItemCompetence } from "./item-competence.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDGemme } from "./rdd-gemme.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { SYSTEM_RDD } from "./constants.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
@ -113,7 +113,7 @@ export class RdDItemSheet extends ItemSheet {
formData.competences = competences;
}
if (this.item.type == 'arme') {
formData.competences = competences.filter(it => it.isCompetenceArme())
formData.competences = competences.filter(it => RdDItemCompetence.isCompetenceArme(it))
}
if (['sort', 'sortreserve'].includes(this.item.type)) {
formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it));
@ -157,7 +157,7 @@ export class RdDItemSheet extends ItemSheet {
super.activateListeners(html);
this.html = html;
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionnelles.isUsing('afficher-prix-joueurs')
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs')
|| game.user.isGM
|| !this.item.isOwned);
HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique());
@ -195,7 +195,7 @@ export class RdDItemSheet extends ItemSheet {
this.html.find('.creer-tache-livre').click((event) => this._getEventActor(event).creerTacheDepuisLivre(this.item));
this.html.find('.consommer-potion').click((event) => this._getEventActor(event).consommerPotion(this.item, this.getActionRenderItem()));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).actionHerbe(this.item));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).dialogFabriquerPotion(this.item));
this.html.find('.alchimie-tache a').click((event) => {
let actor = this._getEventActor(event);

View File

@ -220,32 +220,6 @@ export class RdDItem extends Item {
isService() { return this.type == TYPES.service; }
isCompetence() { return typesObjetsCompetence.includes(this.type) }
isEsquive() {
return (this.isCompetence()
&& this.system.categorie == 'melee'
&& Grammar.includesLowerCaseNoAccent(this.name, 'Esquive'));
}
isCorpsACorps() {
return (this.isCompetence()
&& this.system.categorie == 'melee'
&& Grammar.includesLowerCaseNoAccent(this.name, 'Corps à Corps'));
}
isCompetenceArme() {
if (this.isCompetence()) {
switch (this.system.categorie) {
case 'melee':
return !this.isCorpsACorps() && !this.isEsquive()
case 'tir':
case 'lancer':
return true;
}
}
return false;
}
isCompetencePossession() { return TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
@ -421,16 +395,6 @@ export class RdDItem extends Item {
return Math.max(this.system.encombrement ?? 0, 0);
}
getEncContenu() {
return this.getContenu()
.map(it => it.getRecursiveEnc())
.reduce(Misc.sum(), 0);
}
getRecursiveEnc() {
return this.getEncTotal() + this.getEncContenu()
}
getEncHerbe() {
switch (this.system.categorie) {
case 'Repos': case 'Soin': case 'Alchimie':
@ -440,18 +404,6 @@ export class RdDItem extends Item {
}
getContenu() {
if (this.isConteneur()) {
return this.system.contenu.map(idContenu => this.actor.getItem(idContenu));
}
return []
}
isConteneurContenu(conteneur) {
return this.getContenu()
.find(it => it.id == conteneur.id || it.isConteneurContenu(conteneur))
}
valeurTotale() {
return (this.isService() ? 1 : this.getQuantite()) * this.valeur()
}
@ -498,11 +450,11 @@ export class RdDItem extends Item {
if (this.actor?.isPersonnage()) {
const warn = options.warnIfNot;
if (this.getUtilisationCuisine() == 'brut') {
return 'Cuisiner';
return 'Utiliser';
}
switch (this.type) {
case TYPES.nourritureboisson: return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
case TYPES.potion: return this._actionOrWarnQuantiteZero('Consommer', warn);
case TYPES.potion: return this._actionOrWarnQuantiteZero('Boire', warn);
case TYPES.livre: return this._actionOrWarnQuantiteZero('Lire', warn);
case TYPES.herbe: return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
case TYPES.queue: case TYPES.ombre: return this.system.refoulement > 0 ? 'Refouler' : undefined;
@ -513,8 +465,19 @@ export class RdDItem extends Item {
/* -------------------------------------------- */
async actionPrincipale(actor, onActionItem = async () => { }) {
if (!this.getActionPrincipale()) { return }
await actor?.actionPrincipale(this, onActionItem);
if (!this.getActionPrincipale()) {
return;
}
if (await actor.actionNourritureboisson(this, onActionItem)) {
return;
}
switch (this.type) {
case TYPES.potion: return await actor.consommerPotion(this, onActionItem);
case TYPES.livre: return await actor.actionLire(this);
case TYPES.conteneur: return await this.sheet.render(true);
case TYPES.herbe: return await actor.actionHerbe(this, onActionItem);
case TYPES.queue: case TYPES.ombre: return await actor.actionRefoulement(this);
}
}
_actionOrWarnQuantiteZero(actionName, warn) {
@ -712,7 +675,7 @@ export class RdDItem extends Item {
_armeChatData() {
return [
`<b>Compétence</b>: ${this.system.competence}`,
`<b>Dommages</b>: ${this.system.dommages} ${this.system.mortalite == 'non-mortel' ? '(Non mortel)' : ''}`,
`<b>Dommages</b>: ${this.system.dommages}`,
`<b>Force minimum</b>: ${this.system.force}`,
`<b>Resistance</b>: ${this.system.resistance}`,
...this._inventaireTemplateChatData()

View File

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

View File

@ -42,7 +42,7 @@ export class Misc {
}
static typeName(type, subType) {
return subType ? game.i18n.localize(`TYPES.${type}.${Misc.upperFirst(subType)}`)
return subType ? game.i18n.localize(`${type.toUpperCase()}.Type${Misc.upperFirst(subType)}`)
: '';
}
@ -63,8 +63,8 @@ export class Misc {
static keepDecimals(num, decimals) {
if (decimals <= 0 || decimals > 6) return num;
const power10n = Math.pow(10, parseInt(decimals));
return Math.round(num * power10n) / power10n;
const decimal = Math.pow(10, parseInt(decimals));
return Math.round(num * decimal) / decimal;
}
static getFractionHtml(diviseur) {

View File

@ -1,7 +1,7 @@
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
const TABLE_CARACTERISTIQUES_DERIVEES = {
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 },
@ -58,10 +58,6 @@ export class RdDCarac {
selectedCarac?.label.match(/(Apparence|Force|Agilité|Dextérité|Vue|Ouïe|Odorat-Goût|Empathie|Dérobée|Mêlée|Tir|Lancer)/);
}
static getCaracDerivee(value) {
return TABLE_CARACTERISTIQUES_DERIVEES[Math.min(Math.max(Number(value), 1), 32)];
}
static computeTotal(carac, beaute = undefined) {
const total = Object.values(carac ?? {}).filter(c => !c.derivee)
.map(it => parseInt(it.value))
@ -77,7 +73,7 @@ export class RdDCarac {
/* -------------------------------------------- */
static calculSConst(constitution) {
return RdDCarac.getCaracDerivee(constitution).sconst;
return Number(tableCaracDerivee[Number(constitution)].sconst);
}
/* -------------------------------------------- */
@ -88,7 +84,7 @@ export class RdDCarac {
}
static getCaracXp(targetValue) {
return RdDCarac.getCaracDerivee(targetValue)?.xp ?? 200;
return tableCaracDerivee[targetValue]?.xp ?? 200;
}
@ -101,4 +97,37 @@ export class RdDCarac {
return Grammar.toLowerCaseNoAccent(selectedCarac?.label)?.match(/(apparence|force|agilite|dexterite|vue|ouie|odorat|empathie|melee|tir|lancer|derobee)/);
}
/* -------------------------------------------- */
static computeCarac(system) {
system.carac.force.value = Math.min(system.carac.force.value, parseInt(system.carac.taille.value) + 4);
system.carac.derobee.value = Math.floor(parseInt(((21 - system.carac.taille.value)) + parseInt(system.carac.agilite.value)) / 2);
let bonusDomKey = Math.floor((parseInt(system.carac.force.value) + parseInt(system.carac.taille.value)) / 2);
bonusDomKey = Math.min(Math.max(bonusDomKey, 0), 32); // Clamp de securite
let tailleData = tableCaracDerivee[bonusDomKey];
system.attributs.plusdom.value = tailleData.plusdom;
system.attributs.sconst.value = RdDCarac.calculSConst(system.carac.constitution.value);
system.attributs.sust.value = tableCaracDerivee[Number(system.carac.taille.value)].sust;
system.attributs.encombrement.value = (parseInt(system.carac.force.value) + parseInt(system.carac.taille.value)) / 2;
system.carac.melee.value = Math.floor((parseInt(system.carac.force.value) + parseInt(system.carac.agilite.value)) / 2);
system.carac.tir.value = Math.floor((parseInt(system.carac.vue.value) + parseInt(system.carac.dexterite.value)) / 2);
system.carac.lancer.value = Math.floor((parseInt(system.carac.tir.value) + parseInt(system.carac.force.value)) / 2);
system.sante.vie.max = Math.ceil((parseInt(system.carac.taille.value) + parseInt(system.carac.constitution.value)) / 2);
system.sante.vie.value = Math.min(system.sante.vie.value, system.sante.vie.max)
system.sante.endurance.max = Math.max(parseInt(system.carac.taille.value) + parseInt(system.carac.constitution.value), parseInt(system.sante.vie.max) + parseInt(system.carac.volonte.value));
system.sante.endurance.value = Math.min(system.sante.endurance.value, system.sante.endurance.max);
system.sante.fatigue.max = system.sante.endurance.max * 2;
system.sante.fatigue.value = Math.min(system.sante.fatigue.value, system.sante.fatigue.max);
//Compteurs
system.reve.reve.max = system.carac.reve.value;
system.compteurs.chance.max = system.carac.chance.value;
}
}

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ChatUtility } from "./chat-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { TYPES } from "./item.js";
/* -------------------------------------------- */
@ -181,7 +181,7 @@ export class RdDEmpoignade {
let rollData = {
mode, empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetenceCorpsACorps(),
competence: attacker.getCompetence("Corps à corps").clone(),
selectedCarac: attacker.system.carac.melee,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
@ -210,7 +210,7 @@ export class RdDEmpoignade {
mode: "immobilise",
empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetenceCorpsACorps()
competence: attacker.getCompetence("Corps à corps").clone()
}
const msg = await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(attacker.name),
@ -272,7 +272,7 @@ export class RdDEmpoignade {
mode, attacker, defender, empoignade, attackerRoll,
diffLibre: attackerRoll.diffLibre,
attaqueParticuliere: attackerRoll.particuliere,
competence: defender.getCompetence(competenceName),
competence: defender.getCompetence(competenceName).clone(),
surprise: defender.getSurprise(true),
carac: defender.system.carac,
selectedCarac: defender.system.carac[carac],

View File

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

View File

@ -15,7 +15,7 @@ import { RdDCombatManager, RdDCombat } from "./rdd-combat.js";
import { ChatUtility } from "./chat-utility.js";
import { StatusEffects } from "./settings/status-effects.js";
import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { RdDHerbes } from "./rdd-herbes.js";
@ -29,13 +29,11 @@ import { Environnement } from "./environnement.js";
import { RdDActor } from "./actor.js";
import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDCommerce } from "./actor/commerce.js";
import { RdDEntite } from "./actor/entite.js";
import { RdDVehicule } from "./actor/vehicule.js";
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDCommerceSheet } from "./actor/commerce-sheet.js";
import { RdDCreatureSheet } from "./actor/creature-sheet.js";
import { RdDActorEntiteSheet } from "./actor/entite-sheet.js";
import { RdDActorVehiculeSheet } from "./actor/vehicule-sheet.js";
import { RdDActorCreatureSheet } from "./actor-creature-sheet.js";
import { RdDActorVehiculeSheet } from "./actor-vehicule-sheet.js";
import { RdDActorEntiteSheet } from "./actor-entite-sheet.js";
import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.js";
@ -61,8 +59,6 @@ import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js";
import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js";
import { AppAstrologie } from "./sommeil/app-astrologie.js";
import { RdDItemArmure } from "./item/armure.js";
import { AutoAdjustDarkness as AutoAdjustDarkness } from "./time/auto-adjust-darkness.js";
import { RdDCreature } from "./actor/creature.js";
/**
* RdD system
@ -94,10 +90,10 @@ export class SystemReveDeDragon {
}
this.actorClasses = {
commerce: RdDCommerce,
creature: RdDCreature,
entite: RdDEntite,
creature: RdDActor,
entite: RdDActor,
personnage: RdDActor,
vehicule: RdDVehicule,
vehicule: RdDActor,
}
}
@ -153,7 +149,7 @@ export class SystemReveDeDragon {
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet(SYSTEM_RDD, RdDCommerceSheet, { types: ["commerce"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorSheet, { types: ["personnage"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDCreatureSheet, { types: ["creature"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorCreatureSheet, { types: ["creature"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
@ -186,12 +182,11 @@ export class SystemReveDeDragon {
CONFIG.Combat.documentClass = RdDCombatManager;
// préparation des différents modules
AutoAdjustDarkness.init();
RdDTimestamp.init();
RdDCalendrier.init();
SystemCompendiums.init();
DialogChronologie.init();
ReglesOptionnelles.init();
ReglesOptionelles.init();
RdDUtility.init();
RdDDice.init();
RdDCommands.init();

View File

@ -39,7 +39,7 @@ export class RdDPossession {
let rollData = {
mode: "attaque",
isECNIDefender: false,
competence: competence,
competence: competence.clone(),
possession: possession,
attacker: attacker,
defender: defender,

View File

@ -1,7 +1,7 @@
import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
/**
* difficultés au delà de -10
@ -28,7 +28,7 @@ const reussites = [
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
/* -------------------------------------------- */
const CARAC_MAXIMUM_RESOLUTION = 40;
const caracMaximumResolution = 60;
/* -------------------------------------------- */
export class RdDResolutionTable {
static resolutionTable = this.build()
@ -36,7 +36,7 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static build() {
let table = []
for (var caracValue = 0; caracValue <= CARAC_MAXIMUM_RESOLUTION; caracValue++) {
for (var caracValue = 0; caracValue <= caracMaximumResolution; caracValue++) {
table[caracValue] = this._computeRow(caracValue);
}
return table;
@ -126,7 +126,7 @@ export class RdDResolutionTable {
rolled.bonus = rollData.bonus;
rolled.factorHtml = Misc.getFractionHtml(rollData.diviseurSignificative);
if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) {
if (ReglesOptionelles.isUsing("afficher-colonnes-reussite")) {
rolled.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll);
rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
}

View File

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

View File

@ -6,7 +6,7 @@ import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
/**
* Extend the base Dialog entity to select roll parameters
@ -22,7 +22,7 @@ export class RdDRoll extends Dialog {
const html = await renderTemplate(dialogConfig.html, rollData);
let options = { classes: ["rdd-roll-dialog"], width: 650, height: 'fit-content', 'z-index': 99999, close: html => {} };
let options = { classes: ["rdd-roll-dialog"], width: 600, height: 'fit-content', 'z-index': 99999, close: html => {} };
if (dialogConfig.close) {
options.close = dialogConfig.close;
}
@ -50,6 +50,8 @@ export class RdDRoll extends Dialog {
encTotal: true
},
isMalusEncombrementTotal: RdDItemCompetence.isMalusEncombrementTotal(rollData.competence),
malusArmureValue: actor.getMalusArmure(),
surencMalusValue: actor.computeMalusSurEncombrement(),
encTotal: actor.getEncTotal(),
ajustementAstrologique: actor.ajustementAstrologique(),
surprise: actor.getSurprise(false),
@ -83,7 +85,7 @@ export class RdDRoll extends Dialog {
if (RdDBonus.isDefenseAttaqueFinesse(rollData)) {
facteurSign *= 2;
}
if (!ReglesOptionnelles.isUsing('tripleSignificative')) {
if (!ReglesOptionelles.isUsing('tripleSignificative')) {
facteurSign = Math.min(facteurSign, 4);
}
return facteurSign;
@ -185,7 +187,7 @@ export class RdDRoll extends Dialog {
console.log("RdDRollSelectDialog - Cout reve", ptreve);
this.updateRollResult(html);
});
this.html.find("input.check-mortalite").change((event) => {
this.html.find("[name='coupsNonMortels']").change((event) => {
this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel";
this.updateRollResult(html);
});
@ -289,31 +291,36 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */
async updateRollResult(html) {
const rollData = this.rollData;
let rollData = this.rollData;
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor.getBonusDegat())
rollData.caracValue = parseInt(rollData.selectedCarac.value)
rollData.dmg.mortalite = rollData.dmg.mortalite ?? 'mortel';
rollData.mortalite = rollData.attackerRoll?.dmg.mortalite ?? rollData.dmg.mortalite ?? 'mortel';
rollData.coupsNonMortels = (rollData.attackerRoll?.dmg.mortalite ?? rollData.dmg.mortalite) == 'non-mortel';
rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac);
let dmgText = Misc.toSignedString(rollData.dmg.total);
switch (rollData.mortalite) {
case 'non-mortel': dmgText = `(${dmgText}) non-mortel`; break;
case 'empoignade': dmgText = `empoignade`; break;
}
RollDataAjustements.calcul(rollData, this.actor);
rollData.finalLevel = this._computeFinalLevel(rollData);
const resolutionTable = await RdDResolutionTable.buildHTMLTable(RdDResolutionTable.subTable(rollData.caracValue, rollData.finalLevel))
const adjustements = await this.buildAjustements(rollData);
HtmlUtility.showControlWhen(this.html.find(".use-encTotal"), rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac));
HtmlUtility.showControlWhen(this.html.find(".use-surenc"), rollData.ajustements.surenc.visible && RdDCarac.isActionPhysique(rollData.selectedCarac));
HtmlUtility.showControlWhen(this.html.find(".utilisation-moral"), rollData.use.appelAuMoral);
HtmlUtility.showControlWhen(this.html.find(".divAppelAuMoral"), rollData.use.appelAuMoral);
// HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moral.used);
HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
// Mise à jour valeurs
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));
this.html.find("input.check-mortalite").prop('checked', rollData.dmg.mortalite == 'non-mortel');
this.html.find("label.dmg-arme-actor").text(rollData.dmg.mortalite == 'empoignade'? 'empoignade': Misc.toSignedString(rollData.dmg.total) );
this.html.find("label.arme-mortalite").text(rollData.dmg.mortalite);
// this.html.find("[name='dmg-arme-actor']").text(rollData.dmg.mortalite == 'empoignade'? 'empoignade': Misc.toSignedString(rollData.dmg.total) );
// this.html.find("[name='arme-mortalite']").text(rollData.dmg.mortalite);
this.html.find("[name='coupsNonMortels']").prop('checked', rollData.mortalite == 'non-mortel');
this.html.find(".dmg-arme-actor").text(dmgText);
this.html.find("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable)
}
@ -324,6 +331,30 @@ export class RdDRoll extends Dialog {
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.html`, rollData);
}
/* -------------------------------------------- */
_computeFinalLevel(rollData) {
return RollDataAjustements.sum(rollData.ajustements);
}
/* -------------------------------------------- */
_computeDiffCompetence(rollData) {
if (rollData.competence) {
return Misc.toInt(rollData.competence.system.niveau);
}
if (rollData.draconicList) {
return Misc.toInt(rollData.competence.system.niveau);
}
return 0;
}
/* -------------------------------------------- */
_computeMalusArmure(rollData) {
let malusArmureValue = 0;
if (rollData.malusArmureValue && (rollData.selectedCarac.label == "Agilité" || rollData.selectedCarac.label == "Dérobée")) {
malusArmureValue = rollData.malusArmureValue;
}
return malusArmureValue;
}
/* -------------------------------------------- */
_getTitle(rollData) {
const carac = rollData.selectedCarac.label;
@ -331,19 +362,16 @@ export class RdDRoll extends Dialog {
return carac;
}
const compName = rollData.competence.name;
if (rollData.draconicList && rollData.selectedSort) {
return compName + " - " + rollData.selectedSort.name;
}
// If a weapon is there, add it in the title
const niveau = Misc.toSignedString(rollData.competence.system.niveau)
if (compName == carac) {
// cas des créatures
return `${carac} Niveau ${niveau}`
return carac + " Niveau " + niveau
}
if (rollData.draconicList && rollData.selectedSort) {
// cas de lancer de sort
return `${rollData.competence.name} Niveau ${niveau} ${rollData.selectedSort.name}`
}
if (rollData.arme && rollData.arme.name != compName) {
// ajouter l'arme au titre si son nom n'est pas la compétence
return `${carac} / ${compName} (${rollData.arme.name}) Niveau ${niveau}`
}
return `${carac} / ${compName} Niveau ${niveau}`
const armeTitle = (rollData.arme) ? " (" + rollData.arme.name + ") " : "";
return carac + "/" + compName + armeTitle + " Niveau " + niveau
}
}

View File

@ -2,6 +2,7 @@ import { SHOW_DICE } from "./constants.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { tmrConstants } from "./tmr-constants.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js";
import { ChatUtility } from "./chat-utility.js";
@ -11,7 +12,7 @@ import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDDice } from "./rdd-dice.js";
import { STATUSES } from "./settings/status-effects.js";
import { RdDRencontre } from "./item/rencontre.js";
@ -38,16 +39,14 @@ export class RdDTMRDialog extends Dialog {
title: "Terres Médianes de Rêve",
content: html,
buttons: {
closeButton: {
label: "Fermer", callback: html => this.close()
}
closeButton: { label: "Fermer", callback: html => this.close(html) }
},
default: "closeButton"
}
const dialogOptions = {
classes: ["tmrdialog"],
width: 920, maxheight: 1024, height: 'fit-content',
width: 920, height: 980,
'z-index': 40
}
super(dialogConf, dialogOptions);
@ -56,15 +55,15 @@ export class RdDTMRDialog extends Dialog {
this.actor = actor;
this.actor.tmrApp = this; // reference this app in the actor structure
this.viewOnly = tmrData.mode == "visu"
this.fatigueParCase = this.viewOnly ? 0 : this.actor.getCoutFatigueTMR();
this.fatigueParCase = this.viewOnly || !ReglesOptionelles.isUsing("appliquer-fatigue") ? 0 : this.actor.getTMRFatigue();
this.cumulFatigue = 0;
this.loadRencontres();
this.loadCasesSpeciales();
this.allTokens = [];
this.rencontreState = 'aucune';
this.pixiApp = new PIXI.Application({ width: 720, height: 860 });
this.pixiTMR = new PixiTMR(this, this.pixiApp);
this.subdialog = undefined
this.callbacksOnAnimate = [];
if (!this.viewOnly) {
@ -75,31 +74,6 @@ export class RdDTMRDialog extends Dialog {
this.pixiTMR.load((loader, resources) => this.createPixiSprites());
}
async forceTMRDisplay() {
this.bringToTop();
if (this.subdialog) {
this.subdialog.bringToTop();
}
}
async restoreTMRAfterAction() {
this.subdialog = undefined
await this.maximize();
this.bringToTop();
}
forceTMRContinueAction() {
ui.notifications.warn('Vous devez finir votre action avant de continuer dans les TMR');
this.subdialog.bringToTop();
return;
}
setTMRPendingAction(dialog) {
this.subdialog = dialog
if (dialog instanceof Application) {
dialog.bringToTop();
}
}
isDemiReveCache() {
return !game.user.isGM && this.actor.isTMRCache();
}
@ -201,9 +175,6 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async moveFromKey(move) {
if (this.subdialog) {
return this.forceTMRContinueAction();
}
let oddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
if (move == 'top') oddq.row -= 1;
@ -228,9 +199,7 @@ export class RdDTMRDialog extends Dialog {
super.activateListeners(html);
this.html = html;
document.getElementsByClassName("tmr-row")
.item(0)
.insertCell(0).append(this.pixiApp.view);
document.getElementById("tmrrow1").insertCell(0).append(this.pixiApp.view);
if (this.viewOnly) {
this.html.find('.lancer-sort').remove();
@ -238,13 +207,9 @@ export class RdDTMRDialog extends Dialog {
return;
}
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
this.html.find('tr.tmr-row *').click((event) => {
this.subdialog?.bringToTop();
});
// Roll Sort
this.html.find('.lancer-sort').click((event) => {
this.actor.rollUnSort(this._getActorCoord());
@ -262,8 +227,11 @@ export class RdDTMRDialog extends Dialog {
// Gestion du cout de montée en points de rêve
let reveCout = ((this.tmrdata.isRapide && !EffetsDraconiques.isDeplacementAccelere(this.actor)) ? -2 : -1) - this.actor.countMonteeLaborieuse();
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
await this.actor.reveActuelIncDec(reveCout);
this.cumulFatigue += this.fatigueParCase;
// Le reste...
this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this._getActorCoord());
@ -295,7 +263,7 @@ export class RdDTMRDialog extends Dialog {
let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = this.actor.system.reve.refoulement.value;
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
let fatigueItem = document.getElementById("tmr-fatigue-table");
fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.actor.system.sante.fatigue.value, this.actor.system.sante.endurance.max).html() + "</table>";
}
@ -303,10 +271,6 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async close() {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
this.descenteTMR = true;
if (this.actor.tmrApp) {
this.actor.tmrApp = undefined; // Cleanup reference
@ -314,8 +278,7 @@ export class RdDTMRDialog extends Dialog {
await this.actor.setEffect(STATUSES.StatusDemiReve, false)
this._tellToGM(this.actor.name + " a quitté les terres médianes");
}
await this.actor.santeIncDec((ReglesOptionnelles.isUsing("appliquer-fatigue") ? "fatigue" : "endurance"),
this.cumulFatigue)
await this.actor.santeIncDec("fatigue", this.cumulFatigue)
}
await super.close();
}
@ -330,7 +293,6 @@ export class RdDTMRDialog extends Dialog {
switch (action) {
case 'derober':
await this.derober();
this.restoreTMRAfterAction();
return;
case 'refouler':
await this.refouler();
@ -343,7 +305,6 @@ export class RdDTMRDialog extends Dialog {
break;
}
await this.postRencontre(tmr);
this.restoreTMRAfterAction();
}
async derober() {
@ -399,21 +360,18 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
checkQuitterTMR() {
if (this.actor.isDead()) {
this._tellToGM("Vous êtes mort : vous quittez les Terres médianes !");
this.close();
return true;
}
if (ReglesOptionnelles.isUsing("appliquer-fatigue")
? (this.actor.getFatigueRestante() <= this.cumulFatigue)
: (this.actor.getEnduranceActuelle() <= this.cumulFatigue)
) {
const resteAvantInconscience = this.actor.getFatigueMax() - this.actor.getFatigueActuelle() - this.cumulFatigue;
if (ReglesOptionelles.isUsing("appliquer-fatigue") && resteAvantInconscience <= 0) {
this._tellToGM("Vous vous écroulez de fatigue : vous quittez les Terres médianes !");
this.quitterLesTMRInconscient();
return true;
}
if (this.actor.getReveActuel() == 0) {
this._tellToGM("Vos Points de Rêve sont à 0 : vous quittez les Terres médianes !");
this.quitterLesTMRInconscient();
@ -493,7 +451,7 @@ export class RdDTMRDialog extends Dialog {
setTimeout(() => {
// TODO: remplacer par une boucle while(this.currentRencontre) ?
rencData.nbRounds++;
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
this._tentativeMaitrise(rencData);
@ -573,9 +531,8 @@ export class RdDTMRDialog extends Dialog {
await this.maitriserRencontre();
}
else {
const dialog = new RdDTMRRencontreDialog(this.actor, this.currentRencontre, tmr);
let dialog = new RdDTMRRencontreDialog(this, this.currentRencontre, tmr);
dialog.render(true);
this.setTMRPendingAction(dialog);
}
}
else {
@ -587,12 +544,9 @@ export class RdDTMRDialog extends Dialog {
_presentCite(tmr) {
const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord));
if (presentCite) {
this.minimize();
const caseData = presentCite;
const dialog = EffetsDraconiques.presentCites.choisirUnPresent(caseData, present => {
this._utiliserPresentCite(presentCite, present, tmr)
this.restoreTMRAfterAction();
});
this.setTMRPendingAction(dialog);
EffetsDraconiques.presentCites.choisirUnPresent(caseData, (present => this._utiliserPresentCite(presentCite, present, tmr)));
}
return presentCite;
}
@ -618,6 +572,8 @@ export class RdDTMRDialog extends Dialog {
presentCite: presentCite
};
await this._tentativeMaitrise(rencontreData);
this.maximize();
this.postRencontre(tmr);
}
@ -631,10 +587,7 @@ export class RdDTMRDialog extends Dialog {
? TMRUtility.getTMRType(tmr.coord) + " ??"
: tmr.label + " (" + tmr.coord + ")");
const fakeDialogRencontre = { bringToTop: () => { } };
this.setTMRPendingAction(fakeDialogRencontre)
let myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE });
this.restoreTMRAfterAction()
if (myRoll == 7) {
this._tellToUser(myRoll + ": Rencontre en " + locTMR);
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
@ -824,22 +777,22 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _maitriserTMR(rollData, callbackMaitrise) {
this.minimize(); // Hide
rollData.isTMRCache = rollData.actor.isTMRCache();
const dialog = await RdDRoll.create(this.actor, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html',
close: html => { this.maximize(); } // Re-display TMR
},
{
name: rollData.maitrise.verbe, label: rollData.maitrise.action,
callbacks: [
this.actor.createCallbackExperience(),
{ action: r => { this.restoreTMRAfterAction() } },
{ action: callbackMaitrise }
]
}
);
dialog.render(true);
this.setTMRPendingAction(dialog);
}
/* -------------------------------------------- */
@ -909,8 +862,7 @@ export class RdDTMRDialog extends Dialog {
nettoyerRencontre() {
if (!this.currentRencontre) return; // Sanity check
if (this.currentRencontre.graphics) {
for (let drawRect of this.currentRencontre.graphics) {
// Suppression des dessins des zones possibles
for (let drawRect of this.currentRencontre.graphics) { // Suppression des dessins des zones possibles
this.pixiApp.stage.removeChild(drawRect);
}
}
@ -943,8 +895,8 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
isConnaissanceFleuve(tmrApp, nextTMR) {
return TMRUtility.getTMR(tmrApp).type == 'fleuve' &&
isConnaissanceFleuve(currentTMR, nextTMR) {
return TMRUtility.getTMR(currentTMR).type == 'fleuve' &&
TMRUtility.getTMR(nextTMR).type == 'fleuve' &&
EffetsDraconiques.isConnaissanceFleuve(this.actor);
}
@ -954,15 +906,15 @@ export class RdDTMRDialog extends Dialog {
if (this.viewOnly) {
return;
}
if (this.subdialog) {
return this.forceTMRContinueAction()
}
let clickOddq = TMRUtility.computeEventOddq(event);
let currentOddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
let clickOddq = RdDTMRDialog._computeEventOddq(event.data.originalEvent);
await this._onClickTMRPos(clickOddq); // Vérifier l'état des compteurs reve/fatigue/vie
}
/* -------------------------------------------- */
async _onClickTMRPos(clickOddq) {
let currentOddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
let targetCoord = TMRUtility.oddqToCoordTMR(clickOddq);
let currentCoord = TMRUtility.oddqToCoordTMR(currentOddq);
// Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter)
let deplacementType = this._calculDeplacement(targetCoord, currentCoord, currentOddq, clickOddq);
@ -994,7 +946,7 @@ export class RdDTMRDialog extends Dialog {
await this._messagerDemiReve(targetCoord);
break;
default:
ui.notifications.error("Vous ne pouvez vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre");
ui.notifications.error("Vous ne pouvez pas vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre");
console.log("STATUS :", this.rencontreState, this.currentRencontre);
}
@ -1023,11 +975,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _messagerDemiReve(targetCoord) {
/*
TODO:
Si la case a un sort en réserve, lancer ce sort.
TODO: si la case a un sort en réserve, lancer ce sort.
Si la case est le demi-rêve, ne pas lancer de sort.
Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre
si on essaie de lancer un sort (ou bloquer le lancer de sort)
Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre si on essaie de lancer un sort (ou bloquer le lancer de sort)
*/
this.notifierResonanceSigneDraconique(targetCoord);
await this.actor.rollUnSort(targetCoord);
@ -1044,9 +994,6 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _deplacerDemiReve(targetCoord, deplacementType) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
if (this.currentRencontre != 'normal') {
this.nettoyerRencontre();
}
@ -1057,7 +1004,7 @@ export class RdDTMRDialog extends Dialog {
await this.actor.updateCoordTMR(tmr.coord);
this.forceDemiRevePositionView();
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
this.updateValuesDisplay();
@ -1096,16 +1043,24 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async positionnerDemiReve(coord) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
await this.actor.updateCoordTMR(coord);
this.forceDemiRevePositionView();
let tmr = TMRUtility.getTMR(coord);
await this.postRencontre(tmr);
return tmr;
}
/* -------------------------------------------- */
static _computeEventOddq(origEvent) {
let canvasRect = origEvent.target.getBoundingClientRect();
let x = origEvent.clientX - canvasRect.left;
let y = origEvent.clientY - canvasRect.top;
let col = Math.floor(x / tmrConstants.cellw); // [From 0 -> 12]
y -= col % 2 == 0 ? tmrConstants.col1_y : tmrConstants.col2_y;
let row = Math.floor(y / tmrConstants.cellh); // [From 0 -> 14]
return { col: col, row: row };
}
/* -------------------------------------------- */
/** Retourne les coordonnées x, h, w, h du rectangle d'une case donnée */
_getCaseRectangleCoord(coord) {

View File

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

View File

@ -5,7 +5,7 @@ import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js";
import { TMRUtility } from "./tmr-utility.js";
import { DialogItemAchat } from "./dialog-item-achat.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDItem } from "./item.js";
import { RdDPossession } from "./rdd-possession.js";
@ -28,7 +28,7 @@ const ajustementsEncaissement = Misc.intArray(-10, 26);
/* -------------------------------------------- */
function _buildAllSegmentsFatigue(max) {
const cycle = [5, 2, 4, 1, 3, 0];
const fatigue = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
let fatigue = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
for (let i = 0; i <= max; i++) {
const ligneFatigue = duplicate(fatigue[i]);
const caseIncrementee = cycle[i % 6];
@ -55,8 +55,7 @@ function _cumulSegmentsFatigue(matrix) {
}
/* -------------------------------------------- */
export const MAX_ENDURANCE_FATIGUE = 60;
const fatigueMatrix = _buildAllSegmentsFatigue(MAX_ENDURANCE_FATIGUE);
const fatigueMatrix = _buildAllSegmentsFatigue(60);
const cumulFatigueMatrix = _cumulSegmentsFatigue(fatigueMatrix);
const fatigueMalus = [0, 0, 0, -1, -1, -1, -2, -3, -4, -5, -6, -7]; // Provides the malus for each segment of fatigue
@ -122,7 +121,6 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs-entitee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-effects.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-hautreve.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/archetype.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/vue-detaillee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-main.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-derivee.html',
@ -181,25 +179,24 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/item-queue-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/header-item.html',
// partial enums
'systems/foundryvtt-reve-de-dragon/templates/enum-aspect-tarot.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-caracteristiques.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-aspect-tarot.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categories.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-ingredient.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-vehicule.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-competence.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-draconic.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-heures.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-initpremierround.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-mortalite.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-niveau-ethylisme.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-periode.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-rarete.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-effet.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-draconic.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-type.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-periode.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-effet.html',
// Partials
'systems/foundryvtt-reve-de-dragon/templates/tirage/liste-resultats-recherche.hbs',
'systems/foundryvtt-reve-de-dragon/templates/time/horloge.hbs',
@ -246,8 +243,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/chat-description.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html',
@ -282,7 +278,7 @@ export class RdDUtility {
Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode());
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option));
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionelles.isUsing(option));
Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name)));
Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences));
Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name));
@ -461,15 +457,18 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static getSegmentsFatigue(maxEndurance) {
return fatigueMatrix[Math.min(Math.max(maxEndurance, 1), fatigueMatrix.length)];
static getSegmentsFatigue(maxEnd) {
maxEnd = Math.max(maxEnd, 1);
maxEnd = Math.min(maxEnd, fatigueMatrix.length);
return fatigueMatrix[maxEnd];
}
/* -------------------------------------------- */
static calculMalusFatigue(fatigue, endurance) {
endurance = Math.min(Math.max(endurance, 1), cumulFatigueMatrix.length);
let segments = cumulFatigueMatrix[endurance];
for (let i = 0; i < segments.length; i++) {
static calculMalusFatigue(fatigue, maxEnd) {
maxEnd = Math.max(maxEnd, 1);
maxEnd = Math.min(maxEnd, cumulFatigueMatrix.length);
let segments = cumulFatigueMatrix[maxEnd];
for (let i = 0; i < 12; i++) {
if (fatigue <= segments[i]) {
return fatigueMalus[i]
}
@ -479,7 +478,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static calculFatigueHtml(fatigue, endurance) {
return ReglesOptionnelles.isUsing("appliquer-fatigue") ? {
return ReglesOptionelles.isUsing("appliquer-fatigue") ? {
malus: RdDUtility.calculMalusFatigue(fatigue, endurance),
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(fatigue, endurance).html() + "</table>"
} : { malus: 0, html: '' };
@ -489,7 +488,7 @@ export class RdDUtility {
// Build the nice (?) html table used to manage fatigue.
// max should be the endurance max value
static makeHTMLfatigueMatrix(fatigue, maxEndurance) {
const segments = this.getSegmentsFatigue(maxEndurance);
let segments = this.getSegmentsFatigue(maxEndurance);
return this.makeHTMLfatigueMatrixForSegment(fatigue, segments);
}
@ -555,14 +554,14 @@ export class RdDUtility {
let formula = "2d10";
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre')) {
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "min" + valeurMin;
}
}
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionnelles.isUsing('degat-ajout-malus-libre')) {
if (ReglesOptionelles.isUsing('degat-ajout-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "+" + valeurMin;
@ -572,7 +571,7 @@ export class RdDUtility {
let roll = await RdDDice.roll(formula, options);
// 1 dé fait au minmum la difficulté libre
if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre-simple')) {
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre-simple')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
if (roll.terms[0].results[0].result < valeurMin) {
@ -623,6 +622,25 @@ export class RdDUtility {
return perte.total;
}
/* -------------------------------------------- */
static currentFatigueMalus(value, max) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
max = Math.max(1, Math.min(max, 60));
value = Math.min(max * 2, Math.max(0, value));
let fatigueTab = fatigueMatrix[max];
let fatigueRem = value;
for (let idx = 0; idx < fatigueTab.length; idx++) {
fatigueRem -= fatigueTab[idx];
if (fatigueRem <= 0) {
return fatigueMalus[idx];
}
}
return -7; // This is the max !
}
return 0;
}
/* -------------------------------------------- */
static async responseNombreAstral(callData) {
let actor = game.actors.get(callData.id);

View File

@ -7,7 +7,7 @@ import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDPossession } from "./rdd-possession.js";
import { RdDUtility } from "./rdd-utility.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
/**
* tous les ajustements pouvant s'appliquer.
@ -69,8 +69,8 @@ export const referenceAjustements = {
getValue: (rollData, actor) => -actor.getEncTotal()
},
surenc: {
isVisible: (rollData, actor) => RdDCarac.isActionPhysique(rollData.selectedCarac) && actor.isSurenc(),
isUsed: (rollData, actor) => rollData.use?.surenc && RdDCarac.isActionPhysique(rollData.selectedCarac),
isVisible: (rollData, actor) => actor.isSurenc(),
isUsed: (rollData, actor) => rollData.use?.surenc,
getLabel: (rollData, actor) => 'Sur-encombrement',
getValue: (rollData, actor) => actor.computeMalusSurEncombrement()
},
@ -86,7 +86,7 @@ export const referenceAjustements = {
getValue: (rollData, actor) => actor.getMoralTotal()
},
astrologique: {
isUsed: (rollData, actor) => ReglesOptionnelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData),
isUsed: (rollData, actor) => ReglesOptionelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData),
getLabel: (rollData, actor) => 'Astrologique',
getValue: (rollData, actor) => actor.ajustementAstrologique()
},
@ -164,7 +164,7 @@ export class RollDataAjustements {
descr: reference.getDescr && reference.getDescr(rollData, actor)
}
}
rollData.finalLevel = RollDataAjustements.sum(rollData.ajustements)
rollData.finalLevel = RollDataAjustements.sum(rollData.ajustements);
}
/* -------------------------------------------- */

View File

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

View File

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

View File

@ -47,7 +47,6 @@ export class SystemCompendiums extends FormApplication {
label: "Compendiums système",
hint: "Ouvre la fenêtre de sélection des compendiums système",
icon: "fas fa-bars",
restricted: true,
type: SystemCompendiums
})
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js";
import { RdDDice } from "./rdd-dice.js";
import { tmrConstants } from "./tmr-constants.js";
/* -------------------------------------------- */
const TMRMapping = {
@ -164,7 +163,7 @@ const TMRMapping = {
C12: { type: "lac", label: "Lac de Fricassa" },
D12: { type: "collines", label: "Collines dHuaï" },
E12: { type: "monts", label: "Monts Ajourés" },
F12: { type: "necropole", label: "Nécropole de Throat" },
F12: { type: "necropole", label: "Nécropole de Troat" },
G12: { type: "plaines", label: "Plaines de Lufmil" },
H12: { type: "collines", label: "Collines de Tooth" },
I12: { type: "gouffre", label: "Gouffre Abimeux" },
@ -200,7 +199,7 @@ const TMRMapping = {
K14: { type: "necropole", label: "Nécropole dAntinéar" },
L14: { type: "plaines", label: "Plaines de Jislith" },
M14: { type: "desolation", label: "Désolation dAprès" },
A15: { type: "cite", label: "Cité de Mielh" },
C15: { type: "plaines", label: "Plaines de Toué" },
E15: { type: "foret", label: "Forêt des Furies" },
@ -275,11 +274,11 @@ export class TMRUtility {
const tmr = TMRUtility.getTMR(coord);
return Grammar.articleDetermine(tmr.type) + ' ' + tmr.label;
}
static findTMRLike(type, options = { inclusMauvaise: true }) {
static findTMRLike(type, options = {inclusMauvaise:true}) {
const choix = [...Object.values(TMRType)]
if (options.inclusMauvaise) {
choix.push({ name: 'Mauvaise' });
if (options.inclusMauvaise){
choix.push({name: 'Mauvaise'});
}
const selection = Misc.findAllLike(type, choix).map(it => it.name);
if (selection.length == 0) {
@ -298,7 +297,7 @@ export class TMRUtility {
}
static buildSelectionTypesTMR(typesTMR) {
typesTMR = typesTMR ?? [];
typesTMR = typesTMR?? [];
return Object.values(TMRType).map(value => Misc.upperFirst(value.name))
.sort()
.map(name => { return { name: name, selected: typesTMR.includes(name) } });
@ -376,36 +375,6 @@ export class TMRUtility {
return caseList;
}
// /* -------------------------------------------- */
static computeEventPosition(event) {
if (!event.nativeEvent.target.getBoundingClientRect) {
return { x: 0, y: 0 }
}
const canvasRect = event.nativeEvent.target.getBoundingClientRect();
return {
x: event.nativeEvent.clientX - canvasRect.left,
y: event.nativeEvent.clientY - canvasRect.top
};
}
/* -------------------------------------------- */
static computeEventOddq(event) {
var { x, y } = TMRUtility.computeEventPosition(event);
return TMRUtility.computeOddq(x, y);
}
static computeOddq(x, y) {
const col = Math.floor(x / tmrConstants.cellw); // [From 0 -> 12]
const decallageColonne = col % 2 == 0 ? tmrConstants.col1_y : tmrConstants.col2_y;
const row = Math.floor((y - decallageColonne) / tmrConstants.cellh); // [From 0 -> 14]
return { col, row };
}
static computeEventCoord(event) {
const oddq = TMRUtility.computeEventOddq(event);
return TMRUtility.oddqToCoordTMR(oddq);
}
/* -------------------------------------------- */
// https://www.redblobgames.com/grids/hexagons/#distances
// TMR Letter-row correspond to "odd-q" grid (letter => col, numeric => row )
@ -431,7 +400,7 @@ export class TMRUtility {
col >= 0 && col < 13 &&
row >= 0 &&
(row + col % 2 <= 14)
);
);
// if (x >= 0 && x < 13 && y >= 0 && y < 14) return true;
// if (x >= 0 && x < 13 && x % 2 == 0 && y == 14) return true;
// return false;
@ -475,7 +444,7 @@ export class TMRUtility {
static axial_subtract(a, b) {
return {
q: a.q - b.q,
q: a.q- b.q,
r: a.r - b.r
};
}
@ -487,7 +456,7 @@ export class TMRUtility {
// return Cube(q, r, s)
// }
// /* -------------------------------------------- */
// static computeRealPictureCoordinates(coordOddq) {
// let decallagePairImpair = (coordOddq.col % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;

View File

@ -1,5 +1,4 @@
import { Draconique } from "./draconique.js";
import { PixiTMR } from "./pixi-tmr.js";
export class CarteTmr extends Draconique {
@ -13,25 +12,9 @@ export class CarteTmr extends Draconique {
async onActorCreateOwned(actor, item) { }
code() { return 'tmr' }
img() { return 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmr.webp' }
img() { return 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmp_main_r1.webp' }
createSprite(pixiTMR) {
const img = PixiTMR.getImgFromCode(this.code())
const sprite = new PIXI.Sprite(PIXI.utils.TextureCache[img]);
// Setup the position of the TMR
sprite.x = 0;
sprite.y = 0;
sprite.width = 722;
sprite.height = 860;
// Rotate around the center
sprite.anchor.set(0);
sprite.buttonMode = true;
sprite.tmrObject = pixiTMR;
pixiTMR.addTooltip(sprite, (e,s) => this.computeTooltip(e,s));
pixiTMR.pixiApp.stage.addChild(sprite);
return sprite;
return pixiTMR.carteTmr(this.code());
}
}

View File

@ -11,7 +11,7 @@ const registeredEffects = [
export class Draconique {
static isCaseTMR(item) { return item.type == TYPES.casetmr; }
static isQueueDragon(item) { return item.isQueueDragon(); }
static isSouffleDragon(item) { return item.type == TYPES.souffle; }
static isSouffleDragon(item) {return item.type == TYPES.souffle; }
static isTeteDragon(item) { return item.type == TYPES.tete; }
static isQueueSouffle(item) { return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item); }
@ -78,45 +78,23 @@ export class Draconique {
/**
* @param {*} img l'url du fichier image à utiliser pour le token. Si indéfini (et si createSprite n'est pas surchargé),
* un disque est utilisé.
*/
*/
img() { return undefined }
/**
* factory d'élément graphique PIXI correspondant à l'objet draconique
* factory d'élément graphique PIXI correpsondant à l'objet draconique
* @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.
*/
*/
token(pixiTMR, linkData, coordTMR, type = undefined) {
const token = {
sprite: this.createSprite(pixiTMR),
coordTMR: coordTMR
};
token[type ?? this.code()] = linkData;
this.linkData = linkData;
if (this.tooltip(linkData)) {
pixiTMR.addTooltip(token.sprite, (e, s) => this.computeTooltip(e, s));
}
pixiTMR.addTooltip(token.sprite, this.tooltip(linkData));
return token;
}
/**
* methode en charge de recalculer le tooltip lorsque la souris bouge
* @param {*} event evenement contenant les coordonnées
* @param {*} sprite sprite pour laquelle calculer le tooltip
*/
computeTooltip(event, sprite) {
if (sprite.isOver) {
const oddq = TMRUtility.computeEventOddq(event);
const coord = TMRUtility.oddqToCoordTMR(oddq);
const tmr = TMRUtility.getTMR(coord)
if (tmr){
const label = TMRUtility.getTMRLabel(coord);
const text = this.tooltip(this.linkData);
return text ? `${coord}: ${label}\n${text}` : `${coord}: ${label}`
}
}
return '';
}
/**
* factory d'élément graphique PIXI correpsondant à l'objet draconique
* @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.
@ -138,11 +116,11 @@ export class Draconique {
isCase(item, coord = undefined) {
return Draconique.isCaseTMR(item) && item.system.specific == this.code() && (coord ? item.system.coord == coord : true);
}
find(list, coord = undefined) {
return list.find(c => this.isCase(c, coord));
}
async createCaseTmr(actor, label, tmr, sourceId = undefined) {
const casetmrData = {
name: label, type: 'casetmr', img: this.img(),
@ -150,12 +128,12 @@ export class Draconique {
};
await actor.createEmbeddedDocuments('Item', [casetmrData]);
}
async deleteCasesTmr(actor, draconique) {
let caseTmrs = actor.items.filter(it => this.isCaseForSource(it, draconique));
await actor.deleteEmbeddedDocuments('Item', caseTmrs.map(it => it.id));
}
isCaseForSource(item, draconique) {
return Draconique.isCaseTMR(item) && item.system.specific == this.code() && item.system.sourceid == draconique.id;
}

View File

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

View File

@ -1,8 +1,6 @@
import { RdDTMRDialog } from "../rdd-tmr-dialog.js";
import { tmrConstants, tmrTokenZIndex } from "../tmr-constants.js";
import { TMRUtility } from "../tmr-utility.js";
export const tooltipStyle = new PIXI.TextStyle({
const tooltipStyle = new PIXI.TextStyle({
fontFamily: 'CaslonAntique',
fontSize: 18,
fill: '#FFFFFF',
@ -22,37 +20,54 @@ export class PixiTMR {
this.callbacksOnAnimate = [];
}
async load(onLoad = (loader, resources) => { }) {
// WIP - Deprecated since v7 : let loader = new PIXI.Loader();
load( onLoad = (loader, resources) => {} ) {
let loader = this.pixiApp.loader;
for (const [name, img] of Object.entries(PixiTMR.textures)) {
const texture = await PIXI.Assets.load(img);
let image = PIXI.Sprite.from(texture);
loader = loader.add(name, img);
}
onLoad();
for (let onAnimate of this.callbacksOnAnimate) {
onAnimate();
}
}
static getImgFromCode(code) {
return PixiTMR.textures[code]
loader.onError.add((error, reason) => { console.log("ERROR", error, reason) });
loader.load( (loader, resources) => {
onLoad(loader, resources);
for (let onAnimate of this.callbacksOnAnimate) {
onAnimate();
}
});
}
static register(name, img) {
PixiTMR.textures[name] = img;
}
animate(animation = pixiApp => { }) {
animate(animation = pixiApp=>{})
{
this.callbacksOnAnimate.push(() => animation(this.pixiApp));
}
sprite(code, options = {}) {
let img = PixiTMR.getImgFromCode(code)
const texture = PIXI.utils.TextureCache[img];
if (!texture) {
console.error("Texture manquante", code, PIXI.utils.TextureCache)
return;
carteTmr(code) {
const carteTmr = new PIXI.Sprite(PIXI.utils.TextureCache[code]);
// Setup the position of the TMR
carteTmr.x = 0;
carteTmr.y = 0;
carteTmr.width = 720;
carteTmr.height = 860;
// Rotate around the center
carteTmr.anchor.set(0);
carteTmr.interactive = true;
carteTmr.buttonMode = true;
carteTmr.tmrObject = this;
if (!this.tmrObject.viewOnly) {
carteTmr.on('pointerdown', event => this.onClickBackground(event));
}
this.pixiApp.stage.addChild(carteTmr);
return carteTmr;
}
sprite(code, options = {}) {
const texture = PIXI.utils.TextureCache[code];
if (!texture) {
console.error("Texture manquante", code)
return;
}
let sprite = new PIXI.Sprite(texture);
sprite.width = options.taille ?? tmrConstants.half;
sprite.height = options.taille ?? tmrConstants.half;
@ -60,13 +75,13 @@ export class PixiTMR {
if (options.color) {
sprite.tint = options.color;
}
sprite.zIndex = options.zIndex ?? tmrTokenZIndex.casehumide + 1;
sprite.zIndex = options.zIndex ?? tmrTokenZIndex.casehumide+1;
sprite.alpha = options.alpha ?? 0.75;
sprite.decallage = options.decallage ?? tmrConstants.center;
this.pixiApp.stage.addChild(sprite);
return sprite;
}
}
circle(name, options = {}) {
let sprite = new PIXI.Graphics();
sprite.beginFill(options.color, options.opacity);
@ -77,37 +92,26 @@ export class PixiTMR {
return sprite;
}
addTooltip(sprite, computeTooltip) {
sprite.tooltip = new PIXI.Text('', tooltipStyle);
sprite.tooltip.zIndex = tmrTokenZIndex.tooltip;
sprite.isOver = false;
sprite.eventMode = 'static';
sprite
.on('pointermove', event => this.onPointerMove(event, sprite, computeTooltip))
.on('pointerdown', event => this.onClickBackground(event))
.on('pointerover', event => this.onShowTooltip(event, sprite))
.on('pointerout', event => this.onHideTooltip(event, sprite));
addTooltip(sprite, text) {
if (text) {
sprite.tooltip = new PIXI.Text(text, tooltipStyle);
sprite.tooltip.zIndex = tmrTokenZIndex.tooltip;
sprite.isOver = false;
sprite.interactive = true;
sprite.on('pointerdown', event => this.onClickBackground(event))
.on('pointerover', () => this.onShowTooltip(sprite))
.on('pointerout', () => this.onHideTooltip(sprite));
}
}
onClickBackground(event) {
if (!this.viewOnly) {
this.tmrObject.onClickTMR(event)
}
this.tmrObject.onClickTMR(event)
}
onPointerMove(event, sprite, computeTooltip) {
if (sprite.isOver && sprite.tooltip) {
var { x, y } = TMRUtility.computeEventPosition(event);
const oddq = TMRUtility.computeOddq(x, y);
sprite.tooltip.x = x + (oddq.col > 8 ? - 3 * tmrConstants.full : tmrConstants.half)
sprite.tooltip.y = y + (oddq.row > 10 ? - tmrConstants.half : tmrConstants.half)
sprite.tooltip.text = computeTooltip(event, sprite);
}
}
onShowTooltip(event, sprite) {
onShowTooltip(sprite) {
if (sprite.tooltip) {
if (!sprite.isOver) {
sprite.tooltip.x = sprite.x;
sprite.tooltip.y = sprite.y;
@ -117,7 +121,7 @@ export class PixiTMR {
}
}
onHideTooltip(event, sprite) {
onHideTooltip(sprite) {
if (sprite.tooltip) {
if (sprite.isOver) {
this.pixiApp.stage.removeChild(sprite.tooltip);
@ -126,7 +130,7 @@ export class PixiTMR {
}
}
setPosition(sprite, oddq) {
setPosition( sprite, oddq) {
let decallagePairImpair = (oddq.col % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
let dx = (sprite.decallage == undefined) ? 0 : sprite.decallage.x;
let dy = (sprite.decallage == undefined) ? 0 : sprite.decallage.y;

View File

@ -49,13 +49,12 @@ export class PresentCites extends Draconique {
const presents = await game.system.rdd.rencontresTMR.getPresentsCite()
const buttons = {};
presents.forEach(r => buttons['present'+r.id] = { icon: '<i class="fas fa-check"></i>', label: r.name, callback: async () => onChoixPresent(r) });
let dialog = new Dialog({
let d = new Dialog({
title: "Présent des cités",
content: `La ${this.tmrLabel(casetmr)} vous offre un présent, faites votre choix`,
buttons: buttons
});
dialog.render(true);
return dialog
d.render(true);
}
async ouvrirLePresent(actor, casetmr) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 KiB

View File

@ -171,9 +171,6 @@ i:is(.fas, .far) {
width: fit-content;
}
.tmr-dialog table {
border: none;
}
.system-foundryvtt-reve-de-dragon .sheet-header div.tmr-buttons {
padding: 0;
margin: 0;
@ -290,16 +287,6 @@ table {border: 1px solid #7a7971;}
padding: 0;
}
.grid-competence-archetype {
display: grid;
grid-column: span 3 / span 3;
grid-template-columns: 2fr 2fr 1fr;
gap: 0.5rem;
margin: 0.5rem 0;
padding: 0;
}
.grid-3col {
grid-column: span 3 / span 3;
grid-template-columns: repeat(3, minmax(0, 1fr));
@ -1911,51 +1898,34 @@ div.calendar-timestamp-edit select.calendar-signe-heure {
opacity: 1;
}
.chat-card-button, .chat-card-button-pushed {
border-radius: 0.2rem;
cursor: pointer;
font-family: CaslonPro;
font-size: 0.9rem;
padding: 0.2rem 0.4rem 0rem 0.4rem;
text-decoration: none;
position: relative;
margin: 0.3rem;
border: 2px ridge #846109;
display: inline-block;
}
.chat-card-button{
text-shadow: 1px 1px #4d3534;
box-shadow: inset 1x 1px #a6827e;
color: var(--major-button-color);
.chat-card-button {
box-shadow: inset 0px 1px 0px 0px #a6827e;
background: var(--background-custom-button);
background-color: #7d5d3b00;
border-radius: 3px;
border: 2px ridge #846109;
display: inline-block;
cursor: pointer;
color: #ffffff;
font-family: CaslonPro;
font-size: 0.9rem;
padding: 4px 12px 0px 12px;
text-decoration: none;
text-shadow: 0px 1px 0px #4d3534;
position: relative;
margin:5px;
}
.chat-card-button-pushed {
text-shadow: 1px 1px hsla(202, 30%, 70%, 0.5);
box-shadow: inset -1px -1px #a6827e;
color: hsla(202, 42%, 14%, 0.7);
background: var(--major-button-color);
background-color: #7d5d3b00;
}
.chat-card-button:hover {
background: var(--background-custom-button-hover);
background-color: red;
}
.chat-card-button-pushed:hover {
background: var(--background-custom-button-hover);
background-color: red;
}
.chat-card-button:active, .chat-card-button-pushed:active {
.chat-card-button:active {
position:relative;
top:1px;
}
/* Dropdown Content (Hidden by Default) */
.button-dropdown-content {
display: none;

View File

@ -1,13 +1,13 @@
{
"id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon",
"version": "11.1.2",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.1.2.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json",
"changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"version": "10.7.21",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-10.7.21.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v10/system.json",
"compatibility": {
"minimum": "11",
"verified": "11"
"minimum": "10",
"verified": "10",
"maximum": "10"
},
"description": "Rêve de Dragon RPG for FoundryVTT",
"authors": [
@ -69,10 +69,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/competences.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -81,10 +78,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/arts-et-divertissements.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -93,10 +87,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/competences-creatures.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -105,10 +96,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/competences-entites.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -117,10 +105,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/sorts-oniros.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -129,10 +114,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/sorts-hypnos.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -141,10 +123,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/sorts-narcos.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -153,10 +132,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/sorts-thanatos.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -165,10 +141,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/equipement.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -177,10 +150,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/maladies-et-poisons.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -189,10 +159,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/rappel-des-regles.db",
"type": "JournalEntry",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OBSERVER"
},
"private": false,
"flags": {}
},
{
@ -201,10 +168,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/macros.db",
"type": "Macro",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -213,10 +177,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/queues-de-dragon.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -225,10 +186,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/ombres-de-thanatos.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -237,10 +195,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/souffles-de-dragon.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -249,10 +204,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/tarot-draconique.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -261,10 +213,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/extrait-poetique.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -273,10 +222,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/tetes-de-dragon-pour-haut-revants.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -285,10 +231,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/tetes-de-dragon-pour-tous-personnages.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -297,10 +240,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/rencontres.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -309,10 +249,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/tables-diverses.db",
"type": "RollTable",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -321,10 +258,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/animaux.db",
"type": "Actor",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -333,10 +267,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/voyageurs.db",
"type": "Actor",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": false,
"flags": {}
},
{
@ -345,10 +276,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/vehicules.db",
"type": "Actor",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -357,10 +285,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/archetypes.db",
"type": "Actor",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -369,10 +294,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/humanoides.db",
"type": "Actor",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -381,10 +303,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/entites-de-cauchemar.db",
"type": "Actor",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -393,10 +312,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/invocations.db",
"type": "Actor",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -405,10 +321,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/faune-flore-mineraux.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -417,10 +330,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/meditations-et-ecrits.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -429,10 +339,7 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/recettes-alchimiques.db",
"type": "Item",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
},
{
@ -441,79 +348,10 @@
"system": "foundryvtt-reve-de-dragon",
"path": "packs/scenes-rdd.db",
"type": "Scene",
"ownership": {
"PLAYER": "NONE",
"ASSISTANT": "OWNER"
},
"private": true,
"flags": {}
}
],
"packFolders": [
{
"name": "Rêve de Dragon",
"sorting": "m",
"packs": [
"rappel-des-regles"
],
"folders": [
{
"name": "Personnages",
"sorting": "m",
"packs": [
"voyageurs",
"archetypes",
"humanoides",
"equipement",
"competences",
"arts-et-divertissements",
"meditations-et-ecrits",
"recettes-alchimiques"
]
},
{
"name": "Le Haut-rêve",
"sorting": "m",
"packs": [
"tetes-de-dragon-pour-tous-personnages",
"sorts-oniros",
"sorts-hypnos",
"sorts-narcos",
"sorts-thanatos",
"invocations",
"rencontres",
"queues-de-dragon",
"ombres-de-thanatos",
"souffles-de-dragon",
"tetes-de-dragon-pour-haut-revants"
]
},
{
"name": "Découverte du monde",
"sorting": "m",
"packs": [
"animaux",
"vehicules",
"entites",
"faune-flore-mineraux",
"competences-creatures",
"competences-entites",
"maladies-et-poisons"
]
},
{
"name": "Outils du Gardien",
"sorting": "m",
"packs": [
"scenes-rdd",
"tables-diverses",
"macros",
"tarot-draconique",
"extrait-poetique"
]
}
]
}
],
"socket": true,
"gridDistance": 1,
"gridUnits": "m",

View File

@ -393,6 +393,12 @@
"label": "Encombrement",
"derivee": true
},
"malusarmure": {
"type": "number",
"value": 0,
"label": "Malus Armure",
"derivee": true
},
"protection": {
"type": "number",
"value": 0,
@ -588,8 +594,7 @@
"iscombat": false,
"isnaturelle": true,
"ispossession": false,
"dommages": 0,
"mortalite": "mortel"
"dommages": 0
},
"empoignade": {
"templates": ["description"],
@ -666,7 +671,6 @@
"resistance": 0,
"categorie_parade": "",
"dommages": "0",
"mortalite": "mortel",
"penetration": 0,
"force": "0",
"competence": "",

View File

@ -53,9 +53,7 @@
{{#if options.isObserver}}{{!-- Compétences Tab --}}
<div class="tab competences" data-group="primary" data-tab="competences">
<div class="flexrow">
<span>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/vue-detaillee.html"}}
</span>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/vue-detaillee.html"}}
<span class="flexrow"><a class="show-hide-competences">
{{#if options.showCompNiveauBase}}
<i class="fa-regular fa-filter-slash"></i> Montrer tout
@ -67,17 +65,9 @@
<input class="recherche flex-grow" type="text" value="{{options.recherche.text}}" name="recherche" size="8" data-dtype="String" placeholder=""/>
</span>
<span>
{{#if options.vueDetaillee}}
&nbsp;&nbsp;
{{#if @root.options.vueArchetype}}
<a class="competence-archetype toggle-archetype chat-card-button-pushed"><i class="fa-solid fa-up-right-from-square"></i>Incarnation</a>
{{else}}
<a class="competence-archetype toggle-archetype chat-card-button"><i class="fa-solid fa-people-line"></i>Archétype</a>
{{/if}}
{{/if}}
</span>
</div>
<div class="grid {{#if (and options.vueDetaillee options.vueArchetype)}}grid-competence-archetype{{else}}grid-2col{{/if}}">
<div class="grid grid-2col">
<div class="competence-column">
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.html" competences=(filtreTriCompetences byCateg.generale) categorie="Compétences générales"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.html" competences=(filtreTriCompetences byCateg.particuliere) categorie="Compétences Particulières"}}
@ -93,11 +83,6 @@
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.html" competences=(filtreTriCompetences byCateg.draconic) categorie="Draconic"}}
{{/if}}
</div>
{{#if (and options.vueDetaillee options.vueArchetype)}}
<div>
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/archetype.hbs"}}
</div>
{{/if}}
</div>
</div>
{{/if}}

View File

@ -1,21 +0,0 @@
<div>
<ul class="item-list">
{{#if @root.options.isGM}}
<li>
<a class="nouvelle-incarnation chat-card-button"><i class="fa-solid fa-person-circle-plus"></i> Nouvelle incarnation</a>
</li>
{{/if}}
<li><hr></li>
<li>Niveaux d'archétype</li>
{{#if calc.comptageArchetype}}
{{#each calc.comptageArchetype as |archetype|}}
<li class="item flexrow">
<label class="generic-label">
Niveaux {{numberFormat archetype.niveau decimals=0 sign=true}} : {{archetype.nombre}} / {{archetype.nombreMax}}
</label>
</li>
{{/each}}
{{/if}}
</ul>
</div>

View File

@ -72,7 +72,7 @@
</li>
<li class="caracteristique flexrow list-item" >
<label class="carac-label">Malus armure</label>
<input class="derivee-value" type="number" disabled value="{{calc.malusArmure}}" data-dtype="number"/>
<input class="derivee-value" type="number" disabled value="{{system.attributs.malusarmure.value}}" data-dtype="number"/>
</li>
<li class="caracteristique flexrow list-item">
<label class="carac-label" for="system.attributs.protection.value" >Protection naturelle</label>

View File

@ -14,13 +14,12 @@
<input class="competence-value" type="text" compname="{{name}}" name="comp-value-{{name}}"
value="{{numberFormat system.niveau decimals=0 sign=true}}" data-dtype="number"
{{#if (or (not @root.options.vueDetaillee) @root.options.vueArchetype)}}disabled{{/if}} />
{{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
{{#if @root.options.vueDetaillee}}
<span class="competence-xp {{#unless system.isLevelUp}}tooltip{{/unless}}">
<input class="competence-xp " type="text" compname="{{name}}" name="comp-xp-{{name}}"
value="{{numberFormat system.xp decimals=0 sign=false}}" data-dtype="number"
{{#if (or (not @root.options.vueDetaillee) @root.options.vueArchetype)}}disabled{{/if}} />
value="{{numberFormat system.xp decimals=0 sign=false}}" data-dtype="number"/>
{{#unless system.isLevelUp}}
<span class="tooltiptext left-competence ttt-xp">Vous devez acquérir {{system.xpNext}} points d'Experience pour augmenter de 1 votre compétence {{name}}</span>
{{/unless}}
@ -29,7 +28,7 @@
{{#if (eq system.categorie 'draconic')}}
<input class="competence-xp-sort" type="text" compname="{{name}}" name="comp-xp-sort-{{name}}"
value="{{numberFormat system.xp_sort decimals=0 sign=false}}" data-dtype="number"
{{#if (or (not @root.options.vueDetaillee) @root.options.vueArchetype)}}disabled{{/if}} />
{{#unless @root.options.vueDetaillee}}disabled{{/unless}}/>
{{/if}}
{{#if @root.options.vueDetaillee}}
<div class="item-controls">
@ -42,8 +41,7 @@
<i class="far fa-circle"></i>
{{/if}}
<input class="competence-archetype niveau-archetype" type="text" compname="{{name}}" name="comp-archetype-{{name}}"
value="{{numberFormat system.niveau_archetype decimals=0 sign=true}}" data-dtype="number"
{{#if (not @root.options.vueArchetype)}}disabled{{/if}} />
value="{{numberFormat system.niveau_archetype decimals=0 sign=true}}" data-dtype="number"/>
<a class="item-edit" title="Modifier"><i class="fas fa-edit"></i></a>
{{#if @root.options.isGM}}
<a class="item-delete" title="Supprimer"><i class="fas fa-trash"></i></a>

View File

@ -13,7 +13,18 @@
<span class="generic-label">Total XP compétences</span>
<span class="competence-value">{{calc.competenceXPTotal}}</span>
</li>
{{/if}}
{{#if calc.comptageArchetype}}
<li><hr></li>
<li>Niveaux d'archétype à répartir</li>
{{#each calc.comptageArchetype as |archetype key|}}
{{#if (gt archetype.reste 0)}}
<li class="item flexrow">
<label class="generic-label">Reste {{archetype.reste}} niveaux {{numberFormat archetype.niveau decimals=0 sign=true}} sur {{archetype.nombreMax}}</label>
</li>
{{/if}}
{{/each}}
{{/if}}
{{/if}}
<li>&nbsp;</li>
</ul>
</div>

View File

@ -1 +0,0 @@
<h4>C'est au tour de {{alias}} !</h4>

View File

@ -1,4 +1,4 @@
<h4>Résumé de santé pour {{alias}}</h4>
<h4>C'est au tour de {{alias}} !</h4>
<div data-combatid="{{combatId}}" data-combatmessage="actor-turn-summary">{{blessuresStatus}}</div>
<div>Son état général est de : {{etatGeneral}} {{#if isSonne}} et est <strong>sonné</strong>{{/if}}</div>
{{#if isGrave}}
@ -6,7 +6,5 @@
{{/if}}
{{#if isCritique}}
<div>{{alias}} souffre d'une <strong>Blessure Critique</strong> : faites un
<a class="chat-card-button chat-jet-vie"
data-tokenId="{{tokenId}}"
data-actorId="{{actorId}}">Jet de Vie.<a></div>
<a id="chat-jet-vie" class="chat-card-button" data-actorId="{{actorId}}">Jet de Vie.<a></div>
{{/if}}

View File

@ -1,6 +1,6 @@
{{#if use.moral}}
<span>
Vous avez fait appel {{#if (gt moral 0)}}au moral{{else}}à l'énergie du désespoir{{/if}}
Vous avez fait appel {{#if (gt moral 0)}}au moral{{else}}à l'énergie du déspoir{{/if}}
{{#if (eq perteMoralEchec 'dissolution')}}et échoué, cous marquez un point de dissolution!.
{{else if (eq perteMoralEchec 'perte')}}et échoué, votre moral baisse à {{moral}}.
{{else}}et réussi, votre moral reste de {{moral}}.

View File

@ -1,14 +1,14 @@
<img class="chat-icon" src="{{competence.img}}" alt="{{oeuvre.system.competence}}" />
<h4>
{{alias}} {{#if rolled.isSuccess}}chante{{else}}tente de chanter{{/if}} {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
{{alias}} tente de chanter : {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
</h4>
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
<hr>
<div>
{{#if rolled.isSuccess}}
{{alias}} réussit son interprétation avec une qualité de {{qualiteFinale}} .
{{alias}} réussi son interprétation avec une qualité de {{qualiteFinale}} .
{{else}}
{{alias}} manque d'inspiration, son interprétation a une qualité de {{qualiteFinale}}.
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html"}}
</div>

View File

@ -1,14 +1,14 @@
<img class="chat-icon" src="{{competence.img}}" alt="{{oeuvre.system.competence}}" />
<h4>
{{alias}} {{#if rolled.isSuccess}}danse{{else}}tente de danser{{/if}} {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
{{alias}} tente de danser : {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
</h4>
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
<hr>
<div>
{{#if rolled.isSuccess}}
{{alias}} réussit son interprétation avec une qualité de {{qualiteFinale}} .
{{alias}} réussi son interprétation avec une qualité de {{qualiteFinale}} .
{{else}}
{{alias}} manque d'inspiration, son interprétation a une qualité de {{qualiteFinale}}.
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html"}}
</div>

View File

@ -26,7 +26,7 @@
{{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup
{{else if mort}}vient de mourir
{{else if blessure}}
{{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}}
{{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.labelGravite}}
{{else}}subit une contusion
{{~/if~}}
{{else}}s'en sort sans une égratignure
@ -44,7 +44,7 @@
{{#if (ne dmg.mortalite 'entiteincarnee')}}
{{#if (gt endurance 1)}}et
{{#if sonne}}est <strong>sonné</strong><img class="chat-icon" src="icons/svg/stoned.svg" alt="charge" height="16" width="16" /> jusqu'à la fin du prochain round{{else}}n'est pas sonné{{/if}}!
{{#if hasPlayerOwner}}Jet d'endurance : {{jetEndurance}} / {{resteEndurance}}{{/if}}
{{#if hasPlayerOwner}}Jet d'endurance : Jet d'endurance : {{jetEndurance}} / {{resteEndurance}}{{/if}}
{{/if}}
{{/if}}
{{/if}}

View File

@ -1,6 +1,6 @@
<img class="chat-icon" src="{{competence.img}}" alt="{{oeuvre.system.competence}}" />
<h4>
{{alias}} {{#if rolled.isSuccess}}interprete{{else}}tente de jouer{{/if}} le morceau : {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
{{alias}} tente de jouer le morceau : {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
</h4>
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
<hr>
@ -8,7 +8,7 @@
{{#if rolled.isSuccess}}
{{alias}} réussit son interprétation avec une qualité de {{qualiteFinale}} .
{{else}}
{{alias}} manque d'inspiration, son interprétation a une qualité de {{qualiteFinale}}.
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html"}}
</div>

View File

@ -7,9 +7,9 @@
<hr>
<div>
{{#if rolled.isSuccess}}
{{alias}} réussit son interprétation avec une qualité de {{qualiteFinale}} .
{{alias}} réussi son interprétation avec une qualité de {{qualiteFinale}} .
{{else}}
{{alias}} manque d'inspiration, son interprétation a une qualité de {{qualiteFinale}}.
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html"}}
</div>

View File

@ -1,6 +1,6 @@
<img class="chat-icon" src="{{competence.img}}" alt="{{oeuvre.system.competence}}" />
<h4>
{{alias}} {{#if rolled.isSuccess}}réalise{{else}}tente de cuisiner{{/if}} la recette : {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
{{alias}} tente de cuisiner la recette : {{oeuvre.name}} (niveau {{oeuvre.system.niveau}})
</h4>
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
<hr>

View File

@ -1,29 +1,13 @@
<form class="skill-roll-dialog">
<img class="chat-icon" src="{{competence.img}}" alt="{{competence.name}}"/>
<h2 class="dialog-roll-title"></h2>
<div class="grid grid-2col">
<div class="flex-group-left">
<img class="chat-icon" src="{{competence.img}}" alt="{{competence.name}}"/>
<div class="flexrow">
<label>Caractéristique</label>
<span>
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-select-carac.html"}}
</span>
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-select-carac.html"}}
</div>
{{#if targetToken}}
<div class="flexrow">
<label>Cible:</label>
<label>
<img class="sheet-competence-img" src="{{targetToken.img}}" title="{{targetToken.name}}" />
{{targetToken.name}}
</label>
</div>
{{/if}}
{{#if ajustements.attaqueDefenseurSurpris.used}}
<div class="flexrow">
<label>{{ajustements.attaqueDefenseurSurpris.label}}</label>
</div>
{{/if}}
{{#if arme}}
{{#if attackerRoll}}
{{#if attackerRoll.tactique}}
@ -43,28 +27,41 @@
<div class="tooltiptext ttt-ajustements">
<div>
<strong>Charge</strong> : Les longueurs d'armes n'interviennent pas dans la charge, il faut gérer une initiative aléatoire dans ce cas.
<br>
<strong>Feinte</strong> : Vous devez avoir l'initative sur votre adversaire et y renoncer.
<br><strong>Feinte</strong> : Vous devez avoir l'initative sur votre adversaire et y renoncer.
</div>
</div>
</span>
</div>
{{/if}}
{{#if targetToken}}
<div class="flexrow">
Cible: {{targetToken.name}}
<img class="sheet-competence-img" src="{{targetToken.img}}" title="{{targetToken.name}}" />
</div>
{{/if}}
{{#if ajustements.attaqueDefenseurSurpris.used}}
<div class="flexrow">
<label>{{ajustements.attaqueDefenseurSurpris.label}}</label>
</div>
{{/if}}
{{#unless attackerRoll}}
<div class="flexrow">
<label>D&eacute;gats:</label>
{{#if (eq arme.system.mortalite 'empoignade')}}
{{#if (eq arme.system.mortalite 'non-mortel')}}
<label class="dmg-arme-actor"></label>
{{else if (eq arme.system.mortalite 'empoignade')}}
<label>Empoignade</label>
{{else}}
<span>
{{#unless (eq arme.system.mortalite 'non-mortel')}}
<input class="attribute-value check-mortalite" type="checkbox" name="mortalite" {{#unless (eq mortalite 'mortel')}}checked{{/unless}} />
{{/unless}}
<label class="dmg-arme-actor" name="dmg-arme-actor"></label> (<label class="arme-mortalite" name="arme-mortalite"></label>)
<input class="attribute-value" type="checkbox" name="coupsNonMortels" {{#unless (eq mortalite 'mortel')}}checked{{/unless}} />
<label class="dmg-arme-actor"></label>
</span>
{{/if}}
</div>
{{/if}}
{{/unless}}
{{/if}}
<div class="flexrow"></div>
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-surenc.html"}}
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-enctotal.html"}}
</div>
@ -78,7 +75,6 @@
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffCondition.html"}}
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-forcer.html"}}
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-moral.html"}}
<div class="flexrow"></div>
<div class="placeholder-ajustements" class="flexrow"></div>
</div>
</div>

View File

@ -1,7 +1,8 @@
<form class="tmr-dialog">
<h2 class="comptmrdialog" id="tmrDialogTitle" style="visibility: hidden;"></h2>
<table>
<tr class="tmr-row">
<h2 class="comptmrdialog" id="tmrDialogTitle"></h2>
<table id="tmrsheet">
<tr id="tmrrow1">
<td>
{{#if (eq mode "visu")}}
<div class="flex-group-center">
@ -53,5 +54,6 @@
</td>
</tr>
</table>
</form>

View File

@ -1,3 +0,0 @@
<option value="mortel">Mortel</option>
<option value="non-mortel">Non mortel</option>
<option value="empoignade">Empoignade</option>

View File

@ -37,14 +37,6 @@
<label for="system.dommages">Dommages </label>
<input class="attribute-value" type="text" name="system.dommages" value="{{system.dommages}}" data-dtype="String"/>
</div>
<div class="form-group">
<label for="system.mortalite">Mortalité</label>
<select name="system.mortalite" data-dtype="String">
{{#select system.mortalite}}
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-mortalite.html"}}
{{/select}}
</select>
</div>
<div class="form-group">
<label for="system.resistance">Résistance </label>
<input class="attribute-value" type="text" name="system.resistance" value="{{system.resistance}}" data-dtype="Number"/>

View File

@ -27,14 +27,6 @@
<label for="niveau">Dommages (+dom)</label>
<input class="attribute-value" type="text" name="system.dommages" value="{{system.dommages}}" data-dtype="Number"/>
</div>
<div class="form-group">
<label for="system.mortalite">Mortalité</label>
<select name="system.mortalite" data-dtype="String">
{{#select system.mortalite}}
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-mortalite.html"}}
{{/select}}
</select>
</div>
{{/if}}
{{#if isparade}}
<div class="form-group">

View File

@ -16,7 +16,6 @@
</div>
{{/if}}
<div class="horloge-digitale">
<span>
<a class="toggle-horloge-analogique">
{{#if horlogeAnalogique}}
@ -34,15 +33,6 @@
{{#if isGM}}
<span class="calendar-minute-texte">{{minute}} minutes</span>
{{/if}}
{{#if isGM}}
<span class="toggle-auto-darkness">
{{#if autoDarkness}}
<i class="fa-solid fa-lightbulb"></i>
{{else}}
<i class="fa-regular fa-lightbulb"></i>
{{/if}}
</span>
{{/if}}
</div>
<div class="horloge-analogique {{#unless horlogeAnalogique}}horloge-analogique-hidden{{/unless}}">
{{> 'systems/foundryvtt-reve-de-dragon/templates/time/horloge.hbs' }}