Compare commits

...

22 Commits

Author SHA1 Message Date
0d62b60e38 Merge pull request 'v13.0.16 - La paix d'Illysis' (#782) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
All checks were successful
Release Creation / build (release) Successful in 3m38s
Reviewed-on: #782
2025-11-02 09:55:25 +01:00
9b91850731 Export de personnage en pdf
Première version d'export de personnage pdf
2025-11-01 01:28:44 +01:00
05c7a91f93 Correction d'un problème à l'ouverture
Pour un personnage saisis dans d'anciennes versions avec un sort
de coût entier, la feuille de personnage ne pouvait pas s'ouvrir
2025-11-01 00:44:26 +01:00
f83fdb3b8f Fix: fenêtre jets V2 hors combat
la fenêtre ne marchait pas hors d'un combat
2025-10-29 02:03:43 +01:00
020ff4b014 Merge pull request 'v13.0.15 - Les pièces d'Illysis' (#781) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
All checks were successful
Release Creation / build (release) Successful in 1m45s
Reviewed-on: #781
2025-10-28 18:43:25 +01:00
b3c7453823 Fix achat dans les commerces 2025-10-28 17:57:27 +01:00
18d003aa5d Fix colors of tabs and lists in interface
These colors where misbehaving in dark/light themes
2025-10-28 17:57:27 +01:00
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
161 changed files with 80896 additions and 779 deletions

View File

@@ -26,3 +26,6 @@ Merci à eux !!
Toute la propriété intellectuelle leur appartient, ce système est une adaptation destinée à fonctionner sous FoundryVTT.
L'ensemble du code est sous licence Creative Commons.
# Licences
- L'export pdf utilise la librairie [pdf-lib](https://pdf-lib.js.org/) sous licence [MIT](pdf-lib-LICENSE.md)

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,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

Binary file not shown.

View File

@@ -1,4 +1,55 @@
# 13.0
## 13.0.16 - La paix d'Illysis
- Export de personnages sous forme de feuille de personnage pdf
- Correction d'un problème à l'ouverture de personnages saisis dans d'anciennes versions
- Fenêtre de jet v2
- on peut faire des jets en dehors des combats
## 13.0.15 - Les pièces d'Illysis
- On peut de nouveau acheter dans les commerces
- Corrections V13
- Les textes sur fond "parchemin" ne sont plus affichés en blanc
## 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

View File

@@ -648,15 +648,19 @@ select,
.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[name="tricher"] img {
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
.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;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-buttons {
display: flex;
flex-direction: row-reverse;
@@ -707,6 +711,10 @@ select,
display: flex;
flex-direction: column;
}
.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;
@@ -757,7 +765,10 @@ select,
.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;
@@ -848,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);
@@ -1367,6 +1378,9 @@ select,
margin: 0.1rem 0;
align-items: center;
}
.system-foundryvtt-reve-de-dragon .prosemirror menu {
background-color: var(--color-background-chat-message);
}
.system-foundryvtt-reve-de-dragon .app.sheet .editor.prosemirror {
height: fit-content;
min-height: 5rem;
@@ -1558,22 +1572,70 @@ select,
.system-foundryvtt-reve-de-dragon .type-compendium {
font-size: 0.6rem;
}
.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;
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body,
.system-foundryvtt-reve-de-dragon .application .window-content,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-header :is(
input[type="text"],
input[type="number"],
input[type="password"],
input[type="datetime-local"],
input[type="date"],
input[type="time"]) {
color: rgba(255, 255, 255, 0.75);
background: rgba(255, 255, 255, 0.1);
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body label,
.system-foundryvtt-reve-de-dragon .application .window-content label,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar label,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body .hint,
.system-foundryvtt-reve-de-dragon .application .window-content .hint,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar .hint,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body .permissions-list,
.system-foundryvtt-reve-de-dragon .application .window-content .permissions-list,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar .permissions-list,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body nav.tabs,
.system-foundryvtt-reve-de-dragon .application .window-content nav.tabs,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar nav.tabs,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body nav.tabs button,
.system-foundryvtt-reve-de-dragon .application .window-content nav.tabs button,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar nav.tabs button,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .application .window-content nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body button,
.system-foundryvtt-reve-de-dragon .application .window-content button,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar button {
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body a,
.system-foundryvtt-reve-de-dragon .application .window-content a,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar a {
color: var(--color-dark-3);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-body a.filter.active,
.system-foundryvtt-reve-de-dragon .application .window-content a.filter.active,
.system-foundryvtt-reve-de-dragon .journal-entry .journal-sidebar a.filter.active {
color: var(--color-dark-1);
}
.system-foundryvtt-reve-de-dragon .window-app .window-content {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header {
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header label,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header .hint,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header .permissions-list,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs button,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs button .count,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header div,
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header button {
color: rgba(255, 255, 255, 0.9);
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input {
color: rgba(255, 255, 255, 0.9);
border: 0 none;
margin-bottom: 0.2rem;
}
.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input[type="checkbox"] {
color: rgba(255, 255, 255, 0.75);
}
.system-foundryvtt-reve-de-dragon input[type="number"] {
text-align: right;
padding-right: 0.5rem;
@@ -1614,7 +1676,7 @@ select,
width: calc(100% - 2px);
height: var(--form-field-height);
margin: 0;
color: var(--color-text-dark-primary);
color: var(--rdd-color-text-primary);
border-radius: 0.2rem;
}
.system-foundryvtt-reve-de-dragon form.app-personnage-aleatoire h2 {
@@ -1640,8 +1702,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;
@@ -2316,7 +2379,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%;
@@ -2700,6 +2763,13 @@ select,
.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);

View File

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

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 {
@@ -653,9 +656,15 @@
margin: 0.1rem 0;
align-items: center;
}
.prosemirror {
menu{
background-color: var(--color-background-chat-message);
}
}
.app.sheet .editor.prosemirror {
height: fit-content;
min-height: 5rem;
}
.app.sheet .editor.prosemirror .editor-container {
min-height: 5rem;
@@ -862,26 +871,60 @@
.type-compendium {
font-size: 0.6rem;
}
.window-app.sheet .window-content .sheet-body,
.application .window-content,
.journal-entry .journal-sidebar {
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
label ,
.hint ,
.permissions-list ,
nav.tabs,
nav.tabs button,
nav.tabs button .count,
button {
color: var(--rdd-color-text-primary);
}
a {
color: var(--color-dark-3);
}
a.filter.active {
color: var(--color-dark-1);
}
}
/* ======================================== */
/* Sheet */
.window-app.sheet .window-content .sheet-header{
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
color: rgba(255, 255, 255, 1);
.window-app .window-content{
background: url(../assets/ui/bg_left.webp) no-repeat left top;
color: var(--rdd-color-text-primary);
.sheet-header {
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
label ,
.hint ,
.permissions-list ,
nav.tabs,
nav.tabs button,
nav.tabs button .count,
div,
button {
color: rgba(255, 255, 255, 0.9);
}
input {
//color: rgba(255, 255, 255, 0);
color: rgba(255, 255, 255, 0.9);
border: 0 none;
margin-bottom: 0.2rem;
}
input[type="checkbox"] {
// background-color: hsla(268, 41%, 56%, 0.9);
color: rgba(255, 255, 255, 0.75);
}
}
}
.window-app.sheet .window-content .sheet-header :is(
input[type="text"],
input[type="number"],
input[type="password"],
input[type="datetime-local"],
input[type="date"],
input[type="time"]) {
color: rgba(255, 255, 255, 0.75);
background: rgba(255, 255, 255, 0.1);
border: 0 none;
margin-bottom: 0.2rem;
}
input[type="number"] {
text-align: right;
@@ -925,7 +968,7 @@
width: calc(100% - 2px);
height: var(--form-field-height);
margin: 0;
color: var(--color-text-dark-primary);
color: var(--rdd-color-text-primary);
border-radius: 0.2rem;
}
form.app-personnage-aleatoire {
@@ -955,8 +998,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 {
@@ -1677,7 +1722,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%;
}
@@ -1950,6 +1995,13 @@
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);
}
}
}

View File

@@ -37,6 +37,9 @@
text-align: justify;
display: flex;
flex-direction: column;
div {
display: block;
}
}
div.chat-actions {
grid-area: actions;

View File

@@ -224,15 +224,19 @@
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

@@ -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

@@ -185,13 +185,13 @@ export class RdDActor extends RdDBaseActorSang {
const actions = []
const uniques = []
const addAttaque = (arme, main = undefined, action = 'attaque') => {
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 comp = this.getCompetence(arme.getCompetenceAction(main))
const unique = [comp.id, arme.name, dommages, forceRequise, ecaillesEfficacite].join('|');
if (uniques.includes(unique)) {
return
}
@@ -212,24 +212,20 @@ export class RdDActor extends RdDBaseActorSang {
main: main,
carac: { key: caracCode, value: caracValue },
equipe: arme.system.equipe,
dommagesArme: dommagesArme,
dommages: dommages,
forceRequise: forceRequise,
initiative: RdDInitiative.getRollInitiative(caracValue, niveau, ajustement)
})
}
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 && 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) }
})
.forEach(arme => arme.getTypeAttaques().forEach(t => addAttaque(arme, t)))
addAttaque(RdDItemArme.pugilat(this), ATTAQUE_TYPE.CORPS_A_CORPS)
addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS, 'empoignade')
return actions
}
@@ -245,16 +241,6 @@ export class RdDActor extends RdDBaseActorSang {
}
}
static $getCompetenceAction(arme, main) {
switch (main) {
case ATTAQUE_TYPE.UNE_MAIN: return arme.competence1Mains()
case ATTAQUE_TYPE.DEUX_MAINS: return arme.competence2Mains()
case ATTAQUE_TYPE.LANCER: return arme.system.lancer
case ATTAQUE_TYPE.TIR: return arme.system.tir
default: return arme.system.competence
}
}
/* -------------------------------------------- */
async $perteReveEnchantementsChateauDormants() {
const toUpdate = this.items.filter(it => [ITEM_TYPES.potion, ITEM_TYPES.gemme].includes(it.type))
@@ -756,20 +742,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);
}
@@ -1754,7 +1738,7 @@ export class RdDActor extends RdDBaseActorSang {
this.tmrApp?.close();
this.tmrApp = undefined;
}
} ],
}],
onRollDone: RollDialog.onRollDoneClose,
onClose: () => {
this.tmrApp?.restoreTMRAfterAction();
@@ -2608,7 +2592,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);
}
@@ -2999,6 +2983,7 @@ export class RdDActor extends RdDBaseActorSang {
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
await super.onUpdateActor(update, options, actorId)
}
/* -------------------------------------------- */
@@ -3041,9 +3026,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)
}
/* -------------------------------------------- */

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, renderTemplate, 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";
@@ -89,7 +89,6 @@ export class RdDBaseActorReve extends RdDBaseActor {
getSConst() { return 0 }
/* -------------------------------------------- */
isSurenc() { return false }
computeMalusSurEncombrement() { return 0 }
ajustementAstrologique() { return 0 }
@@ -126,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 }) {
@@ -176,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 => { } }) {
@@ -203,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];
}
@@ -226,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], { render: true})
}
if (status && !effect) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(statusId)], { 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);
}
}
/* -------------------------------------------- */
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)
}
/* -------------------------------------------- */
@@ -309,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',
@@ -387,7 +366,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_COMP], current: PART_COMP },
type: { allowed: DEFAULT_ROLL_TYPES, current: PART_COMP },
selected: {
carac: { key: caracName },
comp: options.resistance ? { key: undefined, forced: true } : undefined
@@ -423,32 +402,22 @@ export class RdDBaseActorReve extends RdDBaseActor {
await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-general.hbs');
}
/* -------------------------------------------- */
async rollCompetenceV2(rollData) {
rollData.ids = rollData?.ids ?? {}
rollData.type = rollData.type ?? { allowed: DEFAULT_ROLL_TYPES }
rollData.ids.actorId = this.id
await RollDialog.create(rollData)
}
async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(idOrName);
if (competence.type != ITEM_TYPES.competencecreature && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
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: DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 }
diff: { type: options.arme ? DIFF.ATTAQUE : DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 },
attaque: options.arme ? { arme: { key: options.arme.id } } : undefined
}
}
if (options.arme) {
rollData.selected.attaque = { arme: { id: options.arme.id }, comp: { id: competence.id } }
rollData.type = { allowed: ATTAQUE_ROLL_TYPES }
}
await this.rollCompetenceV2(rollData)
return
return await RollDialog.create(rollData)
}
let rollData = {
@@ -512,19 +481,13 @@ export class RdDBaseActorReve extends RdDBaseActor {
title: 'Ne pas utiliser les automatisation de combat',
buttonLabel: "Pas d'automatisation",
onAction: async () => {
this.rollCompetenceV2({
ids: {
actorId: this.id,
actorTokenId: token?.id,
},
selected: {
conditions: { value: 0 }
},
const rollData = {
ids: { actorId: this.id, actorTokenId: token?.id, },
type: {
allowed: [ROLL_TYPE_COMP, ROLL_TYPE_ATTAQUE, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE, ROLL_TYPE_JEU],
current: ROLL_TYPE_COMP
allowed: [ROLL_TYPE_ATTAQUE], current: ROLL_TYPE_ATTAQUE
}
})
};
return await RollDialog.create(rollData)
}
})
}
@@ -669,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

