Compare commits

..

42 Commits
13.0.9 ... v13

Author SHA1 Message Date
0b90badb5e Merge pull request 'v13.0.14 - Le familier d'Illysis' (#779) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
All checks were successful
Release Creation / build (release) Successful in 2m39s
Reviewed-on: #779
2025-10-25 23:21:59 +02:00
290b5029d1 Correction couleurs sommaires 2025-10-24 22:20:29 +02:00
a7e4aea52d Fix: meditation V2 2025-10-24 01:22:20 +02:00
755d15509e Ajout du StatusEffect surencombré 2025-10-23 20:21:56 +02:00
02cea84ebb Empoignade V2 2025-10-23 12:01:52 +02:00
c47fc4f5b6 Fix scenes image locations 2025-10-22 22:40:07 +02:00
147e49f2cb Gestion des significatives 2025-10-21 01:40:14 +02:00
7f4f942d50 Amélioration des entités
- l'attaquant ne sait plus que c'est une entité de cauchemar (surprise!)
- l'encaissement indique une blessure dans le tchat... même si ce
  n'est que de l'endurance
- les blurettes suivent les règles des entités de cauchemar (p322)
2025-10-21 01:39:23 +02:00
8f4df1af56 Fix: ajustements surprises
Affichage de l'image de la surprise
2025-10-21 01:39:23 +02:00
77f2de2c5f Jet V2 pour les créatures 2025-10-21 01:39:16 +02:00
e5e271e424 Corrections majuscules/couleurs 2025-10-19 23:53:43 +02:00
43ecca8be1 Merge pull request 'v13.0.13 - L'épanouissement d'Illysis' (#777) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
All checks were successful
Release Creation / build (release) Successful in 2m5s
Reviewed-on: #777
2025-10-18 18:04:36 +02:00
e21f7e398a Création de tâches dans la fenêtre de jets 2025-10-18 00:52:14 +02:00
e16f89743a Affichage des ajustements V2 2025-10-18 00:52:14 +02:00
a6c593c100 Corrections premiers retours 2025-10-18 00:52:14 +02:00
cd8e190082 Merge pull request 'v13.0.12 - La méditation d'Illysis' (#776) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
All checks were successful
Release Creation / build (release) Successful in 1m49s
Reviewed-on: #776
2025-10-16 20:14:03 +02:00
f2106763c1 v13.0.12 2025-10-16 00:55:58 +02:00
5da5cb0314 Gestion des maladresses 2025-10-16 00:35:43 +02:00
6d7f66569a Lancer de sorts V2 2025-10-15 22:42:14 +02:00
3e8963b20b Impacts uniquement sur l'acteur 2025-10-15 22:42:14 +02:00
2cf5b06da8 Correction: rename bringToTop 2025-10-15 22:42:13 +02:00
68c01fc930 Montée TMR sur méditation
ajout d'un bouton pour aller dans les TMR lors d'une
méditation réussie
2025-10-15 22:42:13 +02:00
3bc1c4871b Biugfix: compétences de jeu
Les compétences de jeu spécifiques (doublon) ne remplacent plus
la compétence active si le jet n'est pas un jet de jeu
2025-10-15 00:26:01 +02:00
3d732e9a8a Suppression des signes draconiques
en cas de descente des TMR, suppression des signes
draconiques éphémères durant seulement 1 round
2025-10-15 00:24:35 +02:00
35f226af5c Correction du générateur aléatoire
- couleurs de cheveux/yeux
- les tailles entre 1m00 et 1m09 sont correctement affichées
- possibilité de positionner le sexe masculin/féminin en un clic
2025-10-14 17:44:39 +02:00
126a0701d8 Merge pull request 'v13.0.11 - Le gambit d'Illysis' (#775) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
All checks were successful
Release Creation / build (release) Successful in 2m2s
Reviewed-on: #775
2025-10-09 14:14:44 +02:00
edf920b340 Roll V2: cuisine 2025-10-08 16:36:14 +02:00
293af5ab83 Roll V2: les jeux 2025-10-08 16:35:58 +02:00
1bf9e330f4 Petites améliorations 2025-10-05 22:39:08 +02:00
33dc58138c Merge pull request '13.0.10 - Les papilles d'Illysis' (#774) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
All checks were successful
Release Creation / build (release) Successful in 1m45s
Reviewed-on: #774
2025-10-05 21:39:20 +02:00
fa6769fcd7 Fenêtres Roll V2
Maintenant disponibles pour:
- méditation
- tâches
- soins
2025-10-05 03:09:52 +02:00
47c4478303 Roll V2 sur compétences&combat
Corrections associées
Maintenant, active en mode rollV2
2025-10-03 22:34:07 +02:00
52065fd6f6 changelog 13.0.10 2025-10-03 02:21:22 +02:00
6886f19921 Jet de compétence V2 2025-10-03 02:21:22 +02:00
10a89cc268 Fix regression table rdd 2025-10-03 02:21:22 +02:00
6a987086f8 Correction message init & pugilat 2025-10-03 02:21:22 +02:00
54f470d531 Roll recette de cuisine 2025-10-03 02:21:22 +02:00
37af0dd4f1 Rename ajustements => v1 2025-10-03 02:21:22 +02:00
d46b039a63 Ajout des saignements 2025-10-03 02:21:21 +02:00
7370b633db Gestion attaques v2 et initiative 2025-10-03 02:21:21 +02:00
d0ba1ebf99 utilisation de renderTemplate "interne"
Import de foundry.applications.handlebars.renderTemplate
au travers d'une constante pour éliminer les warnings
2025-09-30 02:04:53 +02:00
053bc23d12 Move HUD button attaque 2025-09-29 01:47:35 +02:00
227 changed files with 4056 additions and 1527 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,0)" style=""><path d="m329.5 29.12-8.1 11.4L359 67.16l8.1-11.44zm-88 5.04 24.2 45.36 1.8 1.29 14.8-40.36zm57.6 12.63-16.4 44.8 40.7 28.81 35.3-31.54c-.9-.58-1.9-1.19-2.8-1.84zM59.83 48.56l10.84 45.83 29.63 2.6 2.7-29.63zM470.9 75.41c-5.6 4.71-12.2 8.59-19.5 11.74 5 46.45-14.7 83.45-45.2 109.75-26.5 22.9-60.9 38.4-95 47.9-2.5 4.8-5 9.2-7.4 13.1 41.5 5.4 93.2-21.2 129.2-60 19.8-21.3 34.8-45.9 41.1-69.2 5.2-19.4 4.7-37.42-3.2-53.29zm-351.3 8.71-3 32.48-32.35-2.9 226.55 271 20-16.7 15.3-12.8zM434 93.09c-4.2 1-8.5 2-12.8 2.7-14.9 2.5-30.1 3.1-43.5.3l-41 36.61c4 7 5 15.7 4.5 24.5-.6 12.6-4.3 26.7-9.3 40.9-3 8.3-6.3 16.6-9.9 24.6 26.9-9.2 52.6-22.3 72.5-39.4 26.2-22.8 42.5-51.6 39.5-90.21zM274 107.4l-51.2 72.2 30.6 36.5 58.2-82.1zM173.8 248.8 34.53 445.2l37.53 26.6L204.3 285.3zm233 79.2L273.3 439.5l19.2 23.1L426 351zm-18.3 77.9-35.3 29.4 39.7 47.6 35.3-29.4z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,-28)" style=""><path d="M259.063 25.094c-56.045 0-106.836 9.775-144.438 26.125-18.8 8.174-34.34 17.96-45.594 29.53-11.254 11.57-18.28 25.338-18.28 40.188 0 9.936 3.17 19.388 8.625 28.03-10.218 21.883-15.844 45.794-15.844 70.782 0 103.158 95.757 187.844 215.532 187.844 119.776 0 215.563-84.686 215.563-187.844 0-24.99-5.653-48.897-15.875-70.78 5.454-8.644 8.625-18.096 8.625-28.032 0-14.85-7.026-28.617-18.28-40.188-11.256-11.57-26.825-21.356-45.626-29.53-37.603-16.35-88.363-26.126-144.408-26.126zm0 18.687c53.848 0 102.554 9.6 136.968 24.564 17.208 7.482 30.775 16.306 39.658 25.437 8.882 9.133 13 18.115 13 27.157 0 9.043-4.118 18.057-13 27.188-8.883 9.13-22.45 17.956-39.657 25.438-34.413 14.963-83.12 24.562-136.967 24.562-53.85 0-102.555-9.6-136.97-24.563-17.206-7.48-30.804-16.306-39.687-25.437-8.882-9.13-12.97-18.145-12.97-27.188 0-9.042 4.088-18.024 12.97-27.156 8.883-9.13 22.48-17.954 39.688-25.436 34.414-14.964 83.12-24.563 136.97-24.563zm-7.782 17.282c-80.57 0-146 26.008-146 57.844 0 31.836 65.43 57.81 146 57.813 40.04 0 76.404-6.613 102.782-16.94-21.316 3.34-45.064 5.845-70.656 5.845-86.066 0-155.937-21.656-155.937-47.906s69.868-47.282 155.936-47.282c20.43 0 39.926.725 57.813 2.906-24.816-7.704-55.957-12.28-89.94-12.28zM87.657 360.5c-9.916 19.897-14.758 36.638-15.78 49.03-1.23 14.906 2.752 22.238 6.655 24.626 3.905 2.388 11.497 2.48 23.376-5.75 9.25-6.41 20.16-17.73 31.375-34.406-16.778-9.432-32.1-20.71-45.624-33.5zm342.75.063c-13.532 12.782-28.872 24.043-45.656 33.468 11.21 16.666 22.13 27.97 31.375 34.376 11.88 8.23 19.472 8.138 23.375 5.75 3.903-2.388 7.886-9.72 6.656-24.625-1.022-12.38-5.855-29.098-15.75-48.967zm-199.25 64.25c1.36 21.275 5.296 37.554 10.344 48.468 6.272 13.56 13.26 17.82 17.72 17.908 4.457.088 11.14-3.683 17.374-16.907 5.133-10.89 9.165-27.52 10.437-49.467a267.366 267.366 0 0 1-27.967 1.468c-9.437 0-18.75-.506-27.907-1.467z" fill="#fff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,0)" style=""><path d="M243.512 23.29c-27.105 18.337-53.533 32.92-82.274 45.337-2.843 17.364-3.948 34.497-4.05 51.584 28.913 15.41 56.096 32.85 83.33 49.634l7.045 4.344-3.432 7.482c-12.12 26.572-24.33 47.087-46.245 70.3l-5.184 5.512-6.46-3.904c-32.974-19.974-74.472-38.724-113.373-53.95l6.826-17.374c36.79 14.4 75.11 32.32 108.153 51.504 15.396-17.198 25.326-33.354 34.713-52.89-43.44-26.91-86.13-53.51-134.69-70.632-23.012 20.357-37.705 45.243-51.942 70.74 8.324 25.495 6.596 53.376-6.596 77.46 48.58-.593 97.994 2.23 150.666 10.26l5.658.837 1.787 5.44c8.85 26.46 11.79 54.41 8.325 83.588l-.987 8.432-8.466-.187c-40.508-.864-80.175-2.138-118.17.234 1.634 15.94-2.31 30.972-7.724 45.025 13.427 28.54 27.38 55.8 48.29 79.39 41.27-19.05 73.564-31.288 115.93-42.85-3.407-13.72-6.918-26.36-11.097-33.62-5.122-8.9-10.207-13.057-17.85-15.256-15.284-4.4-44.533 2.293-92.894 19.454l-6.243-17.594c48.907-17.354 79.702-26.894 104.283-19.82 9.133 2.628 16.884 8.004 23.066 15.46 14.487-7.627 28.415-16.79 42.053-26.996 12.34-45.92 37.29-81.42 66.626-112.107-7.226-13.52-13.208-27.204-20.563-40.613l-3.394-6.168 5-4.965c23.275-23.13 47.34-40.157 71.87-52.487l8.395 16.716c-20.952 10.53-41.503 25.913-61.795 45.152 12.41 23.91 22.263 45.5 39.457 64.826 37.488-27.124 74.943-51.39 116.84-74.938-13.96-30.473-31.345-58.357-56.286-79.462-32.2 13.38-62.527 17.39-92.61 12.29-14.223 13.25-30.094 22.23-48.756 23.337-29.017 1.722-60.74-15.74-99.174-57.672l6.858-6.295.017-.028.006.006 6.88-6.314c36.702 40.043 63.74 52.87 84.32 51.65 18.514-1.1 35.03-14.95 51.684-35.406-28.827-31.81-64.174-59.94-97.822-84.465zM39.324 277.884c-6.06.022-12.104.098-18.142.223 1.673 26.288 5.512 51.288 14.052 73.732 45.88-5.82 93.308-4.96 141.15-3.87 1.518-21.27-.253-41.69-6.058-61.212-45.528-6.565-88.59-9.03-131.002-8.873z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

65
assets/actions/surenc.svg Normal file
View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 448 434"
version="1.1"
id="svg6"
sodipodi:docname="surenc.svg"
width="448"
height="434"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2054"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4355469"
inkscape:cx="224"
inkscape:cy="210"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<g
class=""
id="g4"
transform="translate(-32,-46)">
<path
d="m 256,46 c -45.074,0 -82,36.926 -82,82 0,25.812 12.123,48.936 30.938,64 H 128 L 32,480 H 480 L 384,192 H 307.062 C 325.877,176.936 338,153.812 338,128 338,82.926 301.074,46 256,46 Z m 0,36 c 25.618,0 46,20.382 46,46 0,25.618 -20.382,46 -46,46 -25.618,0 -46,-20.382 -46,-46 0,-25.618 20.382,-46 46,-46 z m -82.215,202.95 h 23.5 v 33.263 l 33.873,-33.264 h 27.283 l -43.883,43.15 48.4,47.974 H 233.54 l -36.255,-35.888 v 35.888 h -23.5 z m 119.934,21.24 c 4.76,0 8.952,0.934 12.573,2.806 3.62,1.872 6.938,4.82 9.95,8.85 v -10.13 h 21.972 v 61.462 c 0,10.986 -3.48,19.368 -10.438,25.146 -6.917,5.82 -16.968,8.727 -30.152,8.727 -4.272,0 -8.4,-0.325 -12.39,-0.976 a 77.367,77.367 0 0 1 -12.024,-2.99 v -17.03 c 3.826,2.198 7.57,3.826 11.23,4.884 3.664,1.098 7.347,1.648 11.05,1.648 7.162,0 12.41,-1.566 15.746,-4.7 3.337,-3.132 5.006,-8.035 5.006,-14.708 v -4.7 c -3.01,3.986 -6.328,6.916 -9.95,8.788 -3.62,1.87 -7.813,2.808 -12.573,2.808 -8.343,0 -15.238,-3.275 -20.69,-9.826 -5.453,-6.592 -8.18,-14.974 -8.18,-25.146 0,-10.214 2.727,-18.576 8.18,-25.086 5.452,-6.55 12.347,-9.827 20.69,-9.827 z m 8.118,15.746 c -4.517,0 -8.038,1.67 -10.56,5.005 -2.523,3.338 -3.784,8.058 -3.784,14.162 0,6.266 1.22,11.026 3.662,14.28 2.442,3.215 6.003,4.823 10.682,4.823 4.557,0 8.096,-1.67 10.62,-5.006 2.522,-3.337 3.784,-8.036 3.784,-14.098 0,-6.104 -1.262,-10.824 -3.785,-14.16 -2.523,-3.337 -6.062,-5.006 -10.62,-5.006 z"
fill="#ffffff"
fill-opacity="1"
id="path2" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

1
assets/ui/fatigue.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,-28)" style=""><path d="M249.094 18.25c-42.675 0-81.852 25.486-110.938 68.438C109.07 129.637 90.72 189.74 90.72 256.28c0 66.543 18.35 126.643 47.436 169.595s68.263 68.47 110.938 68.47 81.883-25.518 110.97-68.47c29.084-42.952 47.436-103.052 47.436-169.594 0-66.54-18.352-126.64-47.438-169.592C330.978 43.736 291.77 18.25 249.094 18.25zm-128.97 241.313c18.356 18.096 37.528 26.765 55.72 27.562 18.19.797 35.927-6.096 52.125-21.5l12.874 13.53c-19.214 18.274-42.25 27.658-65.813 26.626-23.56-1.03-47.1-12.3-68-32.905l13.095-13.313zm264.782 0 13.125 13.312C377.135 293.48 353.564 304.75 330 305.78c-23.563 1.033-46.598-8.35-65.813-26.624l12.875-13.53c16.2 15.403 33.934 22.296 52.125 21.5 18.192-.798 37.365-9.467 55.72-27.563zM251.562 371.656c36.423-.156 72.996 19.144 77.438 58.406-51.33 13.296-102.67 12.767-154 0 3.858-38.638 40.14-58.25 76.563-58.406z" fill="#fff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

1
assets/ui/maladresse.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,0)" style=""><path d="M221.313 16a23.682 23.695 0 0 0-23.688 23.688v106.406a23.682 23.695 0 0 0 2.156 9.72 23.682 23.695 0 0 0 3.157 13.81l41.75 71.626-79 55.438 6.094-48.625a23.682 23.695 0 0 0-8.186-20.97l-66.28-81.937a23.682 23.695 0 0 0-33.314-3.5l-9.188 7.438a23.682 23.695 0 0 0-3.53 33.344l59.78 73.906-11.25 89.937a23.682 23.695 0 0 0 12.47 23.876l37.468 53.47a23.695 23.682 1.57 0 0 2.344 2.812 23.682 23.695 0 0 0 13.594 20.062L262 491.53a23.682 23.695 0 0 0 9.97 2.22 23.682 23.695 0 0 0 23.53-2.063l87.156-60.937a23.682 23.695 0 0 0 5.844-33l-6.78-9.688a23.682 23.695 0 0 0-32.97-5.875l-72.406 50.657-59.063-27.625 120.595-84.626a23.695 23.682 1.57 0 0 5.53-5.5 23.682 23.695 0 0 0 14.626-13.594l37.22-91.53 87.813-44.845a23.694 23.682 1.18 0 0 10.312-31.875L488 122.687a23.694 23.682 1.18 0 0-31.875-10.343l-94.688 48.375a23.694 23.682 1.18 0 0-9.843 9.436 23.682 23.695 0 0 0-8.344 10.47l-27.375 67.31-5.22-7.436a23.682 23.695 0 0 0-3-8.844l-50.81-87.094V39.688A23.682 23.695 0 0 0 233.154 16h-11.843zM77.75 376A59.994 60 0 0 0 16 436a59.994 60 0 1 0 120 0 59.994 60 0 0 0-58.25-60z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,-28)" style=""><path d="M45.95 14.553c-19.38.81-30.594 11.357-30.282 30.283l19.768 30.78c4.43-1.213 9.36-3.838 14.248-7.335l42.474 59.935c-17.018 20.83-31.258 44.44-42.71 70.836l26.55 26.552c11.275-23.6 24.634-44.826 39.918-63.864l210.82 297.475 166.807 33.213L460.33 325.62 162.78 114.745c19.907-16.108 41.842-29.91 65.652-41.578l-26.553-26.55c-27.206 11.803-51.442 26.576-72.735 44.292L69.39 48.56c3.443-4.823 6.062-9.735 7.342-14.242l-30.78-19.765zm400.84 86.933v.008l.003-.008h-.002zm0 .008-28.028 124.97-25.116-80.593-18.105 70.667-26.862-49.64-.584 57.818 128.484 91.69 15.184 87.017-1.168-186.885-34.457 39.713-9.346-154.756zm-300.95 27.98 222.224 196.368 25.645 66.75-66.75-25.645L130.6 144.734a308.453 308.453 0 0 1 15.238-15.26zm32.305 196.274v.004h.005l-.005-.004zm.005.004 28.028 22.775-36.21 4.088 57.82 19.272-105.706 4.09 115.05 27.45L136.1 422.114l127.316 25.696-67.164 43.803 208.494 1.752-87.017-15.185-104.54-150.676-35.037-1.752z" fill="#fff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

1
assets/ui/part-force.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,-28)" style=""><path d="M227.227 21.777c-1.845 0-3.704.05-5.567.157-15.314.875-30.76 5.305-39.494 10.863l-.008 73.15c2.884-.094 5.777-.147 8.676-.142 23.382.036 47.104 3.286 68.47 9.513l.01-87.507c-7.034-3.518-19.178-6.03-32.087-6.033zm80.74 9.16c-11.925.15-23.077 2.364-29.967 5.596l-.008 77.602v7.658c38.486 15.67 64.814 42.48 58.735 78.764l-.96 5.73-5.562 1.674c-17.45 5.253-34.872 9.703-52.225 13.335v25.234c25.562-.704 51.327-2.687 77.145-6.098l.02-197.928c-8.284-5.563-23.508-10.243-38.842-11.328a100.065 100.065 0 0 0-8.336-.238zM143.223 46.294a99.206 99.206 0 0 0-16.491 1.172c-15.67 2.454-31.477 8.565-40.406 15.402l-.01 72.955c18.808-15.81 46.704-25.143 77.15-28.54l.007-57.966c-4.82-1.752-12.018-2.916-20.25-3.023zm258.394 3.46c-10.804.117-20.722 1.93-27.043 4.655l-.02 183.182c25.074-4.02 50.16-9.412 75.122-16.358l1.99-158.447c-8.352-5.9-23.648-11.025-39.05-12.553a100.98 100.98 0 0 0-11-.478zm-222.775 74.202c-53.72.702-101.407 20.365-97.887 66.6 15.836-3.918 30.84-5.893 44.94-6.1 34.84-.51 64.213 9.704 87.318 27.613 34.608-3.11 69.852-10 105.412-20.314.14-41.287-74.098-68.657-139.783-67.8zm-48.877 78.65c-1.296-.003-2.603.012-3.92.045-17.256.436-36.45 4.03-57.566 11.037 5.79 53.808 26.325 106.41 58.5 143.346 6.226 7.15 12.856 13.712 19.875 19.615 29.303 9.282 69.26 12.917 110.534 12.14 3.777-55.805-8.717-108.357-36.193-142.74-21.265-26.61-51.064-43.39-91.232-43.444zm129.326 22.282a545.177 545.177 0 0 1-27.995 4.15 138.77 138.77 0 0 1 4.502 5.346c3.146 3.937 6.094 8.062 8.873 12.334 9.916.144 19.868.125 29.857-.106H259.29v-21.723zm191.817 15.343c-65.406 17.826-131.462 25.41-195.85 25.315 16.998 35.144 23.828 78.093 21.013 122.6 42.482-2.08 85.03-8.23 118.187-15.983 26.693-32.78 47.37-77.118 56.65-131.932zM400.51 389.9c-38.334 9.145-87.95 16.056-136.873 17.454-47.67 1.36-94.336-2.228-129.448-15.262l-.01 78.93c27.187 12.568 76.414 20.205 127.318 20.298 51.224.094 104.214-7.173 139-20.773l.012-80.647z" fill="#fff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,-28)" style=""><path d="M275.03 20c35.223 49.563 53.59 113.64 55.69 173.47C315.154 143 289.092 88.423 250.81 48.75c40.294 79.527 51.15 172.312 37.938 256.094-12.287-75.777-40.564-159.524-92.375-227.156 29.6 70.937 36.64 149.785 24.813 221.843-8.745-51.804-25.41-107.4-52.594-158.81 13.023 54.315 12.854 107.64 3.437 159.28l21.657 6.813 15 4.718-11.28 10.908c-10.68 10.332-19.868 21.905-27.345 34.343 93.614 35.486 232.952 64.53 298.032 41.376-41.02 56.466-210.332 13.822-309.313-18.687-1.514 3.775-2.918 7.594-4.124 11.467a152.536 152.536 0 0 0-6.062 29.657l176.47 66.375c98.5 31.095 150.5-24.62 158.655-81.72C505.253 254.472 485.016 105.66 426.06 20h-22.187c40.092 65.52 66.67 154.216 60.47 255.344-8.154-79.833-42.8-157.214-98.44-219.5 38.676 85.094 56.566 185.746 34.376 288.625.057-118.816-33.1-225.865-105.092-324.47H275.03zm-110.186 1.594c41.255 29.176 74.328 74.093 97.5 120.656-7.702-46.15-21.3-86.79-44-120.656h-53.5zm176.375 0c28.882 15.143 52.096 36.614 71.28 66.78-7.14-27.79-17.217-49.85-31.438-66.78H341.22zM123.686 304.406a179.344 179.344 0 0 1-4.062 64L18.812 336.344V366l91.938 29.094a178.602 178.602 0 0 1-30.313 48.28l50.094 15.75c-3.038-24.898-1.136-49.885 6.282-73.718 7.446-23.92 20.223-46.108 37.032-65.22l-50.156-15.78z" fill="#fff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,4 +1,83 @@
# 13.0
## 13.0.14 - Le familier d'Illysis
- Les réussites particulières en demi-surprise sont de simples réussites
- Les images des scènes par défaut sont corrigées
- Ajout d'une image de status "sur-encombré"
- Correction V13
- couleur lisible dans les sommaires des journaux et des compendiums
- Amélioration des entités:
- l'attaquant ne sait plus que c'est une entité de cauchemar (surprise!)
- l'encaissement indique une blessure dans le tchat... même si ce n'est que de l'endurance
- les blurettes suivent les règles des entités de cauchemar (p322)
- Nouvelle fenêtre de jets de dés
- attaque/défense des créatures
- les attaques/parades avec une arme trop lourde se font en demi-surprise
- les demandes de défense disparaîssent une fois prises en compte
- empoignade
- l'empoignade est possible avec une initiative d'empoignade, ou en cours d'empoignade
- seule la dague, le pugilat et la dague sont possibles en cours d'empoignade
- jet de Dextérité/Dague pour utiliser la dague en cours d'empoignade (p136)
- attaquer avec une arme un empoigneur donne un +4 si pas d'empoignade (p134)
- la différence de taille donne un bonus/malus en cours d'empoignade (p135)
- les dommages de l'empoignade ajoutent/enlèvent un point d'empoignade
- le statut d'empoignade est affiché sur les tokens
- les défenses contre une empoignade sont corrigées
## 13.0.13 - L'épanouissement d'Illysis
- Fix d'erreur au chargement de templates RollDialog
- Nouvelle fenêtre de jets de dés
- Fix: affichage des points de tâche
- Fix: affichage des ajustements cohérent
- L'ouverture depuis les caractéristiques permet plusieurs types de jets
- On peut créer ou modifier les tâches dans la fenêtre de jets de tâches
- attaque/défense
- les maladresses sont affichées dans le résultat du jet
- le message au défenseur s'affiche correctement
- la difficulté d'attaque s'applique à la défense
- on peut choisir les particulières en rapidité
## 13.0.12 - La méditation d'Illysis
- les signes draconiques éphémères de 1 round sont supprimés à la descente des TMRs
- Générateur de description
- correction des termes pour les couleurs des yeux/cheveux
- ajout de boutons pour forcer le sexe masculin/féminin
- Nouvelle fenêtre de jets de dés
- les méditations proposent un bouton pour monter dans les TMRs
- fenêtre de lancer de sorts
- Correction: les compétences de jeux ne remplacent plus les compétences en dehors des jets de jeu
- gestion des maladresses d'attaque et défense
## 13.0.11 - Le gambit d'Illysis
- Nouvelle fenêtre de jets de dés
- jeux
- cuisine et préparation de nourriture
## 13.0.10 - Les papilles d'Illysis
- Ajout d'un statut "saignement" en cas de blessure grave ou critique sans premiers soins
- Nouvelle fenêtre de jets de dés
- jets de méditation
- jets de tâches
- jets de caractéristiques
- jets de compétences
- Boutons d'initiative et d'attaque V2
- fenêtre d'attaque
- choix des armes
- gestion des particulières
- message au défenseur
- gestion des demi-surprises (attaquant/défenseur)
- gestion des tactiques (attaquant/défenseur)
- en cours nouvelle fenêtre de jets
- jets de compétence avec messages
- jets de cuisine séparés avec messages (pour gérer plus tard les spécificités: fabricatioon de plats)
- gestion des empoignades
- Technique: suppression de warnings foundry sur renderTemplate
## 13.0.9 - Le combat d'Illysis
- Fix

View File