@@ -186,6 +186,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
await this.changeBleedingState()
break
}
await super.onCreateItem(item, options, id)
}
async onUpdateItem(item, options, id) {
@@ -194,6 +195,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
await this.changeBleedingState()
break
}
await super.onUpdateItem(item, options, id)
}
async changeBleedingState() {
@@ -313,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,15 +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 => it.isdommages = it.isDommages()
)
.forEach(it => it.isdommages = it.isDommages())
return formData;
}
@@ -230,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

@@ -10,7 +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 { STATUSES } from "../settings/status-effects.js";
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
export class RdDBaseActor extends Actor {
@@ -215,9 +215,11 @@ 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 }
@@ -241,18 +243,68 @@ 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 onCreateItem(item, options, id) {
}
async onUpdateItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { }
async onUpdateItem(item, options, id) {
}
async onDeleteItem(item, options, id) {
if (item.isInventaire()) {
@@ -260,6 +312,7 @@ export class RdDBaseActor extends Actor {
}
}
async _removeItemFromConteneur(item) {
const updates = this.items.filter(it => it.isConteneur() && it.system.contenu.includes(item.id))
.map(conteneur => {
@@ -508,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) {
@@ -568,7 +627,7 @@ export class RdDBaseActor extends Actor {
}
}
}
await this.computeEncTotal();
await this.computeEncTotal()
return result;
}

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

@@ -0,0 +1,288 @@
import { ACTOR_TYPES, ITEM_TYPES } from "../../constants.js"
import { Grammar } from "../../grammar.js"
import { RdDItemArme } from "../../item/arme.js"
import { CATEGORIES_COMPETENCE_COMBAT } from "../../item/base-items.js"
import { Misc } from "../../misc.js"
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
import { PDFDocument } from "./pdf-lib/pdf-lib.esm.js"
const copyProperty = (actor, path) => foundry.utils.getProperty(actor, path)
// const findItem = (actor, itemType, itemName) => actor.itemTypes[itemType].find(it => Grammar.equalsInsensitive(formCompName(it.name), formCompName(itemName)))
// const findItemPos = (actor, itemType, pos) => actor.itemTypes[itemType].length <= pos ? actor.itemTypes[itemType][pos] : length
// const findProperty = (it, path) => it ? foundry.utils.getProperty(it, path) : undefined
// const findItemProperty = (actor, itemType, itemName, path) => findProperty(findItem(actor, itemType, itemName), path)
// const findItemPosProperty = (actor, itemType, pos, path) => findProperty(findItemPos(actor, itemType, pos), path)
// const findArmeProperty = (actor, pos, path) => findProperty(findItemPos(actor, ITEM_TYPES.arme, pos), path)
// const findSortProperty = (actor, pos, path) => findProperty(findItemPos(actor, ITEM_TYPES.sort, pos), path)
// const itemFormPath = (type, pos, property) => `${type}s.${pos}.${property}`
const ACTOR_TO_FORM_MAPPING = [
{ path: 'name' },
{ path: 'system.carac.taille.value' },
{ path: 'system.carac.apparence.value' },
{ path: 'system.carac.apparence.xp' },
{ path: 'system.carac.constitution.value' },
{ path: 'system.carac.constitution.xp' },
{ path: 'system.carac.force.value' },
{ path: 'system.carac.force.value' },
{ path: 'system.carac.force.xp' },
{ path: 'system.carac.agilite.value' },
{ path: 'system.carac.agilite.xp' },
{ path: 'system.carac.dexterite.value' },
{ path: 'system.carac.dexterite.xp' },
{ path: 'system.carac.vue.value' },
{ path: 'system.carac.vue.xp' },
{ path: 'system.carac.ouie.value' },
{ path: 'system.carac.ouie.xp' },
{ path: 'system.carac.odoratgout.value' },
{ path: 'system.carac.odoratgout.xp' },
{ path: 'system.carac.volonte.value' },
{ path: 'system.carac.volonte.xp' },
{ path: 'system.carac.empathie.value' },
{ path: 'system.carac.empathie.xp' },
{ path: 'system.carac.intellect.value' },
{ path: 'system.carac.intellect.xp' },
{ path: 'system.carac.reve.value' },
{ path: 'system.carac.reve.xp' },
{ path: 'system.carac.chance.value' },
{ path: 'system.carac.chance.xp' },
{ path: 'system.age' },
{ path: 'system.sexe' },
{ path: 'system.taille' },
{ path: 'system.poids' },
{ path: 'system.cheveux' },
{ path: 'system.yeux' },
{ path: 'system.beaute' },
{ path: 'system.main' },
{ path: 'system.heure' },
{ path: 'computed.hn.heure', getter: actor => (RdDTimestamp.definition(actor.system.heure)?.heure ?? 0) + 1 },
{ path: 'computed.hn.label', getter: actor => RdDTimestamp.definition(actor.system.heure)?.avecArticle },
{ path: 'system.carac.melee.value' },
{ path: 'system.carac.tir.value' },
{ path: 'system.carac.lancer.value' },
{ path: 'system.carac.derobee.value' },
{ path: 'system.sante.vie.value' },
{ path: 'system.sante.endurance.value' },
{ path: 'system.attributs.sust.value' },
{ path: 'system.attributs.sconst.value' },
{ path: 'system.attributs.encombrement.value' },
{ path: 'system.attributs.plusdom.value', getter: actor => Misc.toSignedString(actor.system.attributs.plusdom.value) },
// , getter: actor => actor.get
]
export default class ExportPdf {
static init() {
Hooks.on("getActorContextOptions", (actorDirectory, menus) => { ExportPdf.onActorDirectoryMenu(actorDirectory, menus) })
}
static onActorDirectoryMenu(actorDirectory, menus) {
menus.push({
name: 'Export PDF',
icon: '<i class="fa-regular fa-file-pdf"></i>',
condition: target => actorDirectory.id == 'actors' && ExportPdf.$isActorPersonnage(this.$getActor(target)),
callback: async target => await ExportPdf.exportActor(target)
})
}
static $getActor(target) {
const entryId = $(target).closest(".directory-item")?.data("entryId")
return game.actors.get(entryId)
}
static $isActorPersonnage(actor) {
return actor?.type == ACTOR_TYPES.personnage
}
static async exportActor(target) {
const actor = ExportPdf.$getActor(target)
if (!ExportPdf.$isActorPersonnage(actor)) {
ui.notifications.error("Pas de personnage sélectionné")
return
}
const templatePdf = '/systems/foundryvtt-reve-de-dragon/assets/feuille-personnage.pdf';
const pdfBytes = await fetch(templatePdf).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(pdfBytes)
const exporter = new ExportPdf(actor, pdfDoc)
exporter.generateFeuillePersonnage()
}
constructor(actor, pdfDoc) {
this.pdfDoc = pdfDoc
this.form = this.pdfDoc.getForm()
this.actor = actor
this.allComps = this.actor.itemTypes[ITEM_TYPES.competence]
this.comps = this.allComps
.filter(it => !it.isNiveauBase())
.sort(Misc.ascending(it => it.name))
this.compsNonArmes = this.comps.filter(it => !ExportPdf.isCompCombat(it))
this.compsArmes = this.comps.filter(it => ExportPdf.isCompCombat(it))
this.addedComps = new Set([])
}
static isCompCombat(comp) {
return CATEGORIES_COMPETENCE_COMBAT.includes(comp.system.categorie) && !Grammar.includesLowerCaseNoAccent(comp.name, "corps à corps") && !Grammar.includesLowerCaseNoAccent(comp.name, "esquive")
}
async generateFeuillePersonnage() {
this.$exportActorFields()
this.$exportCompetences()
this.$exportArchetype()
this.$exportArmes()
this.$exportSorts()
const pdfBytes = await this.pdfDoc.save();
const filename = `rdd-${this.actor.name.slugify()}.pdf`;
foundry.utils.saveDataToFile(pdfBytes, "application/pdf", filename);
}
$exportActorFields() {
ACTOR_TO_FORM_MAPPING.forEach(async (mapping) => {
const path = mapping.path
const value = mapping.getter ? mapping.getter(this.actor) : copyProperty(this.actor, path)
this.$setFormValue(path, value)
})
}
$exportCompetences() {
this.compsNonArmes
.filter(it => !this.addedComps.has(it.id))
.forEach(comp => {
const formCompName = Grammar.toLowerCaseNoAccent(comp.name.replaceAll(/(\s|-|\')/g, '_'))
this.$setFormCompetence(formCompName, comp)
})
const musique = this.compsNonArmes.filter(it => Grammar.includesLowerCaseNoAccent(it.name, 'musique'))
.sort(Misc.descending(it => it.system.niveau))
.filter(it => !this.addedComps.has(it.id))
.find(it => true)
if (musique) {
this.$setFormCompetence('musique', musique)
}
}
$exportArchetype() {
this.allComps.sort(Misc.ascending(it => it.system.niveau_archetype))
.forEach(comp => {
let formCompName = Grammar.toLowerCaseNoAccent(comp.name.replaceAll(/(\s|-|\')/g, '_'))
if (formCompName.includes('musique')) {
formCompName = 'musique'
}
this.$setFormValue(`competences.${formCompName}.niveau_archetype`, comp.system.niveau_archetype)
})
}
$setFormCompetenceArchetype(formCompName, comp, baseFormName = 'competences') {
}
$exportArmes() {
const uniques = new Set([])
const armes = this.actor.itemTypes[ITEM_TYPES.arme].map(arme => arme.getTypeAttaques()
.map(main => {
const compName = arme.getCompetenceAction(main)
const dommages = RdDItemArme.valeurMain(arme.system.dommages, main)
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
const comp = this.compsArmes.find(it => Grammar.equalsInsensitive(it.name, compName)) ?? this.actor.findItemLike(compName, ITEM_TYPES.competence)
const unique = [comp.id, arme.name, dommages, forceRequise].join('|');
if (uniques.has(unique)) {
return undefined
}
uniques.add(unique)
return { arme: arme, comp: comp, main: main }
}))
.reduce((a, b) => a.concat(b))
.filter(it => it != undefined && !it.comp.isNiveauBase())
.sort(Misc.descending(it => it.comp.niveau))
for (let pos = 0; pos < armes.length; pos++) {
const it = armes[pos]
this.$setFormArmeCompetence(pos, it.comp, it.arme, it.main, it.main)
}
// TODO: list comps without weapons
// TODO: list other comps not in the standard list -- use an instance of ExportPdf to hold state/built list
const otherComps = this.comps.filter(it => !this.addedComps.has(it.id))
for (let pos = 0; pos < otherComps.length; pos++) {
const comp = otherComps[pos]
this.$setFormCompetence(pos, comp, 'competences')
this.$setFormValue(`competences.${pos}.name`, comp.name)
}
}
$setFormCompetence(formCompName, comp, baseFormName = 'competences') {
if (this.form.getFieldMaybe(`${baseFormName}.${formCompName}.niveau`)) {
this.addedComps.add(comp.id)
}
if (comp.system.niveau != comp.system.base) {
this.$setFormValue(`${baseFormName}.${formCompName}.niveau`, comp.system.niveau)
}
if (comp.system.xp > 0) {
this.$setFormValue(`${baseFormName}.${formCompName}.xp`, comp.system.xp)
}
if (comp.system.xp_sort > 0) {
this.$setFormValue(`${baseFormName}.${formCompName}.xp_sort`, comp.system.xp_sort)
}
if (CATEGORIES_COMPETENCE_COMBAT.includes(comp.system.categorie)) {
this.$setFormValue(`${baseFormName}.${formCompName}.init`, comp.getBaseInit())
}
}
$setFormArmeCompetence(pos, comp, arme, main) {
this.$setFormCompetence(pos, comp, 'armes')
this.$setFormValue(`armes.${pos}.name`, arme.name)
this.$setFormValue(`armes.${pos}.main`, main)
this.$setFormValue(`armes.${pos}.plusdom`, RdDItemArme.valeurMain(arme.system.dommages, main))
}
$exportSorts() {
const sorts = this.actor.itemTypes[ITEM_TYPES.sort].sort(Misc.ascending(s => ExportPdf.$orderDraconic(s) + s.name))
for (let pos = 0; pos < sorts.length; pos++) {
const sort = sorts[pos]
this.$setFormSort(pos, sort)
}
}
$setFormSort(pos, sort) {
this.$setFormValue(`sorts.${pos}.name`, sort.name)
this.$setFormValue(`sorts.${pos}.voie`, sort.system.draconic)
this.$setFormValue(`sorts.${pos}.tmr`, sort.system.caseTMRSpeciale ?? sort.system.caseTMR)
this.$setFormValue(`sorts.${pos}.diff`, sort.system.difficulte)
this.$setFormValue(`sorts.${pos}.reve`, sort.system.ptreve)
this.$setFormValue(`sorts.${pos}.bonuscase`, sort.system.bonuscase)
}
static $orderDraconic(s) {
switch (s.system.draconic.substring(0, 1)) {
case 'O': return 1
case 'H': return 2
case 'N': return 3
case 'T': return 4
}
return 5
}
$setFormValue(path, value) {
const hasField = this.form.getFieldMaybe(path)
if (hasField && value != undefined) {
const field = this.form.getTextField(path)
field.setText(value.toString())
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@ import { Grammar } from "../../grammar.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"
@@ -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
}

View File

@@ -21,7 +21,7 @@ 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': 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 },
'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 {

View File

@@ -13,53 +13,65 @@ 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)" }
{ 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: "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" }
{ 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: 'finesse', descr: 'en rapidité', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-rapidite.svg'},
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 = {
personnage: 'personnage',
creature: 'creature',

View File

@@ -20,11 +20,11 @@ export class RdDInitiative {
return "1d6" + (base >= 0 ? "+" : "") + base
}
static ajustementInitiative(caracValue, niveau, bonus) {
static ajustementInitiative(caracValue, niveau, bonus = 0) {
return niveau + Math.floor(caracValue / 2) + bonus
}
static formule(phase, carac, niveau, bonusMalus) {
static formule(phase, carac, niveau, bonusMalus = 0) {
const ajustement = RdDInitiative.ajustementInitiative(carac, niveau, bonusMalus)
return { phase, ajustement }
}
@@ -58,6 +58,7 @@ export class RdDInitiative {
return {
roll: roll,
value: value,
rang: formule.phase.rang,
init: formule.phase.rang + value / 100,
label: formule.phase.label
}

View File

@@ -1,7 +1,8 @@
import { Grammar } from "./grammar.js";
import { RdDInitiative } from "./initiative.mjs";
import { RdDItem } from "./item.js";
import { CATEGORIES_COMPETENCES, SANS_COMPETENCE } from "./item/base-items.js";
import { CATEGORIES_COMPETENCE_COMBAT, CATEGORIES_COMPETENCES, SANS_COMPETENCE } from "./item/base-items.js";
import { Misc } from "./misc.js";
const competenceTroncs = [["Esquive", "Dague", "Corps à corps"],
@@ -46,6 +47,30 @@ export class RdDItemCompetence extends RdDItem {
static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }
isNiveauBase() {
return this.system.niveau == this.system.base && this.system.xp == 0
}
getBaseInit() {
const carac = this.getInitCarac()
if (carac == undefined) {
return undefined
}
return RdDInitiative.ajustementInitiative(carac.value, this.system.niveau)
}
getInitCarac() {
if (!this.actor) {
return undefined
}
switch (this.system.categorie) {
case CATEGORIES_COMPETENCES.melee.key: return this.actor.system.carac.melee
case CATEGORIES_COMPETENCES.tir.key: return this.actor.system.carac.tir
case CATEGORIES_COMPETENCES.lancer.key: return this.actor.system.carac.lancer
case CATEGORIES_COMPETENCES.draconic.key: return this.actor.system.carac.reve
}
return undefined
}
/* -------------------------------------------- */
static getLabelCategorie(category) {
return CATEGORIES_COMPETENCES[category].label;

View File

@@ -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

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

@@ -62,7 +62,7 @@ export class RdDItemSort extends Item {
static diffReve(sort) { return RdDItemSort.toVar((sort.system.difficulte.match(/\-?(\d)+/) ? 'R' : 'R ') + sort.system.difficulte) }
static coutReve(sort) { return RdDItemSort.toVar((sort.system.ptreve.match(/(\d)+\+?/) ? 'r' : 'r ') + sort.system.ptreve) }
static coutReve(sort) { return RdDItemSort.toVar((Number.isInteger(sort.system.ptreve || sort.system.ptreve.match(/(\d)+\+?/)) ? 'r' : 'r ') + sort.system.ptreve) }
static getDraconicsSort(competencesDraconic, sort) {
// se baser sur la voie du sort?
switch (Grammar.toLowerCaseNoAccent(sort.name)) {

View File

@@ -1,4 +1,4 @@
import { ITEM_TYPES, renderTemplate } 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,4 +1,4 @@
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";
@@ -27,10 +27,11 @@ export const ATTAQUE_TYPE = {
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 = 'empoignade'
export const EMPOIGNADE = RDD_CONFIG.encaissement.empoignade
/* -------------------------------------------- */
export class RdDItemArme extends RdDItem {
@@ -44,6 +45,16 @@ export class RdDItemArme extends RdDItem {
isParade() { return this.system.resistance > 0 && this.system.categorie_parade }
isBouclier() { return RdDItemArme.getCategorieParade(this).includes('bouclier') }
getCompetenceAction(main) {
switch (main) {
case ATTAQUE_TYPE.UNE_MAIN: return this.competence1Mains()
case ATTAQUE_TYPE.DEUX_MAINS: return this.competence2Mains()
case ATTAQUE_TYPE.LANCER: return this.system.lancer
case ATTAQUE_TYPE.TIR: return this.system.tir
default: return this.system.competence
}
}
/* -------------------------------------------- */
static valeurMain(valeurs, main) {
valeurs = valeurs?.toString() ?? ""
@@ -77,17 +88,26 @@ export class RdDItemArme extends RdDItem {
return arme.name
case ITEM_TYPES.arme:
switch (maniement) {
case ATTAQUE_TYPE.COMPETENCE: return arme.system.competence;
case ATTAQUE_TYPE.COMPETENCE: return arme.system.competence
case ATTAQUE_TYPE.UNE_MAIN: return arme.competence1Mains()
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.LANCER: case 'lancer': return arme.system.lancer
case ATTAQUE_TYPE.CORPS_A_CORPS: return CORPS_A_CORPS
}
}
return undefined
}
getTypeAttaques() {
return [
...(this.system.unemain && this.system.competence && this.system.resistance > 0) ? [ATTAQUE_TYPE.UNE_MAIN] : [],
...(this.system.deuxmains && this.system.competence && this.system.resistance > 0) ? [ATTAQUE_TYPE.DEUX_MAINS] : [],
...(this.system.lancer && this.system.resistance > 0) ? [ATTAQUE_TYPE.LANCER] : [],
...(this.system.tir) ? [ATTAQUE_TYPE.TIR] : [],
]
}
static computeNiveauArmes(armes, competences) {
for (const arme of armes) {
arme.system.niveau = RdDItemArme.niveauCompetenceArme(arme, competences);
@@ -254,15 +274,23 @@ export class RdDItemArme extends RdDItem {
return this.system.resistance > 0 || (this.system.tir != '' && this.system.portee_courte > 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', EMPOIGNADE)
return RdDItemArme.$corpsACorps(actor, 'Empoignade', RDD_CONFIG.encaissement.empoignade)
}
static $corpsACorps(actor, name, cac, system) {
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({
@@ -277,10 +305,10 @@ export class RdDItemArme extends RdDItem {
force: 0,
dommages: "0",
dommagesReels: 0,
mortalite: cac == EMPOIGNADE ? EMPOIGNADE : 'non-mortel',
mortalite: cac == RDD_CONFIG.encaissement.empoignade ? RDD_CONFIG.encaissement.empoignade : RDD_CONFIG.encaissement.nonmortel,
competence: CORPS_A_CORPS,
resistance: 1,
baseInit: cac == EMPOIGNADE ? 3 : 4,
baseInit: cac == RDD_CONFIG.encaissement.empoignade ? 3 : 4,
cac: cac,
deuxmains: true,
categorie_parade: 'sans-armes'

View File

@@ -19,22 +19,23 @@ export const SANS_COMPETENCE = {
}
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" }
generale: { key: 'generale', base: -4, label: "Générales" },
particuliere: { key: 'particuliere', base: -8, label: "Particulières" },
specialisee: { key: 'specialisee', base: -11, label: "Spécialisées" },
connaissance: { key: 'connaissance', base: -11, label: "Connaissances" },
draconic: { key: 'draconic', base: -11, label: "Draconic" },
melee: { key: 'melee', base: -6, label: "Mêlée" },
tir: { key: 'tir', base: -8, label: "Tir" },
lancer: { key: 'lancer', base: -8, label: "Lancer" }
}
export const CATEGORIES_COMPETENCE_COMBAT = [CATEGORIES_COMPETENCES.melee, CATEGORIES_COMPETENCES.tir, CATEGORIES_COMPETENCES.lancer].map(it => it.key)
export const CATEGORIES_COMPETENCES_CREATURES = {
"generale": { base: 0, label: "Générale" },
"naturelle": { base: 0, label: "Arme naturelle" },
"melee": { base: 0, label: "Mêlée" },
"parade": { base: 0, label: "Parade" },
"tir": { base: 0, label: "Tir" },
"lancer": { base: 0, label: "Lancer" },
"possession": { base: 0, label: "Possession" },
generale: { key: 'generale', base: 0, label: "Générale" },
naturelle: { key: 'naturelle', base: 0, label: "Arme naturelle" },
melee: { key: 'melee', base: 0, label: "Mêlée" },
parade: { key: 'parade', base: 0, label: "Parade" },
tir: { key: 'tir', base: 0, label: "Tir" },
lancer: { key: 'lancer', base: 0, label: "Lancer" },
possession: { key: 'possession', base: 0, label: "Possession" },
}

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) {

View File

@@ -12,12 +12,13 @@ const _SPACEHOLDER = { placeholder: true }
const _VENDRE = {
code: 'item-vendre', label: 'Vendre ou donner', icon: it => 'fa-solid fa-comments-dollar',
filter: it => Misc.toInt(it.system.quantite) > 0,
filter: it => Misc.toInt(it.system.quantite) > 0 || it.parent?.type == ACTOR_TYPES.commerce,
action: (item, actor) => item.proposerVente()
}
const _ACHETER = {
code: 'item-acheter', label: 'Acheter', icon: it => 'fa-regular fa-coins',
filter: it => Misc.toInt(it.system.quantite) > 0 && it.parent?.type == ACTOR_TYPES.commerce,
filter: it => it.parent?.type == ACTOR_TYPES.commerce,
allowLimited: true,
action: (item, actor) => actor.vente(item)
}
const _MONTRER = {

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,19 +48,19 @@ 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, attaque) {
const actor = rollData.active.actor
const arme = attaque.arme
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommagesArme)
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommages)
const dmg = {
total: 0,
dmgArme: dmgArme,
@@ -68,11 +69,12 @@ export class RdDBonus {
dmgTactique: attaque.tactique?.dmg ?? 0,
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0,
mortalite: RdDBonus.mortalite(attaque.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
}
@@ -93,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, renderTemplate, 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";
@@ -15,9 +15,8 @@ import { RdDItemCompetence } from "./item-competence.js";
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, 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";
@@ -57,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();
@@ -380,7 +385,7 @@ export class RdDCombat {
if (defenderToken && Misc.isFirstConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id)
rddCombat?.removeChatMessageActionsPasseArme(msg.paramChatDefense.attackerRoll.passeArme)
if (msg.defenderRoll.ids) {/* TODO: delete roll V1 */
if (msg.defenderRoll.v2) {/* TODO: delete roll V1 */
RollDialog.loadRollData(msg.paramChatDefense)
rddCombat?._chatMessageDefenseV2(msg.paramChatDefense)
} else {
@@ -403,7 +408,6 @@ export class RdDCombat {
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of [
'.button-defense',
'.button-parade',
'.button-esquive',
'.button-encaisser',
@@ -475,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);
@@ -561,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
@@ -575,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) {
@@ -586,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) {
@@ -602,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()
@@ -686,11 +688,7 @@ export class RdDCombat {
async doRollAttaque(rollData, callbacks = []) {
// TODO V2 await this.proposerAjustementTirLancer(rollData)
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
},
customChatMessage: true,
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
async (roll) => await this.onAttaqueV2(roll),
...callbacks
@@ -730,26 +728,24 @@ export class RdDCombat {
async _chatMessageDefenseV2(paramDemandeDefense) {
const attackerRoll = paramDemandeDefense.attackerRoll;
RollBasicParts.loadSurprises(attackerRoll)
attackerRoll.passeArme = attackerRoll.passeArme ?? foundry.utils.randomID(16)
attackerRoll.dmg = RdDBonus.dmgRollV2(attackerRoll, attackerRoll.current.attaque)
// attackerRoll.current.attaque.dmg = attackerRoll.dmg
// attaque.dmg = attackerRoll.current.attaque.dmg
const attaque = RollDialog.saveParts(attackerRoll)
const defense = {
attackerRoll: attaque,
ids: RollBasicParts.reverseIds(attaque),
passeArme: attaque.passeArme ?? foundry.utils.randomID(16)
}
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', attackerRoll)
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, 'rollData', defense)
ChatUtility.setMessageData(choixDefense, 'demande-defense', true)
ChatUtility.setMessageData(choixDefense, 'rollData', {
ids: defenseData.ids,
attackerRoll: RollDialog.saveParts(attackerRoll),
passeArme: defenseData.passeArme
})
}
/* -------------------------------------------- */
@@ -804,7 +800,7 @@ export class RdDCombat {
essais: {}
};
if (this.attacker.isCreatureEntite()) {
if (this.attacker.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(rollData);
}
else if (arme) {
@@ -873,7 +869,7 @@ export class RdDCombat {
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',
@@ -892,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';
}
/* -------------------------------------------- */
@@ -1056,7 +1055,7 @@ export class RdDCombat {
dialog.render(true);
}
async defenseV2(attackerRoll) {
async defenseV2(attackerRoll, callbacks = []) {
// this._prepareParade(attackerRoll, arme, competence);
RollDialog.loadRollData(attackerRoll)
await this.doRollDefense({
@@ -1067,15 +1066,14 @@ export class RdDCombat {
opponentId: this.attackerId,
},
type: { allowed: [ROLL_TYPE_DEFENSE], current: ROLL_TYPE_DEFENSE },
attackerRoll: RollDialogAdapter.mapActionAttaque(attackerRoll),
attackerRoll: attackerRoll,
passeArme: attackerRoll.passeArme,
})
}, callbacks)
}
async doRollDefense(rollData, callbacks = []) {
await RollDialog.create(rollData, {
onRollDone: RollDialog.onRollDoneClose,
customChatMessage: true,
callbacks: [
async (roll) => {
this.removeChatMessageActionsPasseArme(roll.passeArme);
@@ -1109,7 +1107,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(defenderRoll);
}
@@ -1236,7 +1234,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(rollData);
}
return rollData;

View File

@@ -4,6 +4,8 @@ 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

@@ -89,6 +89,7 @@ import { Migrations } from './migrations.js'
import RollDialog from "./roll/roll-dialog.mjs"
import ChatRollResult from "./roll/chat-roll-result.mjs"
import ExportPdf from "./actor/export-pdf/export-pdf.mjs"
/**
* RdD system
@@ -296,6 +297,7 @@ export class SystemReveDeDragon {
RdDPossession.init()
TMRRencontres.init()
ExportScriptarium.init()
ExportPdf.init()
RollDialog.init()
ChatRollResult.init()
}

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

@@ -16,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;
/* -------------------------------------------- */
@@ -157,7 +160,7 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static replaceParticuliereDemiSurprise(chances){
static replaceParticuliereDemiSurprise(chances) {
foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'part'), { overwrite: true });
}
@@ -197,6 +200,7 @@ export class RdDResolutionTable {
if (chances.norm < roll * diviseur) {
return reussiteInsuffisante
}
return reussiteSignificative
}
return reussite
}

View File

@@ -1,4 +1,4 @@
import { ENTITE_BLURETTE, ENTITE_INCARNE, renderTemplate } 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

@@ -8,7 +8,7 @@ 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, renderTemplate } from "./constants.js";
import { ACTOR_TYPES, RDD_CONFIG, renderTemplate } from "./constants.js";
import { EMPOIGNADE } from "./item/arme.js";
/**
@@ -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,8 +333,8 @@ 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("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable)

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 {
@@ -613,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
}
@@ -671,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 {
@@ -693,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,17 +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 { EMPOIGNADE } from "../item/arme.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() {
@@ -22,8 +22,10 @@ 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',
@@ -65,7 +67,7 @@ export default class ChatRollResult {
isShowEncaissement(roll) {
switch (roll.type.current) {
case ROLL_TYPE_DEFENSE:
return roll.rolled.isEchec && roll.attackerRoll?.dmg.mortalite != EMPOIGNADE
return roll.rolled.isEchec
}
return false
}
@@ -122,7 +124,7 @@ export default class ChatRollResult {
}
async buildRollHtml(roll) {
const template = `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-${roll.type.current}.hbs`
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 })
}
@@ -130,6 +132,7 @@ export default class ChatRollResult {
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))
@@ -137,7 +140,6 @@ export default class ChatRollResult {
$(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) {
@@ -145,7 +147,7 @@ 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
}
@@ -162,6 +164,7 @@ export default class ChatRollResult {
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)
@@ -217,18 +220,41 @@ export default class ChatRollResult {
})
}
async onClickEncaissement(event) {
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 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) {
@@ -248,7 +274,7 @@ export default class ChatRollResult {
savedRoll.particuliere = choix
savedRoll.particulieres = [RDD_CONFIG.particuliere[choix]]
await this.updateChatMessage(chatMessage, savedRoll)
await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll, callbacks)
await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll)
}
async onClickFaireGouter(event) {
@@ -273,8 +299,7 @@ export default class ChatRollResult {
const chatMessage = ChatUtility.getChatMessage(event)
const typeMaladresse = event.currentTarget.attributes['data-maladresse'].value
const savedRoll = this.loadChatMessageRoll(chatMessage)
await RdDRollTables.getMaladresse({ arme: typeMaladresse == 'avec-arme' })
savedRoll.type.maladresse = true
savedRoll.maladresse = await RdDRollTables.getMaladresse({ arme: typeMaladresse == 'avec-arme', toChat: false })
savedRoll.type.retry = true
await this.updateChatMessage(chatMessage, savedRoll)
}

View File

@@ -26,7 +26,7 @@ export class RollBasicParts {
static loadSurprise(who, forceRequise) {
if (who?.actor) {
foundry.utils.mergeObject(who,
StatusEffects.getActorEffetSurprise(who.actor, forceRequise),
StatusEffects.getActorEffetSurprise(who.actor),
{ overwrite: true, inPlace: true })
}
}
@@ -54,13 +54,27 @@ export class RollBasicParts {
}
}
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.actorTokenId
opponentTokenId: rollData.ids.actorTokenId
}
}

View File

@@ -12,8 +12,7 @@ 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_TACHE, ROLL_TYPE_MEDITATION, ROLL_TYPE_CUISINE, ROLL_TYPE_OEUVRE, ROLL_TYPE_JEU]
export const ALL_ROLL_TYPES = [...DEFAULT_ROLL_TYPES, ...COMBAT_ROLL_TYPES, ...DEMIREVE_ROLL_TYPES]
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 = {

View File

@@ -8,7 +8,7 @@ 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_OEUVRE } from "./roll-constants.mjs";
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE, ROLL_TYPE_OEUVRE } from "./roll-constants.mjs";
import { PART_ATTAQUE } from "./roll-part-attaque.mjs";
/* -------------------------------------------- */
@@ -27,8 +27,8 @@ export class RollDialogAdapter {
RollDialogAdapter.setRollDataRolled(rollData, rolled, rollTitle)
RollDialogAdapter.adjustRollDataForV1(rollData)
RollDialogAdapter.adjustAttaqueParticuliere(rollData)
RollDialogAdapter.adjustAttaqueDmg(rollData)
RollDialogAdapter.adjustDemiSurprise(rollData)
return rolled
}
@@ -96,14 +96,7 @@ export class RollDialogAdapter {
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.ajustements = rollData.ajustements.map(a => { return { label: a.label, value: a.value } })
}
static adjustDemiSurprise(rollData) {
@@ -112,6 +105,18 @@ export class RollDialogAdapter {
}
}
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
@@ -119,7 +124,7 @@ export class RollDialogAdapter {
const attaque = rollData.current.attaque;
const choix = []
const isEmpoignade = attaque.dmg.mortalite == 'empoignade';
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)
@@ -160,8 +165,6 @@ export class RollDialogAdapter {
comp: attackerRoll.competence,
main: RdDItemArme.getMainAttaque(attackerRoll.competence),
equipe: attackerRoll.arme.system.equipe,
// carac: { key: caracCode, value: caracValue },
// dommagesArme: dommagesArme,
diff: attackerRoll.diffLibre,
particuliere: attackerRoll.particuliere,
tactique: RdDBonus.find(attackerRoll.tactique),

View File

@@ -44,11 +44,13 @@ 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 ALL_ROLL_TYPES = [
export const ALL_ROLL_TYPES = [
new RollTypeComp(),
new RollTypeTache(),
new RollTypeAttaque(),
@@ -85,6 +87,8 @@ const ROLL_PARTS = [
new RollPartConditions(),
new RollPartEthylisme(),
new RollPartMalusArmure(),
new RollPartEmpoignadeTaille(),
new RollPartEmpoignade(),
new RollPartEncTotal(),
new RollPartSurEnc(),
new RollPartAppelMoral(),
@@ -175,11 +179,11 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
{
static onCloseDoNothing() {
}
static onRollDoneDoNothing(dialog) {
static onRollDoneDoNothing(dialog, roll) {
dialog.render()
}
static onRollDoneClose(dialog) {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
static onRollDoneClose(dialog, roll) {
if (roll.type.retry || !OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
}
@@ -199,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())
@@ -316,6 +320,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
constructor(rollData, rollOptions) {
super()
this.hooks = []
this.rollData = RollDialog.$prepareRollData(rollData)
this.rollOptions = {
callbacks: [
@@ -323,14 +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 ?? 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)
@@ -386,7 +405,7 @@ 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) {
@@ -408,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()
@@ -430,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)
}
@@ -443,10 +461,11 @@ 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){
async close(options) {
if (this.rollOptions.onClose) {
this.rollOptions.onClose()
}
this.unregisterHooks()
return await super.close(options)
}
@@ -475,13 +494,14 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
await impacts.applyImpacts()
selectedRollType.onApplyImpacts(roll, impacts)
await this.chatRollResult.display(roll, impacts)
this.rollOptions.onRollDone(this)
this.rollOptions.onRollDone(this, roll)
}
static loadRollData(roll) {
RollDialog.$prepareRollData(roll)
RollDialog.calculAjustements(roll)
RollDialog.calculAjustement(roll)
roll.v2 = true
return roll
}
async defaultCallback(roll, rolled) {

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 { DIFF, 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,7 +30,8 @@ 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) {
const attaque = this.findAttaque(refs.attaques, this.getSaved(rollData))
@@ -28,6 +39,10 @@ export class RollPartAttaque extends RollPartSelect {
}
}
isAttaqueEmpoignade(it) {
return it.arme.isEmpoignade()
}
store(rollData, targetData) {
super.store(rollData, targetData)
this.getSaved(targetData).dmg = this.getCurrent(rollData).dmg
@@ -36,7 +51,7 @@ export class RollPartAttaque extends RollPartSelect {
restore(rollData) {
const saved = this.getSaved(rollData)
super.restore(rollData)
if (saved.dmg) {
if (saved.dmg != undefined) {
this.getCurrent(rollData).dmg = this.getSaved(rollData).dmg
}
}
@@ -57,20 +72,28 @@ export class RollPartAttaque extends RollPartSelect {
}
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]
}
@@ -82,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 => {
@@ -99,9 +123,26 @@ 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) {
@@ -115,7 +156,9 @@ export class RollPartAttaque extends RollPartSelect {
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

@@ -35,7 +35,7 @@ export class RollPartCheckbox extends RollPart {
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

@@ -18,12 +18,16 @@ export class RollPartComp extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const selected = this.getSelected(rollData)
refs.all = this.$getActorComps(rollData)
.filter(comp => !selected.forced ||
(selected.key ?
Grammar.includesLowerCaseNoAccent(comp.name, selected.key)
: comp.key == '')
)
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)
}

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

@@ -1,4 +1,4 @@
import { ITEM_TYPES } from "../constants.js"
import { ITEM_TYPES, RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE, RdDItemArme } from "../item/arme.js"
import { CARACS } from "../rdd-carac.js"
import { DIFF, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
@@ -15,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) {
@@ -27,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?.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)
}
@@ -101,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
@@ -110,7 +121,7 @@ export class RollPartDefense extends RollPartSelect {
isArmeDisparate(rollData) {
const armeDefense = this.getCurrent(rollData).arme
if (armeDefense) {
const armeAttaque = rollData.attackerRoll?.arme
const armeAttaque = rollData.attackerRoll?.current.attaque.arme
return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) == 'sign'
}
return false
@@ -123,7 +134,11 @@ export class RollPartDefense extends RollPartSelect {
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

@@ -66,7 +66,7 @@ export class RollPartDiff extends RollPart {
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,10 +1,10 @@
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"
@@ -34,8 +34,9 @@ export class RollPartMeditation extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
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)
@@ -72,9 +73,10 @@ 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) {

View File

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

View File

@@ -1,6 +1,7 @@
import { RDD_CONFIG } from "../constants.js"
import { Misc } from "../misc.js"
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { StatusEffects } from "../settings/status-effects.js"
import { demiReveStatusEffect, StatusEffects } from "../settings/status-effects.js"
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
@@ -39,29 +40,30 @@ export class RollPartSign extends RollPart {
const isCombat = this.isCombat(rollData)
const current = this.getCurrent(rollData)
current.armeDisparate = isCombat && current.armeDisparate
current.surprise = actor.getSurprise(isCombat) // TODO: could be from rollData.active.surprise??
current.reasons = actor.getEffects(it => StatusEffects.niveauSurprise(it) > 0).map(it => it.name)
current.surprise = actor.getSurprise(isCombat, current.forceRequise ?? 0)
current.reasons = actor.getEffects(it => StatusEffects.niveauSurprise(it, isCombat) > 0, current.forceRequise ?? 0)
.map(it => { return { img: it.img, label: game.i18n.localize(it.name) } })
current.diviseur = 1
if (current.surprise == 'demi') {
current.diviseur *= 2
}
if (isCombat && actor.isDemiReve()) {
current.reasons.push('Demi-rêve en combat')
current.reasons.push({ img: RDD_CONFIG.icons.demiReve, label: 'Demi-rêve en combat' })
}
if (this.isParadeArmeDisparate(current)) {
current.diviseur *= 2
current.reasons.push('Armes disparates')
current.reasons.push({ img: RDD_CONFIG.icons.armesDisparates, label: 'Armes disparates' })
}
if (this.isAttaqueFinesse(rollData)) {
current.diviseur *= 2
current.reasons.push('Particulière en finesse')
current.reasons.push({ img: RDD_CONFIG.particuliere.finesse.img, label: 'Particulière en finesse' })
}
if (!ReglesOptionnelles.isUsing('tripleSignificative')) {
current.diviseur = Math.min(current.diviseur, 4);
}
current.reason = current.reasons.join(', ')
current.reason = current.reasons.map(it => it.label).join(', ')
}
isAttaqueFinesse(rollData) {
@@ -76,8 +78,16 @@ export class RollPartSign extends RollPart {
const current = this.getCurrent(rollData)
if (current.surprise == 'demi') {
return [
{ label: 'Significative requise ' + Misc.getFractionOneN(current.diviseur), diff: undefined },
...current.reasons.map(it => { return { label: '<i class="fa-solid fa-triangle-exclamation"></i> ' + it, diff: undefined } })
{
label: 'Significative requise ' + Misc.getFractionOneN(current.diviseur)
},
...current.reasons.map(it => {
return {
label: it.img
? `<img src="${it.img}"> ${it.label}`
: `<i class="fa-solid fa-triangle-exclamation"></i> ${it.label}`
}
})
]
}
return []
@@ -92,7 +102,9 @@ export class RollPartSign extends RollPart {
})
}
setArmeDisparate(rollData, armeDisparate) {
this.getCurrent(rollData).armeDisparate = armeDisparate
setArme(rollData, armeDisparate, forceRequise) {
const current = this.getCurrent(rollData)
current.armeDisparate = armeDisparate
current.forceRequise = forceRequise
}
}

View File

@@ -94,16 +94,11 @@ export class RollPartSort extends RollPartSelect {
getAjustements(rollData) {
const current = this.getCurrent(rollData)
if (current) {
const reserve = current.isReserve ?
[{ label: `Mise en réserve en ${this.getCoord(rollData)}` }] : []
const bonusCase = current.bonusCase ?
[{ label: `Bonus case +${current.bonusCase}%` }] : []
return [
{ label: current.label, diff: current.value },
...bonusCase,
{ label: `Rêve ${current.ptreve}` },
...reserve
]
const sort = { label: current.label, value: current.value }
const reserve = current.isReserve ? [{ label: `Mise en réserve en ${this.getCoord(rollData)}` }] : []
const bonusCase = current.bonusCase ? [{ label: `Bonus case +${current.bonusCase}%` }] : []
const reve = { label: `Rêve ${current.ptreve}` }
return [sort, ...bonusCase, reve, ...reserve]
}
return []
}

View File

@@ -1,3 +1,4 @@
import { RDD_CONFIG } from "../constants.js"
import { RdDCarac } from "../rdd-carac.js"
import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
@@ -10,7 +11,7 @@ export class RollPartSurEnc extends RollPartCheckbox {
visible(rollData) {
return RdDCarac.isActionPhysique(rollData.current.carac.key) && rollData.active.actor.isSurenc()
}
getCheckboxIcon(rollData) { return '<i class="fa-solid fa-weight-hanging"></i>' }
getCheckboxIcon(rollData) { return `<img src="${RDD_CONFIG.icons.surenc}">` }
getCheckboxLabel(rollData) { return "Sur-enc." }
getCheckboxValue(rollData) { return rollData.active.actor.computeMalusSurEncombrement() }
}

View File

@@ -18,10 +18,12 @@ export class RollPartTache extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const selected = this.getSelected(rollData)
refs.forced = selected.forced
refs.all = rollData.active.actor.itemTypes[ITEM_TYPES.tache]
.filter(tache => !selected.forced || tache.id == selected.key)
.filter(tache => tache.system.points_de_tache_courant < tache.system.points_de_tache)
.map(tache => RollPartTache.$extractTache(tache, rollData.active.actor))
refs.taches = refs.all
if (refs.taches.length > 0) {
this.$selectTache(rollData)
@@ -46,6 +48,8 @@ export class RollPartTache extends RollPartSelect {
async _onRender(rollDialog, context, options) {
const selectTache = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-tache"]`)
const buttonCreate = rollDialog.element.querySelector(`roll-section[name="${this.code}"] button[name="create-tache"]`)
const buttonEdit = rollDialog.element.querySelector(`roll-section[name="${this.code}"] button[name="edit-tache"]`)
selectTache.addEventListener("change", e => {
const selectOptions = e.currentTarget.options
@@ -53,14 +57,43 @@ export class RollPartTache extends RollPartSelect {
this.$selectTache(rollDialog.rollData, selectOptions[index]?.value)
rollDialog.render()
})
buttonCreate?.addEventListener(
"click", async e => {
e.preventDefault()
await rollDialog.rollData.active.actor.createItem(ITEM_TYPES.tache, 'Nouvelle tache')
})
buttonEdit?.addEventListener(
"click", e => {
e.preventDefault()
const current = this.getCurrent(rollDialog.rollData)
current.tache?.sheet.render(true)
})
}
getHooks(rollDialog) {
return [
{ hook: "createItem", fn: (item, options, id) => this.onCreateUpdateItem(rollDialog, item, options, id) },
{ hook: "updateItem", fn: (item, options, id) => this.onCreateUpdateItem(rollDialog, item, options, id) }
]
}
onCreateUpdateItem(rollDialog, item, options, id) {
if (item.type == ITEM_TYPES.tache && item.parent?.id == rollDialog.rollData.active.actor.id) {
this.loadRefs(rollDialog.rollData)
rollDialog.render()
}
}
impactOtherPart(part, rollData) {
if (this.visible(rollData)) {
const current = this.getCurrent(rollData)
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [current?.tache.system.carac])
case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
if (current.tache) {
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [current?.tache.system.carac])
case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
}
}
}
return undefined

View File

@@ -15,10 +15,6 @@ export class RollPartTricher extends RollPart {
current.resultat = Misc.inRange(current.resultat == undefined ? -1 : current.resultat, -1, 100)
}
getAjustements(rollData) {
return []
}
async _onRender(rollDialog, context, options) {
const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`)

View File

@@ -6,6 +6,8 @@ export const ROLLDIALOG_SECTION = {
CONDITIONS: 'conditions',
AJUSTEMENTS: 'ajustements',
}
export class RollPart {
static settingKey(rollPart, key) { return `roll-part-${rollPart.code}.${key}` }
@@ -28,7 +30,8 @@ export class RollPart {
rollData.selected[this.code] = {}
}
}
/** l'acteur actif du jet */
getActor(rollData) { return rollData.active.actor }
/** le conteneur de données du RollPart */
getRefs(rollData) {
return rollData.refs[this.code]
@@ -104,10 +107,9 @@ export class RollPart {
return { code: this.code, name: this.name, template: this.template, section: this.section }
}
getAjustements(rollData) {
return []
}
getAjustements(rollData) { return [] }
async _onRender(rollDialog, context, options) { }
getHooks() { return [] }
}

View File

@@ -12,7 +12,7 @@ export class RollTypeTache extends RollType {
title(rollData) {
const current = rollData.current[PART_TACHE]
const tache = current?.tache
return `travaille à sa tâche: ${tache.name ?? ''}`
return tache ? `travaille à sa tâche: ${tache.name}` : `n'a pas de tâches à travailler`
}
onSelect(rollData) {

View File

@@ -47,6 +47,7 @@ export class RollType {
}
setDiffType(rollData, type) {
type = rollData.selected[PART_DIFF].type ?? type
rollData.current[PART_DIFF].type = type
this.setRollDataType(rollData)
}

View File

@@ -1,4 +1,4 @@
import { SYSTEM_RDD } from "../constants.js";
import { RDD_CONFIG, SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
import { RdDBonus } from "../rdd-bonus.js";
@@ -14,14 +14,17 @@ export const STATUSES = {
StatusBleeding: 'bleeding',
StatusDead: 'dead',
StatusDemiReve: 'demi-reve',
StatusSurEnc: 'sur-encombrement',
StatusForceWeak: 'force insuffisante',
}
const forceWeakStatusEffect = { rdd: true, id: STATUSES.StatusForceWeak, name: 'EFFECT.StatusForceWeak', img: 'systems/foundryvtt-reve-de-dragon/assets/actions/weak.svg' };
const demiReveStatusEffect = { rdd: true, id: STATUSES.StatusDemiReve, name: 'EFFECT.StatusDemiReve', img: 'systems/foundryvtt-reve-de-dragon/assets/actions/sort.svg' };
export const forceWeakStatusEffect = { rdd: true, id: STATUSES.StatusForceWeak, name: 'EFFECT.StatusForceWeak', img: RDD_CONFIG.icons.forceWeak };
export const surEncEffect = { rdd: true, id: STATUSES.StatusSurEnc, name: 'EFFECT.StatusSurEnc', img: RDD_CONFIG.icons.surenc };
export const demiReveStatusEffect = { rdd: true, id: STATUSES.StatusDemiReve, name: 'EFFECT.StatusDemiReve', img: RDD_CONFIG.icons.demiReve };
const rddStatusEffects = [
{ rdd: true, id: STATUSES.StatusGrappling, tint: '#33cc33', name: 'EFFECT.StatusGrappling', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' },
{ rdd: true, id: STATUSES.StatusGrappled, tint: '#ff9900', name: 'EFFECT.StatusGrappled', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' },
{ rdd: true, id: STATUSES.StatusGrappling, name: 'EFFECT.StatusGrappling', img: RDD_CONFIG.icons.empoignade },
{ rdd: true, id: STATUSES.StatusGrappled, tint: '#d5633d', name: 'EFFECT.StatusGrappled', img: RDD_CONFIG.icons.empoignade },
{ rdd: true, id: STATUSES.StatusRestrained, name: 'EFFECT.StatusRestrained', img: 'icons/svg/net.svg' },
{ rdd: true, id: STATUSES.StatusStunned, name: 'EFFECT.StatusStunned', img: 'icons/svg/stoned.svg', "duration.rounds": 1 },
@@ -34,7 +37,8 @@ const rddStatusEffects = [
{ rdd: true, id: STATUSES.StatusBleeding, name: 'EFFECT.StatusBleeding', img: 'icons/svg/blood.svg' },
{ rdd: true, id: STATUSES.StatusDead, name: 'EFFECT.StatusDead', img: 'icons/svg/skull.svg' },
demiReveStatusEffect,
forceWeakStatusEffect
forceWeakStatusEffect,
surEncEffect,
];
const statusDemiSurprise = new Set([STATUSES.StatusStunned, STATUSES.StatusProne, STATUSES.StatusRestrained, STATUSES.StatusForceWeak])
@@ -43,8 +47,9 @@ const statusSurpriseTotale = new Set([STATUSES.StatusUnconscious, STATUSES.Statu
export class StatusEffects extends FormApplication {
static onReady() {
const rddEffectIds = rddStatusEffects.map(it => it.id);
const rddEffectIds = rddStatusEffects.map(it => it.id)
rddStatusEffects.forEach(it => {
it.name = game.i18n.localize(it.name)
it.statuses = new Set([it.id])
})
const defaultStatusEffectIds = CONFIG.statusEffects.map(it => it.id);
@@ -82,8 +87,8 @@ export class StatusEffects extends FormApplication {
)
}
static getActorEffetSurprise(actor, forceRequise) {
const effets = actor?.getEffects(StatusEffects.isSurprise, forceRequise) ?? []
static getActorEffetSurprise(actor) {
const effets = actor?.getEffects(StatusEffects.isSurprise) ?? []
return {
effets: effets,
surprise: effets.length > 0
@@ -133,7 +138,7 @@ export class StatusEffects extends FormApplication {
}
static prepareActiveEffect(effectId) {
let status = rddStatusEffects.find(it => it.id == effectId)
let status = rddStatusEffects.find(it => it.statuses? it.statuses.has(effectId) : it.id == effectId)
if (status) {
status = foundry.utils.duplicate(status)
status.statuses = new Set([effectId])

View File

@@ -27,6 +27,7 @@
"dependencies": {
"gulp": "^5.0.0",
"gulp-less": "^5.0.0",
"pdf-lib": "^1.17.1",
"rollup-plugin-visualizer": "^5.12.0"
},
"repository": {

View File

@@ -1408,7 +1408,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!1Nng9d8r6lrPHCaJ.MxFDPQmm1900bWin'
- _id: bNUVmIoLEROEIOIm
name: Cuir / Métal
name: Cuir / métal
type: armure
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_metal.webp
effects: []
@@ -1447,7 +1447,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!1Nng9d8r6lrPHCaJ.bNUVmIoLEROEIOIm'
- _id: OKZvybZ9jJuX8ZDY
name: Cuir Epais
name: Cuir épais
type: armure
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_epais.webp
effects: []
@@ -1485,7 +1485,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!1Nng9d8r6lrPHCaJ.OKZvybZ9jJuX8ZDY'
- _id: sgvPgIF2ars8IeHv
name: Cuir Souple
name: Cuir souple
type: armure
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_souple.webp
effects: []
@@ -1553,7 +1553,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!1Nng9d8r6lrPHCaJ.rZxksRn5Ih4Sj4rN'
- _id: oKCl27wSt33w6j7O
name: Drap Matelassé
name: Drap matelassé
type: armure
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/drap_matelasse.webp
effects: []
@@ -1625,7 +1625,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!1Nng9d8r6lrPHCaJ.Kv9Lb28qekqLU1F7'
- _id: sFm86jqLy1K6q4Px
name: Mailles de Fer
name: Mailles de fer
type: armure
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/mailles.webp
effects: []

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!ryUZTa17LzNv25UY.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!ohmz9Jn4jxD88Kll.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}
@@ -2028,7 +2028,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!ohmz9Jn4jxD88Kll.c5wursWW03ckpyqn'
- _id: i42zzohNKPYS0kMp
name: Drap Matelassé
name: Drap matelassé
type: armure
sort: 6900000
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/drap_matelasse.webp

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!JARnWt2MQWDyRwQt.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}
@@ -2390,7 +2390,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!JARnWt2MQWDyRwQt.RYIOLj7To0rr9RuH'
- _id: bycXvPmShoXAnteu
name: Cuir Souple
name: Cuir souple
type: armure
sort: 8100000
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_souple.webp

View File

@@ -1551,7 +1551,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!SJb0c8FDcYdd41rB.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
img: systems/foundryvtt-reve-de-dragon/icons/competence_survie_desert.webp
@@ -2060,7 +2060,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!SJb0c8FDcYdd41rB.EqKsIbamIeYP3GHz'
- _id: tei7L0X6H6kNrQaI
name: Cuir Souple
name: Cuir souple
type: armure
sort: 7100000
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_souple.webp

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!JQCwAOK64Yijwtch.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}
@@ -2297,7 +2297,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!JQCwAOK64Yijwtch.9mOVjXVNdvnf7isr'
- _id: ISONFNOaWkW2TaE8
name: Cuir / Métal
name: Cuir / métal
type: armure
sort: 8400000
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_metal.webp

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!CRRP8ucJpljX6tq8.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!51vL4MhEE0asjgF2.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}
@@ -2202,7 +2202,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!51vL4MhEE0asjgF2.EqKsIbamIeYP3GHz'
- _id: tei7L0X6H6kNrQaI
name: Cuir Souple
name: Cuir souple
type: armure
sort: 7100000
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/cuir_souple.webp

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!NX1nAqKKIcQlyGua.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!2bRaEDuwZezKAyEq.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}
@@ -2028,7 +2028,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!2bRaEDuwZezKAyEq.c5wursWW03ckpyqn'
- _id: cMiKr3BxOauOkMIm
name: Mailles de Fer
name: Mailles de fer
type: armure
sort: 6900000
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/mailles.webp

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!IeKSXignUpfUTU4m.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}
@@ -2028,7 +2028,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!IeKSXignUpfUTU4m.c5wursWW03ckpyqn'
- _id: cMiKr3BxOauOkMIm
name: Mailles de Fer
name: Mailles de fer
type: armure
sort: 6900000
img: systems/foundryvtt-reve-de-dragon/icons/armes_armures/mailles.webp

View File

@@ -1552,7 +1552,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!pSM0ku0RJNLvHSvF.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
img: systems/foundryvtt-reve-de-dragon/icons/competence_survie_desert.webp

View File

@@ -1552,7 +1552,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!5xPFHgrY5AIP9Mnb.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
img: systems/foundryvtt-reve-de-dragon/icons/competence_survie_desert.webp

View File

@@ -1604,7 +1604,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!U9NNcXQBJmsI9Ttk.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}

View File

@@ -1551,7 +1551,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!oLDROOdwfctyRusH.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
img: systems/foundryvtt-reve-de-dragon/icons/competence_survie_desert.webp

View File

@@ -1551,7 +1551,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!2KN3nKGZ36Qkn7Mf.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
img: systems/foundryvtt-reve-de-dragon/icons/competence_survie_desert.webp

View File

@@ -1605,7 +1605,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!V2WOs8deCYdBT2Jo.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}

View File

@@ -1605,7 +1605,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!UNs4RBLYiGbfxd1c.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}

View File

@@ -1605,7 +1605,7 @@ items:
coreVersion: '12.331'
_key: '!actors.items!jfXs7qaHEWQpIHud.oHnSnCw1RAW5t15S'
- _id: vDOAyWc2YnuhNnFF
name: Survie en Désert
name: Survie en désert
type: competence
sort: 5400000
flags: {}

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