@@ -96,6 +96,7 @@ select,
--gradient-red: linear-gradient(150deg, rgba(255, 0, 0, 0.3), rgba(255, 200, 128, 0.05), rgba(255, 200, 128, 0.1), rgba(255, 10, 0, 0.3));
--gradient-violet: linear-gradient(150deg, rgba(100, 45, 124, 0.6), rgba(216, 157, 192, 0.3), rgba(177, 157, 216, 0.5), rgba(107, 62, 121, 0.3), rgba(100, 45, 124, 0.6));
--gradient-purple-black: linear-gradient(150deg, rgba(0, 0, 0, 0.7), rgba(100, 45, 124, 0.4), rgba(82, 17, 131, 0.3), rgba(100, 45, 124, 0.4), rgba(0, 0, 0, 0.7));
--gradient-warning: linear-gradient(150deg, hsla(32, 100%, 50%, 0.3), hsla(52, 60%, 50%, 0.1), hsla(32, 60%, 50%, 0.1), hsla(32, 100%, 50%, 0.3));
--gradient-silver-light: linear-gradient(30deg, rgba(61, 55, 93, 0.2), rgba(178, 179, 196, 0.1), rgba(59, 62, 63, 0.2), rgba(206, 204, 199, 0.1), rgba(61, 46, 49, 0.2));
--gradient-daylight: conic-gradient(from 0deg, hsla(50, 100%, 80%, 0.7), hsla(30, 30%, 40%, 0.1) 25%, hsla(250, 50%, 40%, 0.1) 25%, hsla(250, 30%, 30%, 0.7) 50%, hsla(250, 50%, 40%, 0.1) 75%, hsla(30, 30%, 40%, 0.1) 75%, hsla(50, 100%, 80%, 0.7));
--background-custom-button: linear-gradient(to bottom, hsla(208, 38%, 21%, 0.988) 5%, hsla(202, 42%, 14%, 0.671) 100%);
@@ -159,7 +160,7 @@ select,
min-height: 100px;
background: var(--rdd-bg-input-alt);
padding: 5px;
border-radius: 3px;
border-radius: 0.2rem;
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .monnaie-content .window-content {
@@ -176,7 +177,7 @@ select,
background: var(--fieldset-background);
color: var(--rdd-color-text-primary);
margin-bottom: 4px;
border-radius: 6px;
border-radius: 0.5rem;
border-color: var(--rdd-color-text-primary);
border-width: 2px;
}
@@ -206,7 +207,7 @@ select,
border: 1px solid var(--rdd-color-border-input);
color: var(--rdd-color-text-input);
padding: 2px 2px;
border-radius: 3px;
border-radius: 0.2rem;
}
.system-foundryvtt-reve-de-dragon .monnaie-content .form-group input[type="checkbox"] {
flex: 0 0 20px;
@@ -250,7 +251,7 @@ select,
min-height: 100px;
background: var(--rdd-bg-input-alt);
padding: 5px;
border-radius: 3px;
border-radius: 0.2rem;
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .munition-content .window-content {
@@ -267,7 +268,7 @@ select,
background: var(--fieldset-background);
color: var(--rdd-color-text-primary);
margin-bottom: 4px;
border-radius: 6px;
border-radius: 0.5rem;
border-color: var(--rdd-color-text-primary);
border-width: 2px;
}
@@ -297,7 +298,7 @@ select,
border: 1px solid var(--rdd-color-border-input);
color: var(--rdd-color-text-input);
padding: 2px 2px;
border-radius: 3px;
border-radius: 0.2rem;
}
.system-foundryvtt-reve-de-dragon .munition-content .form-group input[type="checkbox"] {
flex: 0 0 20px;
@@ -341,7 +342,7 @@ select,
min-height: 100px;
background: var(--rdd-bg-input-alt);
padding: 5px;
border-radius: 3px;
border-radius: 0.2rem;
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .tarot-content .window-content {
@@ -358,7 +359,7 @@ select,
background: var(--fieldset-background);
color: var(--rdd-color-text-primary);
margin-bottom: 4px;
border-radius: 6px;
border-radius: 0.5rem;
border-color: var(--rdd-color-text-primary);
border-width: 2px;
}
@@ -388,7 +389,7 @@ select,
border: 1px solid var(--rdd-color-border-input);
color: var(--rdd-color-text-input);
padding: 2px 2px;
border-radius: 3px;
border-radius: 0.2rem;
}
.system-foundryvtt-reve-de-dragon .tarot-content .form-group input[type="checkbox"] {
flex: 0 0 20px;
@@ -528,6 +529,10 @@ select,
flex-direction: row;
margin: 0.1rem 0;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section subline .warning {
border-radius: 0.5rem;
background: var(--gradient-warning);
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-img {
display: flex;
flex-direction: column;
@@ -555,6 +560,7 @@ select,
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline div.poesie-extrait {
display: flex;
flex-direction: column;
align-items: normal;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline span.status-surprise {
display: flex;
@@ -571,8 +577,12 @@ select,
display: flow;
width: 2.5rem;
text-align: right;
margin: 0 0.2rem 0 0.5rem;
margin: 0 0.2rem;
padding: 0 0.2rem;
border: 1px solid ;
border-radius: 0.2rem;
height: 1.5rem;
background: hsla(0, 0%, 0%, 0.2);
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-action {
flex-basis: content;
@@ -627,24 +637,29 @@ select,
margin: 0 0.1rem;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-carac select[name="select-carac"] {
max-width: 6rem;
min-width: 6.5rem;
max-width: 8rem;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-comp select[name="select-comp"] {
min-width: 8rem;
max-width: 11rem;
margin-left: 1rem;
max-width: 10rem;
margin-left: 1.5rem;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="coeur"] select[name="coeur"] {
max-width: 4rem;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section img {
max-width: 1rem;
max-height: 1rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="tricher"] img {
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-buttons {
display: flex;
@@ -655,7 +670,8 @@ select,
width: 1.5rem;
text-align: center;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat {
font-family: CaslonAntique;
display: grid;
grid-template-areas: "img header buttons" "img resume buttons" "details details details" "actions actions actions";
@@ -663,51 +679,65 @@ select,
grid-template-rows: max-content max-content max-content max-content;
gap: 0 0.5rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-img {
grid-area: img;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img img {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img img,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-img img {
border: 0;
max-height: 3rem;
max-width: 3rem;
object-fit: contain;
height: 100%;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-header {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-header,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-header {
grid-area: header;
font-weight: bold;
font-size: 0.9rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-resume {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-resume,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-resume {
grid-area: resume;
text-align: justify;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-details {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-details,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-details {
grid-area: details;
text-align: justify;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-details div,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-details div {
display: block;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-actions {
grid-area: actions;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-actions a {
display: flex;
flex-direction: row;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a img {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a img,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-actions a img {
margin-right: 0.5rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons {
grid-area: buttons;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a {
border-radius: 0.2rem;
cursor: pointer;
padding: 0.2rem;
@@ -718,21 +748,27 @@ select,
display: inline-block;
align-items: center;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a img {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a img,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a img {
max-width: 1rem;
max-height: 1rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:hover {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:hover,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a:hover {
background: var(--background-custom-button-hover);
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:active {
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:active,
.system-foundryvtt-reve-de-dragon .dialog-content div.roll-chat div.chat-buttons a:active {
position: relative;
top: 1px;
}
.system-foundryvtt-reve-de-dragon .window-header {
background: rgba(0, 0, 0, 0.75);
}
.system-foundryvtt-reve-de-dragon .application .window-content,
.system-foundryvtt-reve-de-dragon .application .window-content {
margin: 0;
padding: 0.2rem;
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content {
margin: 0.2rem;
padding: 0;
@@ -823,7 +859,7 @@ select,
max-width: 1.4rem;
max-height: 1.4rem;
border: 1px;
background: center / contain no-repeat url('../../assets/ui/icone_parchement_vierge.webp');
background: center / contain no-repeat url('../../icons/templates/icone_parchement_vierge.webp');
}
.system-foundryvtt-reve-de-dragon .sheet-header .header-compteurs {
width: calc(60% - 110px - 1rem);
@@ -899,6 +935,12 @@ select,
.system-foundryvtt-reve-de-dragon a:hover {
text-shadow: 1px 0px 0px #ff6600;
}
.system-foundryvtt-reve-de-dragon .tabs .item.active img,
.system-foundryvtt-reve-de-dragon .blessures-list li ul li:first-child:hover img,
.system-foundryvtt-reve-de-dragon i.moral-radio-checkmark-off:hover img,
.system-foundryvtt-reve-de-dragon a:hover img {
filter: drop-shadow(1px 0px 0px #ff6600);
}
.system-foundryvtt-reve-de-dragon .rollable:hover,
.system-foundryvtt-reve-de-dragon .rollable:focus {
color: #000;
@@ -1423,12 +1465,27 @@ select,
.system-foundryvtt-reve-de-dragon .competence-list .item-controls.hidden-controls {
display: none !important;
}
.system-foundryvtt-reve-de-dragon .item-actions-controls a.actionItem i:is(.fas, .fa, .fa-solid, .fa-regular),
.system-foundryvtt-reve-de-dragon .item-actions-controls,
.system-foundryvtt-reve-de-dragon .item-controls {
vertical-align: super;
}
.system-foundryvtt-reve-de-dragon .item-actions-controls img,
.system-foundryvtt-reve-de-dragon .item-controls img {
display: inline;
max-width: 1rem;
max-height: 1rem;
margin: 0 0.1rem;
border: none;
filter: invert(0.8);
}
.system-foundryvtt-reve-de-dragon .item-actions-controls i:is(.fas, .fa, .fa-solid, .fa-regular),
.system-foundryvtt-reve-de-dragon .item-controls i:is(.fas, .fa, .fa-solid, .fa-regular) {
font-size: 0.8em;
color: var(--color-controls-light);
}
.system-foundryvtt-reve-de-dragon .item-actions-controls a.actionItem i:is(.fas, .fa, .fa-solid, .fa-regular):hover,
.system-foundryvtt-reve-de-dragon .item-actions-controls img:hover,
.system-foundryvtt-reve-de-dragon .item-controls img:hover,
.system-foundryvtt-reve-de-dragon .item-actions-controls i:is(.fas, .far, .fa-solid, .fa-regular):hover,
.system-foundryvtt-reve-de-dragon .item-controls i:is(.fas, .far, .fa-solid, .fa-regular):hover {
opacity: 0.6;
}
@@ -1444,38 +1501,38 @@ select,
}
.system-foundryvtt-reve-de-dragon .rdd-roll-part {
align-items: center;
border-radius: 6px;
border-radius: 0.5rem;
padding: 3px;
background: var(--gradient-gold);
}
.system-foundryvtt-reve-de-dragon .rdd-roll-sign {
border-radius: 6px;
border-radius: 0.5rem;
padding: 3px;
background: var(--gradient-silver);
}
.system-foundryvtt-reve-de-dragon .rdd-roll-norm {
border-radius: 6px;
border-radius: 0.5rem;
padding: 3px;
background: var(--gradient-green);
}
.system-foundryvtt-reve-de-dragon .rdd-roll-notSign,
.system-foundryvtt-reve-de-dragon .rdd-roll-echec {
border-radius: 6px;
border-radius: 0.5rem;
padding: 3px;
background: var(--gradient-red);
}
.system-foundryvtt-reve-de-dragon .rdd-roll-epart {
border-radius: 6px;
border-radius: 0.5rem;
padding: 3px;
background: var(--gradient-violet);
}
.system-foundryvtt-reve-de-dragon .rdd-roll-etotal {
border-radius: 6px;
border-radius: 0.5rem;
padding: 3px;
background: var(--gradient-purple-black);
}
.system-foundryvtt-reve-de-dragon .rdd-diviseur {
border-radius: 6px;
border-radius: 0.5rem;
padding: 3px;
background: var(--gradient-red);
}
@@ -1502,12 +1559,8 @@ select,
font-size: 0.8rem;
font-style: italic;
color: rgba(82, 17, 131, 0.9);
overflow: hidden;
}
.system-foundryvtt-reve-de-dragon .poesie-extrait:hover {
max-height: unset;
overflow: visible;
opacity: 1;
overflow-y: scroll;
width: 100%;
}
.system-foundryvtt-reve-de-dragon .poesie-reference {
font-size: 0.7rem;
@@ -1516,6 +1569,12 @@ select,
.system-foundryvtt-reve-de-dragon .type-compendium {
font-size: 0.6rem;
}
.system-foundryvtt-reve-de-dragon .sheet.journal-entry .journal-sidebar {
color: var(--color-text-dark-primary);
}
.system-foundryvtt-reve-de-dragon .sheet.journal-entry .journal-sidebar button {
color: var(--color-text-dark-primary);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-header {
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
color: #ffffff;
@@ -1573,7 +1632,10 @@ select,
height: var(--form-field-height);
margin: 0;
color: var(--color-text-dark-primary);
border-radius: 3px;
border-radius: 0.2rem;
}
.system-foundryvtt-reve-de-dragon form.app-personnage-aleatoire h2 {
min-width: 30rem;
}
.system-foundryvtt-reve-de-dragon .app-calendar-astrologie div.theme-astral {
width: 14rem;
@@ -1595,8 +1657,9 @@ select,
height: 2rem;
}
.system-foundryvtt-reve-de-dragon .window-app .window-content,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body {
background: #f5f5f0 url(../assets/ui/bg_left.webp) no-repeat left top;
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body,
.system-foundryvtt-reve-de-dragon .application .window-content {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
}
.system-foundryvtt-reve-de-dragon section.sheet-body {
padding: 0.25rem 0.5rem;
@@ -1624,7 +1687,7 @@ select,
background: hsla(280, 50%, 50%, 0.1);
padding: 1px 4px;
border: 1px solid var(--color-border-dark-tertiary);
border-radius: 2px;
border-radius: 0.2rem;
white-space: nowrap;
word-break: break-all;
}
@@ -1634,7 +1697,7 @@ select,
font-weight: 560;
padding: 0.1rem 0.3rem;
border: 1px solid var(--color-border-dark-tertiary);
border-radius: 0.25rem;
border-radius: 0.2rem;
white-space: nowrap;
word-break: break-all;
display: ruby;
@@ -1754,7 +1817,7 @@ select,
.system-foundryvtt-reve-de-dragon .xp-level-up {
margin: 0.1rem;
box-shadow: inset 0px 0px 1px #00000096;
border-radius: 0.25rem;
border-radius: 0.2rem;
padding: 0.1rem;
flex: 1 1 5rem;
background: var(--gradient-gold) !important;
@@ -1785,7 +1848,7 @@ select,
.system-foundryvtt-reve-de-dragon .list-item {
margin: 0.1rem;
box-shadow: inset 0px 0px 1px #00000096;
border-radius: 0.25rem;
border-radius: 0.2rem;
padding: 0.1rem;
flex: 1 1 1.5rem;
display: flex;
@@ -1954,13 +2017,13 @@ select,
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 4.6rem;
left: -19rem;
top: 10rem;
left: -9rem;
}
.system-foundryvtt-reve-de-dragon .token-hud-ext.soins {
flex-direction: column;
position: absolute;
top: 14.7rem;
top: 15.5rem;
left: -6rem;
max-width: 8rem;
line-height: 1rem;
@@ -1975,14 +2038,18 @@ select,
width: 9rem;
height: fit-content;
border-radius: 0.3rem;
min-width: 6rem;
min-width: 8rem;
flex-basis: auto;
padding: 0;
line-height: 0.95rem;
line-height: 1.6rem;
margin: 0.2rem;
display: flex;
flex-direction: row;
align-items: center;
}
.system-foundryvtt-reve-de-dragon .rdd-hud-menu label {
font-size: 0.8rem;
.system-foundryvtt-reve-de-dragon div.control-icon.token-hud-icon select,
.system-foundryvtt-reve-de-dragon div.control-icon.token-hud-icon label {
font-size: 1rem;
}
.system-foundryvtt-reve-de-dragon .item-checkbox {
height: 25px;
@@ -2094,6 +2161,9 @@ select,
font-size: 0.7rem;
flex-grow: 3;
}
.system-foundryvtt-reve-de-dragon .chat-message header.message-header .message-metadata {
flex-grow: 3.5;
}
.system-foundryvtt-reve-de-dragon .chat-message hr {
margin: 0.2rem 0;
}
@@ -2264,7 +2334,7 @@ select,
pointer-events: all;
}
.system-foundryvtt-reve-de-dragon div.horloge-roue div.horloge-cercle {
background: hsl(60, 20%, 95%) url(../assets/ui/bg_left.webp) no-repeat left top;
background: hsla(60, 20%, 90%, 0.8);
top: 2%;
left: 2%;
width: 96%;
@@ -2314,7 +2384,7 @@ select,
font-size: 0.8rem;
text-align: center;
vertical-align: middle;
border-radius: 0.3rem;
border-radius: 0.2rem;
}
.system-foundryvtt-reve-de-dragon div.horloge-roue div img {
border: none;
@@ -2641,13 +2711,20 @@ select,
.system-foundryvtt-reve-de-dragon :is(.tooltip, .tooltip-overflow) .ttt-ajustements {
width: 10rem;
background: var(--background-tooltip);
border-radius: 6px;
border-radius: 0.5rem;
font-size: 0.9rem;
padding: 3px 0;
}
.system-foundryvtt-reve-de-dragon :is(.tooltip, .tooltip-overflow) .ttt-ajustements div:nth-child(odd) {
background: var(--background-tooltip-alt);
}
.system-foundryvtt-reve-de-dragon :is(.tooltip, .tooltip-overflow) .ttt-ajustements div img {
display: inline;
margin: 0;
max-width: 1rem;
max-height: 1rem;
filter: invert(0.8);
}
.system-foundryvtt-reve-de-dragon aside#tooltip {
max-width: 15rem;
background: var(--background-tooltip);
@@ -2709,11 +2786,9 @@ select,
}
.system-foundryvtt-reve-de-dragon .chat-card-button:hover {
background: var(--background-custom-button-hover);
background-color: red;
}
.system-foundryvtt-reve-de-dragon .chat-card-button-pushed:hover {
background: var(--background-custom-button-hover);
background-color: red;
}
.system-foundryvtt-reve-de-dragon .chat-card-button:active,
.system-foundryvtt-reve-de-dragon .chat-card-button-pushed:active {

1
icons/cuisine/gibier.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

1
icons/cuisine/herbe.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.6 KiB

1
icons/cuisine/plante.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

1
icons/cuisine/ragout.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -67,6 +67,7 @@
"StatusComma": "Comma",
"StatusDead": "Mort",
"StatusDemiReve": "Demi-rêve",
"StatusSurEnc": "Sur-encombrement",
"StatusForceWeak": "Force insuffisante"
}
}

View File

@@ -27,6 +27,11 @@
--gradient-red: linear-gradient(150deg, rgba(255, 0, 0, 0.3), rgba(255, 200, 128, 0.05),rgba(255, 200, 128, 0.1), rgba(255,10,0,0.3));
--gradient-violet: linear-gradient(150deg, rgba(100, 45, 124, 0.6), rgba(216, 157, 192, 0.3), rgba(177, 157, 216, 0.5), rgba(107, 62, 121, 0.3), rgba(100, 45, 124, 0.6));
--gradient-purple-black: linear-gradient(150deg, rgba(0, 0, 0, 0.7), rgba(100, 45, 124, 0.4), rgba(82, 17, 131, 0.3),rgba(100, 45, 124, 0.4), rgba(0, 0, 0, 0.7));
--gradient-warning: linear-gradient(150deg,
hsla(32, 100%, 50%, 0.3),
hsla(52, 60%, 50%, 0.1),
hsla(32, 60%, 50%, 0.1),
hsla(32, 100%, 50%, 0.3));
--gradient-silver-light: linear-gradient(30deg, rgba(61, 55, 93, 0.2), rgba(178, 179, 196, 0.1), rgba(59, 62, 63, 0.2), rgba(206, 204, 199, 0.1), rgba(61, 46, 49, 0.2));
--gradient-daylight: conic-gradient(
from 0deg,

View File

@@ -12,7 +12,10 @@
background: rgba(0,0,0,0.75);
}
.application .window-content,
.application .window-content {
margin: 0;
padding: 0.2rem;
}
.window-app.sheet .window-content {
margin: 0.2rem;
padding: 0;
@@ -115,7 +118,7 @@
max-width: 1.4rem;
max-height: 1.4rem;
border: 1px;
background: center / contain no-repeat url('../../assets/ui/icone_parchement_vierge.webp');
background: center / contain no-repeat url('../../icons/templates/icone_parchement_vierge.webp');
}
.sheet-header .header-compteurs {
@@ -200,6 +203,9 @@
i.moral-radio-checkmark-off:hover,
a:hover {
text-shadow: 1px 0px 0px #ff6600;
img {
filter: drop-shadow(1px 0px 0px #ff6600);
}
}
.rollable:hover, .rollable:focus {
@@ -754,15 +760,30 @@
.competence-list .item-controls.hidden-controls {
display: none !important;
}
.item-actions-controls,
.item-controls {
vertical-align: super;
// a {
// }
.item-actions-controls a.actionItem i:is(.fas, .fa, .fa-solid, .fa-regular),
.item-controls i:is(.fas, .fa, .fa-solid, .fa-regular) {
font-size: 0.8em;
color: var(--color-controls-light);
}
.item-actions-controls a.actionItem i:is(.fas, .fa, .fa-solid, .fa-regular):hover,
.item-controls i:is(.fas, .far, .fa-solid, .fa-regular):hover {
opacity: 0.6;
img {
display: inline;
max-width: 1rem;
max-height: 1rem;
margin: 0 0.1rem;
border: none;
filter: invert(0.8);
}
i:is(.fas, .fa, .fa-solid, .fa-regular) {
font-size: 0.8em;
color: var(--color-controls-light);
}
img:hover,
i:is(.fas, .far, .fa-solid, .fa-regular):hover {
opacity: 0.6;
}
}
.rdd-roll-dialog .description-sort {
@@ -777,31 +798,31 @@
}
.rdd-roll-part {
align-items: center;
border-radius: 6px; padding: 3px;
border-radius: 0.5rem; padding: 3px;
background: var(--gradient-gold);
}
.rdd-roll-sign{
border-radius: 6px; padding: 3px;
border-radius: 0.5rem; padding: 3px;
background: var(--gradient-silver);
}
.rdd-roll-norm{
border-radius: 6px; padding: 3px;
border-radius: 0.5rem; padding: 3px;
background: var(--gradient-green);
}
.rdd-roll-notSign, .rdd-roll-echec{
border-radius: 6px; padding: 3px;
border-radius: 0.5rem; padding: 3px;
background: var(--gradient-red);
}
.rdd-roll-epart{
border-radius: 6px; padding: 3px;
border-radius: 0.5rem; padding: 3px;
background: var(--gradient-violet);
}
.rdd-roll-etotal{
border-radius: 6px; padding: 3px;
border-radius: 0.5rem; padding: 3px;
background: var(--gradient-purple-black);
}
.rdd-diviseur{
border-radius: 6px; padding: 3px;
border-radius: 0.5rem; padding: 3px;
background: var(--gradient-red);
}
@@ -832,13 +853,8 @@
font-size: 0.8rem;
font-style: italic;
color: rgba(82, 17, 131, 0.9);
overflow: hidden;
}
.poesie-extrait:hover {
max-height: unset;
overflow: visible;
opacity: 1;
overflow-y: scroll;
width: 100%;
}
.poesie-reference {
@@ -849,6 +865,12 @@
.type-compendium {
font-size: 0.6rem;
}
.sheet.journal-entry .journal-sidebar {
color: var(--color-text-dark-primary);
button {
color: var(--color-text-dark-primary);
}
}
/* ======================================== */
/* Sheet */
@@ -913,7 +935,12 @@
height: var(--form-field-height);
margin: 0;
color: var(--color-text-dark-primary);
border-radius: 3px;
border-radius: 0.2rem;
}
form.app-personnage-aleatoire {
h2 {
min-width: 30rem,
}
}
.app-calendar-astrologie{
div.theme-astral{
@@ -937,8 +964,10 @@
}
}
.window-app .window-content, .window-app.sheet .window-content .sheet-body{
background: rgb(245,245,240) url(../assets/ui/bg_left.webp) no-repeat left top;
.window-app .window-content,
.window-app.sheet .window-content .sheet-body,
.application .window-content {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
}
section.sheet-body {
@@ -970,7 +999,7 @@
background: hsla(280, 50%, 50%, 0.1);
padding: 1px 4px;
border: 1px solid var(--color-border-dark-tertiary);
border-radius: 2px;
border-radius: 0.2rem;
white-space: nowrap;
word-break: break-all;
}
@@ -981,7 +1010,7 @@
font-weight: 560;
padding: 0.1rem 0.3rem;
border: 1px solid var(--color-border-dark-tertiary);
border-radius: 0.25rem;
border-radius: 0.2rem;
white-space: nowrap;
word-break: break-all;
display: ruby;
@@ -1111,7 +1140,7 @@
.xp-level-up {
margin: 0.1rem;
box-shadow: inset 0px 0px 1px #00000096;
border-radius: 0.25rem;
border-radius: 0.2rem;
padding: 0.1rem;
flex: 1 1 5rem;
background: var(--gradient-gold) !important;
@@ -1142,7 +1171,7 @@
.list-item {
margin: 0.1rem;
box-shadow: inset 0px 0px 1px #00000096;
border-radius: 0.25rem;
border-radius: 0.2rem;
padding: 0.1rem;
flex: 1 1 1.5rem;
display: flex;
@@ -1319,13 +1348,13 @@
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 4.6rem;
left: -19rem;
top: 10rem;
left: -9rem;
}
.token-hud-ext.soins {
flex-direction: column;
position: absolute;
top: 14.7rem;
top: 15.5rem;
left: -6rem;
max-width: 8rem;
line-height: 1rem;
@@ -1341,14 +1370,18 @@
width: 9rem;
height: fit-content;
border-radius: 0.3rem;
min-width: 6rem;
min-width: 8rem;
flex-basis: auto;
padding: 0;
line-height: 0.95rem;
line-height: 1.6rem;
margin: 0.2rem;
}
.rdd-hud-menu label {
font-size: 0.8rem;
display: flex;
flex-direction: row;
align-items: center;
select, label {
font-size: 1rem;
}
}
/* ======================================== */
.item-checkbox {
@@ -1479,9 +1512,14 @@
.message-content {
text-align: justify;
}
header.message-header .heure-rdd {
font-size: 0.7rem;
flex-grow: 3;
header.message-header{
.heure-rdd {
font-size: 0.7rem;
flex-grow: 3;
}
.message-metadata {
flex-grow: 3.5;
}
}
hr {
margin: 0.2rem 0;
@@ -1650,7 +1688,7 @@
}
div.horloge-roue div.horloge-cercle {
background: hsl(60, 20%, 95%) url(../assets/ui/bg_left.webp) no-repeat left top;
background: hsla(60, 20%, 90%, 0.8);
top: 2%; left: 2%; width: 96%; height: 96%; border-radius: 50%;
}
@@ -1681,7 +1719,7 @@
font-size: 0.8rem;
text-align: center;
vertical-align: middle;
border-radius: 0.3rem;
border-radius: 0.2rem;
}
div.horloge-roue div img {
@@ -1917,12 +1955,19 @@
.ttt-ajustements {
width: 10rem;
background: var(--background-tooltip);
border-radius: 6px;
border-radius: 0.5rem;
font-size: 0.9rem;
padding: 3px 0;
div:nth-child(odd) {
background: var(--background-tooltip-alt);
}
div img {
display: inline;
margin: 0;
max-width: 1rem;
max-height: 1rem;
filter: invert(0.8);
}
}
}
@@ -1994,12 +2039,10 @@
.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 {

View File

@@ -1,87 +1,89 @@
.chat-message {
div.roll-chat {
font-family: CaslonAntique;
display: grid;
grid-template-areas:
"img header buttons"
"img resume buttons"
"details details details"
"actions actions actions";
grid-template-columns: 3rem 1fr 1.4rem;
grid-template-rows: max-content max-content max-content max-content;
gap: 0 0.5rem;
div.chat-img {
grid-area: img;
display: flex;
flex-direction: column;
img {
border: 0;
max-height: 3rem;
max-width: 3rem;
object-fit: contain;
height: 100%;
}
}
div.chat-header {
grid-area: header;
font-weight: bold;
font-size: 0.9rem;
}
div.chat-resume {
grid-area: resume;
text-align: justify;
}
div.chat-details {
grid-area: details;
text-align: justify;
display: flex;
flex-direction: column;
}
div.chat-actions {
grid-area: actions;
display: flex;
flex-direction: column;
a {
display: flex;
flex-direction: row;
img {
margin-right: 0.5rem;
}
}
}
div.chat-buttons {
grid-area: buttons;
display: flex;
flex-direction: column;
a {
border-radius: 0.2rem;
cursor: pointer;
padding: 0.2rem;
position: relative;
box-shadow: inset 1x 1px #a6827e;
color: var(--color-controls);
border: 1px ridge #846109;
display: inline-block;
align-items: center;
img {
max-width: 1rem;
max-height: 1rem;
}
}
a:hover {
background: var(--background-custom-button-hover);
}
a:active{
position:relative;
top:1px;
}
.chat-message div.roll-chat,
.dialog-content div.roll-chat {
font-family: CaslonAntique;
display: grid;
grid-template-areas:
"img header buttons"
"img resume buttons"
"details details details"
"actions actions actions";
grid-template-columns: 3rem 1fr 1.4rem;
grid-template-rows: max-content max-content max-content max-content;
gap: 0 0.5rem;
div.chat-img {
grid-area: img;
display: flex;
flex-direction: column;
img {
border: 0;
max-height: 3rem;
max-width: 3rem;
object-fit: contain;
height: 100%;
}
}
div.chat-header {
grid-area: header;
font-weight: bold;
font-size: 0.9rem;
}
div.chat-resume {
grid-area: resume;
text-align: justify;
}
div.chat-details {
grid-area: details;
text-align: justify;
display: flex;
flex-direction: column;
div {
display: block;
}
}
div.chat-actions {
grid-area: actions;
display: flex;
flex-direction: column;
a {
display: flex;
flex-direction: row;
img {
margin-right: 0.5rem;
}
}
}
div.chat-buttons {
grid-area: buttons;
display: flex;
flex-direction: column;
a {
border-radius: 0.2rem;
cursor: pointer;
padding: 0.2rem;
position: relative;
box-shadow: inset 1x 1px #a6827e;
color: var(--color-controls);
border: 1px ridge #846109;
display: inline-block;
align-items: center;
img {
max-width: 1rem;
max-height: 1rem;
}
}
a:hover {
background: var(--background-custom-button-hover);
}
a:active{
position:relative;
top:1px;
}
}
}

View File

@@ -89,6 +89,10 @@
display: flex;
flex-direction: row;
margin: 0.1rem 0;
.warning {
border-radius: 0.5rem;
background: var(--gradient-warning);
}
}
roll-part-img {
@@ -117,6 +121,7 @@
div.poesie-extrait{
display: flex;
flex-direction: column;
align-items: normal;
}
span.status-surprise{
display: flex;
@@ -137,8 +142,13 @@
display: flow;
width: 2.5rem;
text-align: right;
margin: 0 0.2rem 0 0.5rem;
margin: 0 0.2rem;
padding: 0 0.2rem;
border: 1px solid ;
border-radius: 0.2rem ;
background: hsla(0, 0%, 0%, 0.2);
height: 1.5rem;
background: hsla(0, 0%, 0%, 0.2);
}
roll-action {
@@ -201,28 +211,32 @@
}
roll-carac select[name="select-carac"] {
max-width: 6rem;
min-width: 6.5rem;
max-width: 8rem;
}
roll-comp select[name="select-comp"] {
min-width: 8rem;
max-width: 11rem;
margin-left: 1rem;
max-width: 10rem;
margin-left: 1.5rem;
}
roll-conditions roll-section[name="coeur"] select[name="coeur"] {
max-width: 4rem;
}
roll-conditions roll-section[name="tricher"] img {
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
roll-conditions roll-section img {
max-width: 1rem;
max-height: 1rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
roll-conditions roll-section[name="tricher"] img {
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
}
roll-buttons {
display: flex;

View File

@@ -8,7 +8,7 @@
min-height: 100px; // Hauteur minimale pour la description
background: var(--rdd-bg-input-alt); // Une couleur de fond alternative
padding: 5px;
border-radius: 3px;
border-radius: 0.2rem;
color: var(--rdd-color-text-primary);
}
@@ -28,7 +28,7 @@
background: var(--fieldset-background);
color: var(--rdd-color-text-primary);
margin-bottom: 4px;
border-radius: 6px;
border-radius: 0.5rem;
border-color: var(--rdd-color-text-primary);
border-width: 2px;
}
@@ -64,7 +64,7 @@
--rdd-color-text-input
); // Assurez-vous que cette variable existe
padding: 2px 2px; // Augmentation du padding vertical
border-radius: 3px;
border-radius: 0.2rem;
}
input[type="checkbox"] {

View File

@@ -1,4 +1,4 @@
import { SYSTEM_RDD } from "../constants.js";
import { renderTemplate, SYSTEM_RDD } from "../constants.js";
import { RdDUtility } from "../rdd-utility.js";
const DETAIL_VENTE = 'detailVente';

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "../constants.js";
import { Misc } from "../misc.js";
import { RdDUtility } from "../rdd-utility.js";
import { ChatVente } from "./chat-vente.js";

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "../constants.js";
import { HtmlUtility } from "../html-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { ChatVente } from "./chat-vente.js";

View File

@@ -3,7 +3,7 @@ import { HtmlUtility } from "./html-utility.js";
import { RdDBonus } from "./rdd-bonus.js";
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { CARACS, RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
@@ -174,7 +174,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
await this.getBlessure(event)?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } })
});
this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac('chance-actuelle'))
this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac(CARACS.CHANCE_ACTUELLE))
this.html.find('.button-appel-chance').click(async event => await this.actor.rollAppelChance())
this.html.find('[name="jet-astrologie"]').click(async event => await this.actor.astrologieNombresAstraux())
@@ -208,7 +208,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
}
// Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollCarac('reve-actuel', { resistance: true }))
this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollCarac(CARACS.REVE_ACTUEL, { resistance: true }))
this.html.find('.action-empoignade').click(async event => await RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor)))
this.html.find('.roll-arme').click(async event => {
@@ -342,7 +342,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
/* -------------------------------------------- */
async createEmptyTache() {
await this.actor.createItem('tache', 'Nouvelle tache');
await this.actor.createItem(ITEM_TYPES.tache, 'Nouvelle tache')
}
_getActionCombat(event) {
@@ -375,14 +375,6 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);

View File

@@ -17,9 +17,9 @@ import { Draconique } from "./tmr/draconique.js";
import { CARACS, LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js";
import { DialogConsommer } from "./dialog-item-consommer.js";
import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RollDataAjustements } from "./rolldata-ajustements-v1.js";
import { RdDPossession } from "./rdd-possession.js";
import { ACTOR_TYPES, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { ACTOR_TYPES, renderTemplate, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDConfirm } from "./rdd-confirm.js";
import { DialogRepos } from "./sommeil/dialog-repos.js";
import { RdDBaseActor } from "./actor/base-actor.js";
@@ -47,6 +47,12 @@ import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js";
import { ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_SORT } from "./roll/roll-constants.mjs";
import { PART_TACHE } from "./roll/roll-part-tache.mjs";
import { PART_COMP } from "./roll/roll-part-comp.mjs";
import { PART_OEUVRE } from "./roll/roll-part-oeuvre.mjs";
import { PART_CUISINE } from "./roll/roll-part-cuisine.mjs";
import { PART_SORT } from "./roll/roll-part-sort.mjs";
export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
@@ -98,6 +104,8 @@ export class RdDActor extends RdDBaseActorSang {
}
isPersonnage() { return true }
isFeminin() { return this.system.sexe.length > 0 && this.system.sexe.charAt(0).toLowerCase() == 'f' }
isHautRevant() { return this.system.attributs.hautrevant.value != "" }
/* -------------------------------------------- */
@@ -136,12 +144,12 @@ export class RdDActor extends RdDBaseActorSang {
}
listActions({ isAttaque = false, isEquipe = false }) {
// Recupération des armes
// Recupération des attaques
const actions = this.listActionsAttaque()
.filter(it => !isEquipe || it.arme.system.equipe)
if (!isAttaque && this.system.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', initOnly: true })
actions.push({ label: "Draconic", action: 'haut-reve', initOnly: true })
}
return actions
}
@@ -158,10 +166,10 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
getDemiReve() { return this.system.reve.tmrpos.coord }
getDraconicList() { return this.itemTypes[ITEM_TYPES.competence].filter(it => it.system.categorie == 'draconic') }
getBestDraconic() { return foundry.utils.duplicate([...this.getDraconicList(), PAS_DE_DRACONIC].sort(Misc.descending(it => it.system.niveau)).find(it => true)) }
getDraconics() { return this.itemTypes[ITEM_TYPES.competence].filter(it => it.system.categorie == 'draconic') }
getBestDraconic() { return foundry.utils.duplicate([...this.getDraconics(), PAS_DE_DRACONIC].sort(Misc.descending(it => it.system.niveau)).find(it => true)) }
getDraconicOuPossession() {
return [...this.getDraconicList().filter(it => it.system.niveau >= 0), POSSESSION_SANS_DRACONIC]
return [...this.getDraconics().filter(it => it.system.niveau >= 0), POSSESSION_SANS_DRACONIC]
.sort(Misc.descending(it => it.system.niveau))
.find(it => true)
}
@@ -177,13 +185,13 @@ export class RdDActor extends RdDBaseActorSang {
const actions = []
const uniques = []
const addAttaque = (arme, main) => {
const dommagesArme = RdDItemArme.valeurMain(arme.system.dommages, main)
const addAttaque = (arme, main = undefined) => {
const dommages = RdDItemArme.valeurMain(arme.system.dommages, main)
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
const ecaillesEfficacite = arme.system.magique ? arme.system.ecaille_efficacite : 0;
const comp = this.getCompetence(RdDActor.$getCompetenceAction(arme, main))
const unique = [comp.id, arme.name, dommagesArme, forceRequise, ecaillesEfficacite].join('|');
const unique = [comp.id, arme.name, dommages, forceRequise, ecaillesEfficacite].join('|');
if (uniques.includes(unique)) {
return
}
@@ -196,7 +204,7 @@ export class RdDActor extends RdDBaseActorSang {
const ajustement = (arme.parent?.getEtatGeneral() ?? 0) + ecaillesEfficacite
actions.push({
name: arme.name + (main ? ' ' + main : ''),
label: arme.name + (main ? ' ' + main : ''),
action: 'attaque',
initOnly: false,
arme: arme,
@@ -204,24 +212,24 @@ export class RdDActor extends RdDBaseActorSang {
main: main,
carac: { key: caracCode, value: caracValue },
equipe: arme.system.equipe,
dommagesArme: dommagesArme,
dommages: dommages,
forceRequise: forceRequise,
initiative: RdDInitiative.calculInitiative(niveau, caracValue, ajustement)
initiative: RdDInitiative.getRollInitiative(caracValue, niveau, ajustement)
})
}
addAttaque(RdDItemArme.empoignade(this))
addAttaque(RdDItemArme.corpsACorps(this))
addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS)
this.itemTypes[ITEM_TYPES.arme]
.filter(it => it.isAttaque())
.sort(Misc.ascending(it => it.name))
.forEach(arme => {
if (arme.system.unemain && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.UNE_MAIN) }
if (arme.system.deuxmains && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.DEUX_MAINS) }
if (arme.system.lancer) { addAttaque(arme, ATTAQUE_TYPE.LANCER) }
if (arme.system.unemain && arme.system.competence && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.UNE_MAIN) }
if (arme.system.deuxmains && arme.system.competence && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.DEUX_MAINS) }
if (arme.system.lancer && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.LANCER) }
if (arme.system.tir) { addAttaque(arme, ATTAQUE_TYPE.TIR) }
})
addAttaque(RdDItemArme.pugilat(this), ATTAQUE_TYPE.CORPS_A_CORPS)
return actions
}
@@ -697,11 +705,12 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async combattreReveDeDragon(force) {
const rencontre = await game.system.rdd.rencontresTMR.getReveDeDragon(force);
let rollData = {
actor: this,
competence: this.getDraconicOuPossession(),
canClose: false,
rencontre: await game.system.rdd.rencontresTMR.getReveDeDragon(force),
rencontre: rencontre,
tmr: true,
use: { libre: false, conditions: false },
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.getReveActuel() } }
@@ -732,12 +741,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async sortMisEnReserve(sort, draconic, coord, ptreve) {
await this.createEmbeddedDocuments("Item", [{
type: ITEM_TYPES.sortreserve,
name: sort.name,
img: sort.img,
system: { sortid: sort._id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
}],
await this.createEmbeddedDocuments("Item", [RdDItemSort.prepareSortEnReserve(sort, draconic, ptreve, coord)],
{ renderSheet: false });
this.tmrApp.updateTokens();
}
@@ -751,20 +755,18 @@ export class RdDActor extends RdDBaseActorSang {
let updates = {};
if (caracName == LIST_CARAC_PERSONNAGE.reve.code) {
if (to > Misc.toInt(this.system.reve.seuil.value)) {
updates[`system.reve.seuil.value`] = to; // SFA : Direct and packed changes
//this.setPointsDeSeuil(to);
updates[`system.reve.seuil.value`] = to
}
}
if (caracName == LIST_CARAC_PERSONNAGE.chance.code) {
if (to > Misc.toInt(this.system.compteurs.chance.value)) {
updates[`system.compteurs.chance.value`] = to; // SFA : Direct and packed changes
//this.setPointsDeChance(to);
updates[`system.compteurs.chance.value`] = to
}
}
let selectedCarac = this.findCaracByName(caracName);
const from = selectedCarac.value
updates[`system.carac.${caracName}.value`] = to;
await this.update(updates);
await this.update(updates, { noHook: true });
await ExperienceLog.add(this, XP_TOPIC.CARAC, from, to, caracName);
}
@@ -1342,7 +1344,7 @@ export class RdDActor extends RdDBaseActorSang {
async _apprecierCuisine(item, seForcer) {
const surmonteExotisme = await this._surmonterExotisme(item, seForcer);
if (surmonteExotisme) {
await this.apprecier('gout', 'cuisine', item.system.qualite, item.system.boisson ? "apprécie la boisson" : "apprécie le plat");
await this.apprecier(CARACS.ODORATGOUT, 'cuisine', item.system.qualite, item.system.boisson ? "apprécie la boisson" : "apprécie le plat");
}
else if (seForcer) {
await this.jetDeMoral('malheureux');
@@ -1360,7 +1362,7 @@ export class RdDActor extends RdDBaseActorSang {
if (exotisme < 0 || qualite < 0) {
const competence = qualite > 0 ? 'cuisine' : undefined
const difficulte = Math.min(exotisme, qualite)
const rolled = await this.doRollCaracCompetence('volonte', competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` })
const rolled = await this.doRollCaracCompetence(CARACS.VOLONTE, competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` })
return rolled.isSuccess
}
return true;
@@ -1682,7 +1684,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
computeDraconicAndSortIndex(sortList) {
let draconicList = this.getDraconicList();
let draconicList = this.getDraconics();
for (let sort of sortList) {
let draconicsSort = RdDItemSort.getDraconicsSort(draconicList, sort).map(it => it.name);
for (let index = 0; index < draconicList.length && sort.system.listIndex == undefined; index++) {
@@ -1703,15 +1705,18 @@ export class RdDActor extends RdDBaseActorSang {
ui.notifications.error("Une queue ou un souffle vous empèche de lancer de sort!")
return
}
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
return await this.rollUnSortV2();
}
// Duplication car les pts de reve sont modifiés dans le sort!
let sorts = foundry.utils.duplicate(this.itemTypes[ITEM_TYPES.sort].filter(it => RdDItemSort.isSortOnCoord(it, coord)))
if (sorts.length == 0) {
ui.notifications.info(`Aucun sort disponible en ${TMRUtility.getTMR(coord).label} !`);
return;
return
}
const draconicList = this.computeDraconicAndSortIndex(sorts);
const reve = foundry.utils.duplicate(this.system.carac.reve);
const draconicList = this.computeDraconicAndSortIndex(sorts)
const reve = foundry.utils.duplicate(this.system.carac.reve)
const dialog = await this.openRollDialog({
name: 'lancer-un-sort',
@@ -1734,6 +1739,27 @@ export class RdDActor extends RdDBaseActorSang {
this.tmrApp?.setTMRPendingAction(dialog);
}
async rollUnSortV2() {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [ROLL_TYPE_SORT], current: ROLL_TYPE_SORT }
};
const dialog = await RollDialog.create(rollData, {
callbacks: [roll => {
this.tmrApp?.restoreTMRAfterAction();
if (roll.closeTMR) {
this.tmrApp?.close();
this.tmrApp = undefined;
}
}],
onRollDone: RollDialog.onRollDoneClose,
onClose: () => {
this.tmrApp?.restoreTMRAfterAction();
}
});
this.tmrApp?.setTMRPendingAction(dialog);
}
/* -------------------------------------------- */
isMauvaiseRencontre() { // Gestion queue/souffle 'Mauvaise Rencontre en Perpective'
let addMsg = "";
@@ -1805,11 +1831,7 @@ export class RdDActor extends RdDBaseActorSang {
}
else {
console.log('lancement de sort', rollData.selectedSort)
const precedents = rollData.selectedSort.system.lancements ?? []
const lancements = [...precedents, {
timestamp: game.system.rdd.calendrier.getTimestamp(),
reve: rollData.selectedSort.system.ptreve_reel
}]
const lancements = RdDItemSort.prepareSortAddLancement(rollData.selectedSort, rollData.selectedSort.system.ptreve_reel)
await this.updateEmbeddedDocuments('Item',
[{ _id: rollData.selectedSort._id, 'system.lancements': lancements }]
)
@@ -1922,6 +1944,21 @@ export class RdDActor extends RdDBaseActorSang {
}
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_COMP], current: PART_COMP },
selected: {
carac: { key: caracName },
comp: { key: compName, forced: options.forced },
diff: { value: diff ?? 0 }
}
}
RollDialog.create(rollData, foundry.utils.mergeObject(options, { onRollDone: RollDialog.onRollDoneClose }))
return
}
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(compName);
await this.openRollDialog({
@@ -1944,23 +1981,34 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollTache(id, options = {}) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const tacheData = this.getTache(id)
const compData = this.getCompetence(tacheData.system.competence)
compData.system.defaut_carac = tacheData.system.carac; // Patch !
const tache = this.getTache(id)
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { tache: { key: tache.id, forced: options.forced } },
type: { allowed: [PART_TACHE], current: PART_TACHE }
}
RollDialog.create(rollData, options)
return
}
const compData = this.getCompetence(tache.system.competence)
compData.system.defaut_carac = tache.system.carac; // Patch !
await this.openRollDialog({
name: 'jet-competence',
label: 'Jet de Tâche ' + tacheData.name,
label: 'Jet de Tâche ' + tache.name,
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.hbs',
rollData: {
competence: compData,
tache: tacheData,
diffLibre: tacheData.system.difficulte,
tache: tache,
diffLibre: tache.system.difficulte,
diffConditions: 0,
use: { libre: false, conditions: true },
carac: {
[tacheData.system.carac]: foundry.utils.duplicate(this.system.carac[tacheData.system.carac])
[tache.system.carac]: foundry.utils.duplicate(this.system.carac[tache.system.carac])
}
},
callbacks: [{ action: r => this._tacheResult(r, options) }]
@@ -1994,9 +2042,19 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollJeu(id) {
const oeuvre = this.getJeu(id);
const jeu = this.getJeu(id);
const listCarac = oeuvre.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim());
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { jeu: { key: jeu.id } },
type: { allowed: [ROLL_TYPE_JEU], current: ROLL_TYPE_JEU }
}
await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
return
}
const listCarac = jeu.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim());
const carac = listCarac.length > 0 ? listCarac[0] : 'chance'
const artData = {
art: 'jeu', verbe: 'Jeu',
@@ -2006,14 +2064,25 @@ export class RdDActor extends RdDBaseActorSang {
};
listCarac.forEach(c => artData.forceCarac[c] = this.system.carac[c]);
artData.competence.system.niveauReel = artData.competence.system.niveau;
artData.competence.system.niveau = Math.max(artData.competence.system.niveau, oeuvre.system.base);
await this._rollArtV1(artData, carac, oeuvre);
artData.competence.system.niveau = Math.max(artData.competence.system.niveau, jeu.system.base);
await this._rollArtV1(artData, carac, jeu);
}
/* -------------------------------------------- */
async rollMeditation(id) {
const meditation = foundry.utils.duplicate(this.getMeditation(id));
if (meditation && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { meditation: { key: id } },
type: { allowed: [ROLL_TYPE_MEDITATION], current: ROLL_TYPE_MEDITATION }
}
await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
return
}
const competence = foundry.utils.duplicate(this.getCompetence(meditation.system.competence));
competence.system.defaut_carac = "intellect"; // Meditation = toujours avec intellect
let meditationData = {
@@ -2053,7 +2122,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
_getSignesDraconiques(coord) {
const type = TMRUtility.getTMRType(coord);
return this.itemTypes["signedraconique"].filter(it => it.system.typesTMR.includes(type));
return this.itemTypes[ITEM_TYPES.signedraconique].filter(it => it.system.typesTMR.includes(type));
}
/* -------------------------------------------- */
@@ -2065,18 +2134,19 @@ export class RdDActor extends RdDBaseActorSang {
async rollLireSigneDraconique(coord) {
if (!this.isHautRevant()) {
ui.notifications.info("Seul un haut rêvant peut lire un signe draconique!");
return;
return
}
let signes = this._getSignesDraconiques(coord);
let signes = this._getSignesDraconiques(coord)
if (signes.length == 0) {
ui.notifications.info(`Aucun signe draconiques en ${coord} !`);
return;
ui.notifications.info(`Aucun signe draconiques en ${coord} !`)
return
}
let draconicList = this.getDraconicList()
let draconicList = this.getDraconics()
.map(draconic => {
let draconicLecture = foundry.utils.duplicate(draconic);
draconicLecture.system.defaut_carac = "intellect";
return draconicLecture;
let draconicLecture = foundry.utils.duplicate(draconic)
draconicLecture.system.defaut_carac = "intellect"
return draconicLecture
});
const intellect = this.system.carac.intellect;
@@ -2350,14 +2420,15 @@ export class RdDActor extends RdDBaseActorSang {
if (this.tmrApp) {
ui.notifications.warn("Vous êtes déja dans les TMR....")
this.tmrApp.forceTMRDisplay()
return
return false
}
if (mode != 'visu' && this.isDemiReve()) {
ui.notifications.warn("Les personnage est déjà dans les Terres Médianes, elles s'affichent en visualisation")
ui.notifications.warn("Le personnage est déjà dans les Terres Médianes, elles s'affichent en visualisation")
mode = "visu"; // bascule le mode en visu automatiquement
}
if (mode == 'visu') {
await this._doDisplayTMR(mode)
return false
}
else {
const rencontre = this.getRencontreTMREnAttente();
@@ -2370,6 +2441,7 @@ export class RdDActor extends RdDBaseActorSang {
buttonLabel: 'Monter dans les TMR',
onAction: async () => await this._doDisplayTMR(mode)
})
return true
}
}
@@ -2392,7 +2464,7 @@ export class RdDActor extends RdDBaseActorSang {
let tmrFormData = {
mode: mode,
fatigue: RdDUtility.calculFatigueHtml(fatigue, endurance),
draconic: this.getDraconicList(),
draconic: this.getDraconics(),
sort: this.itemTypes['sort'],
signes: this.itemTypes['signedraconique'],
caracReve: parseInt(this.system.carac.reve.value),
@@ -2407,6 +2479,29 @@ export class RdDActor extends RdDBaseActorSang {
await this.tmrApp.onDeplacement()
}
async quitterTMR(message, viewOnly, cumulFatigue) {
if (this.tmrApp) {
this.tmrApp = undefined
const appliquerFatigue = ReglesOptionnelles.isUsing("appliquer-fatigue");
await this.santeIncDec(
appliquerFatigue ? "fatigue" : "endurance",
(appliquerFatigue ? 1 : -1) * cumulFatigue)
if (!viewOnly) {
await this.supprimerSignesDraconiques(it => it.system.ephemere && it.system.duree == '1 round', { render: false })
await this.setEffect(STATUSES.StatusDemiReve, false)
ChatUtility.tellToUserAndGM(message)
}
}
}
async supprimerSignesDraconiques(filter = it => true, options = { render: true }) {
const signes = this.itemTypes[ITEM_TYPES.signedraconique].filter(filter)
if (signes.length > 0) {
this.deleteEmbeddedDocuments("Item", signes.map(item => item.id), options)
}
}
/* -------------------------------------------- */
async rollSoins(blesse, blessureId) {
const blessure = blesse.blessuresASoigner().find(it => it.id == blessureId);
@@ -2414,14 +2509,17 @@ export class RdDActor extends RdDBaseActorSang {
if (!blessure.system.premierssoins.done) {
const tache = await this.getTacheBlessure(blesse, blessure);
return await this.rollTache(tache.id, {
onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r)
onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r),
title: 'Premiers soins',
forced: true
});
}
if (!blessure.system.soinscomplets.done) {
else if (!blessure.system.soinscomplets.done) {
const diff = blessure.system.difficulte + (blessure.system.premierssoins.bonus ?? 0);
return await this.rollCaracCompetence("dexterite", "Chirurgie", diff, {
return await this.rollCaracCompetence(CARACS.DEXTERITE, "Chirurgie", diff, {
title: "Soins complets",
onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r)
onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r),
forced: true
})
}
}
@@ -2436,6 +2534,7 @@ export class RdDActor extends RdDBaseActorSang {
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && !blessure.system.premierssoins.done) {
const tache = rollData.tache;
if (rollData.rolled.isETotal) {
@@ -2506,7 +2605,7 @@ export class RdDActor extends RdDBaseActorSang {
if (item?.isEquipable()) {
const isEquipe = !item.system.equipe;
await item.update({ "system.equipe": isEquipe });
this.computeEncTotal();
this.computeEncTotal()
if (isEquipe)
this.verifierForceMin(item);
}
@@ -2897,6 +2996,7 @@ export class RdDActor extends RdDBaseActorSang {
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
await super.onUpdateActor(update, options, actorId)
}
/* -------------------------------------------- */
@@ -2916,7 +3016,9 @@ export class RdDActor extends RdDBaseActorSang {
break
case ITEM_TYPES.race:
await this.onCreateOwnedRace(item, options, id)
break
}
await super.onCreateItem(item, options, id)
await item.onCreateItemTemporel(this);
await item.onCreateDecoupeComestible(this);
}
@@ -2937,9 +3039,12 @@ export class RdDActor extends RdDBaseActorSang {
break
case ITEM_TYPES.empoignade:
await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item)
// TODO: check remaining emp.
await this.setEffect(STATUSES.StatusGrappled, false)
await this.setEffect(STATUSES.StatusGrappling, false)
break
}
super.onDeleteItem(item, options, id)
await super.onDeleteItem(item, options, id)
}
/* -------------------------------------------- */
@@ -3050,24 +3155,11 @@ export class RdDActor extends RdDBaseActorSang {
async _rollArtV2(oeuvreId) {
const oeuvre = this.items.get(oeuvreId)
const rollData = {
title: `Interpretation de ${oeuvre.name} par ${this.name}`,
type: {
allowed: ["oeuvre"],
current: "oeuvre",
},
selected: {
oeuvre: { key: oeuvre.id },
},
ids: {
actorId: this.id
}
ids: { actorId: this.id },
selected: { oeuvre: { key: oeuvre.id } },
type: { allowed: [PART_OEUVRE], current: PART_OEUVRE, },
}
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
}
})
await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
}
/* -------------------------------------------- */
@@ -3163,14 +3255,26 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollRecetteCuisine(id) {
const oeuvre = this.getRecetteCuisine(id);
const recette = this.getRecetteCuisine(id);
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_CUISINE], current: PART_CUISINE },
selected: {
cuisine: { key: recette.id }
}
}
RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
return
}
const artData = {
verbe: 'Cuisiner',
compName: 'cuisine',
proportions: 1,
ajouterEquipement: false
};
await this._rollArtV1(artData, 'odoratgout', oeuvre, r => this._resultRecetteCuisine(r));
await this._rollArtV1(artData, 'odoratgout', recette, r => this._resultRecetteCuisine(r));
}
/* -------------------------------------------- */
@@ -3203,6 +3307,18 @@ export class RdDActor extends RdDBaseActorSang {
}
async preparerNourriture(item) {
if (item.getUtilisationCuisine() == 'brut' && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_CUISINE], current: PART_CUISINE },
selected: {
cuisine: { key: item.id }
}
}
RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
return
}
if (item.getUtilisationCuisine() == 'brut') {
const nourriture = {
name: 'Plat de ' + item.name,
@@ -3213,7 +3329,7 @@ export class RdDActor extends RdDBaseActorSang {
exotisme: item.system.exotisme,
ingredients: item.name
}
};
}
const artData = {
verbe: 'Préparer',
compName: 'cuisine',

View File

@@ -56,13 +56,15 @@ export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
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.", "")
await this.actor.updateCarac(caracName, parseInt(event.target.value))
});
if (event.currentTarget.name.includes("carac.")) {
let caracName = event.currentTarget.name.replace("carac.", "")
await this.actor.updateCarac(caracName, parseInt(event.currentTarget.value))
}
})
// On competence change
this.html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value
await this.actor.updateCompetence(compName, parseInt(event.target.value))
await this.actor.updateCompetence(compName, parseInt(event.currentTarget.value))
});
}
}

View File

@@ -1,4 +1,4 @@
import { ENTITE_INCARNE, SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { renderTemplate, SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js";
import { RdDResolutionTable } from "../rdd-resolution-table.js";
@@ -11,7 +11,7 @@ import { ITEM_TYPES } from "../constants.js";
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
import { Targets } from "../targets.js";
import { RdDConfirm } from "../rdd-confirm.js";
import { RdDCarac } from "../rdd-carac.js";
import { CARACS, RdDCarac } from "../rdd-carac.js";
import { RdDRollResult } from "../rdd-roll-result.js";
import { RdDItemArme } from "../item/arme.js";
@@ -23,8 +23,12 @@ import { RdDCombat } from "../rdd-combat.js";
import { RdDEmpoignade } from "../rdd-empoignade.js";
import { RdDPossession } from "../rdd-possession.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, POSSESSION_SANS_DRACONIC } from "../item/base-items.js";
import { RollDataAjustements } from "../rolldata-ajustements.js";
import { RollDataAjustements } from "../rolldata-ajustements-v1.js";
import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
import RollDialog from "../roll/roll-dialog.mjs";
import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE } from "../roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
import { PART_COMP } from "../roll/roll-part-comp.mjs";
/**
* Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
@@ -85,7 +89,6 @@ export class RdDBaseActorReve extends RdDBaseActor {
getSConst() { return 0 }
/* -------------------------------------------- */
isSurenc() { return false }
computeMalusSurEncombrement() { return 0 }
ajustementAstrologique() { return 0 }
@@ -122,6 +125,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
computeResumeBlessure() { }
countBlessures(filter = it => !it.isContusion()) { return 0 }
async santeIncDec(name, inc, isCritique = false) { }
async finDeRound(options = { terminer: false }) {
@@ -172,10 +177,11 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
getCompetences(name = undefined, options = { onMessage: message => { } }) {
const all = [...this.itemTypes[ITEM_TYPES.competence], ...this.itemTypes[ITEM_TYPES.competencecreature]]
if (name == undefined) {
return this.itemTypes[ITEM_TYPES.competence]
return all
}
return RdDItemCompetence.findCompetences(this.itemTypes[ITEM_TYPES.competence], name, options)
return RdDItemCompetence.findCompetences(all, name, options)
}
getCompetenceCorpsACorps(options = { onMessage: message => { } }) {
@@ -199,6 +205,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
getPossession(possessionId) {
return this.itemTypes[ITEM_TYPES.possession].find(it => it.system.possessionid == possessionId);
}
getEmpoignades() {
return this.itemTypes[ITEM_TYPES.empoignade];
}
@@ -222,53 +229,13 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
}
/* -------------------------------------------- */
isEffectAllowed(effectId) { return false }
getEffects(filter = e => true, forceRequise = undefined) {
const effects = this.getEmbeddedCollection("ActiveEffect")
const selected = effects.filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) {
selected.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
}
return selected
}
getEffectByStatus(statusId) {
return this.getEffects().find(it => it.statuses.has(statusId));
}
async setEffect(statusId, status) {
if (this.isEffectAllowed(statusId)) {
const effect = this.getEffectByStatus(statusId);
if (!status && effect) {
await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
}
if (status && !effect) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(statusId)]);
}
}
}
async removeEffect(id) {
this.removeEffects(it => it.id == id)
}
async removeEffects(filter = e => true) {
if (game.user.isGM) {
const effectsToRemove = this.getEffects(filter);
const ids = effectsToRemove.map(it => it.id);
await this.deleteEmbeddedDocuments('ActiveEffect', ids);
}
}
/* -------------------------------------------- */
isDemiReve() {
return this.getEffectByStatus(STATUSES.StatusDemiReve) != undefined
return this.getEffectsByStatus(STATUSES.StatusDemiReve).length > 0
}
getSurprise(isCombat = undefined) {
return StatusEffects.getSurprise(this.getEffects(), isCombat)
getSurprise(isCombat = undefined, forceRequise = undefined) {
return StatusEffects.getSurprise(this.getEffects(e => true, isCombat, forceRequise), isCombat)
}
/* -------------------------------------------- */
@@ -305,6 +272,22 @@ export class RdDBaseActorReve extends RdDBaseActor {
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const competence = this.getCompetence(compName);
const rollData = {
ids: { actorId: this.id },
type: { allowed: DEFAULT_ROLL_TYPES, current: PART_COMP },
selected: {
carac: { key: caracName },
comp: { key: competence.name },
diff: { value: diff }
}
}
RollDialog.create(rollData, options)
return
}
const competence = this.getCompetence(compName);
await this.openRollDialog({
name: 'jet-competence',
@@ -377,9 +360,22 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
/* -------------------------------------------- */
async rollCarac(caracName, options = {}) {
if (Grammar.equalsInsensitive(caracName, 'taille')) {
if (Grammar.equalsInsensitive(caracName, CARACS.TAILLE)) {
return
}
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: DEFAULT_ROLL_TYPES, current: PART_COMP },
selected: {
carac: { key: caracName },
comp: options.resistance ? { key: undefined, forced: true } : undefined
}
}
RollDialog.create(rollData, options)
return
}
foundry.utils.mergeObject(options, { resistance: false, diff: 0 }, { overwrite: false })
RdDEmpoignade.checkEmpoignadeEnCours(this)
let selectedCarac = this.getCaracByName(caracName)
@@ -406,10 +402,24 @@ export class RdDBaseActorReve extends RdDBaseActor {
await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-general.hbs');
}
/* -------------------------------------------- */
async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(idOrName);
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: options.arme ? ATTAQUE_ROLL_TYPES : DEFAULT_ROLL_TYPES },
selected: {
carac: competence.type == ITEM_TYPES.competencecreature ? { key: competence.name } : undefined,
comp: { key: competence.name },
diff: { type: options.arme ? DIFF.ATTAQUE : DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 },
attaque: options.arme ? { arme: { key: options.arme.id } } : undefined
}
}
return await RollDialog.create(rollData)
}
let rollData = {
carac: this.system.carac,
competence: competence,
@@ -427,7 +437,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
}
});
return;
return
}
// Transformer la competence de créature
MappingCreatureArme.setRollDataCreature(rollData)
@@ -449,6 +459,40 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
}
rollAttaque(token) {
token = token ?? RdDUtility.getSelectedToken(this)
if (Targets.hasTargets()) {
Targets.selectOneTargetToken(target => {
if (Targets.isTargetEntite(target)) {
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée!!!!`)
return
}
RdDCombat.rddCombatTarget(target, this, token).attaqueV2();
})
}
else {
return RdDConfirm.confirmer({
settingConfirmer: "confirmer-combat-sans-cible",
content: `<p>Voulez vous faire une attaque 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 () => {
const rollData = {
ids: { actorId: this.id, actorTokenId: token?.id, },
type: {
allowed: [ROLL_TYPE_ATTAQUE], current: ROLL_TYPE_ATTAQUE
}
};
return await RollDialog.create(rollData)
}
})
}
}
/** --------------------------------------------
* @param {*} arme item d'arme/compétence de créature
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
@@ -588,7 +632,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
async accorder(entite, when = 'avant-encaissement') {
if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
|| entite == undefined
|| !entite.isEntite([ENTITE_INCARNE])
|| !entite.isEntiteIncarnee()
|| entite.isEntiteAccordee(this)) {
return true
}

View File

@@ -34,17 +34,17 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
getFatigueActuelle() {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
return Math.max(0, Math.min(this.getFatigueMax(), Misc.toInt(this.system.sante.fatigue?.value)))
return Math.max(0, Math.min(this.getFatigueMax(), Misc.toInt(this.system.sante.fatigue?.value)))
}
return 0;
}
isCumulFatigueCauseSommeil(cumulFatigue){
isCumulFatigueCauseSommeil(cumulFatigue) {
return ReglesOptionnelles.isUsing("appliquer-fatigue")
? (this.getFatigueRestante() <= cumulFatigue)
: (this.getEnduranceActuelle() <= cumulFatigue)
? (this.getFatigueRestante() <= cumulFatigue)
: (this.getEnduranceActuelle() <= cumulFatigue)
}
getFatigueRestante() {return this.getFatigueMax() - this.getFatigueActuelle() }
getFatigueRestante() { return this.getFatigueMax() - this.getFatigueActuelle() }
getFatigueMin() { return this.system.sante.endurance.max - this.system.sante.endurance.value }
malusFatigue() {
@@ -116,6 +116,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
blessure: blessure
});
}
/* -------------------------------------------- */
async santeIncDec(name, inc, isCritique = false) {
if (name == 'fatigue' && !ReglesOptionnelles.isUsing("appliquer-fatigue")) {
@@ -161,7 +162,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
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 })
await this.update({ "system.sante": sante }, { render: true })
if (this.isDead()) {
await this.setEffect(STATUSES.StatusComma, true);
}
@@ -179,6 +180,28 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
}
async onCreateItem(item, options, id) {
switch (item.type) {
case ITEM_TYPES.blessure:
await this.changeBleedingState()
break
}
await super.onCreateItem(item, options, id)
}
async onUpdateItem(item, options, id) {
switch (item.type) {
case ITEM_TYPES.blessure:
await this.changeBleedingState()
break
}
await super.onUpdateItem(item, options, id)
}
async changeBleedingState() {
const bleeding = this.itemTypes[ITEM_TYPES.blessure].find(it => it.isBleeding())
await this.setEffect(STATUSES.StatusBleeding, bleeding ? true : false)
}
/* -------------------------------------------- */
async ajouterBlessure(encaissement, attackerToken = undefined) {
@@ -195,7 +218,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken);
if (blessure.isCritique()) {
encaissement.endurance = endActuelle;
encaissement.endurance = endActuelle
}
if (blessure.isMort()) {
@@ -292,7 +315,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
}
isSonne() {
return this.getEffectByStatus(STATUSES.StatusStunned)
return this.getEffectsByStatus(STATUSES.StatusStunned).length > 0
}
isEffectAllowed(effectId) { return true }

View File

@@ -49,17 +49,14 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
formData.calc = {
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
encTotal: await this.actor.computeEncTotal(),
encTotal: this.actor.getEncTotal(),
}
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature)
.forEach(it => {
const competenceCreature = new RdDItemCompetenceCreature(it.toObject(), { parent: it.parent });
it.isdommages = competenceCreature.isDommages();
})
.forEach(it => it.isdommages = it.isDommages())
return formData;
}
@@ -232,14 +229,6 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);

View File

@@ -1,6 +1,6 @@
import { ChatVente } from "../achat-vente/chat-vente.js";
import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js";
import { renderTemplate, SYSTEM_SOCKET_ID } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Monnaie } from "../item-monnaie.js";
import { ITEM_TYPES } from "../constants.js";
@@ -10,6 +10,7 @@ import { RdDConfirm } from "../rdd-confirm.js";
import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { RdDItem } from "../item.js";
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
export class RdDBaseActor extends Actor {
@@ -45,8 +46,10 @@ export class RdDBaseActor extends Actor {
}
static init() {
Handlebars.registerHelper('actor-isFeminin', actor => actor.isFeminin())
Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
Hooks.on("updateItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onUpdateItem(item, options, id))
Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id))
Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId))
}
@@ -212,12 +215,15 @@ export class RdDBaseActor extends Actor {
return game.users.players.find(player => player.active && player.character?.id == this.id);
}
isCreatureEntite() { return this.isCreature() || this.isEntite() }
isCreatureOuEntite() { return this.isCreature() || this.isEntite() }
isCreature() { return false }
isEntite(typeentite = []) { return false }
isEntite() { return false }
isEntiteIncarnee() { return false }
isEntiteNonIncarnee() { return false }
isHautRevant() { return false }
isVehicule() { return false }
isPersonnage() { return false }
isFeminin() { return false }
getItem(id, type = undefined) {
const item = this.items.get(id);
if (type == undefined || (item?.type == type)) {
@@ -237,20 +243,76 @@ export class RdDBaseActor extends Actor {
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
getEncombrementMax() { return 0 }
isSurenc() { return false }
/* -------------------------------------------- */
isEffectAllowed(effectId) { return false }
getEffects(filter = e => true, forceRequise = undefined) {
const effects = this.getEmbeddedCollection("ActiveEffect")
const selected = effects.filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) {
selected.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
}
return selected
}
getEffectsByStatus(effectId) {
return this.getEffects().filter(it => it.statuses.has(effectId))
}
async setEffect(effectId, status) {
if (this.isEffectAllowed(effectId)) {
const effects = this.getEffectsByStatus(effectId)
if (!status && effects.length > 0) {
await this.deleteEmbeddedDocuments('ActiveEffect', effects.map(it => it.id), { render: true })
}
if (status && effects.length == 0) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(effectId)], { render: true })
}
}
}
async removeEffect(id) {
this.removeEffects(it => it.id == id)
}
async removeEffects(filter = e => true) {
if (game.user.isGM) {
const effectsToRemove = this.getEffects(filter);
const ids = effectsToRemove.map(it => it.id);
await this.deleteEmbeddedDocuments('ActiveEffect', ids);
}
}
/* -------------------------------------------- */
async updateCarac(caracName, to) {
}
async onUpdateActor(change, options, actorId) {
const updatedCarac = change?.system?.carac
if (updatedCarac && (updatedCarac.force || updatedCarac.reve || updatedCarac.taille)) {
console.log(' onUpdateActor', change, options, actorId)
await this.setEffect(STATUSES.StatusSurEnc, this.isSurenc())
}
}
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { }
async onCreateItem(item, options, id) {
}
async onUpdateItem(item, options, id) {
}
async onDeleteItem(item, options, id) {
if (item.isInventaire()) {
await this._removeItemFromConteneur(item)
}
}
async _removeItemFromConteneur(item) {
const updates = this.items.filter(it => it.isConteneur() && it.system.contenu.includes(item.id))
.map(conteneur => {
@@ -499,16 +561,22 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
async computeEncTotal() {
if (!this.pack) {
if (this.pack) {
this.encTotal = 0
}
else {
const wasSurenc = this.isSurenc()
this.encTotal = this.items.filter(it => RdDItem.getItemTypesInventaire().includes(it.type))
.map(it => it.getEncTotal()).reduce(Misc.sum(), 0)
return this.encTotal;
const isSurenc = this.isSurenc()
if (isSurenc != wasSurenc) {
await this.setEffect(STATUSES.StatusSurEnc, isSurenc)
}
}
return 0;
}
getEncTotal() {
return Math.floor(this.encTotal ?? 0);
return Math.floor(this.encTotal ?? 0)
}
async createItem(type, name = undefined) {
@@ -559,7 +627,7 @@ export class RdDBaseActor extends Actor {
}
}
}
await this.computeEncTotal();
await this.computeEncTotal()
return result;
}
@@ -738,7 +806,7 @@ export class RdDBaseActor extends Actor {
name: this.getAlias(),
system: { description: this.system.description }
}
foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.hbs', chatData)
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.hbs', chatData)
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
}
@@ -769,11 +837,15 @@ export class RdDBaseActor extends Actor {
return this.itemTypes[ITEM_TYPES.possession]
.map(p => {
return {
name: p.name,
label: p.name,
action: 'possession',
possessionid: p.system.possessionid,
}
})
}
listActionsCombat() {
const possessions = this.listActionsPossessions()
return possessions.length > 0 ? possessions : this.listActions({})
}
}

View File

@@ -43,7 +43,7 @@ export class RdDActorEntiteSheet extends RdDBaseActorReveSheet {
this.html.find('.resonance-add').click(async event =>
await DialogSelect.select({
label: "Choisir un acteur à accorder",
list: game.actors.filter(it => it.isPersonnage() && it.prototypeToken.actorLink)
list: game.actors.filter(it => true)
},
it => this.resonanceAdd(it.id))
)

View File

@@ -1,5 +1,6 @@
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
import { ITEM_TYPES } from "../constants.js";
import { RdDItemBlessure } from "../item/blessure.js";
import { Misc } from "../misc.js";
import { RdDCarac } from "../rdd-carac.js";
import { RdDEncaisser } from "../rdd-roll-encaisser.js";
@@ -16,11 +17,10 @@ export class RdDEntite extends RdDBaseActorReve {
return item.type == ITEM_TYPES.competencecreature
}
isEntite(typeentite = []) {
return (typeentite.length == 0 || typeentite.includes(this.system.definition.typeentite));
}
isNonIncarnee() { return this.isEntite([ENTITE_NONINCARNE]) }
isEntite() { return true }
isEntiteNonIncarnee() { return this.system.definition.typeentite == ENTITE_NONINCARNE }
isEntiteIncarnee() { return [ENTITE_INCARNE, ENTITE_BLURETTE].includes(this.system.definition.typeentite) }
isEntiteBlurette() { return this.system.definition.typeentite !== ENTITE_BLURETTE }
getReveActuel() {
return Misc.toInt(this.system.carac.reve?.value)
@@ -49,20 +49,20 @@ export class RdDEntite extends RdDBaseActorReve {
}
async remiseANeuf() {
await this.removeEffects(e => true);
if (!this.isNonIncarnee()) {
if (!this.isEntiteNonIncarnee()) {
await this.update({
'system.sante.endurance.value': this.system.sante.endurance.max
});
}
await this.removeEffects(e => true)
}
isDead() {
return this.isNonIncarnee() ? false : this.system.sante.endurance.value <= 0
return this.isEntiteNonIncarnee() ? false : this.system.sante.endurance.value <= 0
}
async santeIncDec(name, inc, isCritique = false) {
if (name == 'endurance' && !this.isNonIncarnee()) {
if (name == 'endurance' && !this.isEntiteNonIncarnee()) {
const oldValue = this.system.sante.endurance.value;
const endurance = Math.max(0,
Math.min(oldValue + inc,
@@ -78,7 +78,7 @@ export class RdDEntite extends RdDBaseActorReve {
}
async encaisser() {
if (this.isNonIncarnee()) {
if (this.isEntiteNonIncarnee()) {
return
}
await RdDEncaisser.encaisser(this)
@@ -89,15 +89,19 @@ export class RdDEntite extends RdDBaseActorReve {
}
async onAppliquerJetEncaissement(encaissement, attackerToken) {
if (this.isEntiteNonIncarnee()) {
return
}
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
foundry.utils.mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue,
endurance: perteEndurance.perte
});
endurance: perteEndurance.perte,
blessure: RdDItemBlessure.prepareBlessure(encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken)
})
}
isEntiteAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
if (this.isEntiteIncarnee()) {
let resonnance = this.system.sante.resonnance
return (resonnance.actors.find(it => it == attacker.id))
}
@@ -106,7 +110,7 @@ export class RdDEntite extends RdDBaseActorReve {
/* -------------------------------------------- */
async setEntiteReveAccordee(actor) {
if (this.isEntite([ENTITE_INCARNE])) {
if (this.isEntiteIncarnee()) {
if (this.system.sante.resonnance.actors.find(it => it == actor.id)) {
// déjà accordé
return

View File

@@ -29,7 +29,7 @@ export class ExperienceLog {
};
console.log('ExperienceLog.add', newXpLog)
const newExperienceLog = (actor.system.experiencelog ?? []).concat([newXpLog]);
await actor.update({ [`system.experiencelog`]: newExperienceLog });
await actor.update({ [`system.experiencelog`]: newExperienceLog }, { noHook: true });
}
static labelTopic(topic) {

View File

@@ -1,8 +1,8 @@
import { Grammar } from "../../grammar.js"
import { RdDItemArme } from "../../item/arme.js"
import { EMPOIGNADE, RdDItemArme } from "../../item/arme.js"
import { RdDItemCompetence } from "../../item-competence.js"
import { RdDItemSort } from "../../item-sort.js"
import { ITEM_TYPES } from "../../constants.js"
import { ITEM_TYPES, RDD_CONFIG } from "../../constants.js"
import { Misc } from "../../misc.js"
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
import { RdDBonus } from "../../rdd-bonus.js"
@@ -141,7 +141,7 @@ export class Mapping {
static prepareArmes(actor) {
const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme)
armes.push(RdDItemArme.corpsACorps(actor));
armes.push(RdDItemArme.pugilat(actor));
armes.push(RdDItemArme.empoignade(actor));
return armes.map(arme => [
arme.system.unemain ? Mapping.prepareArme(actor, arme, '(1 main)') : undefined,
@@ -160,7 +160,7 @@ export class Mapping {
return undefined
}
const categorie = Mapping.complementCategorie(arme, maniement)
const dommages = Mapping.dommagesArme(actor, arme, maniement)
const dommages = Mapping.dommages(actor, arme, maniement)
return {
name: arme.name + categorie,
niveau: Misc.toSignedString(competence.system.niveau),
@@ -170,12 +170,13 @@ export class Mapping {
arme: arme
}
}
static dommagesArme(actor, arme, maniement) {
static dommages(actor, arme, maniement) {
const dmgArme = RdDItemArme.dommagesReels(arme, maniement)
const dommages = Misc.toSignedString(dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme))
switch (arme.system.mortalite) {
case 'non-mortel': return `(${dommages})`
case 'empoignade': return '-'
case RDD_CONFIG.encaissement.nonmortel: return `(${dommages})`
case RDD_CONFIG.encaissement.empoignade: return '-'
}
return dommages
}
@@ -274,11 +275,10 @@ export class Mapping {
}
static getDescription(actor) {
const sexe = actor.system.sexe
const sexeFeminin = sexe.length > 0 && sexe.charAt(0).toLowerCase() == 'f' ? 'Née' : 'Né'
const naissance = actor.isFeminin() ? 'née' : 'né'
const race = ['', 'humain'].includes(Grammar.toLowerCaseNoAccent(actor.system.race)) ? '' : (actor.system.race + ' ')
const heure = actor.system.heure
const hn = `${sexeFeminin} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
const hn = `${naissance} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
const age = (actor.system.age && actor.system.age >0) ? `${actor.system.age} ans` : undefined
const taille = actor.system.taille
const poids = actor.system.poids

View File

@@ -20,14 +20,15 @@ const PATHS = [
const RANDOM_VALUES = {
'system.sexe': { 'masculin': 1, 'féminin': 1 },
'system.main': { 'droitier': 51, 'gaucher': 15, 'ambidextre': 6 },
'system.cheveux': { 'noirs': 2, 'bruns': 5, 'châtains clair': 5, 'blonds': 4, 'blonds très clair': 1, 'roux carotte': 1, 'roux cuivré': 3 },
'system.yeux': { 'noirs': 2, 'noisettes': 3, 'bruns vert': 4, 'verts': 3, 'bleus clair': 3, 'bleus gris': 2, 'gris': 1, 'mauves': 1, 'indigos': 1 },
'system.cheveux': { 'noirs': 2, 'bruns': 5, 'châtains': 3, 'châtain clair': 5, 'blonds': 4, 'blond platine': 1, 'roux carotte': 1, 'roux cuivré': 3, 'chauve': 1 },
'system.yeux': { 'noirs': 2, 'noisette': 3, 'brun-vert': 4, 'verts': 3, 'bleu clair': 3, 'bleu gris': 2, 'gris': 1, 'mauves': 1, 'indigos': 1 },
}
export class AppPersonnageAleatoire extends FormApplication {
static preloadHandlebars() {
foundry.applications.handlebars.loadTemplates([
'systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/random/sexe-aleatoire.hbs',
])
}
@@ -49,14 +50,14 @@ export class AppPersonnageAleatoire extends FormApplication {
this.current = foundry.utils.duplicate(actor)
this.checked = {
'name': false,
'system.sexe': true,
'system.age': true,
'system.taille': true,
'system.poids': true,
'system.main': true,
'system.heure': true,
'system.cheveux': true,
'system.yeux': true
'system.sexe': (this.actor.system.sexe ?? '') == '',
'system.age': this.actor.system.age == 0,
'system.taille': (this.actor.system.taille ?? '') == '',
'system.poids': (this.actor.system.poids ?? '') == '',
'system.main': (this.actor.system.main ?? '') == '',
'system.heure': (this.actor.system.heure ?? '') == '',
'system.cheveux': (this.actor.system.cheveux ?? '') == '',
'system.yeux': (this.actor.system.yeux ?? '') == '',
}
}
@@ -76,6 +77,8 @@ export class AppPersonnageAleatoire extends FormApplication {
this.html.find("button.button-apply").click(async event => await this.onApply())
this.html.find("input.current-value").change(async event => await this.onChange(event))
this.html.find("div.random-field[data-path='system.heure'] select.current-value").change(async event => await this.onChange(event))
this.html.find('a[data-action="sexe-masculin"]').click(async event => await this.onSexe('masculin'))
this.html.find('a[data-action="sexe-feminin"]').click(async event => await this.onSexe('féminin'))
this.html.find("a.random").click(async event => await this.onRandom(event))
this.html.find("a.reset").click(async event => await this.onReset(event))
this.html.find("a.randomize-selected").click(async event => await this.onRandomizeSelected())
@@ -96,6 +99,10 @@ export class AppPersonnageAleatoire extends FormApplication {
const fields = this.html.find(selector).parents("div.random-field:first")
return fields[0].attributes['data-path'].value
}
async onSexe(sexe) {
this.current['system.sexe'] = sexe
this.render()
}
async onChange(event) {
const path = this.getPath(event.currentTarget)
@@ -180,8 +187,9 @@ export class AppPersonnageAleatoire extends FormApplication {
const variation = Math.floor((caracTaille.poidsMax - caracTaille.poidsMin + base / 5) / 2)
const total = await RdDDice.rollTotal(`2d${variation} + ${base}`)
const cm = total % 100
const dm = cm < 10 ? '0' : ''
const m = (total - cm) / 100
return `${m}m${cm}`
return `${m}m${dm}${cm}`
}

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "../../constants.js";
export class TextRollManager {

View File

@@ -1,5 +1,5 @@
import { Misc } from "./misc.js";
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
@@ -106,6 +106,25 @@ export class ChatUtility {
return await ChatMessage.create(messageData)
}
static tellToUser(message) {
ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id] });
}
static tellToGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getGMs()
});
}
static tellToUserAndGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getUserAndGMs()
})
}
static getOwners(document) {
return document ? game.users.filter(it => document.getUserLevel(it) == CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) : [game.user]
}
@@ -190,8 +209,8 @@ export class ChatUtility {
if (rddTimestamp) {
const timestamp = new RdDTimestamp(rddTimestamp);
const timestampData = timestamp.toCalendrier();
const dateHeure = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData);
$(html).find('header.message-header .message-sender').after(dateHeure)
const dateHeure = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData);
$(html).find('header.message-header .message-timestamp').after(dateHeure)
}
}

View File

@@ -1,5 +1,6 @@
import { RdDBaseActor } from "../actor/base-actor.js";
import { ChatUtility } from "../chat-utility.js";
import { renderTemplate } from "../constants.js";
const INFO_COEUR = 'info-coeur';

View File

@@ -9,47 +9,67 @@ export const ENTITE_INCARNE = 'incarne'
export const ENTITE_NONINCARNE = 'nonincarne'
export const ENTITE_BLURETTE = 'blurette'
export const renderTemplate = foundry.applications.handlebars.renderTemplate
export const RDD_CONFIG = {
niveauEthylisme : [
{value: "1", label: "Aucun"},
{value: "0", label: "Eméché (0)"},
{value: "-1", label: "Gris (-1)"},
{value: "-2", label: "Pinté (-2)"},
{value: "-3", label: "Pas Frais (-3)"},
{value: "-4", label: "Ivre (-4)"},
{value: "-5", label: "Bu (-5)"},
{value: "-6", label: "Complètement fait (-6)"},
{value: "-7", label: "Ivre mort (-7)"}
niveauEthylisme: [
{ value: '1', label: 'Aucun' },
{ value: '0', label: 'Eméché (0)' },
{ value: '-1', label: 'Gris (-1)' },
{ value: '-2', label: 'Pinté (-2)' },
{ value: '-3', label: 'Pas Frais (-3)' },
{ value: '-4', label: 'Ivre (-4)' },
{ value: '-5', label: 'Bu (-5)' },
{ value: '-6', label: 'Complètement fait (-6)' },
{ value: '-7', label: 'Ivre mort (-7)' }
],
categorieEntite: {
"cauchemar": "Cauchemar",
"reve": "Rêve"
'cauchemar': 'Cauchemar',
'reve': 'Rêve'
},
typeEntite: {
"incarne": "Incarnée",
"nonincarne": "Non Incarnée",
"blurette": "Blurette"
[ENTITE_INCARNE]: 'Incarnée',
[ENTITE_NONINCARNE]: 'Non Incarnée',
[ENTITE_BLURETTE]: 'Blurette'
},
heuresRdD : [
{value : "vaisseau", label: "Vaisseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd01.webp"},
{value : "sirene", label: "Sirène", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd02.webp"},
{value : "faucon", label: "Faucon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd03.webp"},
{value : "couronne", label: "Couronne", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd04.webp"},
{value : "dragon", label: "Dragon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd05.webp"},
{value : "epees", label: "Epées", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd06.webp"},
{value : "lyre", label: "Lyre", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd07.webp"},
{value : "serpent", label: "Serpent", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd08.webp"},
{value : "poissonacrobate", label: "Poisson Acrobate", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd09.webp"},
{value : "araignee", label: "Araignée", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd10.webp"},
{value : "roseau", label: "Roseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd11.webp"},
{value : "chateaudormant", label: "Chateau Dormant", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd12.webp"}
heuresRdD: [
{ value: 'vaisseau', label: 'Vaisseau', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd01.webp' },
{ value: 'sirene', label: 'Sirène', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd02.webp' },
{ value: 'faucon', label: 'Faucon', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd03.webp' },
{ value: 'couronne', label: 'Couronne', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd04.webp' },
{ value: 'dragon', label: 'Dragon', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd05.webp' },
{ value: 'epees', label: 'Epées', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd06.webp' },
{ value: 'lyre', label: 'Lyre', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd07.webp' },
{ value: 'serpent', label: 'Serpent', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd08.webp' },
{ value: 'poissonacrobate', label: 'Poisson Acrobate', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd09.webp' },
{ value: 'araignee', label: 'Araignée', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd10.webp' },
{ value: 'roseau', label: 'Roseau', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd11.webp' },
{ value: 'chateaudormant', label: 'Chateau Dormant', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.webp' }
],
raretes: [
{value: "Commune", label: "Commune"},
{value: "Frequente", label: "Fréquente"},
{value: "Rare", label: "Rare"},
{value: "Rarissime", label: "Rarissime"}
]
{ value: 'Commune', label: 'Commune' },
{ value: 'Frequente', label: 'Fréquente' },
{ value: 'Rare', label: 'Rare' },
{ value: 'Rarissime', label: 'Rarissime' }
],
particuliere: {
force: { key: 'force', descr: 'en force', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-force.svg' },
finesse: { key: 'finesse', descr: 'en finesse', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-finesse.svg' },
rapidite: { key: 'rapidite', descr: 'en rapidité', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-rapidite.svg' },
},
icons: {
armesDisparates: 'systems/foundryvtt-reve-de-dragon/assets/actions/armes-disparates.svg',
demiReve: 'systems/foundryvtt-reve-de-dragon/assets/actions/sort.svg',
empoignade: 'systems/foundryvtt-reve-de-dragon/assets/actions/empoignade.svg',
forceWeak: 'systems/foundryvtt-reve-de-dragon/assets/actions/weak.svg',
surenc: 'systems/foundryvtt-reve-de-dragon/assets/actions/surenc.svg',
},
encaissement: {
mortel: 'mortel',
nonmortel: 'non-mortel',
entiteincarnee: 'entiteincarnee',
empoignade: 'empoignade'
}
}
export const ACTOR_TYPES = {
@@ -107,13 +127,3 @@ export const ITEM_TYPES = {
extraitpoetique: 'extraitpoetique',
}
export const CATEGORIES_COMPETENCES = {
"generale": { base: -4, label: "Générales" },
"particuliere": { base: -8, label: "Particulières" },
"specialisee": { base: -11, label: "Spécialisées" },
"connaissance": { base: -11, label: "Connaissances" },
"draconic": { base: -11, label: "Draconic" },
"melee": { base: -6, label: "Mêlée" },
"tir": { base: -8, label: "Tir" },
"lancer": { base: -8, label: "Lancer" }
}

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js"
export class DialogChoixXpCarac extends Dialog {

View File

@@ -1,4 +1,4 @@
import { SYSTEM_RDD } from "./constants.js";
import { renderTemplate, SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";

View File

@@ -1,4 +1,5 @@
import { ChatUtility } from "./chat-utility.js";
import { renderTemplate } from "./constants.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { TMRUtility } from "./tmr-utility.js";

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js";

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
export class DialogConsommer extends Dialog {

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js"
export class DialogSelect extends Dialog {
static extractIdNameImg(it) { return { id: it.id, name: it.name, img: it.img } }

View File

@@ -1,4 +1,4 @@
import { Misc } from "./misc.js";
import { renderTemplate } from "./constants.js";
export class DialogSplitItem extends Dialog {

View File

@@ -1,4 +1,4 @@
import { HIDE_DICE, SHOW_DICE } from "./constants.js";
import { HIDE_DICE, renderTemplate, SHOW_DICE } from "./constants.js";
import { RdDUtility } from "./rdd-utility.js";
/**

View File

@@ -1,4 +1,4 @@
import { ITEM_TYPES } from "../constants.js"
import { ITEM_TYPES, renderTemplate } from "../constants.js"
import { RdDItemSort } from "../item-sort.js"
import { Misc } from "../misc.js"

View File

@@ -1,7 +1,66 @@
export const MAP_PHASE = {
possession: { label: "possession", rang: 10 },
draconic: { label: "draconic", rang: 9 },
tir: { label: "tir", rang: 8 },
lancer: { label: "lancer", rang: 7 },
arme: { label: "mêlée", rang: 5 },
pugilat: { label: "pugilat", rang: 4 },
naturelle: { label: "créature", rang: 4 },
empoignade: { label: "empoignade", rang: 3 },
autre: { label: "autre action", rang: 2 },
demi: { label: "demi-surprise", rang: 0 },
totale: { label: "surprise totale", rang: -1 },
}
export const PHASE = Object.values(MAP_PHASE)
export class RdDInitiative {
static calculInitiative(niveau, caracValue, bonus = 0) {
let base = niveau + Math.floor(caracValue / 2) + bonus;
return "1d6" + (base >= 0 ? "+" : "") + base;
static getRollInitiative(caracValue, niveau, bonus = 0) {
const base = RdDInitiative.ajustementInitiative(caracValue, niveau, bonus)
return "1d6" + (base >= 0 ? "+" : "") + base
}
static ajustementInitiative(caracValue, niveau, bonus) {
return niveau + Math.floor(caracValue / 2) + bonus
}
static formule(phase, carac, niveau, bonusMalus) {
const ajustement = RdDInitiative.ajustementInitiative(carac, niveau, bonusMalus)
return { phase, ajustement }
}
static phaseArme(categorie, arme) {
switch (categorie) {
case "tir":
case "lancer":
return MAP_PHASE[categorie]
default:
switch (arme.system.cac) {
case "empoignade":
case "pugilat":
case "naturelle":
return MAP_PHASE[arme.system.cac]
default:
return MAP_PHASE['arme']
}
}
}
static phase(keyOrRang) {
return MAP_PHASE[keyOrRang] ?? PHASE.find(it => it.rang == keyOrRang) ?? MAP_PHASE.autre
}
static async roll(formule) {
const sign = formule.ajustement >= 0 ? "+" : ""
const roll = new Roll(`1d6 + ${sign} + ${formule.ajustement}`)
await roll.evaluate()
const value = Math.max(roll.total, 0)
return {
roll: roll,
value: value,
rang: formule.phase.rang,
init: formule.phase.rang + value / 100,
label: formule.phase.label
}
}
}

View File

@@ -10,15 +10,15 @@ export class RdDItemCompetenceCreature extends RdDItem {
static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }
isAttaque() { return this.getCategorieAttaque() != undefined }
isParade() { return this.system.iscombat && (this.system.categorie_parade ?? '') != '' }
isBouclier() { return this.system.categorie_parade.includes('bouclier') }
attaqueCreature() {
const categorieAttaque = this.getCategorieAttaque()
if (categorieAttaque != undefined) {
const initative = RdDInitiative.calculInitiative(this.system.niveau, this.system.carac_value);
const initative = RdDInitiative.getRollInitiative(this.system.carac_value, this.system.niveau);
const attaque = {
name: this.name,
label: this.name,
action: this.isCompetencePossession() ? 'possession' : 'attaque',
initOnly: false,
arme: new RdDItemArme({
@@ -32,6 +32,7 @@ export class RdDItemCompetenceCreature extends RdDItem {
initiative: initative,
mortalite: this.system.mortalite,
dommages: this.system.dommages,
forceRequise: 0,
equipe: true,
resistance: 100,
penetration: 0,
@@ -43,7 +44,8 @@ export class RdDItemCompetenceCreature extends RdDItem {
carac: { key: this.name, value: this.system.carac_value },
equipe: true,
mortalite: this.system.mortalite,
dmg: this.system.dommages,
dommages: this.system.dommages,
//dmg: this.system.dommages,
initiative: initative
};
return attaque
@@ -51,10 +53,6 @@ export class RdDItemCompetenceCreature extends RdDItem {
return undefined;
}
isAttaque() {
return this.getCategorieAttaque() != undefined
}
getCategorieAttaque() {
switch (this.system.categorie) {
case "melee":

View File

@@ -260,7 +260,7 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
async _updateObject(event, formData) {
switch (this.item.type) {
case ITEM_TYPES.sort:
formData['system.bonuscase'] = RdDItemSort.bonuscasesToString(RdDItemSheetV1._listCaseTmr(
@@ -273,8 +273,7 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
formData['system.niveau'] = formData['system.niveau'] ?? formData['system.base']
break
}
return this.item.update(formData)
return await super._updateObject(event, formData)
}
/* -------------------------------------------- */

View File

@@ -148,10 +148,17 @@ export class RdDItemSort extends Item {
/* -------------------------------------------- */
static incrementBonusCase(actor, sort, coord) {
let bonuscase = RdDItemSort.calculBonuscase(sort, coord)
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }]);
}
static calculBonuscase(sort, coord) {
if (TMRUtility.isFleuve(coord)) {
coord = FLEUVE_COORD;
coord = FLEUVE_COORD
}
let list = RdDItemSort.stringToBonuscases(sort.system.bonuscase);
let list = RdDItemSort.stringToBonuscases(sort.system.bonuscase)
const existing = list.find(it => it.case == coord)
const bonus = Number(existing?.bonus ?? 0) + 1
if (existing) {
@@ -160,11 +167,9 @@ export class RdDItemSort extends Item {
else {
list.push({ case: coord, bonus: 1 })
}
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': RdDItemSort.bonuscasesToString(list) }]);
return RdDItemSort.bonuscasesToString(list)
}
/* -------------------------------------------- */
static getCaseBonus(sort, coord) {
const search = TMRUtility.isFleuve(coord)
@@ -189,4 +194,22 @@ export class RdDItemSort extends Item {
.map(it => it.split(':'))
.map(it => { return { case: it[0], bonus: it[1] } });
}
static prepareSortEnReserve(sort, draconic, ptreve, coord) {
return {
type: ITEM_TYPES.sortreserve,
name: sort.name,
img: sort.img,
system: { sortid: sort._id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
};
}
static prepareSortAddLancement(sort, reveSort) {
const precedents = sort.system.lancements ?? []
const lancements = [...precedents, {
timestamp: game.system.rdd.calendrier.getTimestamp(),
reve: reveSort
}]
return lancements
}
}

View File

@@ -1,4 +1,4 @@
import { ITEM_TYPES } from "./constants.js";
import { ITEM_TYPES, RDD_CONFIG, renderTemplate } from "./constants.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES, CATEGORIES_COMPETENCES_CREATURES } from "./item/base-items.js";
import { ITEM_ACTIONS, DEFAULT_ACTIONS, COMMON_ACTIONS } from "./item/item-actions.js";
@@ -629,7 +629,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} ${this.system.mortalite == RDD_CONFIG.encaissement.nonmortel ? '(Non mortel)' : ''}`,
`<b>Force minimum</b>: ${this.system.force}`,
`<b>Resistance</b>: ${this.system.resistance}`,
...this._inventaireTemplateChatData()

View File

@@ -1,9 +1,10 @@
import { ITEM_TYPES } from "../constants.js";
import { ITEM_TYPES, RDD_CONFIG } from "../constants.js";
import { RdDItem } from "../item.js";
import { BASE_CORPS_A_CORPS } from "./base-items.js";
import { Grammar } from "../grammar.js";
import { RdDInitiative } from "../initiative.mjs";
import { MappingCreatureArme } from "./mapping-creature-arme.mjs";
import { Misc } from "../misc.js";
const nomCategorieParade = {
"sans-armes": "Sans arme",
@@ -21,10 +22,16 @@ const nomCategorieParade = {
export const ATTAQUE_TYPE = {
UNE_MAIN: '(1 main)',
DEUX_MAINS: '(2 mains)',
CORPS_A_CORPS: '(corps à corps)',
COMPETENCE: 'competence',
TIR: '(tir)',
LANCER: '(lancer)'
}
export const ATTAQUE_TYPE_MELEE = [ATTAQUE_TYPE.UNE_MAIN, ATTAQUE_TYPE.DEUX_MAINS, ATTAQUE_TYPE.CORPS_A_CORPS]
export const CORPS_A_CORPS = 'Corps à corps'
export const PUGILAT = 'pugilat'
export const EMPOIGNADE = RDD_CONFIG.encaissement.empoignade
/* -------------------------------------------- */
export class RdDItemArme extends RdDItem {
@@ -62,7 +69,7 @@ export class RdDItemArme extends RdDItem {
case ITEM_TYPES.competencecreature:
return MappingCreatureArme.armeCreature(arme);
}
return RdDItemArme.corpsACorps();
return RdDItemArme.pugilat();
}
static getCompetenceArme(arme, maniement) {
@@ -76,6 +83,7 @@ export class RdDItemArme extends RdDItem {
case ATTAQUE_TYPE.DEUX_MAINS: return arme.competence2Mains()
case ATTAQUE_TYPE.TIR: case 'tir': return arme.system.tir
case ATTAQUE_TYPE.LANCER: case 'lancer': return arme.system.lancer;
case ATTAQUE_TYPE.CORPS_A_CORPS: return CORPS_A_CORPS
}
}
return undefined
@@ -244,47 +252,49 @@ export class RdDItemArme extends RdDItem {
}
isAttaque() {
return this.system.resistance > 0 || this.system.portee_courte > 0
return this.system.resistance > 0 || (this.system.tir != '' && this.system.portee_courte > 0)
}
static corpsACorps(actor) {
let competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS
let melee = actor ? actor.system.carac['melee'].value : 0
isEmpoignade() {
return this.system.mortalite == RDD_CONFIG.encaissement.empoignade
}
isUtilisableEmpoigne() {
return this.system.baseInit == 3 || this.system.baseInit == 4 || this.system.competence == "Dague"
}
static pugilat(actor) {
return RdDItemArme.$corpsACorps(actor, 'Pugilat', PUGILAT)
}
static empoignade(actor) {
return RdDItemArme.$corpsACorps(actor, 'Empoignade', RDD_CONFIG.encaissement.empoignade)
}
static $corpsACorps(actor, name, cac) {
const competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS
const melee = actor ? actor.system.carac['melee'].value : 0
return new RdDItemArme({
_id: competence.id,
name: 'Corps à corps',
_id: Misc.fakeId(cac),
name: name,
type: ITEM_TYPES.arme,
img: competence.img,
system: {
initiative: RdDInitiative.calculInitiative(competence.system.niveau, melee),
initiative: RdDInitiative.getRollInitiative(melee, competence.system.niveau),
equipe: true,
rapide: true,
force: 0,
dommages: "0",
dommagesReels: 0,
mortalite: 'non-mortel',
competence: 'Corps à corps',
mortalite: cac == RDD_CONFIG.encaissement.empoignade ? RDD_CONFIG.encaissement.empoignade : RDD_CONFIG.encaissement.nonmortel,
competence: CORPS_A_CORPS,
resistance: 1,
baseInit: 4,
cac: 'pugilat',
baseInit: cac == RDD_CONFIG.encaissement.empoignade ? 3 : 4,
cac: cac,
deuxmains: true,
categorie_parade: 'sans-armes'
}
})
}
static mainsNues(actor) {
const mainsNues = RdDItemArme.corpsACorps(actor)
mainsNues.name = 'Mains nues'
return mainsNues;
}
static empoignade(actor) {
const empoignade = RdDItemArme.corpsACorps(actor)
empoignade.name = 'Empoignade'
empoignade.system.cac = 'empoignade'
empoignade.system.baseInit = 3
empoignade.system.mortalite = 'empoignade'
return empoignade
}
}

View File

@@ -71,21 +71,25 @@ export class RdDItemBlessure extends RdDItem {
return 0
}
static async createBlessure(actor, gravite, localisation = '', attackerToken) {
const definition = RdDItemBlessure.getDefinition(gravite)
const blessure = {
static async createBlessure(actor, gravite, localisation = '', attackerToken = undefined) {
const blessure = RdDItemBlessure.prepareBlessure(gravite, localisation, attackerToken);
const blessures = await actor.createEmbeddedDocuments('Item', [blessure])
return blessures[0]
}
static prepareBlessure(gravite, localisation, attackerToken) {
const definition = RdDItemBlessure.getDefinition(gravite);
return {
name: definition.label,
type: 'blessure',
img: definition.icon,
system: {
gravite: gravite,
difficulte: - gravite,
difficulte: -gravite,
localisation: localisation,
origine: attackerToken?.name ?? ""
}
}
const blessures = await actor.createEmbeddedDocuments('Item', [blessure])
return blessures[0]
}
static async createTacheSoinBlessure(actor, gravite) {
@@ -209,6 +213,9 @@ export class RdDItemBlessure extends RdDItem {
isCritique() {
return this.system.gravite > 4 && this.system.gravite <= 6
}
isBleeding() {
return this.system.gravite > 2 && !this.system.premierssoins.done
}
isMort() {
return this.system.gravite > 6
}

View File

@@ -3,7 +3,6 @@ import { RdDInitiative } from "../initiative.mjs";
export class MappingCreatureArme {
/* -------------------------------------------- */
static setRollDataCreature(rollData) {
const code = Grammar.toLowerCaseNoAccentNoSpace(rollData.competence.name);
@@ -26,7 +25,7 @@ export class MappingCreatureArme {
competence: item.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: item.system.niveau,
initiative: RdDInitiative.calculInitiative(item.system.niveau, item.system.carac_value),
initiative: RdDInitiative.getRollInitiative(item.system.carac_value, item.system.niveau),
equipe: true,
resistance: 100,
dommagesReels: item.system.dommages,

View File

@@ -6,8 +6,8 @@ const tableEffets = [
{ code: "passeur", resultat: "succes", description: "Déplacer le demi-rêve à (force) cases", method: EffetsRencontre.passeur},
{ code: "reve+f", resultat: "succes", description: "Gain de (force) points de rêve" , method: EffetsRencontre.reve_plus_force},
{ code: "teleport", resultat: "succes", description: "Déplacer le demi-rêve (même type)", method: EffetsRencontre.teleportation_typecase },
{ code: "part+tete", resultat: "succes", description: "Tête de dragon sur réussite particulière", method: EffetsRencontre.rdd_part_tete },
{ code: "part+xp", resultat: "succes", description: "Expérience sur réussite particulière", method: EffetsRencontre.experience_particuliere },
{ code: "part+tete", resultat: "succes", description: "Tête de dragon sur particulière", method: EffetsRencontre.rdd_part_tete },
{ code: "part+xp", resultat: "succes", description: "Expérience sur particulière", method: EffetsRencontre.experience_particuliere },
{ code: "seuil", resultat: "succes", description: "Récupération de seuil de rêve", method: EffetsRencontre.regain_seuil },
{ code: "reve-1", resultat: "echec", description: "Perte de 1 point de rêve", method: EffetsRencontre.reve_moins_1 },
@@ -19,7 +19,7 @@ const tableEffets = [
{ code: "aleatoire", resultat: "echec", description: "Déplacement aléatoire", method: EffetsRencontre.deplacement_aleatoire },
{ code: "sort-aleatoire", resultat: "echec", description: "Déclenche un sort en réserve aléatoire", method: EffetsRencontre.sort_aleatoire },
{ code: "rompu", resultat: "echec", description: "Demi-rêve interrompu", method: EffetsRencontre.demireve_rompu },
{ code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon sur échec", method: EffetsRencontre.rdd_echec_queue },
{ code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon", method: EffetsRencontre.rdd_echec_queue },
{ code: "reve+1", resultat: "succes", description: "Gain de 1 point de rêve", method: EffetsRencontre.reve_plus_1 },
{ code: "vie-f", resultat: "echec", description: "Perte de (force) points de vie", method: EffetsRencontre.vie_moins_force },

View File

@@ -34,9 +34,13 @@ export class Misc {
return ((n % m) + m) % m;
}
static inRange(value, min,max){
static inRange(value, min, max) {
if (min > max) {
return Misc.inRange(value, max, min)
}
return Math.max(min, Math.min(value, max))
}
static sum() {
return (a, b) => Number(a) + Number(b);
}
@@ -61,6 +65,10 @@ export class Misc {
return 0;
}
static fakeId(base) {
return (base + foundry.utils.randomID(16)).substring(0, 16)
}
static typeName(type, subType) {
return subType ? game.i18n.localize(`TYPES.${type}.${subType}`)
: '';
@@ -111,6 +119,10 @@ export class Misc {
list.forEach(it => addToObj(obj, it))
return obj;
}
static indexed(list, index = 'index') {
let i = 0;
return list.map(it => { it[index] = i++; return it })
}
static concat(lists) {
return lists.reduce((a, b) => a.concat(b), []);

View File

@@ -1,3 +1,4 @@
import { RDD_CONFIG } from "./constants.js";
import { RdDItemArme } from "./item/arme.js";
import { RdDPossession } from "./rdd-possession.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
@@ -35,7 +36,7 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static dmg(rollData, actor, isEntiteIncarnee = false) {
static dmg(rollData, actor) {
const diff = rollData.diffLibre;
const dmgArme = RdDBonus.dmgArme(rollData.arme, rollData.arme?.system.dommagesReels)
const forceRequise = rollData.arme ? RdDItemArme.valeurMain(rollData.arme.system.force ?? 0, RdDItemArme.getMainAttaque(rollData.competence)) : 0
@@ -47,33 +48,33 @@ export class RdDBonus {
penetration: RdDBonus._peneration(rollData),
dmgTactique: RdDBonus.dmgBonus(rollData.tactique),
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used),
mortalite: RdDBonus._calculMortalite(rollData, isEntiteIncarnee),
dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris?.used),
mortalite: RdDBonus.mortalite(rollData.dmg?.mortalite, rollData.arme?.system.mortalite),
dmgActor: RdDBonus.bonusDmg(actor, rollData.selectedCarac?.label.toLowerCase(), dmgArme),
dmgForceInsuffisante: Math.min(0, actor.getForce() - forceRequise)
}
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante
return dmg;
return dmg
}
static dmgRollV2(rollData, current) {
static dmgRollV2(rollData, attaque) {
const actor = rollData.active.actor
const attaque = current.attaque
const arme = attaque.arme
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommagesArme)
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommages)
const dmg = {
total: 0,
dmgArme: dmgArme,
penetration: arme.penetration(),
penetration: arme?.penetration() ?? 0,
diff: attaque.diff,
dmgTactique: current.tactique?.dmg ?? 0,
dmgParticuliere: 0, // TODO RdDBonus._dmgParticuliere(rollData),
dmgTactique: attaque.tactique?.dmg ?? 0,
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0,
mortalite: RdDBonus.mortalite(current.dmg?.mortalite, arme.system.mortalite, rollData.opponent?.actor?.isEntite()),
mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme?.system.mortalite),
dmgActor: RdDBonus.bonusDmg(actor, attaque.carac.key, dmgArme, attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - (attaque.forceRequise ?? 0)),
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0
}
dmg.isEmpoignade = dmg.mortalite == RDD_CONFIG.encaissement.empoignade
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante + dmg.dmgDiffLibre
return dmg
}
@@ -94,21 +95,14 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static _calculMortalite(rollData, isEntiteIncarnee) {
return RdDBonus.mortalite(rollData.dmg?.mortalite, rollData.arme?.system.mortalite, isEntiteIncarnee)
}
static mortalite(mortaliteSelect, mortaliteArme, isEntiteIncarnee) {
return isEntiteIncarnee ? "entiteincarnee"
: mortaliteSelect
?? mortaliteArme
?? "mortel";
static mortalite(mortaliteSelect, mortaliteArme) {
return mortaliteSelect ?? mortaliteArme ?? RDD_CONFIG.encaissement.mortel
}
/* -------------------------------------------- */
static dmgArme(arme, dommagesMain) {
static dmgArme(arme, dommages) {
if (arme) {
let dmgBase = dommagesMain ?? Number(arme.system.dommages ?? 0);
let dmgBase = dommages ?? Number(arme.system.dommages ?? 0);
//Le bonus dégats magiques ne peut pas faire dépasser le bonus de l'arme (cf p.278)
return dmgBase + Math.min(dmgBase, arme.system.magique ? arme.system.ecaille_efficacite : 0);
}

View File

@@ -1,5 +1,5 @@
import { ChatUtility } from "./chat-utility.js";
import { ENTITE_BLURETTE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { HIDE_DICE, renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
@@ -10,15 +10,15 @@ import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { Targets } from "./targets.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDItemArme } from "./item/arme.js";
import { EMPOIGNADE, RdDItemArme } from "./item/arme.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDInitiative } from "./initiative.mjs";
import { MAP_PHASE, RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs";
import { PART_DEFENSE } from "./roll/roll-part-defense.mjs";
import { RollDialogAdapter } from "./roll/roll-dialog-adapter.mjs";
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js";
import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs";
import { RollBasicParts } from "./roll/roll-basic-parts.mjs";
/* -------------------------------------------- */
const premierRoundInit = [
@@ -56,6 +56,12 @@ export class RdDCombatManager extends Combat {
}
}
static getCombatant(actorId, tokenId) {
return game.combat.combatants.find(it => it.actor.id == actorId &&
it.token.id == tokenId
);
}
/* -------------------------------------------- */
async nextRound() {
await this.finDeRound();
@@ -67,7 +73,7 @@ export class RdDCombatManager extends Combat {
if (Misc.isFirstConnectedGM()) {
await this.finDeRound({ terminer: true })
ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
game.messages.filter(m => ChatUtility.getMessageData(m, 'rollData') != undefined && ChatUtility.getMessageData(m, 'rollData') != undefined)
.forEach(it => it.delete())
RdDEmpoignade.deleteAllEmpoignades()
}
@@ -106,14 +112,18 @@ export class RdDCombatManager extends Combat {
return combatant.actor
}
static calculAjustementInit(actor, arme) {
const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
const etatGeneral = actor.getEtatGeneral() ?? 0
return efficacite + etatGeneral
static bonusArme(arme) {
return (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
}
/************************************************************************************/
static etatGeneral(actor) {
return actor.getEtatGeneral() ?? 0;
}
/** ***********************************************************************************
* @override lance l'initiative de plusieurs combattants (tous/ous les PNJs) en une fois
*/
async rollInitiative(ids, messageOptions = {}) {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, messageOptions)
ids = typeof ids === "string" ? [ids] : ids
@@ -121,18 +131,17 @@ export class RdDCombatManager extends Combat {
return this
}
async rollInitRdD(id, formula, messageOptions = {}) {
async rollInitRdD(id, formule, messageOptions = {}) {
const combatant = this.combatants.get(id);
const actor = RdDCombatManager.getActorCombatant(combatant)
if (actor) {
const rollFormula = formula ?? RdDCombatManager.getFirstInitRollFormula(actor)
const roll = combatant.getInitiativeRoll(rollFormula);
if (!roll.total) {
await roll.evaluate();
}
const total = Math.max(roll.total, 0.00);
console.log("Compute init for", rollFormula, roll, total, combatant);
await this.updateEmbeddedDocuments("Combatant", [{ _id: combatant._id || combatant.id, initiative: total }]);
formule = formule ?? RdDCombatManager.getFirstInitRollFormula(actor)
const init = await RdDInitiative.roll(formule)
await this.updateEmbeddedDocuments("Combatant", [{
_id: combatant._id || combatant.id,
initiative: init.init, 'system.init': init
}])
// Send a chat message
let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode");
@@ -144,10 +153,10 @@ export class RdDCombatManager extends Combat {
alias: combatant.token?.name,
sound: CONFIG.sounds.dice,
},
flavor: `${combatant.token?.name} a fait son jet d'Initiative (${messageOptions.info})<br>`
flavor: `${combatant.token?.name} a une initiatyive de ${init.value} : ${messageOptions.info}<br>`
},
messageOptions);
roll.toMessage(messageData, { rollMode, create: true });
init.roll.toMessage(messageData, { rollMode, create: true });
RdDCombatManager.processPremierRoundInit();
}
@@ -159,16 +168,11 @@ export class RdDCombatManager extends Combat {
if (actions.length > 0) {
const action = actions[0]
const init = RdDCombatManager.getInitData(actor, action)
const ajustement = RdDCombatManager.calculAjustementInit(actor, action)
return RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement);
const ajustement = RdDCombatManager.bonusArme(action.arme) + RdDCombatManager.etatGeneral(actor)
return RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement);
}
let ajustement = RdDCombatManager.calculAjustementInit(actor, undefined);
return RdDCombatManager.formuleInitiative(2, 10, 0, ajustement);
}
static formuleInitiative(rang, carac, niveau, bonusMalus) {
return `${rang} +( (${RdDInitiative.calculInitiative(niveau, carac, bonusMalus)} )/100)`;
return RdDInitiative.formule(MAP_PHASE['autre'], 10, 0, actor.getEtatGeneral() ?? 0);
}
/* -------------------------------------------- */
@@ -180,7 +184,6 @@ export class RdDCombatManager extends Combat {
for (let combatant of game.combat.combatants) {
if (combatant.initiativeData?.arme?.type == "arme") {
// TODO: get init data premier round
const initiativeData = combatant.initiativeData;
const action = combatant.initiativeData.arme;
const fromArme = Grammar.toLowerCaseNoAccentNoSpace(action.system.initpremierround)
const initData = premierRoundInit.find(it => fromArme.includes(initData.pattern))
@@ -200,10 +203,27 @@ export class RdDCombatManager extends Combat {
}
/* -------------------------------------------- */
static incDecInit(combatantId, incDecValue) {
const combatant = game.combat.combatants.get(combatantId);
let initValue = combatant.initiative + incDecValue;
game.combat.setInitiative(combatantId, initValue);
static applyInitiativeCommand(combatantId, command, commandValue) {
switch (command) {
case 'delta': return RdDCombatManager.incDecInit(combatantId, commandValue);
case 'autre': return RdDCombatManager.rollInitiativeAction(combatantId,
{ name: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } });
}
}
static async incDecInit(combatantId, incDecValue) {
const combatant = game.combat.combatants.get(combatantId)
if (combatant?.initiative && incDecValue != 0) {
const value = combatant.system.init.value + incDecValue
const newInit = combatant.initiative + incDecValue / 100;
await game.combat.updateEmbeddedDocuments("Combatant", [{
_id: combatantId,
initiative: newInit,
'system.init.value': value,
'system.init.init': newInit,
}])
}
}
/* -------------------------------------------- */
@@ -220,56 +240,42 @@ export class RdDCombatManager extends Combat {
}
}
options = [
{ name: "Incrémenter initiative", condition: true, icon: '<i class="fa-solid fa-plus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +0.01); } },
{ name: "Décrémenter initiative", condition: true, icon: '<i class="fa-solid fa-minus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), -0.01); } }
{ name: "Incrémenter initiative", condition: true, icon: '<i class="fa-solid fa-plus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +1); } },
{ name: "Décrémenter initiative", condition: true, icon: '<i class="fa-solid fa-minus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), -1); } }
].concat(options);
}
/* -------------------------------------------- */
static async rollInitiativeAction(combatantId, action) {
const combatant = game.combat.combatants.get(combatantId)
const actor = RdDCombatManager.getActorCombatant(combatant)
if (actor == undefined) { return [] }
combatant.initiativeData = { arme: action } // pour reclasser l'init au round 0
if (actor == undefined) { return }
const init = RdDCombatManager.getInitData(actor, action)
const ajustement = RdDCombatManager.calculAjustementInit(actor, action.arme)
const rollFormula = RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement);
const ajustement = RdDCombatManager.bonusArme(actor, action.arme) + RdDCombatManager.etatGeneral(actor)
const formule = RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement);
await game.combat.rollInitRdD(combatantId, rollFormula, init);
combatant.initiativeData
await game.combat.rollInitRdD(combatantId, formule, init);
combatant.initiativeData = { action, formule } // pour reclasser l'init au round 0
}
static getInitData(actor, action) {
if (actor.getSurprise() == "totale") { return { offset: -1, info: "Surprise Totale", carac: 0, niveau: 0 } }
if (actor.getSurprise() == "demi") { return { offset: 0, info: "Demi Surprise", carac: 0, niveau: 0 } }
if (action.action == 'autre') { return { offset: 2, info: "Autre Action", carac: 0, niveau: 0 } }
if (action.action == 'possession') { return { offset: 10, info: "Possession", carac: actor.getReveActuel(), niveau: 0 } }
if (action.action == 'haut-reve') { return { offset: 9, info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } }
if (actor.getSurprise() == "totale") { return { phase: MAP_PHASE['totale'], info: "Surprise Totale", carac: 0, niveau: 0 } }
if (actor.getSurprise() == "demi") { return { phase: MAP_PHASE['demi'], info: "Demi Surprise", carac: 0, niveau: 0 } }
if (action.action == 'autre') { return { phase: MAP_PHASE['autre'], info: "Autre Action", carac: 0, niveau: 0 } }
if (action.action == 'possession') { return { phase: MAP_PHASE['possession'], info: "Possession", carac: actor.getReveActuel(), niveau: 0 } }
if (action.action == 'haut-reve') { return { phase: MAP_PHASE['draconic'], info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } }
const comp = action.comp
return {
offset: RdDCombatManager.initOffset(comp?.system.categorie, action.arme),
info: action.name + " / " + comp.name,
phase: RdDInitiative.phaseArme(comp?.system.categorie, action.arme),
info: action.label,
carac: actor.getCaracInit(comp),
niveau: comp?.system.niveau ?? (['(lancer)', '(tir)'].includes(action.main) ? -8 : -6)
}
}
static initOffset(categorie, arme) {
switch (categorie) {
case "tir": return 8
case "lancer": return 7
default:
switch (arme.system.cac) {
case "empoignade": return 3
case "pugilat": return 4
case "naturelle": return 4
default: return 5
}
}
}
/* -------------------------------------------- */
static displayInitiativeMenu(html, combatantId) {
const combatant = game.combat.combatants.get(combatantId)
@@ -297,13 +303,8 @@ export class RdDCombatManager extends Combat {
? possessions
: actor.listActions({ isEquipe: true })
for (let index = 0; index < actions.length; index++) {
actions[index].index = index
}
return actions
return Misc.indexed(actions)
}
}
/* -------------------------------------------- */
@@ -383,8 +384,13 @@ export class RdDCombat {
let defenderToken = canvas.tokens.get(msg.defenderToken.id)
if (defenderToken && Misc.isFirstConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id)
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme)
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
rddCombat?.removeChatMessageActionsPasseArme(msg.paramChatDefense.attackerRoll.passeArme)
if (msg.defenderRoll.v2) {/* TODO: delete roll V1 */
RollDialog.loadRollData(msg.paramChatDefense)
rddCombat?._chatMessageDefenseV2(msg.paramChatDefense)
} else {
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
}
}
}
@@ -402,7 +408,6 @@ export class RdDCombat {
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of [
'.button-defense',
'.button-parade',
'.button-esquive',
'.button-encaisser',
@@ -464,8 +469,8 @@ export class RdDCombat {
/* -------------------------------------------- */
async onEvent(button, event) {
const chatMessage = ChatUtility.getChatMessage(event);
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll');
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'rollData');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'rollData');
console.log('RdDCombat', attackerRoll, defenderRoll);
const armeParadeId = event.currentTarget.attributes['data-armeid']?.value;
@@ -474,8 +479,6 @@ export class RdDCombat {
switch (button) {
case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '.button-defense': return this.defenseV2(attackerRoll);
case '.button-parade': return this.parade(attackerRoll, armeParadeId);
case '.button-esquive': return this.esquive(attackerRoll, compId, competence);
case '.button-encaisser': return this.encaisser(attackerRoll, defenderRoll);
@@ -560,7 +563,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static isEchecTotal(rollData) {
if (rollData.ids /* roll V2*/) {
if (rollData.v2 /* roll V2*/) {
// TODO: en cas de demi-surprise à l'attaque, tout échec est un echec total.
// TODO: en cas de demi-surprise en défense, pas de changement à la règle de base
return rollData.rolled.isETotal
@@ -574,7 +577,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static isParticuliere(rollData) {
if (rollData.ids /* roll V2*/) {
if (rollData.v2 /* roll V2*/) {
return rollData.rolled.isPart
}
if (rollData.attackerRoll || !rollData.ajustements.surprise.used) {
@@ -585,7 +588,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static isReussite(rollData) {
if (rollData.ids /* roll V2*/) {
if (rollData.v2 /* roll V2*/) {
return rollData.rolled.isSuccess
}
if (!rollData.ajustements.surprise.used) {
@@ -601,7 +604,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async proposerAjustementTirLancer(rollData) {
if (['tir', 'lancer'].includes(rollData.competence.system.categorie)) {
if (this.defender.isEntite([ENTITE_BLURETTE])) {
if (this.defender.isEntiteBlurette()) {
ChatMessage.create({
content: `<strong>La cible est une blurette, l'arme à distance sera perdue dans le blurêve`,
whisper: ChatUtility.getGMs()
@@ -665,12 +668,95 @@ export class RdDCombat {
return { msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3 };
}
async attaqueV2() {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return
}
await this.doRollAttaque({
ids: {
actorId: this.attackerId,
actorTokenId: this.attackerTokenId,
opponentId: this.defender.id,
opponentTokenId: this.defenderTokenId,
},
type: { allowed: ['attaque'], current: 'attaque' },
passeArme: foundry.utils.randomID(16),
})
}
async doRollAttaque(rollData, callbacks = []) {
// TODO V2 await this.proposerAjustementTirLancer(rollData)
await RollDialog.create(rollData, {
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
async (roll) => await this.onAttaqueV2(roll),
...callbacks
]
})
}
async onAttaqueV2(attackerRoll) {
if (!this.defender || !attackerRoll.rolled.isSuccess || attackerRoll.particulieres?.length > 1) {
return
}
if (!await this.attacker.accorder(this.defender, 'avant-defense')) {
return;
}
RollDialog.loadRollData(attackerRoll)
const surpriseDefender = this.defender.getSurprise(true);
const paramChatDefense = {
attackerRoll: attackerRoll,
isPossession: this.isPossession(attackerRoll),
defender: this.defender,
attacker: this.attacker,
attackerId: this.attackerId,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
surprise: surpriseDefender,
}
if (Misc.isFirstConnectedGM()) {
await this._chatMessageDefenseV2(paramChatDefense);
}
else {
this._socketSendMessageDefense(paramChatDefense, {});
}
}
async _chatMessageDefenseV2(paramDemandeDefense) {
const attackerRoll = paramDemandeDefense.attackerRoll;
RollBasicParts.loadSurprises(attackerRoll)
attackerRoll.dmg = RdDBonus.dmgRollV2(attackerRoll, attackerRoll.current.attaque)
const defenseData = RollBasicParts.prepareDefense(attackerRoll)
const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', defenseData)
});
// flag pour garder les jets d'attaque/defense
ChatUtility.setMessageData(choixDefense, 'demande-defense', true)
ChatUtility.setMessageData(choixDefense, 'rollData', {
ids: defenseData.ids,
attackerRoll: RollDialog.saveParts(attackerRoll),
passeArme: defenseData.passeArme
})
}
/* -------------------------------------------- */
async attaque(competence, arme) {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return
}
if (arme.system.cac == 'empoignade') {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
return this.attacker.rollCompetence(competence.name, { arme: arme })
}
if (arme.system.cac == EMPOIGNADE) {
RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender)
return
}
@@ -714,7 +800,7 @@ export class RdDCombat {
essais: {}
};
if (this.attacker.isCreatureEntite()) {
if (this.attacker.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(rollData);
}
else if (arme) {
@@ -723,9 +809,9 @@ export class RdDCombat {
}
else {
// sans armes: à mains nues
rollData.arme = RdDItemArme.corpsACorps(this.attacker)
rollData.arme = RdDItemArme.pugilat(this.attacker)
rollData.arme.system.niveau = competence.system.niveau
rollData.arme.system.initiative = RdDInitiative.calculInitiative(competence.system.niveau, this.attacker.system.carac['melee'].value);
rollData.arme.system.initiative = RdDInitiative.getRollInitiative(this.attacker.system.carac['melee'].value, competence.system.niveau);
}
return rollData;
}
@@ -776,14 +862,14 @@ export class RdDCombat {
passeArme: rollData.passeArme
})
});
ChatUtility.setMessageData(choixParticuliere, 'attacker-roll', rollData);
ChatUtility.setMessageData(choixParticuliere, 'rollData', rollData);
}
/* -------------------------------------------- */
async _onAttaqueNormale(attackerRoll) {
console.log("RdDCombat.onAttaqueNormale >>>", attackerRoll);
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite());
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker);
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
attackerRoll.show = {
cible: this.defender?.getAlias() ?? 'la cible',
@@ -802,7 +888,10 @@ export class RdDCombat {
/* -------------------------------------------- */
isPossession(attackerRoll) {
return attackerRoll.selectedCarac.label.toLowerCase() == 'possession';
const carac = attackerRoll.v2
? attackerRoll.current.carac?.label
: attackerRoll.selectedCarac.label
return carac?.toLowerCase() == 'possession';
}
/* -------------------------------------------- */
@@ -852,10 +941,10 @@ export class RdDCombat {
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', paramDemandeDefense),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense-v1.hbs', paramDemandeDefense),
});
// flag pour garder les jets d'attaque/defense
ChatUtility.setMessageData(choixDefense, 'defender-roll', defenderRoll);
ChatUtility.setMessageData(choixDefense, 'rollData', defenderRoll);
}
/* -------------------------------------------- */
@@ -869,7 +958,7 @@ export class RdDCombat {
defenderToken: this.defenderToken,
defenderRoll: defenderRoll,
paramChatDefense: paramChatDefense,
rollMode: true
rollMode: true,
}
});
}
@@ -906,7 +995,7 @@ export class RdDCombat {
essais: attackerRoll.essais
})
});
ChatUtility.setMessageData(choixEchecTotal, 'attacker-roll', attackerRoll);
ChatUtility.setMessageData(choixEchecTotal, 'rollData', attackerRoll);
}
/* -------------------------------------------- */
@@ -966,8 +1055,9 @@ export class RdDCombat {
dialog.render(true);
}
async defenseV2(attackerRoll) {
async defenseV2(attackerRoll, callbacks = []) {
// this._prepareParade(attackerRoll, arme, competence);
RollDialog.loadRollData(attackerRoll)
await this.doRollDefense({
ids: {
actorId: this.defender.id,
@@ -975,19 +1065,15 @@ export class RdDCombat {
opponentTokenId: this.attackerTokenId,
opponentId: this.attackerId,
},
type: { allowed: ['defense'], current: 'defense' },
attackerRoll: RollDialogAdapter.mapActionAttaque(attackerRoll),
type: { allowed: [ROLL_TYPE_DEFENSE], current: ROLL_TYPE_DEFENSE },
attackerRoll: attackerRoll,
passeArme: attackerRoll.passeArme,
})
}, callbacks)
}
async doRollDefense(rollData, callbacks = []) {
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
},
customChatMessage: true,
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
async (roll) => {
this.removeChatMessageActionsPasseArme(roll.passeArme);
@@ -1021,7 +1107,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(defenderRoll);
}
@@ -1034,22 +1120,17 @@ export class RdDCombat {
if (RdDCombat.isReussite(rollData)) {
if (isParade) {
await this.computeDeteriorationArme(rollData)
if (RdDCombat.isParticuliere(rollData)) {
await this.infoAttaquantDesarme(rollData)
}
}
if (RdDCombat.isParticuliere(rollData)) {
await this._onDefenseParticuliere(rollData, isEsquive)
}
}
this.removeChatMessageActionsPasseArme(rollData.passeArme)
}
async _onDefenseParticuliere(rollData, isEsquive) {
if (isEsquive) {
ChatUtility.createChatWithRollMode(
{ content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" },
this.defender)
}
else if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) {
async infoAttaquantDesarme(rollData) {
if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) {
// TODO: attaquant doit jouer résistance et peut être désarmé p132
ChatUtility.createChatWithRollMode(
{ content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` },
@@ -1153,7 +1234,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(rollData);
}
return rollData;

View File

@@ -19,6 +19,7 @@ import { TMRUtility } from "./tmr-utility.js";
import { DialogFatigueVoyage } from "./voyage/dialog-fatigue-voyage.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { renderTemplate } from "./constants.js";
const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
@@ -460,12 +461,7 @@ export class RdDCommands {
async supprimerSignesDraconiquesEphemeres() {
if (game.user.isGM) {
game.actors.forEach(actor => {
const ephemeres = actor.items.filter(item => item.type = 'signedraconique' && item.system.ephemere);
if (ephemeres.length > 0) {
actor.deleteEmbeddedDocuments("Item", ephemeres.map(item => item.id));
}
});
game.actors.forEach(actor => actor.supprimerSignesDraconiques(it => it.system.ephemere))
}
else {
ui.notifications.warn("Seul le MJ est autorisé à utiliser la commande /signe");

View File

@@ -1,9 +1,11 @@
import { STATUSES } from "./settings/status-effects.js";
import { ITEM_TYPES } from "./constants.js";
import { ITEM_TYPES, renderTemplate } from "./constants.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDRoll } from "./rdd-roll.js";
import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs";
import { MAP_PHASE } from "./initiative.mjs";
import { RdDCombatManager } from "./rdd-combat.js";
/* -------------------------------------------- */
export class RdDEmpoignade {
@@ -12,6 +14,49 @@ export class RdDEmpoignade {
static init() {
}
/* -------------------------------------------- */
static isCombatantEmpoignade(actorId, tokenId) {
const combatant = RdDCombatManager.getCombatant(actorId, tokenId)
return MAP_PHASE.empoignade.rang == combatant?.system.init.rang
}
static async ajustementEmpoignade(attacker, defender, adjust = 1) {
const empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const empId = empoignade?.system.empoignadeid ?? foundry.utils.randomID(16)
if (empoignade) {
if (empoignade.system.empoigneurid == defender.id) {
adjust = - adjust
}
empoignade.system.pointsemp += adjust
await RdDEmpoignade.$updateEtatEmpoignade(empoignade, attacker, defender)
}
else {
await RdDEmpoignade.$createEtatEmpoignade({
name: `Empoignade de ${attacker.name} sur ${defender.name}`,
type: ITEM_TYPES.empoignade,
system: {
description: "",
empoignadeid: empId,
empoigneurid: attacker.id,
empoigneid: defender.id,
pointsemp: adjust,
empoigneurname: attacker.name,
empoignename: defender.name
}
}, attacker, defender)
}
const result = RdDEmpoignade.getEmpoignadeById(defender, empId);
const defGrappled = result.system.pointsemp == (result.system.empoigneid == defender.id ? 2 : -2)
const attGrappled = result.system.pointsemp == (result.system.empoigneurid == attacker.id ? -2 : 2)
const grappling = Math.abs(result.system.pointsemp) > 0
await defender.setEffect(STATUSES.StatusGrappling, grappling && !defGrappled)
await attacker.setEffect(STATUSES.StatusGrappling, grappling && !attGrappled)
await defender.setEffect(STATUSES.StatusGrappled, defGrappled)
await attacker.setEffect(STATUSES.StatusGrappled, attGrappled)
return result
}
/* -------------------------------------------- */
static registerChatCallbacks(html) {
$(html).on("click", '.defense-empoignade-cac', event => {
@@ -184,7 +229,7 @@ export class RdDEmpoignade {
selectedCarac: attacker.system.carac.melee,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
if (attacker.isCreatureEntite()) {
if (attacker.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(rollData)
}
if (empoignade.system.pointsemp >= 2) {
@@ -237,10 +282,7 @@ export class RdDEmpoignade {
if (rollData.rolled.isSuccess && isNouvelle) {
const objectEmpoignade = rollData.empoignade.toObject();
// Creer l'empoignade sur attaquant/defenseur
attacker.creerObjetParMJ(objectEmpoignade);
defender.creerObjetParMJ(objectEmpoignade);
RdDEmpoignade.$createEtatEmpoignade(rollData.empoignade)
}
rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
@@ -259,7 +301,7 @@ export class RdDEmpoignade {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
if (!empoignade) {
ui.notifications.warn("Une erreur s'est produite : Aucune empoignade trouvée !!")
@@ -317,18 +359,33 @@ export class RdDEmpoignade {
}
/* -------------------------------------------- */
static async $updateEtatEmpoignade(empoignade) {
console.log("UPDATE Empoignade", empoignade)
static async $createEtatEmpoignade(empoignade) {
console.log("CREATE Empoignade", empoignade)
let defender = game.actors.get(empoignade.system.empoigneid)
let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
let update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol }
await defender.updateEmbeddedDocuments('Item', [update])
let attacker = game.actors.get(empoignade.system.empoigneurid)
emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol }
await attacker.updateEmbeddedDocuments('Item', [update])
// Creer l'empoignade sur attaquant/defenseur
await attacker.creerObjetParMJ(empoignade)
await defender.creerObjetParMJ(empoignade)
}
/* -------------------------------------------- */
static async $updateEtatEmpoignade(empoignade, attacker, defender) {
console.log("UPDATE Empoignade", empoignade)
const belligerants = [
attacker ?? game.actors.get(empoignade.system.empoigneurid),
defender ?? game.actors.get(empoignade.system.empoigneid)]
await Promise.all(
belligerants.map(async belligerant => {
const emp = RdDEmpoignade.getEmpoignadeById(belligerant, empoignade.system.empoignadeid)
return await belligerant.updateEmbeddedDocuments('Item', [{
_id: emp._id,
"system.pointsemp": empoignade.system.pointsemp,
"system.ausol": empoignade.system.ausol
}])
}))
}
/* -------------------------------------------- */
@@ -347,7 +404,7 @@ export class RdDEmpoignade {
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
empoignade.system.ausol = true
await this.$updateEtatEmpoignade(empoignade)
@@ -366,7 +423,7 @@ export class RdDEmpoignade {
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
await defender.setEffect(STATUSES.StatusProne, true);
await this.$deleteEmpoignade(empoignade)
@@ -382,7 +439,7 @@ export class RdDEmpoignade {
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
//console.log("Perte d'endurance :!!!", perteMode)
let endValue = defender.system.sante.endurance.value
@@ -423,9 +480,17 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async createEmpoignade(attacker, defender) {
return await Item.create({
name: "Empoignade en cours de " + attacker.name + ' sur ' + defender.name,
type: 'empoignade',
system: { description: "", empoignadeid: foundry.utils.randomID(16), compteempoigne: 0, empoigneurid: attacker.id, empoigneid: defender.id, ptsemp: 0, empoigneurname: attacker.name, empoignename: defender.name }
name: "Empoignade de " + attacker.name + ' sur ' + defender.name,
type: ITEM_TYPES.empoignade,
system: {
description: "",
empoignadeid: foundry.utils.randomID(16),
empoigneurid: attacker.id,
empoigneid: defender.id,
pointsemp: 0,
empoigneurname: attacker.name,
empoignename: defender.name
}
},
{
temporary: true

View File

@@ -1,4 +1,4 @@
import { ATTAQUE_TYPE, RdDItemArme } from "./item/arme.js";
import { ATTAQUE_TYPE, EMPOIGNADE, PUGILAT, RdDItemArme } from "./item/arme.js";
import { ITEM_TYPES } from "./constants.js";
export class RdDHotbar {
@@ -28,8 +28,8 @@ export class RdDHotbar {
return ' ' + maniement
case 'tir': return ' (tir)';
case 'lancer': return ' (lancer)';
case 'pugilat': return ' (pugilat)';
case 'empoignade': return ' (empoignade)';
case PUGILAT: return ' (pugilat)';
case EMPOIGNADE: return ' (empoignade)';
}
return ''
}
@@ -63,8 +63,8 @@ export class RdDHotbar {
case ITEM_TYPES.competence:
await this.createItemMacro(item, slot++, 'competence')
if (item.isCorpsACorps()) {
await this.createItemMacro(item, slot++, 'pugilat')
await this.createItemMacro(item, slot++, 'empoignade')
await this.createItemMacro(item, slot++, PUGILAT)
await this.createItemMacro(item, slot++, EMPOIGNADE)
}
else if (item.isCompetenceArme()) {
ui.notifications.info(`${item.name} est une compétence d'arme, la macro n'est pas liée à un arme.<br>
@@ -121,9 +121,9 @@ export class RdDHotbar {
case ITEM_TYPES.competence:
if (item.isCorpsACorps()) {
switch (categorieArme) {
case 'pugilat':
return actor.rollArme(RdDItemArme.corpsACorps(actor));
case 'empoignade':
case PUGILAT:
return actor.rollArme(RdDItemArme.pugilat(actor));
case EMPOIGNADE:
return actor.rollArme(RdDItemArme.empoignade(actor));
}
}

View File

@@ -1,4 +1,5 @@
import { ChatUtility } from "./chat-utility.js"
import { renderTemplate } from "./constants.js"
const vents = [
{ min: 0, max: 0, valeur: 'Calme' },

View File

@@ -1,5 +1,6 @@
import { RdDBaseActor } from "./actor/base-actor.js";
import { ChatUtility } from "./chat-utility.js";
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";

View File

@@ -95,7 +95,7 @@ export class RdDPossession {
static selectCompetenceDraconicOuPossession(rollData, rollingActor) {
rollData.competence = rollingActor.getDraconicOuPossession();
if (rollingActor.isCreatureEntite()) {
if (rollingActor.isCreatureOuEntite()) {
const carac = rollingActor.system.carac
rollData.carac = carac
rollData.competence.system.defaut_carac = 'reve'

View File

@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
@@ -15,17 +16,20 @@ const levelDown = [
];
const levelImpossible = { score: 0, norm: 0, sign: 0, part: 0, epart: 0, etotal: 1 };
const reussiteNormale = { code: "norm", isPart: false, isSign: false, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 1, ptQualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.norm) };
const reussiteSignificative = { code: "sign", isPart: false, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 2, ptQualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) };
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 reussites = [
{ code: "etotal", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: -4, ptQualite: -6, quality: "Echec total", condition: (target, roll) => roll >= target.etotal && roll <= 100 },
{ code: "epart", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: false, ptTache: -2, ptQualite: -4, quality: "Echec particulier", condition: (target, roll) => (roll >= target.epart && roll < target.etotal) },
{ code: "echec", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Echec normal", condition: (target, roll) => (roll > target.norm && roll < target.etotal) },
{ code: "norm", isPart: false, isSign: false, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 1, ptQualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.norm) },
{ code: "sign", isPart: false, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 2, ptQualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) },
reussiteNormale,
reussiteSignificative,
{ code: "part", isPart: true, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 3, ptQualite: 2, quality: "Réussite Particulière!", condition: (target, roll) => (roll > 0 && roll <= target.part) },
{ code: "error", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: 0, ptQualite: 0, quality: "Jet de dés invalide", condition: (target, roll) => (roll <= 0 || roll > 100) }
];
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
/* -------------------------------------------- */
const CARAC_MAXIMUM_RESOLUTION = 40;
/* -------------------------------------------- */
@@ -66,9 +70,9 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static _computeCell(niveau, percentage) {
static _computeCell(level, percentage) {
return {
niveau: niveau,
level: level,
score: percentage,
norm: Math.min(99, percentage),
sign: this._reussiteSignificative(percentage),
@@ -155,6 +159,11 @@ export class RdDResolutionTable {
}
}
/* -------------------------------------------- */
static replaceParticuliereDemiSurprise(chances) {
foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'part'), { overwrite: true });
}
/* -------------------------------------------- */
static significativeRequise(chances) {
chances.roll = Math.min(chances.part + 1, chances.sign)
@@ -188,9 +197,10 @@ export class RdDResolutionTable {
static computeReussite(chances, roll, diviseur) {
const reussite = reussites.find(x => x.condition(chances, roll))
if (diviseur > 1 && reussite.isSuccess) {
if (chances > roll * diviseur) {
if (chances.norm < roll * diviseur) {
return reussiteInsuffisante
}
return reussiteSignificative
}
return reussite
}
@@ -236,7 +246,7 @@ export class RdDResolutionTable {
maxCarac = Math.min(maxCarac, minCarac + 20);
minLevel = Math.max(minLevel, -10);
maxLevel = Math.max(Math.min(maxLevel, 30), minLevel + colonnes);
return await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.hbs', {
return await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.hbs', {
carac: carac,
difficulte: level,
min: minLevel,

View File

@@ -1,4 +1,4 @@
import { ENTITE_BLURETTE, ENTITE_INCARNE } from "./constants.js";
import { ENTITE_NONINCARNE, RDD_CONFIG, renderTemplate } from "./constants.js";
import { RdDUtility } from "./rdd-utility.js";
/**
@@ -16,34 +16,29 @@ export class RdDEncaisser extends Dialog {
/* -------------------------------------------- */
constructor(html, actor) {
let dialogConf = {
if (actor.isEntiteNonIncarnee([ENTITE_NONINCARNE])) {
throw `${actor.name} est une entité non incarnée et ne peut pas subnir de dommages`
}
const dialogConf = {
title: "Jet d'Encaissement",
content: html,
}
if (!actor.isEntite()) {
dialogConf.default = "mortel";
dialogConf.buttons = {
"mortel": { label: "Mortel", callback: html => this.performEncaisser("mortel") },
"non-mortel": { label: "Non-mortel", callback: html => this.performEncaisser("non-mortel") },
"sonne": { label: "Sonné", callback: html => this.actor.setSonne() },
};
}
else if (actor.isEntite([ENTITE_BLURETTE, ENTITE_INCARNE])) {
dialogConf.default = "entiteincarnee"
dialogConf.buttons = {
"entiteincarnee": { label: "Entité incarnée", callback: html => this.performEncaisser("entiteincarnee") }
default: RDD_CONFIG.encaissement.mortel,
buttons: {
[RDD_CONFIG.encaissement.mortel]: { label: "Mortel", callback: html => this.performEncaisser(RDD_CONFIG.encaissement.mortel) },
}
}
let dialogOptions = {
classes: ["rdd-roll-dialog"],
width: 320,
height: 'fit-content'
if (!actor.isEntite()) {
dialogConf.buttons[RDD_CONFIG.encaissement.nonmortel] = { label: "Non-mortel", callback: html => this.performEncaisser(RDD_CONFIG.encaissement.nonmortel) }
dialogConf.buttons["sonne"] = { label: "Sonné", callback: html => this.actor.setSonne() }
}
// Select proper roll dialog template and stuff
super(dialogConf, dialogOptions);
super(dialogConf, {
classes: ["rdd-roll-dialog"],
width: 320,
height: 'fit-content'
});
this.actor = actor;
this.modifier = 0;

View File

@@ -25,7 +25,7 @@ export class RdDRollDialogEthylisme extends Dialog {
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.bringToTop();
this.bringToFront();
this.html.find(".force-alcool").change((event) => {
this.rollData.forceAlcool = Misc.toInt(event.currentTarget.value);

View File

@@ -1,5 +1,7 @@
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRollResult } from "./rdd-roll-result.js";
const titleTableDeResolution = 'Table de résolution';
/**
@@ -20,7 +22,7 @@ export class RdDRollResolutionTable extends Dialog {
RdDRollResolutionTable.resolutionTable.render(true);
}
else{
RdDRollResolutionTable.resolutionTable.bringToTop();
RdDRollResolutionTable.resolutionTable.bringToFront();
}
}
@@ -68,7 +70,7 @@ export class RdDRollResolutionTable extends Dialog {
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.bringToTop();
this.bringToFront();
this.html.find("[name='diffLibre']").val(Misc.toInt(this.rollData.diffLibre));
@@ -98,14 +100,14 @@ export class RdDRollResolutionTable extends Dialog {
async onLancer() {
await RdDResolutionTable.rollData(this.rollData);
console.log("RdDRollResolutionTable -=>", this.rollData, this.rollData.rolled);
await RdDResolutionTable.displayRollData(this.rollData);
await RdDRollResult.displayRollData(this.rollData);
}
/* -------------------------------------------- */
async onLancerFermer() {
await RdDResolutionTable.rollData(this.rollData);
console.log("RdDRollResolutionTable -=>", this.rollData, this.rollData.rolled);
await RdDResolutionTable.displayRollData(this.rollData);
await RdDRollResult.displayRollData(this.rollData);
}
/* -------------------------------------------- */

View File

@@ -1,4 +1,5 @@
import { ChatUtility } from "./chat-utility.js";
import { renderTemplate } from "./constants.js";
export class RdDRollResult {
@@ -13,6 +14,6 @@ export class RdDRollResult {
static async buildRollDataHtml(rollData, template = 'chat-resultat-general.hbs') {
rollData.show = rollData.show || {};
return await foundry.applications.handlebars.renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
}
}

View File

@@ -1,4 +1,4 @@
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RollDataAjustements } from "./rolldata-ajustements-v1.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDItemSort } from "./item-sort.js";
@@ -8,8 +8,8 @@ import { RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { Grammar } from "./grammar.js";
import { ACTOR_TYPES } from "./constants.js";
import { RdDUtility } from "./rdd-utility.js";
import { ACTOR_TYPES, RDD_CONFIG, renderTemplate } from "./constants.js";
import { EMPOIGNADE } from "./item/arme.js";
/**
* Extend the base Dialog entity to select roll parameters
@@ -23,7 +23,7 @@ export class RdDRoll extends Dialog {
RdDRoll._ensureCorrectAction(action);
RdDRoll._setDefaultOptions(actor, rollData);
const html = await foundry.applications.handlebars.renderTemplate(dialogConfig.html, rollData);
const html = await renderTemplate(dialogConfig.html, rollData);
let options = { classes: ["rdd-roll-dialog"], width: 650, height: 'fit-content', 'z-index': 99999, close: html => { } };
if (dialogConfig.close) {
@@ -128,7 +128,7 @@ export class RdDRoll extends Dialog {
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.bringToTop();
this.bringToFront();
console.log('RdDRoll.activateListeners', this.rollData);
@@ -206,7 +206,7 @@ export class RdDRoll extends Dialog {
this.updateRollResult(html);
});
this.html.find("input.check-mortalite").change((event) => {
this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel";
this.rollData.dmg.mortalite = event.currentTarget.checked ? RDD_CONFIG.encaissement.nonmortel : RDD_CONFIG.encaissement.mortel;
this.updateRollResult(html);
});
this.html.find('.cuisine-proportions').change((event) => {
@@ -333,11 +333,9 @@ export class RdDRoll extends Dialog {
// 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("input.check-mortalite").prop('checked', rollData.dmg.mortalite == RDD_CONFIG.encaissement.nonmortel);
this.html.find("label.dmg-arme-actor").text(rollData.dmg.isEmpoignade ? EMPOIGNADE : Misc.toSignedString(rollData.dmg.total));
this.html.find("label.arme-mortalite").text(rollData.dmg.mortalite);
// this.html.find("[name='dmg-arme-actor']").text(rollData.dmg.mortalite == 'empoignade'? 'empoignade': Misc.toSignedString(rollData.dmg.total) );
// this.html.find("[name='arme-mortalite']").text(rollData.dmg.mortalite);
this.html.find("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable)
}
@@ -345,7 +343,7 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */
async buildAjustements(rollData) {
return await foundry.applications.handlebars.renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.hbs`, rollData);
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/partial-roll-ajustements.hbs`, rollData);
}
/* -------------------------------------------- */

View File

@@ -1,5 +1,5 @@
import { SHOW_DICE, SYSTEM_RDD } from "./constants.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { renderTemplate, SHOW_DICE, SYSTEM_RDD } from "./constants.js";
import { RollDataAjustements } from "./rolldata-ajustements-v1.js";
import { RdDUtility } from "./rdd-utility.js";
import { COORD_TMR_INCONNU, TMRUtility } from "./tmr-utility.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
@@ -47,7 +47,7 @@ export class RdDTMRDialog extends Dialog {
static async create(actor, tmrData) {
await PixiTMR.init()
let html = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.hbs', tmrData);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.hbs', tmrData);
if (tmrData.mode != 'visu' && !game.user.isGM) {
ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatUtility.getGMs() });
}
@@ -82,7 +82,7 @@ export class RdDTMRDialog extends Dialog {
this.subdialog = undefined
this.displaySize = undefined
if (!this.viewOnly && !game.user.isGM) {
this.$tellToGM(this.actor.name + " monte dans les terres médianes (" + tmrData.mode + ")");
ChatUtility.tellToGM(this.actor.name + " monte dans les terres médianes (" + tmrData.mode + ")");
}
this.callbacksOnAnimate = [];
const displaySize = TMR_DISPLAY_SIZE.clamp(game.settings.get(SYSTEM_RDD, TMR_DISPLAY_SIZE.code) ?? TMR_DISPLAY_SIZE.def);
@@ -125,7 +125,11 @@ export class RdDTMRDialog extends Dialog {
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getCoordActor()));
this.html.find('form.tmr-dialog *').click(event => this.subdialog?.bringToTop());
this.html.find('form.tmr-dialog *').click(event => {
if (this.subdialog?.rendered){
this.subdialog?.bringToFront()
}
})
// Roll Sort
this.html.find('.lancer-sort').click(event => this.lancerUnSort());
@@ -169,26 +173,26 @@ export class RdDTMRDialog extends Dialog {
async forceTMRDisplay() {
if (this.rendered) {
this.bringToTop()
this.bringSubDialogToTop();
this.bringToFront()
this.bringSubDialogToFront();
}
}
bringSubDialogToTop() {
if (this.subdialog?.bringToTop && this.subdialog?.element && this.subdialog?.element[0]) {
this.subdialog.bringToTop();
bringSubDialogToFront() {
if (this.subdialog?.bringToFront && this.subdialog?.element && this.subdialog?.element[0]) {
this.subdialog.bringToFront();
}
}
async restoreTMRAfterAction() {
this.subdialog = undefined
await this.maximize()
this.bringToTop()
this.bringToFront()
}
forceTMRContinueAction() {
ui.notifications.warn('Vous devez finir votre action avant de continuer dans les TMR');
this.bringSubDialogToTop();
this.bringSubDialogToFront();
return false
}
@@ -343,19 +347,8 @@ export class RdDTMRDialog extends Dialog {
this.forceTMRContinueAction()
return false
}
this.descenteTMR = true;
if (this.actor.tmrApp) {
this.actor.tmrApp = undefined // Cleanup reference
const appliquerFatigue = ReglesOptionnelles.isUsing("appliquer-fatigue")
await this.actor.santeIncDec(
appliquerFatigue ? "fatigue" : "endurance",
(appliquerFatigue ? 1 : -1) * this.cumulFatigue)
if (!this.viewOnly) {
await this.actor.setEffect(STATUSES.StatusDemiReve, false)
this.$tellToUserAndGM(message)
}
}
this.descenteTMR = true
await await this.actor.quitterTMR(message, this.viewOnly, this.cumulFatigue)
this.pixiTMR.close();
this.pixiTMR = undefined
await super.close();
@@ -412,7 +405,7 @@ export class RdDTMRDialog extends Dialog {
async $ignorerRencontre() {
if (this.currentRencontre) {
console.log("-> ignorer", this.currentRencontre);
this.$tellToGM(this.actor.name + " a ignoré: " + this.currentRencontre.name);
ChatUtility.tellToGM(this.actor.name + " a ignoré: " + this.currentRencontre.name);
await this.$deleteRencontreTMRAtPosition()
this.updateTokens();
this.$updateValuesDisplay();
@@ -508,7 +501,7 @@ export class RdDTMRDialog extends Dialog {
ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
content: await foundry.applications.handlebars.renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.hbs`, rencData)
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.hbs`, rencData)
});
this.$updateValuesDisplay();
@@ -578,29 +571,6 @@ export class RdDTMRDialog extends Dialog {
}, 500);
}
/* -------------------------------------------- */
_tellToUser(message) {
ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id] });
}
/* -------------------------------------------- */
$tellToGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getGMs()
});
}
/* -------------------------------------------- */
$tellToUserAndGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getUserAndGMs()
})
}
/* -------------------------------------------- */
async manageRencontre(tmr) {
if (this.viewOnly) {
@@ -676,14 +646,14 @@ export class RdDTMRDialog extends Dialog {
? TMRUtility.getTMRType(tmr.coord) + " ??"
: tmr.label + " (" + tmr.coord + ")");
this.setTMRPendingAction({ bringToTop: () => { } })
this.setTMRPendingAction({ bringToFront: () => { } })
const myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE });
this.restoreTMRAfterAction()
if (myRoll == 7) {
this._tellToUser(myRoll + ": Rencontre en " + coordTMR);
ChatUtility.tellToUser(myRoll + ": Rencontre en " + coordTMR);
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
} else {
this._tellToUser(myRoll + ": Pas de rencontre en " + coordTMR);
ChatUtility.tellToUser(myRoll + ": Pas de rencontre en " + coordTMR);
return undefined;
}
}

View File

@@ -1,6 +1,9 @@
/* -------------------------------------------- */
import { renderTemplate } from "./constants.js";
import { HtmlUtility } from "./html-utility.js";
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js";
import { Targets } from "./targets.js";
/* -------------------------------------------- */
@@ -30,47 +33,92 @@ export class RdDTokenHud {
const combatant = game.combat.combatants.find(c => c.tokenId == tokenId)
const actor = RdDCombatManager.getActorCombatant(combatant, { warning: false })
if (actor) {
const actions = RdDCombatManager.listActionsActorCombatant(actor)
// initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions)
// combat
await RdDTokenHud.addExtensionHudCombat(html, combatant, token, actions.filter(it => !it.initOnly))
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
await RdDTokenHud.addExtensionHudCombat(html, combatant, actor, token)
}
else {
const actions = RdDCombatManager.listActionsActorCombatant(actor)
// initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions)
// combat
await RdDTokenHud.addExtensionHudAttaques(html, combatant, token, actions.filter(it => !it.initOnly))
}
}
}
}
static async addExtensionHudCombat(html, combatant, actor, token) {
const actionsActor = actor.listActionsCombat();
const ajustements = combatant?.initiative ?
[
{ label: 'Initiative +1', action: 'delta', value: 1 },
{ label: 'Initiative -1', action: 'delta', value: -1 }
] : []
const autres = [{ label: "Autre action", action: 'autre' }]
const actions = Misc.indexed(actionsActor.concat(ajustements).concat(autres))
const hudData = { combatant, token, actions };
const hud = $(await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/hud-actor-combat.hbs', hudData))
$(html).find('div.col.left').append(hud)
const list = hud.find('div.rdd-hud-list')
RdDTokenHud.setupHudToggle(hud, list)
const selectInitiative = list.find('select[name="initiative"]');
selectInitiative.change(event => {
const action = actions.find(it => it.index == event.currentTarget.value)
console.log('select initiative', combatant.id, action)
if (action) {
switch (action.action) {
case 'delta':
RdDCombatManager.incDecInit(combatant.id, action.value);
break
case 'autre':
RdDCombatManager.rollInitiativeAction(combatant.id,
{ label: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } });
break
default:
RdDCombatManager.rollInitiativeAction(combatant.id, action)
}
selectInitiative.select("")
}
})
list.find('.rdd-attaque-v2').click(event => combatant.actor.rollAttaque(token))
}
static async addExtensionHudInit(html, combatant, actions) {
const hudData = {
combatant, actions,
commandes: [
{ name: "Autre action", command: 'autre' },
{ name: 'Initiative +1', command: 'inc', value: 0.01 },
{ name: 'Initiative -1', command: 'dec', value: -0.01 }]
{ label: "Autre action", command: 'autre' },
{ label: 'Initiative +1', command: 'delta', value: 1 },
{ label: 'Initiative -1', command: 'deltac', value: -1 }]
};
const controlIconCombat = $(html).find('.control-icon[data-action=combat]');
await RdDTokenHud._configureSubMenu(controlIconCombat,
await RdDTokenHud._configureSubMenu(it => controlIconCombat.after(it),
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-init.hbs',
hudData,
(event) => {
let initCommand = event.currentTarget.attributes['data-command']?.value;
let combatantId = event.currentTarget.attributes['data-combatant-id']?.value;
let initCommand = event.currentTarget.attributes['data-command']?.value
let initCommandValue = Number(event.currentTarget.attributes['data-command-value']?.value ?? 0)
let combatantId = event.currentTarget.attributes['data-combatant-id']?.value
if (initCommand) {
RdDTokenHud._initiativeCommand(initCommand, combatantId);
RdDCombatManager.applyInitiativeCommand(combatantId, initCommand, initCommandValue)
} else {
let index = event.currentTarget.attributes['data-action-index'].value;
let action = hudData.actions[index];
RdDCombatManager.rollInitiativeAction(combatantId, action);
let index = event.currentTarget.attributes['data-action-index'].value
let action = hudData.actions[index]
RdDCombatManager.rollInitiativeAction(combatantId, action)
}
});
})
}
static async addExtensionHudCombat(html, combatant, token, actions) {
static async addExtensionHudAttaques(html, combatant, token, actions) {
const hudData = { combatant, token, actions, commandes: [] };
const controlIconTarget = $(html).find('.control-icon[data-action=target]');
await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.hbs', hudData,
const divColLeft = $(html).find('div.col.left');
await RdDTokenHud._configureSubMenu(it => divColLeft.append(it),
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.hbs',
hudData,
(event) => {
const actionIndex = event.currentTarget.attributes['data-action-index']?.value;
const action = hudData.actions[actionIndex];
@@ -90,7 +138,7 @@ export class RdDTokenHud {
const hudSoins = { blessures: target.actor.blessuresASoigner() ?? [] };
if (hudSoins.blessures.length > 0) {
const controlIconTarget = $(html).find('.control-icon[data-action=combat]');
await RdDTokenHud._configureSubMenu(controlIconTarget,
await RdDTokenHud._configureSubMenu(it => controlIconTarget.after(it),
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-soins.hbs',
hudSoins,
(event) => {
@@ -101,15 +149,6 @@ export class RdDTokenHud {
}
}
static _initiativeCommand(initCommand, combatantId) {
switch (initCommand) {
case 'inc': return RdDCombatManager.incDecInit(combatantId, 0.01);
case 'dec': return RdDCombatManager.incDecInit(combatantId, -0.01);
case 'autre': return RdDCombatManager.rollInitiativeAction(combatantId,
{ name: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } });
}
}
/* -------------------------------------------- */
static async addTokenHudExtensions(app, html, tokenId) {
console.log(`Adding token HUD extensions for token ${tokenId}`);
@@ -129,20 +168,24 @@ export class RdDTokenHud {
}
/* -------------------------------------------- */
static async _configureSubMenu(insertionPoint, template, hudData, onMenuItem) {
const hud = $(await foundry.applications.handlebars.renderTemplate(template, hudData));
static async _configureSubMenu(callInsertion, template, hudData, onMenuItem) {
const hud = $(await renderTemplate(template, hudData));
const list = hud.find('div.rdd-hud-list');
RdDTokenHud._toggleHudListActive(hud, list);
RdDTokenHud.setupHudToggle(hud, list)
hud.find('img.rdd-hud-togglebutton').click(event => RdDTokenHud._toggleHudListActive(hud, list));
list.find('.rdd-hud-menu').click(onMenuItem);
insertionPoint.after(hud);
callInsertion(hud);
}
static _toggleHudListActive(hud, list) {
hud.toggleClass('active');
HtmlUtility.showControlWhen(list, hud.hasClass('active'));
static setupHudToggle(hud, list) {
function toggleHudList(hud, list) {
hud.toggleClass('active')
HtmlUtility.showControlWhen(list, hud.hasClass('active'))
}
toggleHudList(hud, list)
$(hud).find('img.rdd-hud-togglebutton').click(event => toggleHudList(hud, list))
}
}

View File

@@ -75,30 +75,27 @@ const fatigueLineMalus = [0, -1, -2, -3, -4, -5, -6, -7];
/* -------------------------------------------- */
const nomEthylisme = ["Emeché", "Gris", "Pinté", "Pas frais", "Ivre", "Bu", "Complètement fait", "Ivre mort"];
/* -------------------------------------------- */
const definitionsEncaissement = {
"mortel": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 2 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "2", gravite: 4 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "4 + @over20", gravite: 6 },
],
"non-mortel": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 2 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "0", gravite: 2 },
],
"entiteincarnee": [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 0 },
{ minimum: 20, maximum: undefined, endurance: "3d6 + @over20", vie: "0", gravite: 0 },
]
};
const TABLE_ENCAISSEMENT_MORTEL = [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 2 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "2", gravite: 4 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "4 + @over20", gravite: 6 },
]
const TABLE_ENCAISSEMENT_NONMORTEL = [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 0 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 2 },
{ minimum: 20, maximum: undefined, endurance: "100", vie: "0", gravite: 2 },
]
const TABLE_ENCAISSEMENT_ENTITE = [
{ minimum: undefined, maximum: 0, endurance: "0", vie: "0", gravite: -1 },
{ minimum: 1, maximum: 10, endurance: "1d4", vie: "0", gravite: 0 },
{ minimum: 11, maximum: 15, endurance: "1d6", vie: "0", gravite: 2 },
{ minimum: 16, maximum: 19, endurance: "2d6", vie: "0", gravite: 4 },
{ minimum: 20, maximum: undefined, endurance: "3d6 + @over20", vie: "0", gravite: 6 },
]
/* -------------------------------------------- */
export class RdDUtility {
@@ -301,6 +298,8 @@ export class RdDUtility {
Handlebars.registerHelper('json-stringify', object => JSON.stringify(object))
// math
Handlebars.registerHelper('math-sum', (...values) => values.slice(0, -1).reduce(Misc.sum(), 0))
Handlebars.registerHelper('math-abs', diff => Math.abs(parseInt(diff)))
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('repeat', function (n, block) {
let accum = '';
@@ -611,20 +610,20 @@ export class RdDUtility {
/* -------------------------------------------- */
static async getLocalisation(type = 'personnage') {
const loc = { result: await RdDDice.rollTotal("1d20")};
const loc = { result: await RdDDice.rollTotal("1d20") };
if (type == 'personnage') {
if (loc.result <= 3) loc.txt = "Jambe, genou, pied, jarret";
else if (loc.result <= 7) loc.txt = "Hanche, cuisse, fesse";
else if (loc.result <= 9) loc.txt = "Ventre, reins";
else if (loc.result <= 12) loc.txt = "Poitrine, dos";
else if (loc.result <= 14) loc.txt = "Avant-bras, main, coude";
else if (loc.result <= 18) loc.txt = "Epaule, bras, omoplate";
else if (loc.result == 19) loc.txt = "Tête";
else if (loc.result == 20) loc.txt = "Tête (visage)";
if (loc.result <= 3) loc.txt = "Jambe, genou, pied, jarret";
else if (loc.result <= 7) loc.txt = "Hanche, cuisse, fesse";
else if (loc.result <= 9) loc.txt = "Ventre, reins";
else if (loc.result <= 12) loc.txt = "Poitrine, dos";
else if (loc.result <= 14) loc.txt = "Avant-bras, main, coude";
else if (loc.result <= 18) loc.txt = "Epaule, bras, omoplate";
else if (loc.result == 19) loc.txt = "Tête";
else if (loc.result == 20) loc.txt = "Tête (visage)";
} else {
if (loc.result <= 7) loc.txt = "Jambes/Pattes";
else if (loc.result <= 18) loc.txt = "Corps";
else if (loc.result <= 20) loc.txt = "Tête";
if (loc.result <= 7) loc.txt = "Jambes/Pattes";
else if (loc.result <= 18) loc.txt = "Corps";
else if (loc.result <= 20) loc.txt = "Tête";
}
return loc
}
@@ -669,13 +668,13 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async prepareEncaissement(actor, dmg, roll, armure) {
static async prepareEncaissement(targetActor, dmg, roll, armure) {
const jetTotal = roll.total + dmg.total - armure
const encaissement = RdDUtility._selectEncaissement(jetTotal, dmg.mortalite);
const over20 = Math.max(jetTotal - 20, 0);
const encaissement = RdDUtility.$selectEncaissement(targetActor, jetTotal, dmg.mortalite)
const over20 = Math.max(jetTotal - 20, 0)
encaissement.dmg = dmg
if (ReglesOptionnelles.isUsing('localisation-aleatoire')) {
encaissement.dmg.loc = dmg.loc ?? await RdDUtility.getLocalisation(actor.type)
encaissement.dmg.loc = dmg.loc ?? await RdDUtility.getLocalisation(targetActor.type)
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'
}
else {
@@ -691,15 +690,21 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static _selectEncaissement(degats, mortalite) {
const table = definitionsEncaissement[mortalite] === undefined ? definitionsEncaissement["mortel"] : definitionsEncaissement[mortalite];
for (let encaissement of table) {
if ((encaissement.minimum === undefined || encaissement.minimum <= degats)
&& (encaissement.maximum === undefined || degats <= encaissement.maximum)) {
return foundry.utils.duplicate(encaissement);
}
static $selectEncaissement(targetActor, degats, mortalite) {
const table = RdDUtility.$getTableEncaissement(targetActor, mortalite)
const encaissement = table.find(it => ((it.minimum ?? degats) <= degats && degats <= (it.maximum ?? degats)))
return foundry.utils.duplicate(encaissement ?? table[0])
}
static $getTableEncaissement(targetActor, mortalite) {
if (targetActor.isEntite()) {
return TABLE_ENCAISSEMENT_ENTITE
}
return foundry.utils.duplicate(table[0]);
switch (mortalite) {
case RDD_CONFIG.encaissement.nonmortel: return TABLE_ENCAISSEMENT_NONMORTEL
case RDD_CONFIG.encaissement.entiteincarnee: return TABLE_ENCAISSEMENT_ENTITE
}
return TABLE_ENCAISSEMENT_MORTEL
}
/* -------------------------------------------- */

View File

@@ -1,9 +1,17 @@
import { ChatUtility } from "../chat-utility.js"
import RollDialog from "./roll-dialog.mjs"
import RollDialog, { ALL_ROLL_TYPES } from "./roll-dialog.mjs"
import { RdDCarac } from "../rdd-carac.js"
import { RdDCombat } from "../rdd-combat.js"
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { RdDResolutionTable } from "../rdd-resolution-table.js"
import { RDD_CONFIG, renderTemplate } from "../constants.js"
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js"
import { RollTypeCuisine } from "./roll-type-cuisine.mjs"
import { RollTypeMeditation } from "./roll-type-meditation.mjs"
import { PART_DEFENSE } from "./roll-part-defense.mjs"
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
import { RdDRollTables } from "../rdd-rolltables.js"
import { RdDEmpoignade } from "../rdd-empoignade.js"
export default class ChatRollResult {
static init() {
@@ -14,14 +22,18 @@ export default class ChatRollResult {
static onReady() {
foundry.applications.handlebars.loadTemplates({
'partial-infojet': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-infojet.hbs',
'partial-appel-chance': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-appel-chance.hbs',
'partial-attaque-particuliere': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-attaque-particuliere.hbs',
'partial-choix-maladresse': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-choix-maladresse.hbs',
'partial-maladresse': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-maladresse.hbs',
'partial-encaissement': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-encaissement.hbs',
'partial-recul-choc': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-recul-choc.hbs',
'partial-info-appel-moral': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-info-appel-moral.hbs',
})
}
async display(roll) {
async display(roll, impacts) {
this.prepareDisplay(roll)
const chatMessage = await ChatUtility.createChatWithRollMode(
@@ -31,18 +43,19 @@ export default class ChatRollResult {
roll.active.actor,
roll.current?.rollmode?.key
)
const save = RollDialog.saveParts(roll)
ChatUtility.setMessageData(chatMessage, 'rollData', save)
const save = RollDialog.saveParts(roll, impacts)
await this.saveChatMessageRoll(chatMessage, save)
return chatMessage
}
prepareDisplay(roll) {
roll.done = roll.done || {}
roll.show = roll.show || {}
roll.done = roll.done ?? {}
roll.show = roll.show ?? {}
roll.show.chance = this.isAppelChancePossible(roll)
roll.show.encaissement = this.isShowEncaissement(roll)
roll.show.recul = this.getReculChoc(roll)
roll.show.recul = this.getRecul(roll)
roll.show.maladresse = this.getMaladresse(roll)
}
isAppelChancePossible(roll) {
@@ -52,39 +65,81 @@ export default class ChatRollResult {
}
isShowEncaissement(roll) {
return roll.rolled.isEchec &&
roll.attackerRoll?.dmg.mortalite != 'empoignade'
switch (roll.type.current) {
case ROLL_TYPE_DEFENSE:
return roll.rolled.isEchec
}
return false
}
getReculChoc(roll, defender = roll.active.actor, attacker = roll.opponent.actor) {
const attaque = roll.attackerRoll
if (attaque &&
(roll.rolled.isEchec || !roll.current.defense.isEsquive) &&
(attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) {
const taille = defender.system.carac.taille.value
const impact = attacker.system.carac.force.value + roll.attackerRoll?.dmg.dmgArme
return {
raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force',
taille: taille,
impact: impact,
chances: RdDResolutionTable.computeChances(10, taille-impact).norm,
diff: taille - impact
}
getMaladresse(roll) {
switch (roll.type.current) {
case ROLL_TYPE_DEFENSE:
if (roll.rolled.isETotal) {
const arme = roll.current[PART_DEFENSE].arme
return arme ? 'avec-arme' : 'sans-arme'
}
break
case ROLL_TYPE_ATTAQUE:
if (roll.rolled.isETotal || (roll.rolled.isEchec && roll.active.surprise == 'demi')) {
const arme = roll.current[PART_ATTAQUE].arme
return arme.system.baseInit > 4 ? 'avec-arme' : 'sans-arme'
}
break
}
return undefined
}
getRecul(roll, defender = roll.active.actor, attacker = roll.opponent?.actor) {
switch (roll.type.current) {
case ROLL_TYPE_DEFENSE:
{
const attaque = roll.attackerRoll
if (attaque &&
(roll.rolled.isEchec || !roll.current.defense.isEsquive) &&
(attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) {
const taille = defender.system.carac.taille.value
const impact = attacker.system.carac.force.value + roll.attackerRoll?.dmg.dmgArme
return {
raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force',
taille: taille,
impact: impact,
chances: RdDResolutionTable.computeChances(10, taille - impact).norm,
diff: taille - impact
}
}
break
}
case ROLL_TYPE_ATTAQUE:
{
const attaque = roll
if (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key) {
return {
raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force',
}
}
}
}
return undefined
}
async buildRollHtml(roll) {
const template = `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-${roll.type.current}.hbs`
return await foundry.applications.handlebars.renderTemplate(template, roll)
const template = ALL_ROLL_TYPES.find(it => it.code == roll.type.current).chatResultTemplate
const html = await renderTemplate(template, roll)
return await RdDTextEditor.enrichHTML(html, undefined, { showLink: false })
}
async chatListeners(html) {
$(html).on("click", '.appel-chance', event => this.onClickAppelChance(event))
$(html).on("click", '.appel-destinee', event => this.onClickAppelDestinee(event))
$(html).on("click", '.button-defense', event => this.onClickDefense(event))
$(html).on("click", '.encaissement', event => this.onClickEncaissement(event))
$(html).on("click", '.resister-recul', event => this.onClickRecul(event))
$(html).on("click", '.choix-particuliere', event => this.onClickChoixParticuliere(event))
$(html).on("click", '.faire-gouter', event => this.onClickFaireGouter(event))
$(html).on("click", '.monter-tmr-normale', event => this.onClickMonteeTMR(event, 'normal'))
$(html).on("click", '.monter-tmr-rapide', event => this.onClickMonteeTMR(event, 'rapide'))
$(html).on("click", '.tirer-maladresse', event => this.onClickTirerMaladresse(event))
}
getCombat(roll) {
@@ -92,15 +147,24 @@ export default class ChatRollResult {
case ROLL_TYPE_DEFENSE:
return RdDCombat.rddCombatForAttackerAndDefender(roll.ids.opponentId, roll.ids.opponentTokenId, roll.ids.actorTokenId)
case ROLL_TYPE_ATTAQUE:
return RdDCombat.rddCombatForAttackerAndDefender(roll.ids.actorId, roll.ids.actorTokenId, roll.ids.opponentId)
return RdDCombat.rddCombatForAttackerAndDefender(roll.ids.actorId, roll.ids.actorTokenId, roll.ids.opponentTokenId)
}
return undefined
}
async saveChatMessageRoll(chatMessage, savedRoll) {
await ChatUtility.setMessageData(chatMessage, 'rollData', savedRoll)
}
loadChatMessageRoll(chatMessage) {
return ChatUtility.getMessageData(chatMessage, 'rollData')
}
async updateChatMessage(chatMessage, savedRoll) {
ChatUtility.setMessageData(chatMessage, 'rollData', savedRoll)
await this.saveChatMessageRoll(chatMessage, savedRoll)
const copy = foundry.utils.duplicate(savedRoll)
RollDialog.loadRollData(copy)
savedRoll.dmg = copy.current.attaque?.dmg
this.prepareDisplay(copy)
chatMessage.update({ content: await this.buildRollHtml(copy) })
chatMessage.render(true)
@@ -108,7 +172,7 @@ export default class ChatRollResult {
onClickAppelChance(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const savedRoll = this.loadChatMessageRoll(chatMessage)
const actor = game.actors.get(savedRoll.ids.actorId)
actor.rollAppelChance(
() => this.onAppelChanceSuccess(savedRoll, chatMessage),
@@ -116,10 +180,13 @@ export default class ChatRollResult {
event.preventDefault()
}
onAppelChanceSuccess(savedRoll, chatMessage) {
async onAppelChanceSuccess(savedRoll, chatMessage) {
const reRoll = foundry.utils.duplicate(savedRoll)
console.log('onAppelChanceSuccess savedRoll', savedRoll)
reRoll.type.retry = true
await this.updateChatMessage(chatMessage, reRoll)
const callbacks = [r => ChatUtility.removeChatMessageId(chatMessage.id)]
// TODO: annuler les effets
switch (reRoll.type.current) {
case ROLL_TYPE_DEFENSE:
@@ -142,7 +209,7 @@ export default class ChatRollResult {
onClickAppelDestinee(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const savedRoll = this.loadChatMessageRoll(chatMessage)
const actor = game.actors.get(savedRoll.ids.actorId)
actor.appelDestinee(async () => {
@@ -153,23 +220,46 @@ export default class ChatRollResult {
})
}
async onClickDefense(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
const attackerRoll = savedRoll.attackerRoll
this.getCombat(attackerRoll)?.defenseV2(attackerRoll,
[roll => { ChatUtility.removeChatMessageId(chatMessage.id) }]
)
}
async onClickEncaissement(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const isMessageDemande = ChatUtility.getMessageData(chatMessage, 'demande-defense')
const savedRoll = this.loadChatMessageRoll(chatMessage)
const attaque = savedRoll.attackerRoll
const defender = game.actors.get(savedRoll.ids.actorId)
const attacker = game.actors.get(savedRoll.ids.opponentId)
const defenderToken = savedRoll.ids.actorTokenId ? canvas.tokens.get(savedRoll.ids.actorTokenId) : undefined
const attackerToken = savedRoll.ids.opponentTokenId ? canvas.tokens.get(savedRoll.ids.opponentTokenId) : undefined
await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
savedRoll.done.encaissement = true
await this.updateChatMessage(chatMessage, savedRoll)
switch (attaque.dmg.mortalite) {
case RDD_CONFIG.encaissement.empoignade:
savedRoll.done = savedRoll.done ?? {}
savedRoll.done.empoignade = await RdDEmpoignade.ajustementEmpoignade(attackerToken.actor, defenderToken.actor)
break
case RDD_CONFIG.encaissement.entiteincarnee:
case RDD_CONFIG.encaissement.nonmortel:
case RDD_CONFIG.encaissement.mortel:
const defender = defenderToken?.actor ?? game.actors.get(savedRoll.ids.actorId)
const attacker = attackerToken?.actor ?? game.actors.get(savedRoll.ids.opponentId)
await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
break
}
if (isMessageDemande) {
ChatUtility.removeChatMessageId(chatMessage.id)
} else {
savedRoll.done.encaissement = true
await this.updateChatMessage(chatMessage, savedRoll)
}
}
async onClickRecul(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const savedRoll = this.loadChatMessageRoll(chatMessage)
const defender = game.actors.get(savedRoll.ids.actorId)
const attacker = game.actors.get(savedRoll.ids.opponentId)
savedRoll.done.recul = await defender.encaisserRecul(attacker.getForce(), savedRoll.attackerRoll.dmg.dmgArme)
@@ -177,4 +267,40 @@ export default class ChatRollResult {
await this.updateChatMessage(chatMessage, savedRoll)
}
async onClickChoixParticuliere(event) {
const choix = event.currentTarget.attributes['data-particuliere'].value
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
savedRoll.particuliere = choix
savedRoll.particulieres = [RDD_CONFIG.particuliere[choix]]
await this.updateChatMessage(chatMessage, savedRoll)
await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll)
}
async onClickFaireGouter(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
if (!savedRoll.type.retry) {
savedRoll.type.retry = true
await this.updateChatMessage(chatMessage, savedRoll)
}
await new RollTypeCuisine().onFaireGouter(savedRoll)
}
async onClickMonteeTMR(event, mode) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
if (await new RollTypeMeditation().onMonteeTMR(savedRoll, mode)) {
savedRoll.done.meditation = true
await this.updateChatMessage(chatMessage, savedRoll)
}
}
async onClickTirerMaladresse(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const typeMaladresse = event.currentTarget.attributes['data-maladresse'].value
const savedRoll = this.loadChatMessageRoll(chatMessage)
savedRoll.maladresse = await RdDRollTables.getMaladresse({ arme: typeMaladresse == 'avec-arme', toChat: false })
savedRoll.type.retry = true
await this.updateChatMessage(chatMessage, savedRoll)
}
}

View File

@@ -1,44 +1,46 @@
import { ActorToken } from "../actor-token.mjs"
import { TokenActor } from "../technical/actor-token.mjs"
import { StatusEffects } from "../settings/status-effects.js"
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
import { PART_DEFENSE } from "./roll-part-defense.mjs"
export class RollBasicParts {
restore(rollData) {
static restore(rollData) {
rollData.ids.sceneId = rollData.ids.sceneId ?? canvas.scene.id
rollData.active = RollBasicParts.$getActor(rollData)
rollData.opponent = RollBasicParts.$getOpponent(rollData)
rollData.active = RollBasicParts.getTokenActor(rollData)
rollData.opponent = RollBasicParts.getTokenActorOpponent(rollData)
if (rollData.type.opposed == undefined) {
rollData.type.opposed = rollData.opponent != null
}
}
loadSurprises(rollData, type) {
static loadSurprises(rollData, type = rollData.type.current) {
if (!rollData.type.passif) {
this.loadSurprise(rollData.active, this.getForceRequiseActiveActor(rollData, type))
this.loadSurprise(rollData.opponent, 0)
RollBasicParts.loadSurprise(rollData.active, RollBasicParts.getForceRequiseActiveActor(rollData, type))
RollBasicParts.loadSurprise(rollData.opponent, 0)
}
}
loadSurprise(who, forceRequise) {
static loadSurprise(who, forceRequise) {
if (who?.actor) {
foundry.utils.mergeObject(who,
StatusEffects.getActorEffetSurprise(who.actor, forceRequise),
StatusEffects.getActorEffetSurprise(who.actor),
{ overwrite: true, inPlace: true })
}
}
getForceRequiseActiveActor(rollData, type) {
static getForceRequiseActiveActor(rollData, type) {
switch (type) {
case ROLL_TYPE_ATTAQUE: return rollData.current[PART_ATTAQUE].attaque.forceRequise
case ROLL_TYPE_ATTAQUE: return rollData.current[PART_ATTAQUE].forceRequise
case ROLL_TYPE_DEFENSE: return rollData.current[PART_DEFENSE].forceRequise
default: return 0
}
}
initFrom(rollData) {
static initFrom(rollData) {
const isOpposed = rollData.type.opposed && rollData.opponent
return {
selected: {},
type: rollData.type,
@@ -46,36 +48,60 @@ export class RollBasicParts {
sceneId: rollData.ids.sceneId,
actorId: rollData.active.id,
actorTokenId: rollData.active.tokenId,
opponentId: rollData.type.opposed ? rollData.opponent.id : undefined,
opponentTokenId: rollData.type.opposed ? rollData.opponent.tokenId : undefined,
opponentId: isOpposed ? rollData.opponent.id : undefined,
opponentTokenId: isOpposed ? rollData.opponent.tokenId : undefined,
}
}
}
static $getActor(rollData) {
static prepareDefense(attackerRoll) {
if (!attackerRoll.passeArme) {
attackerRoll.passeArme = foundry.utils.randomID(16);
}
return {
ids: RollBasicParts.reverseIds(attackerRoll),
active: attackerRoll.opponent,
opponent: attackerRoll.active,
attackerRoll: attackerRoll,
passeArme: attackerRoll.passeArme,
show: { encaissement: true }
}
}
static reverseIds(rollData) {
return {
sceneId: rollData.ids.sceneId,
actorId: rollData.ids.opponentId,
actorTokenId: rollData.ids.opponentTokenId,
opponentId: rollData.ids.actorId,
opponentTokenId: rollData.ids.actorTokenId
}
}
static getTokenActor(rollData) {
if (rollData.ids.actorTokenId) {
return ActorToken.fromTokenId(rollData.ids.actorTokenId, rollData.ids.sceneId)
return TokenActor.fromTokenId(rollData.ids.actorTokenId, rollData.ids.sceneId)
}
else {
const actorId = rollData.ids.actorId ?? (canvas.tokens.controlled.length == 1
/** TODO: jets de plusieurs personnages??? */
? canvas.tokens.controlled[0]
: undefined)
return ActorToken.fromActorId(actorId, () => { throw new Error("Pas d'acteur sélectionné") })
return TokenActor.fromActorId(actorId, () => { throw new Error("Pas d'acteur sélectionné") })
}
}
static $getOpponent(rollData) {
static getTokenActorOpponent(rollData) {
if (rollData.ids.opponentTokenId) {
return ActorToken.fromTokenId(rollData.ids.opponentTokenId, rollData.ids.sceneId)
return TokenActor.fromTokenId(rollData.ids.opponentTokenId, rollData.ids.sceneId)
}
else if (rollData.ids.opponentId) {
return ActorToken.fromActorId(rollData.ids.opponentId)
return TokenActor.fromActorId(rollData.ids.opponentId)
}
else {
const targets = Array.from(game.user.targets)
if (targets.length == 1) {
return ActorToken.fromToken(targets[0])
return TokenActor.fromToken(targets[0])
}
else {
return undefined

View File

@@ -1,6 +1,7 @@
export const ROLL_TYPE_ATTAQUE = 'attaque'
export const ROLL_TYPE_COMP = 'comp'
export const ROLL_TYPE_CUISINE = 'cuisine'
export const ROLL_TYPE_DEFENSE = 'defense'
export const ROLL_TYPE_JEU = 'jeu'
export const ROLL_TYPE_MEDITATION = 'meditation'
@@ -8,6 +9,12 @@ export const ROLL_TYPE_OEUVRE = 'oeuvre'
export const ROLL_TYPE_SORT = 'sort'
export const ROLL_TYPE_TACHE = 'tache'
export const ATTAQUE_ROLL_TYPES = [ROLL_TYPE_ATTAQUE]
export const COMBAT_ROLL_TYPES = [ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE]
export const DEMIREVE_ROLL_TYPES = [ROLL_TYPE_SORT]
export const DEFAULT_ROLL_TYPES = [ROLL_TYPE_COMP, ROLL_TYPE_ATTAQUE, ROLL_TYPE_TACHE, ROLL_TYPE_MEDITATION, ROLL_TYPE_CUISINE, ROLL_TYPE_OEUVRE, ROLL_TYPE_JEU]
export const DIFF = {
LIBRE: 'libre',
ATTAQUE: 'attaque',

View File

@@ -6,6 +6,10 @@ import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { PART_OEUVRE } from "./roll-part-oeuvre.mjs";
import { RdDItemArme } from "../item/arme.js";
import { RdDBonus } from "../rdd-bonus.js";
import { ITEM_TYPES, RDD_CONFIG } from "../constants.js";
import { CARACS } from "../rdd-carac.js";
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE, ROLL_TYPE_OEUVRE } from "./roll-constants.mjs";
import { PART_ATTAQUE } from "./roll-part-attaque.mjs";
/* -------------------------------------------- */
export class RollDialogAdapter {
@@ -15,21 +19,22 @@ export class RollDialogAdapter {
carac: rollData.current.carac.value,
diff: rollData.current.totaldiff,
bonus: rollData.current.bonus,
sign: rollData.current.sign,
showDice: rollData.options.showDice,
rollMode: rollData.current.rollmode.key
})
const rolled = await RollDialogAdapter.rollChances(rollData, chances)
RollDialogAdapter.adjustRollDataForV1(rollData, rolled, rollTitle)
RollDialogAdapter.setRollDataRolled(rollData, rolled, rollTitle)
RollDialogAdapter.adjustRollDataForV1(rollData)
RollDialogAdapter.adjustAttaqueParticuliere(rollData)
RollDialogAdapter.adjustAttaqueDmg(rollData)
RollDialogAdapter.adjustDemiSurprise(rollData)
return rolled
}
static computeChances({ carac, diff, bonus, sign, showDice, rollMode }) {
static computeChances({ carac, diff, bonus, showDice, rollMode }) {
const chances = foundry.utils.duplicate(RdDResolutionTable.computeChances(carac, diff))
RdDResolutionTable._updateChancesWithBonus(chances, bonus, diff)
RdDResolutionTable._updateChancesFactor(chances, sign)
chances.showDice = showDice
chances.rollMode = rollMode
return chances
@@ -37,58 +42,112 @@ export class RollDialogAdapter {
static async rollChances(rollData, chances) {
const rolled = await RdDResolutionTable.rollChances(chances,
rollData.current.sign,
rollData.current.sign.diviseur,
rollData.current.resultat)
rolled.caracValue = rollData.current.carac.value
rolled.finalLevel = rollData.current.totaldiff
rolled.bonus = rollData.current.bonus ?? 0
rolled.factorHtml = Misc.getFractionOneN(rollData.current.sign.diviseur)
if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) {
rolled.niveauNecessaire = RdDResolutionTable.findNiveauNecessaire(rolled.caracValue, rolled.roll)
rolled.ajustementNecessaire = rolled.niveauNecessaire - diff
}
return rolled
}
static adjustRollDataForV1(rollData, rolled, rollTitle) {
static setRollDataRolled(rollData, rolled, rollTitle) {
rollData.rolled = rolled
rollData.choix = rollData.choix ?? {}
rollData.show = rollData.show ?? {}
rollData.show.title = rollTitle
}
static adjustRollDataForV1(rollData) {
const rolled = rollData.rolled
// temporaire pour être homogène roll v1
rollData.alias = rollData.active.actor.getAlias()
// pour experience
rollData.finalLevel = rollData.current.totaldiff
if (rollData.use == undefined) { rollData.use = {} }
if (rollData.show == undefined) { rollData.show = {} }
if (rollData.ajustements == undefined) {
rollData.ajustements = {}
}
rollData.selectedCarac = rollData.active.actor.system.carac[rollData.current.carac.key]
rollData.selectedCarac = rollData.active.actor.getCaracByName(rollData.current.carac.key)
const compKey = rollData.current.comp?.key
if (compKey) {
rollData.competence = rollData.refs[PART_COMP].all.find(it => it.key == compKey)?.comp
rollData.jetResistance = rollData.type.jetResistance
}
const oeuvreKey = rollData.current.oeuvre?.key
if (oeuvreKey) {
const oeuvreCurrent = rollData.current[PART_OEUVRE];
rollData.oeuvre = oeuvreCurrent.oeuvre
// rollData.oeuvre = rollData.refs[PART_OEUVRE].oeuvres.find(it => it.key == oeuvreKey)?.oeuvre
rollData.art = oeuvreCurrent.art.type
if (rollData.type.current == ROLL_TYPE_OEUVRE) {
const oeuvreKey = rollData.current.oeuvre?.key
if (rollData.type.current == ROLL_TYPE_OEUVRE && oeuvreKey) {
const oeuvreCurrent = rollData.current[PART_OEUVRE];
rollData.oeuvre = oeuvreCurrent.oeuvre
rollData.art = oeuvreCurrent.art.type
}
}
// pour appel moral
rollData.diviseurSignificative = rollData.current.sign
if (rollData.current[PART_APPELMORAL]?.checked) {
rollData.use.moral = true
}
rollData.rolled = rolled
if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) {
rolled.niveauNecessaire = this.findNiveauNecessaire(carac, rolled.roll)
rolled.niveauNecessaire = RdDResolutionTable.findNiveauNecessaire(rollData.selectedCarac.value, rolled.roll)
rolled.ajustementNecessaire = rolled.niveauNecessaire - diff
}
rollData.ajustements = rollData.ajustements.map(aj => {
return {
used: true,
label: aj.label,
value: aj.diff,
descr: aj.diff == undefined ? aj.label : undefined
}
})
rollData.show.title = rollTitle
rollData.ajustements = rollData.ajustements.map(a => { return { label: a.label, value: a.value } })
}
static adjustDemiSurprise(rollData) {
if (rollData.active.surprise == 'demi' && rollData.rolled.isPart) {
RdDResolutionTable.replaceParticuliereDemiSurprise(rollData.rolled)
}
}
static adjustAttaqueDmg(rollData) {
switch (rollData.type.current) {
case ROLL_TYPE_ATTAQUE:
rollData.dmg = RdDBonus.dmgRollV2(rollData, rollData.current.attaque)
break
case ROLL_TYPE_DEFENSE:
rollData.dmg = RdDBonus.dmgRollV2(rollData.attackerRoll, rollData.attackerRoll.current.attaque)
break
}
}
static adjustAttaqueParticuliere(rollData) {
if (rollData.type.current != ROLL_TYPE_ATTAQUE || !rollData.rolled.isPart) {
return
}
const attaque = rollData.current.attaque;
const choix = []
const isEmpoignade = attaque.dmg.isEmpoignade
const isCharge = attaque.tactique == 'charge'
/* TODO: cas de créatures faisant des lancers, Glou, Glipzouk */
const isMeleeDiffNegative = (attaque.comp.type == ITEM_TYPES.competencecreature || rollData.current.carac.key == CARACS.MELEE)
&& rollData.current.diff.value < 0
// force toujours, sauf empoignade
if (!isEmpoignade) {
choix.push(RDD_CONFIG.particuliere.force)
}
// finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum
if (!isCharge && (isEmpoignade || isMeleeDiffNegative)) {
choix.push(RDD_CONFIG.particuliere.finesse)
}
// rapidité seulement en mêlée, si l'arme le permet, et si la difficulté libre est de -1 minimum
if (!isCharge && !isEmpoignade && isMeleeDiffNegative && attaque.arme.system.rapide) {
choix.push(RDD_CONFIG.particuliere.rapidite)
}
if (choix.length == 1) {
rollData.particuliere = choix[0].key
}
rollData.particulieres = choix
}
static mapActionAttaque(attackerRoll) {
@@ -99,17 +158,13 @@ export class RollDialogAdapter {
return {
// correspond à l'attaque de RollPartAttaque (dans rollDta.current.attaque)
label: label,
attaque: {
// correspond aux actions d'attaques dans RdDActor.listActionsAttaque
name: label,
// action: 'attaque',
arme: attackerRoll.arme,
comp: attackerRoll.competence,
main: RdDItemArme.getMainAttaque(attackerRoll.competence),
equipe: attackerRoll.arme.system.equipe,
// carac: { key: caracCode, value: caracValue },
// dommagesArme: dommagesArme,
},
// correspond aux actions d'attaques dans RdDActor.listActionsAttaque
name: label,
// action: 'attaque',
arme: attackerRoll.arme,
comp: attackerRoll.competence,
main: RdDItemArme.getMainAttaque(attackerRoll.competence),
equipe: attackerRoll.arme.system.equipe,
diff: attackerRoll.diffLibre,
particuliere: attackerRoll.particuliere,
tactique: RdDBonus.find(attackerRoll.tactique),

View File

@@ -39,26 +39,31 @@ import { RollDialogAdapter } from "./roll-dialog-adapter.mjs";
import { ROLLDIALOG_SECTION } from "./roll-part.mjs";
import { ROLL_TYPE_COMP } from "./roll-constants.mjs";
import ChatRollResult from "./chat-roll-result.mjs";
import { renderTemplate } from "../constants.js";
import { RollTypeCuisine } from "./roll-type-cuisine.mjs";
import { RollPartCuisine } from "./roll-part-cuisine.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "../settings/options-avancees.js";
import { ActorImpacts } from "../technical/actor-impacts.mjs";
import { RollPartEmpoignade } from "./roll-part-empoignade.mjs";
import { RollPartEmpoignadeTaille } from "./roll-part-empoignade-taille.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
const doNothing = (dialog) => { }
const ALL_ROLL_TYPES = [
export const ALL_ROLL_TYPES = [
new RollTypeComp(),
new RollTypeTache(),
new RollTypeAttaque(),
new RollTypeDefense(),
new RollTypeSort(),
new RollTypeMeditation(),
new RollTypeCuisine(),
new RollTypeOeuvre(),
new RollTypeJeu(),
// new RollTypeResistance ??
// new RollTypeFixedCarac ??
]
const BASIC_PARTS = new RollBasicParts()
const ROLL_PARTS = [
new RollPartActor(),
new RollPartAction(),
@@ -72,6 +77,7 @@ const ROLL_PARTS = [
new RollPartMeditation(),
new RollPartSort(),
new RollPartTache(),
new RollPartCuisine(),
new RollPartOeuvre(),
new RollPartJeu(),
@@ -81,6 +87,8 @@ const ROLL_PARTS = [
new RollPartConditions(),
new RollPartEthylisme(),
new RollPartMalusArmure(),
new RollPartEmpoignadeTaille(),
new RollPartEmpoignade(),
new RollPartEncTotal(),
new RollPartSurEnc(),
new RollPartAppelMoral(),
@@ -169,6 +177,15 @@ const ROLL_PARTS = [
/* -------------------------------------------- */
export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2)
{
static onCloseDoNothing() {
}
static onRollDoneDoNothing(dialog, roll) {
dialog.render()
}
static onRollDoneClose(dialog, roll) {
if (roll.type.retry || !OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
}
static init() {
}
@@ -186,7 +203,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
ChatRollResult.onReady()
foundry.applications.handlebars.loadTemplates(ALL_ROLL_TYPES.map(m => m.template))
foundry.applications.handlebars.loadTemplates(ALL_ROLL_TYPES.map(m => m.chatResultTemplate))
foundry.applications.handlebars.loadTemplates(ROLL_PARTS.map(p => p.template))
ROLL_PARTS.forEach(p => p.onReady())
@@ -224,6 +241,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
static async create(rollData, rollOptions = {}) {
const rollDialog = new RollDialog(rollData, rollOptions)
rollDialog.render(true)
return rollDialog
}
static get PARTS() {
@@ -256,13 +274,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
rollData.selected = rollData.selected ?? {}
rollData.type = rollData.type ?? {}
rollData.type.retry = rollData.type.retry ?? false
BASIC_PARTS.restore(rollData)
RollBasicParts.restore(rollData)
const potential = ALL_ROLL_TYPES.find(m => m.code == rollData.type.current)?.code
const allowed = rollData.type.retry && potential
? [potential]
: (rollData.type.allowed ?? ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code))
const rollType = allowed.find(c => c == rollData.type.current) ?? (allowed.length > 0 ? allowed[0].code : ROLL_TYPE_COMP);
: (rollData.type.allowed ?? ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code) ?? [ROLL_TYPE_COMP])
const rollType = allowed.find(c => c == rollData.type.current) ?? allowed[0]
rollData.type.allowed = allowed
rollData.type.current = rollType
@@ -272,29 +290,37 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
rollData.options = rollData.options ?? { rollMode: game.settings.get("core", "rollMode") }
ROLL_PARTS.forEach(p => p.initialize(rollData))
ROLL_PARTS.forEach(p => p.restore(rollData))
ROLL_PARTS.filter(p => p.isValid(rollData))
.forEach(p => {
p.restore(rollData)
p.loadRefs(rollData)
p.prepareContext(rollData)
})
return rollData
}
static saveParts(rollData) {
const target = BASIC_PARTS.initFrom(rollData)
static saveParts(rollData, impacts) {
const target = RollBasicParts.initFrom(rollData)
ROLL_PARTS.filter(p => p.isActive(rollData))
.forEach(p => p.storeClean(rollData, target))
target.attackerRoll = rollData.attackerRoll
target.rolled = rollData.rolled
target.result = rollData.result
target.done = target.done ?? {}
target.done = rollData.done ?? {}
target.dmg = rollData.dmg
if (impacts) {
target.reverse = {
active: impacts.active?.reverseImpacts(),
opponent: impacts.opponent?.reverseImpacts()
}
}
return target
}
constructor(rollData, rollOptions) {
super()
this.hooks = []
this.rollData = RollDialog.$prepareRollData(rollData)
this.rollOptions = {
callbacks: [
@@ -302,13 +328,28 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
async r => await r.active.actor.appliquerAppelMoral(r),
...(rollOptions.callbacks ?? [])
],
customChatMessage: rollOptions.customChatMessage,
onRollDone: rollOptions.onRollDone ?? doNothing
onRollDone: rollOptions.onRollDone ?? RollDialog.onRollDoneDoNothing,
onClose: rollOptions.onClose ?? RollDialog.onCloseDoNothing
}
this.chatRollResult = new ChatRollResult();
this.chatRollResult = new ChatRollResult()
this.selectType()
this.registerHooks(rollData);
}
registerHooks(rollData) {
ROLL_PARTS.filter(p => p.isValid(rollData))
.forEach(p => p.getHooks(this).forEach(h => {
const hook = h.hook;
const id = Hooks.on(hook, h.fn)
this.hooks.push({ hook, id })
}))
}
unregisterHooks() {
this.hooks.forEach(h => Hooks.off(h.hook, h.id))
}
selectType() {
const selectedType = this.getSelectedType();
this.rollData.type.label = selectedType.title(this.rollData)
@@ -324,16 +365,16 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
return ROLL_PARTS.filter(p => p.isActive(rollData))
}
// get title() {
// return this.rollData.title ?? `Jet de dés de ${this.rollData.active.actor.name}`
// }
rollTitle(rollData) {
return rollData.label ?? ROLL_PARTS
const title = rollData.label ?? ROLL_PARTS
.filter(it => it.section == ROLLDIALOG_SECTION.ACTION)
.filter(it => it.isActive(rollData))
.map(it => it.title(rollData))
.reduce(Misc.joining(' '))
.reduce(Misc.joining(' '));
if (this.rollOptions.title) {
return `${this.rollOptions.title} ${title}`
}
return title
}
async _onRender(context, options) {
@@ -364,11 +405,11 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
return RollDialog.getActiveParts(rollData)
.map(p => p.getAjustements(rollData))
.reduce((a, b) => a.concat(b))
.sort((a, b) => a.diff == undefined ? 1 : b.diff == undefined ? -1 : 0)
.sort((a, b) => a.value == undefined ? 1 : b.value == undefined ? -1 : 0)
}
async buildHTMLTable(carac, diff) {
return await foundry.applications.handlebars.renderTemplate('roll-table', { carac, diff })
return await renderTemplate('roll-table', { carac, diff })
}
async _prepareContext() {
@@ -376,7 +417,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
const types = ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData))
.map(m => m.toTypeData(rollData))
BASIC_PARTS.loadSurprises(rollData, this.getSelectedType().code)
RollBasicParts.loadSurprises(rollData, this.getSelectedType().code)
rollData.type.label = this.getSelectedType()?.title(rollData)
//TOCHECK: set type.label ?
const visibleRollParts = RollDialog.getActiveParts(rollData)
@@ -386,7 +427,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
visibleRollParts.forEach(p => p.prepareContext(rollData))
RollDialog.calculAjustements(rollData)
RollDialog.calculAjustement(rollData)
const templates = RollDialog.getActiveParts(rollData).map(p => p.toTemplateData())
const context = await super._prepareContext()
@@ -408,12 +449,11 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
}
}
static calculAjustements(rollData) {
static calculAjustement(rollData) {
rollData.ajustements = RollDialog.getAjustements(rollData)
rollData.ajustements.forEach(it => it.isDiff = it.diff != undefined)
rollData.current.totaldiff = rollData.ajustements
.map(adj => adj.diff)
.filter(d => d != undefined)
.filter(a => a.value != undefined)
.map(a => a.value)
.reduce(Misc.sum(), 0)
}
@@ -421,34 +461,52 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
return ALL_ROLL_TYPES.find(m => m.code == this.rollData.type.current)
}
async close(options) {
if (this.rollOptions.onClose) {
this.rollOptions.onClose()
}
this.unregisterHooks()
return await super.close(options)
}
async roll() {
// ROLL_PARTS.filter(p => p.isActive(this.rollData))
// .forEach(p => p.validate(this.rollData))
const roll = RollDialog.saveParts(this.rollData)
RollDialog.loadRollData(roll)
const selectedRollType = this.getSelectedType(roll);
selectedRollType.onSelect(roll)
roll.current.resultat = this.rollData.current[PART_TRICHER]?.resultat ?? -1
roll.rolled = await this.$rollDice(roll)
roll.result = this.getSelectedType(roll).getResult(roll)
console.info('RollDialog.roll:', roll)
await Promise.all(this.rollOptions.callbacks.map(async callback => await callback(roll)))
await this.chatRollResult.display(roll)
roll.choix = {}
roll.rolled = await RollDialogAdapter.rollDice(roll, this.rollTitle(roll))
this.rollOptions.onRollDone(this)
const impacts = new ActorImpacts(roll.active)
roll.result = selectedRollType.getResult(roll, impacts)
console.info('RollDialog.roll:', roll)
const callbacks = [
...this.rollOptions.callbacks,
...selectedRollType.callbacks(this.rollOptions),
]
await Promise.all(callbacks.map(async callback => await callback(roll)))
await impacts.applyImpacts()
selectedRollType.onApplyImpacts(roll, impacts)
await this.chatRollResult.display(roll, impacts)
this.rollOptions.onRollDone(this, roll)
}
static loadRollData(roll) {
RollDialog.$prepareRollData(roll)
RollDialog.calculAjustements(roll)
RollDialog.calculAjustement(roll)
roll.v2 = true
return roll
}
async defaultCallback(rollData, rolled) {
await rollData.active.actor.appliquerAjoutExperience(rollData)
await rollData.active.actor.appliquerAppelMoral(rollData)
async defaultCallback(roll, rolled) {
await roll.active.actor.appliquerAjoutExperience(roll)
await roll.active.actor.appliquerAppelMoral(roll)
}
async $rollDice(rollData) {
return await RollDialogAdapter.rollDice(rollData, this.rollTitle(rollData))
}
}

View File

@@ -1,15 +1,25 @@
import { RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { RdDBonus } from "../rdd-bonus.js"
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs"
import { CARACS } from "../rdd-carac.js"
import { RdDEmpoignade } from "../rdd-empoignade.js"
import { DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP } from "./roll-constants.mjs"
import RollDialog from "./roll-dialog.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
import { PART_DIFF } from "./roll-part-diff.mjs"
import { RollPartSelect } from "./roll-part-select.mjs"
import { PART_SIGN } from "./roll-part-sign.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
export const PART_ATTAQUE = 'attaque'
const TACTIQUES = RdDBonus.tactiques.filter(it => it.isTactique)
const FILTER_ATTAQUE_EMPOIGNADE = attaque => attaque.arme.isEmpoignade()
const FILTER_ATTAQUE_NON_EMPOIGNADE = attaque => !attaque.arme.isEmpoignade()
const FILTER_ATTAQUE_EMPOIGNE = attaque => attaque.arme.isUtilisableEmpoigne() && ATTAQUE_TYPE_MELEE.includes(attaque.main)
export class RollPartAttaque extends RollPartSelect {
get code() { return PART_ATTAQUE }
@@ -20,49 +30,70 @@ export class RollPartAttaque extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const attaques = rollData.active.actor.listAttaques()
refs.attaques = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor))
refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor))
this.filterAttaquesEmpoignade(rollData)
refs.tactiques = TACTIQUES
if (refs.attaques.length > 0) {
this.$selectAttaque(rollData)
const attaque = this.findAttaque(refs.attaques, this.getSaved(rollData))
this.$selectAttaque(rollData, attaque?.key)
}
}
isAttaqueEmpoignade(it) {
return it.arme.isEmpoignade()
}
store(rollData, targetData) {
super.store(rollData, targetData)
this.getSaved(targetData).dmg = this.getCurrent(rollData).dmg
}
restore(rollData) {
const saved = this.getSaved(rollData)
super.restore(rollData)
this.getCurrent(rollData).dmg = this.getSaved(rollData).dmg
if (saved.dmg != undefined) {
this.getCurrent(rollData).dmg = this.getSaved(rollData).dmg
}
}
findAttaque(attaques, saved) {
return attaques.find(at => at.arme.id == saved?.arme?.id &&
at.comp.id == saved?.comp?.id
)
}
choices(refs) { return refs.attaques }
static $extractAttaque(attaque, actor) {
return {
key: `${attaque.action}::${attaque.name}`,
label: attaque.name,
attaque: attaque,
tactique: TACTIQUES[0],
}
attaque.key = `${attaque.action}::${attaque.label}`
attaque.tactique = TACTIQUES[0]
attaque.initialDiff = attaque.comp?.system.default_diffLibre ?? 0
return attaque
}
prepareContext(rollData) {
this.filterAttaquesEmpoignade(rollData)
const current = this.getCurrent(rollData)
current.dmg = RdDBonus.dmgRollV2(rollData, current)
}
filterAttaquesEmpoignade(rollData) {
const refs = this.getRefs(rollData)
const isEmpoignade = RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
refs.isEmpoignadeEnCours = RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor)
const filterAttaques = isEmpoignade ?
FILTER_ATTAQUE_EMPOIGNADE
: refs.isEmpoignadeEnCours
? FILTER_ATTAQUE_EMPOIGNE
: FILTER_ATTAQUE_NON_EMPOIGNADE
refs.attaques = refs.all.filter(filterAttaques)
}
getAjustements(rollData) {
const current = this.getCurrent(rollData)
const ajustements = []
if (current.tactique) {
ajustements.push({ label: current.tactique.label, diff: current.tactique.attaque })
}
if (rollData.opponent?.surprise) {
ajustements.push({ label: rollData.opponent.surprise.label, diff: rollData.opponent.surprise.attaque })
}
return ajustements
const tactique = current.tactique ? [{ label: current.tactique.label, value: current.tactique.attaque }] : []
const surprise = rollData.opponent?.surprise ? [{ label: rollData.opponent.surprise.label, value: rollData.opponent.surprise.attaque }] : []
return [...tactique, ...surprise]
}
@@ -74,6 +105,7 @@ export class RollPartAttaque extends RollPartSelect {
const selectAttaque = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-attaque"]`)
const selectTactique = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-tactique"]`)
const checkMortalite = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="check-mortalite"]`)
const utiliserDagueEmpoignade = rollDialog.element.querySelector(`roll-section[name="${this.code}"] a.utiliser-dague-empoignade`)
const current = this.getCurrent(rollDialog.rollData)
selectAttaque.addEventListener("change", e => {
@@ -91,17 +123,42 @@ export class RollPartAttaque extends RollPartSelect {
})
checkMortalite?.addEventListener("change", e => {
current.dmg.mortalite = (e.currentTarget.checked ? 'mortel' : 'non-mortel')
current.dmg.mortalite = (e.currentTarget.checked ? RDD_CONFIG.encaissement.mortel : RDD_CONFIG.encaissement.nonmortel)
rollDialog.render()
})
utiliserDagueEmpoignade?.addEventListener("click", e => {
e.preventDefault()
const rollData = rollDialog.rollData
this.utiliserDagueEmpoignade(rollData)
})
}
utiliserDagueEmpoignade(rollData) {
RollDialog.create({
ids: { actorId: rollData.ids.actorId, actorTokenId: rollData.ids.actorTokenId },
type: { allowed: [ROLL_TYPE_COMP], current: ROLL_TYPE_COMP },
selected: {
carac: { key: CARACS.DEXTERITE, forced: true },
comp: { key: 'Dague', forced: true },
diff: { type: DIFF.IMPOSEE, value: -4 }
}
})
}
impactOtherPart(part, rollData) {
if (this.visible(rollData)) {
const current = this.getCurrent(rollData)
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [current.attaque.carac.key])
case PART_COMP: return part.filterComps(rollData, [current.attaque.comp?.name])
case PART_CARAC: return part.filterCaracs(rollData, [current.carac.key])
case PART_COMP: return part.filterComps(rollData, [current.comp.name])
case PART_DIFF: {
if (current.initialDiff) {
part.setDiff(rollData, { type: DIFF.ATTAQUE, value: current.initialDiff })
current.initialDiff = undefined
}
break
}
case PART_SIGN: return part.setArme(rollData, false, current.forceRequise)
}
}
return undefined

View File

@@ -1,3 +1,4 @@
import { Grammar } from "../grammar.js"
import { RollPartSelect } from "./roll-part-select.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
@@ -12,22 +13,36 @@ export class RollPartCarac extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
refs.all = this.$getActorCaracs(rollData)
const selected = this.getSelected(rollData)
const actor = rollData.active.actor
refs.all = [...this.$getActorCaracs(actor), ...this.$getCaracCompetenceCreature(actor)]
.filter(c => !selected.forced ||
(selected.key ?
Grammar.includesLowerCaseNoAccent(c.label, selected.key)
: c.key == '')
)
refs.caracs = refs.all
this.$selectCarac(rollData)
}
choices(refs) { return refs.caracs }
$getActorCaracs(rollData) {
return Object.entries(rollData.active.actor.getCarac())
$getActorCaracs(actor) {
return Object.entries(actor.getCarac())
.filter(([key, c]) => key != 'taille')
/* TODO: filter by context */
.map(([key, carac]) => {
return RollPartCarac.$extractCarac(key, carac)
})
}
$getCaracCompetenceCreature(actor) {
if (actor.isPersonnage()) {
return []
}
return actor.getCompetences()
.map(it => { return { key: it.name, label: it.name, value: parseInt(it.system.carac_value) } })
}
static $extractCarac(key, carac) {
return {
key: key,
@@ -40,7 +55,6 @@ export class RollPartCarac extends RollPartSelect {
allowed = allowed.filter(it => it != undefined)
const refs = this.getRefs(rollData)
refs.caracs = allowed.length > 0
// ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.key)))
? refs.all.filter(it => allowed.includes(it.key))
: refs.all
this.$selectCarac(rollData)

View File

@@ -28,16 +28,14 @@ export class RollPartCheckbox extends RollPart {
/* TODO: user setting? */
current.checked = true
}
if (current.value == undefined) {
current.value = this.getCheckboxValue(rollData)
}
current.value = this.getCheckboxValue(rollData)
current.icon = this.getCheckboxIcon(rollData)
}
getAjustements(rollData) {
const current = this.getCurrent(rollData)
if (current.checked) {
return [{ label: this.getCheckboxLabelAjustement(rollData), diff: current.value }]
return [{ label: this.getCheckboxLabelAjustement(rollData), value: current.value }]
}
return []
}

View File

@@ -47,7 +47,7 @@ export class RollPartCoeur extends RollPartSelect {
if (current.key != '') {
return [{
label: "Coeur pour " + current.label,
diff: current.value
value: current.value
}]
}
return []

View File

@@ -17,7 +17,17 @@ export class RollPartComp extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
refs.all = this.$getActorComps(rollData)
const selected = this.getSelected(rollData)
const all = this.$getActorComps(rollData)
if (selected.forced) {
refs.all = all.filter(comp => Grammar.equalsInsensitive(comp.label, selected.key))
if (refs.all.length == 0) {
refs.all = all.filter(comp => Grammar.includesLowerCaseNoAccent(comp.label, selected.key))
}
}
else {
refs.all = all
}
refs.comps = refs.all
this.$selectComp(rollData)
}
@@ -26,7 +36,7 @@ export class RollPartComp extends RollPartSelect {
$getActorComps(rollData) {
const competences = (rollData.active.actor?.getCompetences() ?? [])
.map(RollPartComp.$extractComp)
.map(RollPartComp.extractComp)
.sort(Misc.ascending(it => Grammar.toLowerCaseNoAccentNoSpace(it.label)))
/* TODO: filter competences */
const listCompetences = [
@@ -36,7 +46,7 @@ export class RollPartComp extends RollPartSelect {
return listCompetences
}
static $extractComp(comp) {
static extractComp(comp) {
return {
key: comp.name,
label: comp.name,
@@ -45,13 +55,16 @@ export class RollPartComp extends RollPartSelect {
}
}
filterComps(rollData, allowed = []) {
filterComps(rollData, allowed = [], sorting = undefined) {
const sans = allowed.includes('')
allowed = allowed.filter(it => it != undefined)
const refs = this.getRefs(rollData)
refs.comps = allowed.length > 0
// ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.label)))
? refs.all.filter(it => allowed.includes(it.label))
? refs.all.filter(it => allowed.includes(it.label) || (sans && it.key == ''))
: refs.all
if (sorting && refs.comps.length > 0) {
refs.comps.sort(sorting)
}
this.$selectComp(rollData)
}
@@ -60,7 +73,7 @@ export class RollPartComp extends RollPartSelect {
}
setSpecialComp(rollData, comps) {
this.getRefs(rollData).comps = comps.map(RollPartComp.$extractComp)
this.getRefs(rollData).comps = comps.map(RollPartComp.extractComp)
.sort(Misc.ascending(it => Grammar.toLowerCaseNoAccentNoSpace(it.label)))
}

View File

@@ -57,7 +57,7 @@ export class RollPartConditions extends RollPart {
getAjustements(rollData) {
const current = this.getCurrent(rollData)
if (current.value != 0) {
return [{ label: DESCR_CONDITIONS, diff: current.value }]
return [{ label: DESCR_CONDITIONS, value: current.value }]
}
return []
}

View File

@@ -0,0 +1,174 @@
import { ITEM_TYPES } from "../constants.js"
import { CARACS } from "../rdd-carac.js"
import { ROLL_TYPE_CUISINE } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
import { RollPartSelect } from "./roll-part-select.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
export const PART_CUISINE = "cuisine"
export class RollPartCuisine extends RollPartSelect {
onReady() {
foundry.applications.handlebars.loadTemplates({ 'roll-oeuvre-recettecuisine': `systems/foundryvtt-reve-de-dragon/templates/roll/roll-oeuvre-recettecuisine.hbs` })
}
get code() { return PART_CUISINE }
get section() { return ROLLDIALOG_SECTION.CHOIX }
isValid(rollData) { return rollData.active.actor.isPersonnage() }
visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_CUISINE) }
restore(rollData) {
super.restore(rollData)
this.$restoreSavedOptions(rollData)
}
store(rollData, targetData) {
const current = this.getCurrent(rollData)
this.setSaved(targetData, {
key: current.key,
fabriquer: current.fabriquer,
proportions: current.proportions,
value: current.value,
})
}
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const actor = rollData.active.actor
refs.cuisine = actor.getCompetence('Cuisine')
const recettes = actor.items
.filter(it => it.type == ITEM_TYPES.recettecuisine)
.map(RollPartCuisine.$extractPreparationRecette)
const ingredientsBruts = actor.items
.filter(it => it.getUtilisationCuisine() == 'brut')
.map(RollPartCuisine.$extractPreparationBrut)
refs.preparations = [RollPartCuisine.$preparationBasique(), ...recettes, ...ingredientsBruts]
refs.preparations.forEach(p => p.comp = refs.cuisine)
if (refs.preparations.length > 0) {
this.$selectPreparation(rollData)
this.$restoreSavedOptions(rollData)
}
}
$restoreSavedOptions(rollData) {
const saved = this.getSaved(rollData)
const current = this.getCurrent(rollData)
if (saved.fabriquer != undefined) {
current.fabriquer = saved.fabriquer
}
if (saved.proportions != undefined) {
current.proportions = saved.proportions
}
if (saved.value != undefined) {
current.value = saved.value
}
}
choices(refs) { return refs.preparations }
static $preparationBasique() {
return {
key: '',
label: "Improvisation du moment",
img: RollPartCuisine.$getImgIngredient(),
sust: 1,
exotisme: 0,
ingredients: "Ce qu'il y a sous la main",
proportions: 8,
proportionsMax: 50,
value: 0,
fabriquer: false,
}
}
static $extractPreparationRecette(recette) {
const proportions = recette.system.sust ?? 1
return {
key: recette.id,
label: recette.name,
img: 'systems/foundryvtt-reve-de-dragon/icons/cuisine/ragout.svg',
sust: 1,
exotisme: recette.system.exotisme,
ingredients: recette.system.ingredients,
proportions: proportions,
proportionsMax: proportions * 10,
value: -recette.system.niveau,
recette: recette,
fabriquer: true,
}
}
static $extractPreparationBrut(ingredient) {
return {
key: ingredient.id,
label: ingredient.name + ' cuisiné',
img: RollPartCuisine.$getImgIngredient(ingredient.type),
sust: ingredient.system.sust ?? 1,
exotisme: ingredient.system.exotisme,
ingredients: ingredient.name,
proportions: Math.min(10, ingredient.system.quantite),
proportionsMax: Math.min(50, ingredient.system.quantite),
value: 0,
ingredient: ingredient,
fabriquer: true,
}
}
static $getImgIngredient(type = ITEM_TYPES.ingredient) {
switch (type) {
case ITEM_TYPES.herbe:
case ITEM_TYPES.plante:
return `systems/foundryvtt-reve-de-dragon/icons/cuisine/${type}.svg`
case ITEM_TYPES.faune:
return 'systems/foundryvtt-reve-de-dragon/icons/cuisine/gibier.svg'
}
return 'systems/foundryvtt-reve-de-dragon/icons/cuisine/ragout.svg'
}
$selectPreparation(rollData, key) {
this.selectByKey(rollData, key, 0)
}
async _onRender(rollDialog, context, options) {
const current = this.getCurrent(rollDialog.rollData)
const selectPreparation = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-preparation"]`)
const inputDiff = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="diff-var"]`)
const checkboxFabriquer = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="fabriquer"]`)
const inputProportions = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="proportions"]`)
selectPreparation.addEventListener("change", e => {
const selectOptions = e.currentTarget.options
const index = selectOptions.selectedIndex
this.$selectPreparation(rollDialog.rollData, selectOptions[index]?.value)
rollDialog.render()
})
checkboxFabriquer?.addEventListener("change", e => {
current.fabriquer = e.currentTarget.checked
})
inputDiff?.addEventListener("change", e => {
current.value = parseInt(e.currentTarget.value)
})
inputProportions?.addEventListener("change", e => {
current.proportions = parseInt(e.currentTarget.value)
})
}
impactOtherPart(part, rollData) {
if (this.visible(rollData)) {
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [CARACS.ODORATGOUT])
case PART_COMP: return part.filterComps(rollData, [this.getRefs(rollData).cuisine.name])
}
}
return undefined
}
}

View File

@@ -1,9 +1,6 @@
import { ITEM_TYPES } from "../constants.js"
import { Grammar } from "../grammar.js"
import { ITEM_TYPES, RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE, RdDItemArme } from "../item/arme.js"
import { RdDBonus } from "../rdd-bonus.js"
import { CARACS } from "../rdd-carac.js"
import { StatusEffects } from "../settings/status-effects.js"
import { DIFF, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
@@ -18,6 +15,8 @@ export class RollPartDefense extends RollPartSelect {
get code() { return PART_DEFENSE }
get section() { return ROLLDIALOG_SECTION.CHOIX }
isValid(rollData) { return rollData.attackerRoll != undefined }
visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_DEFENSE) }
static getDiffAttaque(attackerRoll) {
@@ -30,13 +29,22 @@ export class RollPartDefense extends RollPartSelect {
const attackerRoll = rollData.attackerRoll
const defenseur = rollData.active.actor
refs.isDistance = [ATTAQUE_TYPE.TIR, ATTAQUE_TYPE.LANCER].find(it => it == attackerRoll?.main)
const esquives = refs.isDistance == ATTAQUE_TYPE.TIR ? [] : defenseur.getCompetencesEsquive()
.map(it => RollPartDefense.$extractEsquive(it, defenseur))
const isEmpoignade = attackerRoll.dmg.isEmpoignade
const isEmpoignadeEnCours = isEmpoignade && defenseur.itemTypes[ITEM_TYPES.empoignade].find(it =>
[it.system.empoigneurid, it.system.empoigneid].includes(rollData.ids.opponentId) &&
it.system.pointsemp != 0)
const parades = defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
.map(it => RollPartDefense.$extractParade(it, attackerRoll?.attaque.arme, defenseur))
const esquives = (refs.isDistance == ATTAQUE_TYPE.TIR || isEmpoignadeEnCours)
? []
: defenseur.getCompetencesEsquive()
const parades = isEmpoignade
? [RdDItemArme.empoignade(defenseur)]
: defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
refs.defenses = [...esquives, ...parades].filter(it => it != undefined)
refs.defenses = [
...esquives.map(it => RollPartDefense.$extractEsquive(it, defenseur)),
...parades.map(it => RollPartDefense.$extractParade(it, attackerRoll?.arme, defenseur))
]
this.$selectDefense(rollData)
}
@@ -104,7 +112,7 @@ export class RollPartDefense extends RollPartSelect {
case PART_CARAC: return part.filterCaracs(rollData, [current.carac])
case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
case PART_DIFF: return part.setDiff(rollData, this.getDiffDefense(rollData))
case PART_SIGN: return part.setArmeDisparate(rollData, this.isArmeDisparate(rollData))
case PART_SIGN: return part.setArme(rollData, this.isArmeDisparate(rollData), current.forceRequise)
}
}
return undefined
@@ -113,21 +121,24 @@ export class RollPartDefense extends RollPartSelect {
isArmeDisparate(rollData) {
const armeDefense = this.getCurrent(rollData).arme
if (armeDefense) {
const armeAttaque = rollData.attackerRoll?.attaque.arme
const armeAttaque = rollData.attackerRoll?.current.attaque.arme
return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) == 'sign'
}
return false
}
getDiffDefense(rollData) {
const current = this.getCurrent(rollData)
const refs = this.getRefs(rollData)
if (refs.isDistance || !rollData.attackerRoll) {
// Déterminer la difficulté de parade
// TODO: Déterminer la difficulté de parade
return { diff: 0, type: DIFF.LIBRE }
}
else {
return { diff: rollData.attackerRoll.diff ?? 0, type: DIFF.DEFENSE }
const attackerRoll = rollData.attackerRoll
const diff = attackerRoll.v2
? attackerRoll.selected.diff.value
: attackerRoll.diff
return { diff: diff ?? 0, type: DIFF.DEFENSE }
}
}
}

View File

@@ -1,10 +1,15 @@
import { DIFF, DIFFS, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_TACHE } from "./roll-constants.mjs";
import { DIFF, DIFFS, ROLL_TYPE_CUISINE, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_TACHE } from "./roll-constants.mjs";
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs";
import { Misc } from "../misc.js";
export const PART_DIFF = "diff"
const EXCLUDED_ROLL_TYPES = [ROLL_TYPE_TACHE, ROLL_TYPE_MEDITATION, ROLL_TYPE_SORT, ROLL_TYPE_OEUVRE]
const EXCLUDED_ROLL_TYPES = [
ROLL_TYPE_TACHE,
ROLL_TYPE_MEDITATION,
ROLL_TYPE_SORT,
ROLL_TYPE_CUISINE,
ROLL_TYPE_OEUVRE]
export class RollPartDiff extends RollPart {
@@ -51,17 +56,17 @@ export class RollPartDiff extends RollPart {
)
}
setDiff(rollData, diffDefense) {
setDiff(rollData, diff) {
const current = this.getCurrent(rollData)
current.value = diffDefense.diff
current.type = diffDefense.type
current.value = diff.diff ?? current.value
current.type = diff.type ?? current.type
}
getAjustements(rollData) {
const current = this.getCurrent(rollData)
return [{
label: current.label,
diff: current.value
value: current.value
}]
}

View File

@@ -0,0 +1,40 @@
import { RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { RdDEmpoignade } from "../rdd-empoignade.js"
import { COMBAT_ROLL_TYPES } from "./roll-constants.mjs"
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
const EMPOIGNADE_TAILLE = "empoignade-taille"
export class RollPartEmpoignadeTaille extends RollPartCheckbox {
get code() { return EMPOIGNADE_TAILLE }
isValid(rollData) {
return RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
}
visible(rollData) {
return COMBAT_ROLL_TYPES.includes(rollData.type.current) &&
RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor) &&
this.getTailleDiff(rollData) != 0
}
getTailleDiff(rollData) {
const taille = rollData.active.actor.getTaille()
const tailleOpponent = rollData.opponent.actor.getTaille()
const diff = taille - tailleOpponent
const diffTailleAbs = Math.max(0, Math.abs(diff) - 1)
const signDiff = Math.sign(diff)
return signDiff * diffTailleAbs
}
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.empoignade}">` }
getCheckboxLabel(rollData) {
return `Taille ${rollData.active.actor.getTaille()} vs ${rollData.opponent.actor.getTaille()} `
}
getCheckboxValue(rollData) {
return this.getTailleDiff(rollData)
}
}

View File

@@ -0,0 +1,30 @@
import { RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { RdDEmpoignade } from "../rdd-empoignade.js"
import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs"
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
const EMPOIGNADE = "empoignade"
export class RollPartEmpoignade extends RollPartCheckbox {
get code() { return EMPOIGNADE }
isValid(rollData) {
return RdDEmpoignade.isCombatantEmpoignade(rollData.ids.opponentId, rollData.ids.opponentTokenId) &&
!RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
}
visible(rollData) {
return rollData.type.current == ROLL_TYPE_ATTAQUE &&
ATTAQUE_TYPE_MELEE.includes(rollData.current[PART_ATTAQUE].main) &&
RdDEmpoignade.isCombatantEmpoignade(rollData.ids.opponentId, rollData.ids.opponentTokenId) &&
!RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId) &&
!RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor)
}
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.empoignade}">` }
getCheckboxLabel(rollData) { return "vs. empoigneur" }
getCheckboxValue(rollData) { return 4 }
}

View File

@@ -1,3 +1,4 @@
import { RDD_CONFIG } from "../constants.js"
import { RdDItemCompetence } from "../item-competence.js"
import { RdDCarac } from "../rdd-carac.js"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
@@ -25,7 +26,7 @@ export class RollPartEncTotal extends RollPartCheckbox {
})
}
getCheckboxIcon(rollData) { return '<i class="fa-solid fa-weight-hanging"></i>' }
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.surenc}">` }
getCheckboxLabel(rollData) { return "Enc. total" }
getCheckboxValue(rollData) { return - rollData.active.actor.getEncTotal() }
}

View File

@@ -1,15 +1,16 @@
import { Grammar } from "../grammar.js"
import { ITEM_TYPES } from "../constants.js"
import { CARACS } from "../rdd-carac.js"
import { ROLL_TYPE_JEU } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
import { PART_COMP, RollPartComp } from "./roll-part-comp.mjs"
import { RollPartSelect } from "./roll-part-select.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
import { RdDItem } from "../item.js"
import { Misc } from "../misc.js"
export const PART_JEU = "jeu"
const COMPETENCE_JEU = 'Jeu'
export const COMPETENCE_JEU = 'Jeu'
export class RollPartJeu extends RollPartSelect {
@@ -21,8 +22,12 @@ export class RollPartJeu extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
refs.jeux = rollData.active.actor.itemTypes[ITEM_TYPES.jeu]
.map(it => RollPartJeu.$extractJeu(it, rollData.active.actor))
const actor = rollData.active.actor
const compJeu = actor.getCompetence(COMPETENCE_JEU)
refs.jeux = actor.itemTypes[ITEM_TYPES.jeu]
.map(it => RollPartJeu.$extractJeu(it, compJeu))
if (refs.jeux.length > 0) {
this.$selectJeu(rollData)
}
@@ -30,18 +35,17 @@ export class RollPartJeu extends RollPartSelect {
choices(refs) { return refs.jeux }
static $extractJeu(jeu, actor) {
const comp = actor.getCompetence(COMPETENCE_JEU)
static $extractJeu(jeu, compJeu) {
const caracs = jeu.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim())
const base = RollPartJeu.$getJeuBase(jeu, comp, caracs)
const base = RollPartJeu.$getJeuBase(jeu, compJeu, caracs)
return {
key: jeu.id,
label: jeu.name,
caracs: caracs,
jeu: jeu,
value: (base ?? comp).system.niveau,
value: 0,
base: base,
comp: comp
comp: compJeu
}
}
@@ -49,10 +53,10 @@ export class RollPartJeu extends RollPartSelect {
if (jeu.system.base < comp.system.niveau) {
return undefined
}
return {
return new RdDItem({
id: comp.id,
name: `Jeu ${jeu.name}`,
type: comp.type,
name: `Jeu ${Misc.lowerFirst(jeu.name)}`,
type: ITEM_TYPES.competence,
img: comp.img,
system: foundry.utils.mergeObject(
{
@@ -61,9 +65,9 @@ export class RollPartJeu extends RollPartSelect {
default_carac: caracs.length > 0 ? caracs[0] : CARACS.CHANCE
},
comp.system,
{ inplace: true, overwrite: false }
{ overwrite: false }
)
}
})
}
prepareContext(rollData) {
@@ -77,6 +81,15 @@ export class RollPartJeu extends RollPartSelect {
$selectJeu(rollData, key) {
this.selectByKey(rollData, key, 0)
if (rollData.type.current == ROLL_TYPE_JEU) {
RollPartJeu.forceCompJeu(rollData)
}
}
static forceCompJeu(rollData) {
const jeu = rollData.current[PART_JEU].base ?? rollData.current[PART_JEU].comp
rollData.refs[PART_COMP].comps = [jeu].map(it => RollPartComp.extractComp(it))
rollData.current[PART_COMP] = rollData.refs[PART_COMP].comps[0]
}
async _onRender(rollDialog, context, options) {
@@ -95,7 +108,7 @@ export class RollPartJeu extends RollPartSelect {
const current = this.getCurrent(rollData)
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, current.caracs)
case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
}
}
return undefined

View File

@@ -1,36 +1,52 @@
import { ITEM_TYPES } from "../constants.js"
import { Grammar } from "../grammar.js"
import { CARACS, RdDCarac } from "../rdd-carac.js"
import { CARACS } from "../rdd-carac.js"
import { RdDTimestamp } from "../time/rdd-timestamp.js"
import { TMRUtility } from "../tmr-utility.js"
import { ROLL_TYPE_MEDITATION } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_CARAC, RollPartCarac } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
import { RollPartSelect } from "./roll-part-select.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
export const PART_MEDITATION = "meditation"
const COMPORTEMENTS = ['isComportement', 'isHeure', 'isPurification', 'isVeture']
export class RollPartMeditation extends RollPartSelect {
get code() { return PART_MEDITATION }
get section() { return ROLLDIALOG_SECTION.CHOIX }
store(rollData, targetData) {
const current = this.getCurrent(rollData)
this.setSaved(targetData, {
key: current.key,
isComportement: current.isComportement,
isHeure: current.isHeure,
isPurification: current.isPurification,
isVeture: current.isVeture
})
}
isValid(rollData) { return rollData.active.actor.isPersonnage() && rollData.active.actor.isHautRevant() }
visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_MEDITATION) }
loadRefs(rollData) {
const refs = this.getRefs(rollData)
foundry.utils.mergeObject(refs,
{
meditations: rollData.active.actor.itemTypes[ITEM_TYPES.meditation]
.map(it => RollPartMeditation.$extractMeditation(it, rollData.active.actor))
}
)
const actor = rollData.active.actor
refs.meditations = actor.itemTypes[ITEM_TYPES.meditation]
.map(it => RollPartMeditation.$extractMeditation(it, actor))
if (refs.meditations.length > 0) {
this.$selectMeditation(rollData)
this.$selectConditionMeditation(rollData)
const selected = this.getSelected(rollData)
const current = this.getCurrent(rollData)
COMPORTEMENTS.filter(it => selected[it]).forEach(it => current[it] = selected[it])
}
}
choices(refs) { return refs.meditations }
static $extractMeditation(meditation, actor) {
@@ -48,12 +64,7 @@ export class RollPartMeditation extends RollPartSelect {
getMalusConditions(rollData) {
const current = this.getCurrent(rollData)
const conditionsManquantes = [
current.isComportement,
current.isHeure,
current.isPurification,
current.isVeture
].filter(it => !it).length
const conditionsManquantes = COMPORTEMENTS.filter(it => !current[it]).length
return -2 * conditionsManquantes
}
@@ -62,22 +73,28 @@ export class RollPartMeditation extends RollPartSelect {
}
getAjustements(rollData) {
const malusEchecs = { label: "Méditation", diff: this.getMalusEchecs(rollData) }
const malusConditions = { label: "Conditions", diff: this.getMalusConditions(rollData) }
return [malusConditions, ...(malusEchecs.diff == 0 ? [] : [malusEchecs])]
const malus = this.getMalusEchecs(rollData)
const malusEchecs = malus == 0 ? [] : [{ label: "ditation", value: malus }]
const malusConditions = { label: "Conditions", value: this.getMalusConditions(rollData) }
return [malusConditions, ...malusEchecs]
}
$selectMeditation(rollData, key) {
const previous = this.getCurrent(rollData)
const current = this.selectByKey(rollData, key, 0)
if (current.key != previous.key) {
const heureMonde = RdDTimestamp.getWorldTime().heure
const heureMeditation = RdDTimestamp.findHeure(current.meditation.system.heure)?.heure
current.isHeure = heureMeditation == heureMonde
current.isTMR = Grammar.equalsInsensitive(current.meditation.system.tmr, TMRUtility.getTMRType(rollData.active.actor.system.reve.tmrpos.coord))
this.$selectConditionMeditation(rollData)
}
}
$selectConditionMeditation(rollData) {
const current = this.getCurrent(rollData)
current.heureMonde = RdDTimestamp.getWorldTime().heure
current.heureMeditation = RdDTimestamp.findHeure(current.meditation.system.heure)?.heure
current.isHeure = current.heureMeditation == current.heureMonde
current.isTMR = Grammar.equalsInsensitive(current.meditation.system.tmr, TMRUtility.getTMRType(rollData.active.actor.system.reve.tmrpos.coord))
}
async _onRender(rollDialog, context, options) {
const selectMeditation = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-meditation"]`)
@@ -88,10 +105,7 @@ export class RollPartMeditation extends RollPartSelect {
rollDialog.render()
})
this.setupListenerCondition(rollDialog, 'isComportement')
this.setupListenerCondition(rollDialog, 'isHeure')
this.setupListenerCondition(rollDialog, 'isPurification')
this.setupListenerCondition(rollDialog, 'isVeture')
COMPORTEMENTS.forEach(it => this.setupListenerCondition(rollDialog, it))
}
setupListenerCondition(rollDialog, inputName) {
@@ -108,7 +122,7 @@ export class RollPartMeditation extends RollPartSelect {
const current = this.getCurrent(rollData)
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [CARACS.INTELLECT])
case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
}
}
return undefined

View File

@@ -11,6 +11,7 @@ export const PART_OEUVRE = "oeuvre"
const ARTS = [
{ type: ITEM_TYPES.oeuvre, action: "interpréte l'oeuvre", competence: it => it.system.competence, caracs: it => [it.system.default_carac] },
{ type: ITEM_TYPES.musique, action: "joue le morceau:", competence: it => 'Musique', caracs: it => [CARACS.OUIE] },
{ type: ITEM_TYPES.chant, action: "chante", competence: it => 'Chant', caracs: it => [CARACS.OUIE] },
{
type: ITEM_TYPES.danse, action: "danse:", competence: it => 'Danse', caracs: it => {
@@ -20,8 +21,6 @@ const ARTS = [
return caracs
}
},
{ type: ITEM_TYPES.musique, action: "joue le morceau:", competence: it => 'Musique', caracs: it => [CARACS.OUIE] },
{ type: ITEM_TYPES.recettecuisine, action: "cuisine le plat:", competence: it => 'Cuisine', caracs: it => [CARACS.ODORATGOUT] },
]
export class RollPartOeuvre extends RollPartSelect {

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