Compare commits

...
Author SHA1 Message Date
VincentVk 36c771518d v13.0.21 2025-12-08 20:12:17 +01:00
VincentVk dd7a3700dc Appréciation des services 2025-12-08 20:10:56 +01:00
VincentVk f0736015a7 Surcharge de couleurs de style
Les titres dans les descriptions n'étaient pas bien affichés dans la
fenêtre de jet
2025-12-08 19:40:21 +01:00
VincentVk aae603d798 Appréciation de plats 2025-12-08 19:39:21 +01:00
VincentVk d9bc6309fb Correction des bonus des Cyans 2025-12-08 01:42:57 +01:00
VincentVk c14361919c Update compendiums 2025-12-08 01:42:55 +01:00
VincentVk b26c39cf21 Ajout des options d'appréciation
L'appréciation utilise:

- un niveau de qualité (qui réutilise la qualité sur les items en ayant)
- un bon moment (coeur/musique/...)
- un niveau de jet de moral
- une caractéristique (perception)
- une compétence

Les bon moments passés sont remis à zéro lors du passage de
château dormant.

Ajout des jets de moral très heureux.

Ajout de jet d'appréciation sur les résultats des oeuvres et des jeux.
2025-12-08 01:42:47 +01:00
VincentVk ca2d17bd25 Encombrement avec décimales 2025-12-07 23:48:41 +01:00
VincentVk a243209c90 Fix: selection parmi les cibles 2025-12-07 02:52:19 +01:00
VincentVk 90435c07b4 cleanup unused imports 2025-12-06 21:12:30 +01:00
VincentVk 46d09c7bd9 Dimension des zones editeur
amélioration des zones de saisie des éditeurs (journaux, ...)
2025-12-06 21:12:30 +01:00
VincentVk 05036877ed Possessions dans HUD
Permettre de commencer les possessions V2
2025-12-06 21:12:25 +01:00
VincentVk 98e8c7a10c Gestion de possession - fin de round
- affichage du message pour indiquer l'état d'une possession en fin de
round
- bouton de conjuration depuis la feuille de personnage
2025-12-05 20:12:14 +01:00
VincentVk 19cabe816e Gestion défense et compteur de possession 2025-12-05 20:12:14 +01:00
VincentVk 706aa657b1 Méthode rollPossession pour attaque possession
- Ajout d'une méthode pour ouvrir le dialogue en mode possession
- gestion des information de possession
- fenêtre défense possession
- préparation des messages de tchat
2025-12-05 20:12:09 +01:00
VincentVk fd3f988a4f Roll V2 mode possession 2025-11-28 19:44:06 +01:00
VincentVk 981282d809 Ajout de méthode getConjurations
Pour lister les compétences de possession/conjuration de manière
homogène (tout actor doué de rêve)
2025-11-28 00:04:16 +01:00
giteadmin fcce8b0a4b Merge pull request 'feature/v13-empoignade-v2' (#785) from feature/v13-empoignade-v2 into v13
Release Creation / build (release) Failing after 10m34s
Reviewed-on: https, #785
2025-11-27 14:38:25 +01:00
VincentVk 461a398965 Police homogène pour la navigation 2025-11-27 02:08:25 +01:00
VincentVk eef0893d1a Version 13.0.20 2025-11-27 01:55:19 +01:00
VincentVk c50f1287b5 Ajustement astrologique rituels 2025-11-27 01:14:46 +01:00
VincentVk acd40f3154 Fix dépense rêve sort
La dépense de rêve de sort se faisait en parallèle de la fermeture
de fenêtre des TMRs, qui modifiait la fatigue. Du coup, l'update de
la dépense de rêve ne se faisait pas
2025-11-27 01:02:09 +01:00
VincentVk 764f9f81c8 Centrage logo pause 2025-11-26 23:39:15 +01:00
VincentVk 70e8cd74a2 Correction feuille méditation joueur 2025-11-26 21:27:39 +01:00
VincentVk e96f4e01bd Majuscule Survie en désert 2025-11-24 18:04:43 +01:00
VincentVk c44c2d7c96 Ajout d'un statut "malade/empoisonné" 2025-11-24 18:04:43 +01:00
VincentVk e2ee73bc4e Empoignade V2
Correction, de l'empoignade en mode jet V2

- le défenseur diminue le nombre de points d'empoignade
- amélioration des messages liés à l'empoignade
- ajout de choix de pertes d'endurance à infliger
2025-11-24 18:04:43 +01:00
VincentVk 3f19886342 Fix affichage sur perte d'endurance
décaller l'ajout d'effets après l'update permet
d'éviter les résultats "invisibles"
2025-11-24 18:04:43 +01:00
VincentVk 9a5afc918e Fix suppression effets 2025-11-24 18:04:43 +01:00
VincentVk 2ac35ef5d1 Corrections de styles incorrects
- combo de sélection des dommages d'empoignade
- boutons de navigation dans les journaux
- boutons de blessures dans la feuille de personnage
2025-11-24 18:04:42 +01:00
giteadmin 33c6b35601 Merge pull request '## 13.0.19 - L'introspection d'Illysis' (#784) from feature/v13-combats-v2 into v13
Release Creation / build (release) Successful in 2m10s
Reviewed-on: https, #784
2025-11-14 17:46:26 +01:00
VincentVk 82f870db05 Bouton pour jet de résistance
Ajout d'un bouton à côté de la carac rêve
2025-11-13 01:13:47 +01:00
VincentVk 878efab321 Correction expérience sur résistance 2025-11-13 00:10:08 +01:00
VincentVk 659ddbd0a4 Jet V2 - Ajout des écailles 2025-11-12 23:09:10 +01:00
VincentVk 22b9e11021 Correction particulières 2025-11-12 22:21:27 +01:00
VincentVk 02ec8880fc Correction combat V2 avec plusieurs joueurs
Les messages de défense fonctionnent sur les jets V2
2025-11-12 22:10:51 +01:00
VincentVk 521cf50b7b Pas de suppression surenc/demireve
L'icône surencombrement ne peut plus être supprimée si on est
sur-encombré.
2025-11-12 22:10:51 +01:00
VincentVk 1cc6fab3a9 Autoriser attaque sans initiative 2025-11-12 22:10:51 +01:00
VincentVk 7baccbabb9 Gestion des droits jets V2 2025-11-12 22:10:42 +01:00
giteadmin d9b24b0f97 Correction sur changelog
Release Creation / build (release) Successful in 2m37s
2025-11-12 15:21:43 +01:00
giteadmin d504a0e131 Merge pull request '13.0.17 - Le reflet d'Illysis' (#783) from feature/v13-mineurs into v13
Reviewed-on: https, #783
2025-11-12 15:19:43 +01:00
VincentVk 09ca86a7fd Suppression de message d'erreur
Si un acteur n'a pas toutes les compétences (ie: invocation), les
messages indiquant que les compétences sont manquantes ne
sont plus affichés
2025-11-11 23:09:28 +01:00
VincentVk 5b68837023 Ajout icone service écuries 2025-11-11 23:09:28 +01:00
VincentVk 7452c54c04 Feuille personnage avec police 2025-11-11 23:09:28 +01:00
giteadmin af1976b9d8 Update release script
Release Creation / build (release) Successful in 1m32s
2025-11-08 00:44:16 +01:00
giteadmin 9fe45ce99f Update release script
Release Creation / build (release) Failing after 1m14s
2025-11-08 00:33:26 +01:00
giteadmin 852964e77b Update release script
Release Creation / build (release) Failing after 1m32s
2025-11-08 00:29:16 +01:00
giteadmin b87dea1661 Update release script
Release Creation / build (release) Failing after 2m22s
2025-11-08 00:24:27 +01:00
giteadmin d2835c7472 Update release script
Release Creation / build (release) Failing after 52s
2025-11-08 00:22:18 +01:00
giteadmin f3c58424e5 Update release script
Release Creation / build (release) Failing after 5s
2025-11-08 00:06:19 +01:00
giteadmin c600eb3965 Update release script
Release Creation / build (release) Has been cancelled
2025-11-08 00:04:57 +01:00
giteadmin 4a7848cb04 Fix other CSS dakr/light theme issues
Release Creation / build (release) Has been cancelled
2025-11-08 00:02:51 +01:00
giteadmin 0d62b60e38 Merge pull request 'v13.0.16 - La paix d'Illysis' (#782) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
Reviewed-on: https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/pulls/782
2025-11-02 09:55:25 +01:00
VincentVk 9b91850731 Export de personnage en pdf
Première version d'export de personnage pdf
2025-11-01 01:28:44 +01:00
VincentVk 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
VincentVk 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
giteadmin 020ff4b014 Merge pull request 'v13.0.15 - Les pièces d'Illysis' (#781) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
Reviewed-on: https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/pulls/781
2025-10-28 18:43:25 +01:00
VincentVk b3c7453823 Fix achat dans les commerces 2025-10-28 17:57:27 +01:00
VincentVk 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
giteadmin 0b90badb5e Merge pull request 'v13.0.14 - Le familier d'Illysis' (#779) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
Reviewed-on: https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/pulls/779
2025-10-25 23:21:59 +02:00
VincentVk 290b5029d1 Correction couleurs sommaires 2025-10-24 22:20:29 +02:00
VincentVk a7e4aea52d Fix: meditation V2 2025-10-24 01:22:20 +02:00
VincentVk 755d15509e Ajout du StatusEffect surencombré 2025-10-23 20:21:56 +02:00
VincentVk 02cea84ebb Empoignade V2 2025-10-23 12:01:52 +02:00
VincentVk c47fc4f5b6 Fix scenes image locations 2025-10-22 22:40:07 +02:00
VincentVk 147e49f2cb Gestion des significatives 2025-10-21 01:40:14 +02:00
VincentVk 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
VincentVk 8f4df1af56 Fix: ajustements surprises
Affichage de l'image de la surprise
2025-10-21 01:39:23 +02:00
VincentVk 77f2de2c5f Jet V2 pour les créatures 2025-10-21 01:39:16 +02:00
VincentVk e5e271e424 Corrections majuscules/couleurs 2025-10-19 23:53:43 +02:00
giteadmin 43ecca8be1 Merge pull request 'v13.0.13 - L'épanouissement d'Illysis' (#777) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
Reviewed-on: https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/pulls/777
2025-10-18 18:04:36 +02:00
VincentVk e21f7e398a Création de tâches dans la fenêtre de jets 2025-10-18 00:52:14 +02:00
VincentVk e16f89743a Affichage des ajustements V2 2025-10-18 00:52:14 +02:00
VincentVk a6c593c100 Corrections premiers retours 2025-10-18 00:52:14 +02:00
giteadmin cd8e190082 Merge pull request 'v13.0.12 - La méditation d'Illysis' (#776) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
Reviewed-on: https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/pulls/776
2025-10-16 20:14:03 +02:00
VincentVk f2106763c1 v13.0.12 2025-10-16 00:55:58 +02:00
VincentVk 5da5cb0314 Gestion des maladresses 2025-10-16 00:35:43 +02:00
VincentVk 6d7f66569a Lancer de sorts V2 2025-10-15 22:42:14 +02:00
VincentVk 3e8963b20b Impacts uniquement sur l'acteur 2025-10-15 22:42:14 +02:00
VincentVk 2cf5b06da8 Correction: rename bringToTop 2025-10-15 22:42:13 +02:00
VincentVk 68c01fc930 Montée TMR sur méditation
ajout d'un bouton pour aller dans les TMR lors d'une
méditation réussie
2025-10-15 22:42:13 +02:00
VincentVk 3bc1c4871b Biugfix: compétences de jeu
Les compétences de jeu spécifiques (doublon) ne remplacent plus
la compétence active si le jet n'est pas un jet de jeu
2025-10-15 00:26:01 +02:00
VincentVk 3d732e9a8a Suppression des signes draconiques
en cas de descente des TMR, suppression des signes
draconiques éphémères durant seulement 1 round
2025-10-15 00:24:35 +02:00
VincentVk 35f226af5c Correction du générateur aléatoire
- couleurs de cheveux/yeux
- les tailles entre 1m00 et 1m09 sont correctement affichées
- possibilité de positionner le sexe masculin/féminin en un clic
2025-10-14 17:44:39 +02:00
giteadmin 126a0701d8 Merge pull request 'v13.0.11 - Le gambit d'Illysis' (#775) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
Reviewed-on: https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/pulls/775
2025-10-09 14:14:44 +02:00
VincentVk edf920b340 Roll V2: cuisine 2025-10-08 16:36:14 +02:00
VincentVk 293af5ab83 Roll V2: les jeux 2025-10-08 16:35:58 +02:00
VincentVk 1bf9e330f4 Petites améliorations 2025-10-05 22:39:08 +02:00
giteadmin 33dc58138c Merge pull request '13.0.10 - Les papilles d'Illysis' (#774) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13
Reviewed-on: https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/pulls/774
2025-10-05 21:39:20 +02:00
VincentVk fa6769fcd7 Fenêtres Roll V2
Maintenant disponibles pour:
- méditation
- tâches
- soins
2025-10-05 03:09:52 +02:00
VincentVk 47c4478303 Roll V2 sur compétences&combat
Corrections associées
Maintenant, active en mode rollV2
2025-10-03 22:34:07 +02:00
VincentVk 52065fd6f6 changelog 13.0.10 2025-10-03 02:21:22 +02:00
VincentVk 6886f19921 Jet de compétence V2 2025-10-03 02:21:22 +02:00
VincentVk 10a89cc268 Fix regression table rdd 2025-10-03 02:21:22 +02:00
VincentVk 6a987086f8 Correction message init & pugilat 2025-10-03 02:21:22 +02:00
VincentVk 54f470d531 Roll recette de cuisine 2025-10-03 02:21:22 +02:00
VincentVk 37af0dd4f1 Rename ajustements => v1 2025-10-03 02:21:22 +02:00
VincentVk d46b039a63 Ajout des saignements 2025-10-03 02:21:21 +02:00
VincentVk 7370b633db Gestion attaques v2 et initiative 2025-10-03 02:21:21 +02:00
VincentVk d0ba1ebf99 utilisation de renderTemplate "interne"
Import de foundry.applications.handlebars.renderTemplate
au travers d'une constante pour éliminer les warnings
2025-09-30 02:04:53 +02:00
VincentVk 053bc23d12 Move HUD button attaque 2025-09-29 01:47:35 +02:00
1373 changed files with 124548 additions and 11937 deletions
+55 -56
View File
@@ -6,71 +6,70 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: localhost
steps:
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
- run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner."
#- uses: actions/checkout@v3
- uses: RouxAntoine/checkout@v3.5.4
with:
ref: 'v13'
- name: Add NODE/NPM bin directory to PATH
shell: bash
run: echo "/opt/alt/alt-nodejs20/root/usr/bin/" >> $GITHUB_PATH
# get part of the tag after the `v`
- name: Extract tag version number
id: get_version
uses: battila7/get-version-action@v2
- uses: RouxAntoine/checkout@v3.5.4
with:
ref: "v13"
# Substitute the Manifest and Download URLs in the module.json
- name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1
with:
files: 'system.json'
env:
version: ${{steps.get_version.outputs.version-without-v}}
url: https://www.uberwald.me/gitea/${{gitea.repository}}
manifest: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/system.json
download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/rddsystem.zip
# get part of the tag after the `v`
- name: Extract tag version number
id: get_version
uses: battila7/get-version-action@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '18' # Use the node version your project requires
# Substitute the Manifest and Download URLs in the module.json
- name: Substitute Manifest and Download Links For Versioned Ones
id: sub_manifest_link_version
uses: microsoft/variable-substitution@v1
with:
files: "system.json"
env:
version: ${{steps.get_version.outputs.version-without-v}}
url: https://gitea.scriptarium.org/${{gitea.repository}}
manifest: https://gitea.scriptarium.org/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/system.json
download: https://gitea.scriptarium.org/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/rddsystem.zip
- name: Install dependencies
run: npm install
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: "18" # Use the node version your project requires
- name: Build the compendiums
run: node ./tools/packCompendiumsToDist.mjs
- name: Install dependencies
run: npm install
# Create a zip file with all files required by the module to add to the release
- run: |
apt update -y
apt install -y zip
- name: Build the compendiums
run: node ./tools/packCompendiumsToDist.mjs
- run: zip -r ./rddsystem.zip system.json template.json README.md LICENSE.txt assets/ css/ fonts/ icons lang/ module/ packs/ pic/ sounds/ styles/ templates/
- name: Create ZIP file
run: zip -r ./rddsystem.zip system.json template.json README.md LICENSE.txt assets/ css/ fonts/ icons lang/ module/ packs/ pic/ sounds/ styles/ templates/
- name: setup go
uses: actions/setup-go@v3
with:
go-version: '>=1.20.1'
- name: setup go
uses: actions/setup-go@v3
with:
go-version: ">=1.20.1"
- name: Use Go Action
id: use-go-action
uses: https://gitea.com/actions/release-action@main
with:
files: |-
./rddsystem.zip
system.json
api_key: '${{secrets.ALLOW_PUSH_RELEASE}}'
- name: Use Go Action
id: use-go-action
uses: https://gitea.com/actions/release-action@main
with:
files: |-
./rddsystem.zip
system.json
api_key: "${{secrets.RDD_PUBLISH_RELEASE}}"
- name: Publish to Foundry server
uses: djlechuck/foundryvtt-publish-package-action@v1
with:
token: ${{ secrets.FOUNDRYVTT_RELEASE_TOKEN }}
id: 'foundryvtt-reve-de-dragon'
version: ${{github.event.release.tag_name}}
manifest: 'https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/system.json'
notes: 'https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v13/changelog.md'
compatibility-minimum: '13'
compatibility-verified: '13'
- name: Publish to Foundry server
uses: djlechuck/foundryvtt-publish-package-action@v1
with:
token: ${{ secrets.RDD_PUBLISH_RELEASE_FOUNDRY }}
id: "foundryvtt-reve-de-dragon"
version: ${{github.event.release.tag_name}}
manifest: "https://gitea.scriptarium.org/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/system.json"
notes: "https://gitea.scriptarium.org/Scriptarium/foundryvtt-reve-de-dragon/raw/branch/v13/changelog.md"
compatibility-minimum: "13"
compatibility-verified: "13"
+4 -1
View File
@@ -25,4 +25,7 @@ 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.
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)
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><defs><linearGradient x1="0" x2="1" y1="1" y2="0" id="lorc-shattered-sword-gradient-4"><stop offset="0%" stop-color="#4a4a4a" stop-opacity="1"></stop><stop offset="100%" stop-color="#c9c9c9" stop-opacity="1"></stop></linearGradient><radialGradient id="lorc-shattered-sword-gradient-5"><stop offset="0%" stop-color="#9013fe" stop-opacity="1"></stop><stop offset="100%" stop-color="#85562c" stop-opacity="1"></stop></radialGradient><radialGradient id="lorc-shattered-sword-gradient-7"><stop offset="0%" stop-color="#9013fe" stop-opacity="1"></stop><stop offset="100%" stop-color="#85562c" stop-opacity="1"></stop></radialGradient></defs><g class="" transform="translate(1,-2)" style=""><path d="m496.223 16.818-48.475 44.205 48.475 4.575v-48.78zm-41.55 1.22-68.91 11.28-7.04 65.547 75.95-76.828zM352.26 77.907l-87.887 50.3 15.447 41.026 82.662-16.87-10.222-74.456zm64.34 25.022-32.967 67.443 24.842 70.684 54.314-134.19-46.19-3.937zm-271.362 72.808c-10.02.155-20.714 3.165-30.79 9.057 4.143 8.863 3.382 18.708-2.776 24.873-6.158 6.165-15.993 6.927-24.846 2.78-12.923 22.167-11.88 47.34 2.485 61.73l29.84-29.87c58.655 42.87 109.37 92.94 150.57 151.24l-36.19 36.23 6.61-6.604c14.283 14.298 39.192 15.42 61.23 2.732-4.88-9.197-4.366-19.74 2.107-26.22 6.47-6.478 17-6.993 26.185-2.112 12.66-22.062 11.54-47-2.732-61.3l-28.248 28.28c-57.035-42.41-107.91-92.665-151.426-150.386l35.455-35.492-6.61 6.603c-7.834-7.843-18.863-11.726-30.862-11.542zm108.45 4.512c-17.61 17.41-39.674 39.21-62.22 61.375a728.952 728.952 0 0 0 27.407 30.094l59.063-59.126-24.25-32.344zm78.28 32.688L297 219.968l-65 65.063a728.12 728.12 0 0 0 35 32.376c22.02-23.134 43.764-46.41 64.97-69.406v-35.063zM173.47 313.25c-31.146 40.858-67.877 74.323-108.905 101.875 4.13 13.708 16.394 26.374 30.624 30.563 29.466-41.26 61.113-79.902 100.97-109.97a637.777 637.777 0 0 0-22.688-22.468zM44.5 410.28l-20.844 20.845c7.133 24.65 27.843 44.912 53.72 53.438l19.28-19.313c-26.948-4.28-48.314-26.91-51.875-52.938l-.28-2.03z" fill="#fff" fill-opacity="1" transform="translate(51.2, 51.2) scale(0.8, 0.8) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
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

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

After

Width:  |  Height:  |  Size: 2.1 KiB

+1
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

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><defs><linearGradient x1="0" x2="1" y1="1" y2="0" id="delapouite-sparkles-gradient-4"><stop offset="0%" stop-color="#4a4a4a" stop-opacity="1"></stop><stop offset="100%" stop-color="#c9c9c9" stop-opacity="1"></stop></linearGradient><radialGradient id="delapouite-sparkles-gradient-5"><stop offset="0%" stop-color="#9013fe" stop-opacity="1"></stop><stop offset="100%" stop-color="#85562c" stop-opacity="1"></stop></radialGradient><radialGradient id="delapouite-sparkles-gradient-7"><stop offset="0%" stop-color="#9013fe" stop-opacity="1"></stop><stop offset="100%" stop-color="#85562c" stop-opacity="1"></stop></radialGradient></defs><g class="" transform="translate(1,-2)" style=""><path d="M237.4 20.73c-6.1 42.1-26.8 64.2-63.9 64 31.6 4.5 63.8 8 63.9 64.07-.6-46.1 24.5-63.07 64.1-64.07-38-1.5-64.9-16.3-64.1-64zm127.8 11.58c-9.1 14.25-20.8 21.29-38.9 10.28 14.9 11.79 18.6 24.76 10.2 38.97 8.9-11.18 17.5-22.73 39-10.27-17.8-10.06-18.8-23.57-10.3-38.98zM59.68 41.69c-2.7 18.8-12 28.6-28.5 28.5 14.1 2 28.4 3.6 28.5 28.52-.3-20.5 10.9-28.12 28.5-28.52-16.9-.7-28.9-7.3-28.5-28.5zM431 66.28c-2.7 18.8-12 28.6-28.5 28.5 14.1 2 28.4 3.6 28.5 28.52-.3-20.5 10.9-28.12 28.5-28.52-16.9-.7-28.9-7.3-28.5-28.5zM120.3 116.4c-15.8 53.7-47.76 48-79.35 43.4C76.6 170 90.3 197.1 84.28 239.2c12.66-46 42.62-52.6 79.42-43.4-37.6-12.1-56.9-35.4-43.4-79.4zm187 5c-8.8 61.6-39.3 94-93.6 93.7 46.2 6.5 93.6 11.7 93.6 93.7-.8-67.3 35.9-92.2 93.8-93.7-55.5-2.2-94.9-23.9-93.8-93.7zm136.8 38.3c-13.1 21.6-29.5 28.8-49.7 20.1 16.3 9.7 33 19.1 20.1 49.6 10.3-25.2 27.9-28.7 49.7-20-20.3-9.7-31.6-23.9-20.1-49.7zM50.7 243.2c9.16 16.7 7.63 30.1-5.61 40 12.46-6.9 24.85-14.3 39.91 5.6-12.57-16.2-8.2-29 5.61-40-13.92 9.7-27.47 11.6-39.91-5.6zm137.2.3c11.4 26.8-.5 41.3-21.7 50.9 22.7-8.5 40.8-4.5 50.9 21.7-12.7-31.8 4.8-41.2 21.7-50.9-21 8.5-37.8.9-50.9-21.7zm228 12.6c-26.6 64.7-68.7 91.7-127.8 76.4 48.6 19.8 98.8 38.5 76.4 127.9 17.5-73.7 64.4-90.7 127.9-76.5-59.9-17.5-96.9-52-76.5-127.8zM99.94 295.5c15.66 57.8.86 98.1-47.32 118.5 43.46-11.8 87.38-25.2 118.68 47.4-26.4-59.3-3.4-95.4 47.3-118.8-50 19.2-93.1 15-118.66-47.1zm169.36 61c-21.8 20.6-43 23.6-63.2 7.3 15.5 16.3 31.6 32.4 7.2 63.3 19.8-25.6 41.2-24.1 63.3-7.3-20.2-17.4-28.6-37.5-7.3-63.3zM443.2 404c-2.7 18.8-12 28.6-28.5 28.5 14.1 2 28.4 3.6 28.5 28.5-.3-20.5 10.9-28.1 28.5-28.5-16.9-.7-28.9-7.3-28.5-28.5zm-169.7 36c-2.7 18.8-12 28.6-28.5 28.5 14.1 2 28.4 3.6 28.5 28.5-.3-20.5 10.9-28.1 28.5-28.5-16.9-.7-28.9-7.3-28.5-28.5z" fill="#fff" transform="translate(51.2, 51.2) scale(0.8, 0.8) rotate(0, 256, 256) skewX(0) skewY(0)" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+1
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="M256.25 20.313c-108.64 0-196.78 90.592-196.78 202.937 0 84.615 49.994 156.904 121.093 187.438v-16.375c0-25.638-6.2-40.132-13.875-48.47-7.675-8.336-17.567-11.4-27.72-11.937-18.252-.97-32.4-12.266-40.905-27-6.47-11.205-5.975-24.767-.532-36.97 5.444-12.2 15.49-23.71 29.19-33.905 27.394-20.39 69.79-35.56 122.436-35.56 52.702 0 98.475 15.648 129.344 36.218 15.435 10.284 27.24 21.746 34.22 33.968 6.976 12.222 8.93 26.41 2.186 38.094-4.19 7.257-11.026 11.988-18.687 15.938-7.663 3.95-16.085 6.78-24.595 6.78-11.865 0-20.63 2.214-27.438 10.157s-12.437 23.63-12.437 52.688v16.437c71.195-30.488 121.28-102.814 121.28-187.5 0-112.347-88.14-202.938-196.78-202.938zm-76.656 86.718c1.53.02 3.05.078 4.562.157 14.525.766 28.29 4.3 41.063 9.97l17.31 7.687-16.624 9.062-84.062 45.875-8.938-16.405 65.156-35.563c-24.195-5.513-50.92-1.877-82.062 17.532l-9.875-15.844c25.778-16.066 50.528-22.75 73.47-22.47zm150.312 0c22.944-.28 47.722 6.404 73.5 22.47l-9.875 15.844c-31.14-19.41-57.865-23.045-82.06-17.53l65.155 35.56-8.938 16.407-84.062-45.874L267 124.844l17.313-7.688c12.772-5.67 26.506-9.203 41.03-9.97 1.514-.078 3.034-.136 4.563-.155zm-73.594 170.282c-68.947 0-113.104 15.367-116.843 37.907l.5.03c13.487.714 28.843 5.31 40.468 17.938 11.624 12.627 18.812 32.418 18.812 61.125v100.312h113.813V394.312c0-31.4 5.766-51.81 16.937-64.843 11.17-13.034 27.003-16.69 41.625-16.69.352 0 .76-.03 1.156-.06-6.417-22.614-49.872-35.408-116.467-35.408z" fill="#fff" fill-opacity="1"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><polygon points="256,512,0,256,256,0,512,256" fill="#000" fill-opacity="1"></polygon><g class="" transform="translate(8,-12)" style=""><path d="M282.83 18.877c21.567 6.146 36.328 15.205 40.572 28.77a71.007 71.007 0 0 0-4.066 2.363c-25.655 5.807-53.084 18.634-81.047 34.363 20.225-4.69 36.705-4.42 47.544 3.324-5.712 10.3-10.797 22.294-15.123 36.06-21.343 67.922-125.678-80.577-189.065-5.255C41.47 166.24 92.702 212.342 26.5 294.29c35.583-4.14 45.11-9.47 62.416-36.21 10.44-16.13 28.52-22.1 44.668-21.45-12.718 11.206-25.786 30.354-38.21 59.895 19.832-18.528 44.927-38.68 71.603-40.005.912 6.354-2.408 13.74-12.008 21.418C93.21 327.327 15.58 364.185 19.83 476.504c5.558-11.267 11.646-20.31 17.574-28.617 5.98 29.582 28.2 53.8 92.99 40.482-46.928-6.407-76.268-59.586-45.355-82.528 62.716-46.544 128.82 1.436 269.9-75.342a79.832 79.832 0 0 1 7.164-3.46c-2.246 19.6-12.367 39.84-22.362 57.14 14.26-10.38 25.415-20.147 33.928-29.262 2.14 26.14-11.748 54.65-25.393 78.268 43.26-31.49 61.19-57.976 63.207-78.422 9.334 36.678-1.895 95.547-25.03 123.492 77.553-39.433 106.608-77.127 109.76-190.664 1.662-59.824-66.23-60.536-56.435-101.344 11.945-49.756 6.768-84.69-7.565-107.947-19.6-49.73-79.99-59.74-149.385-59.423zM89.8 47.684C54.69 47.534 45.233 83.056 55.724 117c17.146-51.504 70.414-44.24 111.17-34.367-33.282-25.005-58.707-34.87-77.096-34.95zm254.595 30.742c18.643 21.37 29.373 43.02 10.105 65.732-26.562-16.677-26.985-39.252-10.105-65.732zm74.494 48.6c3.655-.013 7.78.35 12.473 1.09-1.706 30.506-14.4 33-46.634 32.154 8.335-22.146 14.416-33.176 34.16-33.245zm-84.677 40.316c9.652.1 20.258 2.84 30.598 8.918 44.65 26.246 21.934 73.314-4.1 74.78-15.174.855 1.443-23.842-16.17-38.476-15.258-12.678-38.596 12.53-45.204-5.78-8.218-22.693 10.21-39.696 34.877-39.442zm60.18 246.168c-24.195 27.825-58.89 60.1-99.444 31.41 16.625 45.643 68.87 70.465 99.443-31.41z" fill="#fff" fill-opacity="1" transform="translate(102.4, 102.4) scale(0.6, 0.6) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

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

After

Width:  |  Height:  |  Size: 1.1 KiB

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

After

Width:  |  Height:  |  Size: 1.2 KiB

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

After

Width:  |  Height:  |  Size: 1.2 KiB

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

After

Width:  |  Height:  |  Size: 2.1 KiB

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

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

+110
View File
@@ -0,0 +1,110 @@
<?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 378.00079 384.97681"
version="1.1"
id="svg23"
sodipodi:docname="resistance.svg"
width="378.00079"
height="384.97681"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata27">
<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>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1875"
inkscape:window-height="1352"
id="namedview25"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4355469"
inkscape:cx="186.7504"
inkscape:cy="196.1768"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg23" />
<defs
id="defs17">
<linearGradient
x1="0"
x2="1"
y1="1"
y2="0"
id="lorc-aura-gradient-4">
<stop
offset="0%"
stop-color="#4a4a4a"
stop-opacity="1"
id="stop2" />
<stop
offset="100%"
stop-color="#c9c9c9"
stop-opacity="1"
id="stop4" />
</linearGradient>
<radialGradient
id="lorc-aura-gradient-5">
<stop
offset="0%"
stop-color="#9013fe"
stop-opacity="1"
id="stop7" />
<stop
offset="100%"
stop-color="#85562c"
stop-opacity="1"
id="stop9" />
</radialGradient>
<radialGradient
id="lorc-aura-gradient-7">
<stop
offset="0%"
stop-color="#9013fe"
stop-opacity="1"
id="stop12" />
<stop
offset="100%"
stop-color="#85562c"
stop-opacity="1"
id="stop14" />
</radialGradient>
</defs>
<g
class=""
transform="translate(-68.2496,-61.8232)"
id="g21">
<path
d="m 320.938,13.28 c -16.646,34.584 -38.466,60.157 -63.094,60.157 -24.522,0 -47.035,-25.275 -63.656,-59.593 0.366,39.358 -9.71,90.884 -30.938,105.125 -21.228,14.24 -49.64,-12.002 -78.844,-32.126 17.455,34.04 42.095,67.5 29.78,92.28 -12.21,24.576 -59.172,35.96 -92.874,35.626 29.338,19.29 78.842,45.803 78.844,74.188 0.002,28.384 -49.504,53.71 -78.844,73 33.702,-0.333 80.663,11.612 92.876,36.187 12.227,24.61 -9.03,56.31 -33.75,85.563 44.826,-15.413 65.142,-5.735 85.374,10.812 h 31.75 C 154.822,459.086 125.5,386.671 125.5,302.936 125.498,184.316 184.42,88.03 256.906,88.03 c 72.488,0 131.406,96.29 131.406,214.907 0,83.74 -29.317,156.153 -72.062,191.563 h 27.313 c 19.847,-14.62 39.796,-25.65 89.687,-9.28 -26.233,-30.264 -42.2,-62.484 -29.97,-87.095 12.257,-24.665 56.658,-36.612 90.533,-36.188 -29.4,-19.297 -75.344,-44.584 -75.344,-73 0,-28.415 45.943,-54.89 75.342,-74.187 -33.874,0.424 -78.273,-10.962 -90.53,-35.625 -12.315,-24.78 9.982,-58.24 27.437,-92.28 -29.202,20.12 -57.583,46.385 -78.845,32.124 -21.262,-14.263 -31.382,-66.13 -30.938,-105.69 z m -68.97,93.75 c -19.56,2.543 -37.343,25.564 -37.343,55.407 0,16.447 5.67,30.986 14,41.032 l 10.156,12.218 -15.593,2.937 c -10.815,2.035 -18.743,7.737 -25.53,17.063 -6.79,9.325 -11.984,22.344 -15.626,37.343 -6.585,27.128 -8.078,60.24 -8.31,89.47 h 36.093 l 0.656,8.656 9.124,122.563 h 76.187 l 8.095,-122.5 0.563,-8.72 h 34.375 c -0.026,-29.592 -0.44,-63.166 -6.407,-90.5 -3.295,-15.095 -8.287,-28.096 -15.156,-37.313 -6.87,-9.216 -15.133,-14.897 -27.28,-16.78 l -15.94,-2.47 10.064,-12.593 c 7.97,-9.996 13.375,-24.36 13.375,-40.406 -0.002,-31.817 -19.884,-55.313 -41.44,-55.313 -2.54,0 -3.96,-0.103 -4.03,-0.094 h -0.03 z"
fill="#ffffff"
fill-opacity="1"
transform="matrix(0.8,0,0,0.8,51.2,51.2)"
id="path19" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+156 -1
View File
@@ -1,12 +1,167 @@
# 13.0
## 13.0.21 - La folie d'Illysis
- Jets d'appréciation et de moral
- concerne la danse, musique, chant, jeu, les oeuvres, la nourriture, les services
- gestion des compétences d'appréciation pour les oeuvres
- pas de jet de moral si la qualité n'est pas suffisante (inférieure au moral,
à la compétence, moral déjà gagné pour la même raison)
- jet d'appréciation avec en option la compétence et la qualité
- jet de moral très heureux (pour Spasme)
- les jets de moral sont liés à des "bon moments"
- la liste des bons moments passés par un acteur est remise à zéro à Château Dormant
- Fenêtre de jet v2
- les possessions sont maintenant gérées avec les fenêtres V2
- on peut choisir sa voie de draconic pour contrer une possession ou conjurer
- La zone de saisie dans les journaux est mieux adaptée
- Correction des bonus de caractéristiques des cyans
## 13.0.20 - L'immobilisme d'Illysis
- Centrage du logo de pause
- Ajout d'un statut malade/empoisonné
- Fenêtre de jet v2
- correction du compteur empoignade
- amélioration des messages liés à l'empoignade
- amélioration des choix de perte d'endurance
- correction de la dépense de rêve sur lancement de sort
- ajustement astrologique sur rituels
## 13.0.19 - L'introspection d'Illysis
- Ajout d'un bouton pour les jets de résistance sous rêve actuel
- Fenêtre de jet v2
- restrictions au propriétaire du personnage
- ouverture de la fenêtre de jets
- appel à la chance
- appel à la destinée
- fermeture par défaut selon le type de jets:
- jets d'attaque
- jets de défense
- jets imposés par un choix d'action
- lancer de sorts
- jets suite à appel à la chance
- gestion des écailles d'efficacité sur les armes
- permettre de faire une attaque même si l'initiative n'est pas faite
- les messages de défense s'affichent correctement avec plusieurs joueurs
- les particulières en force/rapidité sont correctement affichées dans le tchat
- l'expérience sur jet de résistance est limitée à 1 point
## 13.0.18 - Le reflet d'Illysis
- Mise à jour de la feuille de personnage pdf template (avec police correcte)
- Ajout d'une icône "écurie" pour les services des commerces & auberges
- Suppression de message d'erreur sur compétence
manquante à l'ouverture des acteurs d'invocation
## 13.0.17 - Le chant d'Illysis
- Corrections CSS
- Migration Uberwald -> Scriptarium
## 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
- les signes draconiques éphémères de 1 round sont supprimés à la descente des TMRs
- Générateur de description
- correction des termes pour les couleurs des yeux/cheveux
- ajout de boutons pour forcer le sexe masculin/féminin
- Nouvelle fenêtre de jets de dés
- les méditations proposent un bouton pour monter dans les TMRs
- fenêtre de lancer de sorts
- Correction: les compétences de jeux ne remplacent plus les compétences en dehors des jets de jeu
- gestion des maladresses d'attaque et défense
## 13.0.11 - Le gambit d'Illysis
- Nouvelle fenêtre de jets de dés
- jeux
- cuisine et préparation de nourriture
## 13.0.10 - Les papilles d'Illysis
- Ajout d'un statut "saignement" en cas de blessure grave ou critique sans premiers soins
- Nouvelle fenêtre de jets de dés
- jets de méditation
- jets de tâches
- jets de caractéristiques
- jets de compétences
- Boutons d'initiative et d'attaque V2
- fenêtre d'attaque
- choix des armes
- gestion des particulières
- message au défenseur
- gestion des demi-surprises (attaquant/défenseur)
- gestion des tactiques (attaquant/défenseur)
- en cours nouvelle fenêtre de jets
- jets de compétence avec messages
- jets de cuisine séparés avec messages (pour gérer plus tard les spécificités: fabricatioon de plats)
- gestion des empoignades
- Technique: suppression de warnings foundry sur renderTemplate
## 13.0.9 - Le combat d'Illysis
- Fix
- La montée en TMR fonctionne
- ajout d'un status "Force insuffisante"
- clarification des maladresses à l'attaque (en demi surprise, ou à cause d'un échec total)
- Nouvelle fenêtre de jets de dés
- ajout du statut "Force insuffisante" aux acteurs si la
- ajout du statut "Force insuffisante" aux acteurs si la
force est insuffisante pour l'arme
- avancement du mode attaque
- choix de tactique
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

+4 -2
View File
@@ -1,6 +1,6 @@
{
"TYPES": {
"Actor": {
"Actor": {
"personnage": "Personnage",
"creature": "Créature",
"entite": "Entité de cauchemar",
@@ -63,10 +63,12 @@
"StatusUnarmed": "Désarmé",
"StatusGrappling": "Empoignade",
"StatusGrappled": "Empoigné",
"StatusMalade": "Malade",
"StatusRestrained": "Immobilisé",
"StatusComma": "Comma",
"StatusDead": "Mort",
"StatusDemiReve": "Demi-rêve",
"StatusSurEnc": "Sur-encombrement",
"StatusForceWeak": "Force insuffisante"
}
}
}
+10 -2
View File
@@ -1,4 +1,4 @@
:root {
body {
/* =================== 2. DEBUGGING HIGHLIGHTERS ============ */
// --debug-background-color-red: #ff000054;
@@ -13,6 +13,7 @@
/* =================== 3. some constants ============ */
--fieldset-background: url(/ui/parchment.jpg);
--rdd-color-text-primary: hsla(0, 0%, 4%, 0.9);
--color-text-primary: hsla(0, 0%, 4%, 0.9);
--rdd-input-background:hsla(0, 0%, 0%, 0.1);
--rdd-color-border-input: hsla(0, 0%, 0%, 0.2);
--rdd-bg-input: hsla(0, 0%, 100%, 0.1);
@@ -27,6 +28,11 @@
--gradient-red: linear-gradient(150deg, rgba(255, 0, 0, 0.3), rgba(255, 200, 128, 0.05),rgba(255, 200, 128, 0.1), rgba(255,10,0,0.3));
--gradient-violet: linear-gradient(150deg, rgba(100, 45, 124, 0.6), rgba(216, 157, 192, 0.3), rgba(177, 157, 216, 0.5), rgba(107, 62, 121, 0.3), rgba(100, 45, 124, 0.6));
--gradient-purple-black: linear-gradient(150deg, rgba(0, 0, 0, 0.7), rgba(100, 45, 124, 0.4), rgba(82, 17, 131, 0.3),rgba(100, 45, 124, 0.4), rgba(0, 0, 0, 0.7));
--gradient-warning: linear-gradient(150deg,
hsla(32, 100%, 50%, 0.3),
hsla(52, 60%, 50%, 0.1),
hsla(32, 60%, 50%, 0.1),
hsla(32, 100%, 50%, 0.3));
--gradient-silver-light: linear-gradient(30deg, rgba(61, 55, 93, 0.2), rgba(178, 179, 196, 0.1), rgba(59, 62, 63, 0.2), rgba(206, 204, 199, 0.1), rgba(61, 46, 49, 0.2));
--gradient-daylight: conic-gradient(
from 0deg,
@@ -39,7 +45,9 @@
hsla(50, 100%, 80%, 0.7)
);
--background-custom-button: linear-gradient(to bottom, hsla(208, 38%, 21%, 0.988) 5%, hsla(202, 42%, 14%, 0.671) 100%);
--background-color-button: hsl(25, 5%, 60%);
--background-color-custom-button: hsla(208, 38%, 21%, 0.988);
--background-custom-button: linear-gradient(to bottom, var(--background-color-custom-button) 5%, hsla(202, 42%, 14%, 0.671) 100%);
--background-custom-button-hover: linear-gradient(to bottom, hsla(0, 100%, 25%, 1) 5%, hsla(0, 97%, 12%, 1) 100%);
--background-control-selected: linear-gradient(to bottom, hsla(0, 100%, 25%, 0.5) 5%, hsla(0, 100%, 12%, 0.5) 100%);
--background-tooltip: hsla(60, 12%, 85%, 0.95);
+2 -3
View File
@@ -70,8 +70,7 @@
/* Fonts */
.sheet header.sheet-header h1 input,
.window-app .window-header,
#actors .directory-list,
#navigation #scene-list .scene.nav-item {
#sidebar {
font-family: "GoudyAcc"
}
@@ -80,6 +79,6 @@
.window-app input,
.sheet header.sheet-header .header-compteurs,
.sheet header.sheet-header .flex-group-center.flex-fatigue,
select, .item-checkbox, #sidebar, #players, #navigation #nav-toggle {
select, .item-checkbox, #players {
font-family: "CaslonAntique"; /* For sheet parts; For nav and title */
}
File diff suppressed because it is too large Load Diff
+89 -76
View File
@@ -1,87 +1,100 @@
.chat-message {
div.roll-chat {
font-family: CaslonAntique;
display: grid;
grid-template-areas:
"img header buttons"
"img resume buttons"
"details details details"
"actions actions actions";
grid-template-columns: 3rem 1fr 1.4rem;
grid-template-rows: max-content max-content max-content max-content;
gap: 0 0.5rem;
.chat-message div.roll-chat,
.dialog-content div.roll-chat {
font-family: CaslonAntique;
display: grid;
grid-template-areas:
"img header buttons"
"img resume buttons"
"details details details"
"description description description"
"actions actions actions"
;
grid-template-columns: 3rem 1fr 1.4rem;
grid-template-rows: max-content max-content max-content max-content;
gap: 0 0.5rem;
div.chat-img {
grid-area: img;
div.chat-img {
grid-area: img;
display: flex;
flex-direction: column;
img {
border: 0;
max-height: 3rem;
max-width: 3rem;
object-fit: contain;
height: 100%;
}
}
div.chat-header {
grid-area: header;
font-weight: bold;
font-size: 0.9rem;
}
div.chat-resume {
grid-area: resume;
text-align: justify;
}
div.chat-details {
grid-area: details;
text-align: justify;
display: flex;
flex-direction: column;
div {
display: block;
}
}
div.chat-actions {
grid-area: actions;
display: flex;
flex-direction: column;
a {
display: flex;
flex-direction: column;
flex-direction: row;
img {
border: 0;
max-height: 3rem;
max-width: 3rem;
object-fit: contain;
height: 100%;
margin-right: 0.5rem;
}
}
div.chat-header {
grid-area: header;
font-weight: bold;
font-size: 0.9rem;
}
div.chat-description {
grid-area: description;
text-align: justify;
display: flex;
flex-direction: column;
div {
display: block;
}
div.chat-resume {
grid-area: resume;
text-align: justify;
}
div.chat-details {
grid-area: details;
text-align: justify;
display: flex;
flex-direction: column;
}
div.chat-actions {
grid-area: actions;
display: flex;
flex-direction: column;
a {
display: flex;
flex-direction: row;
img {
margin-right: 0.5rem;
}
}
div.chat-buttons {
grid-area: buttons;
display: flex;
flex-direction: column;
a {
border-radius: 0.2rem;
cursor: pointer;
padding: 0.2rem;
position: relative;
box-shadow: inset 1x 1px #a6827e;
color: var(--color-controls);
border: 1px ridge #846109;
display: inline-block;
align-items: center;
img {
max-width: 1rem;
max-height: 1rem;
}
}
div.chat-buttons {
grid-area: buttons;
display: flex;
flex-direction: column;
a {
border-radius: 0.2rem;
cursor: pointer;
padding: 0.2rem;
position: relative;
box-shadow: inset 1x 1px #a6827e;
color: var(--color-controls);
border: 1px ridge #846109;
display: inline-block;
align-items: center;
img {
max-width: 1rem;
max-height: 1rem;
}
}
a:hover {
background: var(--background-custom-button-hover);
}
a:active{
position:relative;
top:1px;
}
a:hover {
background: var(--background-custom-button-hover);
}
a:active{
position:relative;
top:1px;
}
}
}
}
+35 -12
View File
@@ -89,8 +89,12 @@
display: flex;
flex-direction: row;
margin: 0.1rem 0;
.warning {
border-radius: 0.5rem;
background: var(--gradient-warning);
}
}
roll-part-img {
display: flex;
flex-direction: column;
@@ -117,6 +121,7 @@
div.poesie-extrait{
display: flex;
flex-direction: column;
align-items: normal;
}
span.status-surprise{
display: flex;
@@ -137,8 +142,13 @@
display: flow;
width: 2.5rem;
text-align: right;
margin: 0 0.2rem 0 0.5rem;
margin: 0 0.2rem;
padding: 0 0.2rem;
border: 1px solid ;
border-radius: 0.2rem ;
background: hsla(0, 0%, 0%, 0.2);
height: 1.5rem;
background: hsla(0, 0%, 0%, 0.2);
}
roll-action {
@@ -150,6 +160,9 @@
font-size: 1.2rem;
font-weight: bold;
roll-section[name="resistance"] {
align-items: flex-end;
}
roll-section {
display: flex;
flex-direction: row;
@@ -164,6 +177,12 @@
margin: 0 1rem;
padding: 0;
}
img.flag-resistance {
max-width: 1.5rem;
max-height: 1.5rem;
text-align: right;
filter: invert(0.6);
}
}
}
@@ -201,28 +220,32 @@
}
roll-carac select[name="select-carac"] {
max-width: 6rem;
}
min-width: 6.5rem;
max-width: 8rem;
}
roll-comp select[name="select-comp"] {
min-width: 8rem;
max-width: 11rem;
margin-left: 1rem;
}
max-width: 10rem;
margin-left: 1.5rem;
}
roll-conditions roll-section[name="coeur"] select[name="coeur"] {
max-width: 4rem;
}
roll-conditions roll-section[name="tricher"] img {
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
roll-conditions roll-section img {
max-width: 1rem;
max-height: 1rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
roll-conditions roll-section[name="tricher"] img {
/* image de d100 */
max-width: 2.5rem;
max-height: 2.5rem;
}
roll-buttons {
display: flex;
+3 -3
View File
@@ -8,7 +8,7 @@
min-height: 100px; // Hauteur minimale pour la description
background: var(--rdd-bg-input-alt); // Une couleur de fond alternative
padding: 5px;
border-radius: 3px;
border-radius: 0.2rem;
color: var(--rdd-color-text-primary);
}
@@ -28,7 +28,7 @@
background: var(--fieldset-background);
color: var(--rdd-color-text-primary);
margin-bottom: 4px;
border-radius: 6px;
border-radius: 0.5rem;
border-color: var(--rdd-color-text-primary);
border-width: 2px;
}
@@ -64,7 +64,7 @@
--rdd-color-text-input
); // Assurez-vous que cette variable existe
padding: 2px 2px; // Augmentation du padding vertical
border-radius: 3px;
border-radius: 0.2rem;
}
input[type="checkbox"] {
+1 -1
View File
@@ -1,4 +1,4 @@
import { SYSTEM_RDD } from "../constants.js";
import { renderTemplate, SYSTEM_RDD } from "../constants.js";
import { RdDUtility } from "../rdd-utility.js";
const DETAIL_VENTE = 'detailVente';
+1
View File
@@ -1,3 +1,4 @@
import { renderTemplate } from "../constants.js";
import { Misc } from "../misc.js";
import { RdDUtility } from "../rdd-utility.js";
import { ChatVente } from "./chat-vente.js";
+1
View File
@@ -1,3 +1,4 @@
import { renderTemplate } from "../constants.js";
import { HtmlUtility } from "../html-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { ChatVente } from "./chat-vente.js";
+9 -15
View File
@@ -3,7 +3,7 @@ import { HtmlUtility } from "./html-utility.js";
import { RdDBonus } from "./rdd-bonus.js";
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { CARACS, RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
@@ -18,6 +18,7 @@ import { RdDBaseActorSangSheet } from "./actor/base-actor-sang-sheet.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
import { MORAL } from "./moral/apprecier.mjs";
/* -------------------------------------------- */
/**
@@ -174,7 +175,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
await this.getBlessure(event)?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } })
});
this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac('chance-actuelle'))
this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac(CARACS.CHANCE_ACTUELLE))
this.html.find('.button-appel-chance').click(async event => await this.actor.rollAppelChance())
this.html.find('[name="jet-astrologie"]').click(async event => await this.actor.astrologieNombresAstraux())
@@ -208,7 +209,8 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
}
// Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollCarac('reve-actuel', { resistance: true }))
this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollReveActuel({ resistance: false }))
this.html.find('.button-reve-resistance').click(async event => await this.actor.rollReveActuel({ diff: -8, resistance: true }))
this.html.find('.action-empoignade').click(async event => await RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor)))
this.html.find('.roll-arme').click(async event => {
@@ -274,9 +276,9 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
this.html.find('.seuil-reve-value').change(async event => await this.actor.setPointsDeSeuil(event.currentTarget.value))
this.html.find('.stress-test').click(async event => await this.actor.transformerStress())
this.html.find('.moral-malheureux').click(async event => await this.actor.jetDeMoral('malheureuse'))
this.html.find('.moral-neutre').click(async event => await this.actor.jetDeMoral('neutre'))
this.html.find('.moral-heureux').click(async event => await this.actor.jetDeMoral('heureuse'))
this.html.find('.moral-malheureux').click(async event => await this.actor.jetDeMoral(MORAL.MALHEUREUX))
this.html.find('.moral-neutre').click(async event => await this.actor.jetDeMoral(MORAL.NEUTRE))
this.html.find('.moral-heureux').click(async event => await this.actor.jetDeMoral(MORAL.HEUREUX))
this.html.find('.button-ethylisme').click(async event => await this.actor.jetEthylisme())
this.html.find('.ptreve-actuel-plus').click(async event => await this.actor.reveActuelIncDec(1))
@@ -342,7 +344,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 +377,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);
+280 -170
View File
@@ -17,9 +17,9 @@ import { Draconique } from "./tmr/draconique.js";
import { CARACS, LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js";
import { DialogConsommer } from "./dialog-item-consommer.js";
import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RollDataAjustements } from "./rolldata-ajustements-v1.js";
import { RdDPossession } from "./rdd-possession.js";
import { ACTOR_TYPES, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { ACTOR_TYPES, renderTemplate, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDConfirm } from "./rdd-confirm.js";
import { DialogRepos } from "./sommeil/dialog-repos.js";
import { RdDBaseActor } from "./actor/base-actor.js";
@@ -41,12 +41,19 @@ import { RdDItemCompetence } from "./item-competence.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { RdDRencontre } from "./item/rencontre.js";
import { DialogSelect } from "./dialog-select.js";
import { PAS_DE_DRACONIC, POSSESSION_SANS_DRACONIC } from "./item/base-items.js";
import { CATEGORIES_COMPETENCES, PAS_DE_DRACONIC, POSSESSION_SANS_DRACONIC } from "./item/base-items.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js";
import { ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_SORT } from "./roll/roll-constants.mjs";
import { PART_TACHE } from "./roll/roll-part-tache.mjs";
import { PART_COMP } from "./roll/roll-part-comp.mjs";
import { PART_OEUVRE } from "./roll/roll-part-oeuvre.mjs";
import { PART_CUISINE } from "./roll/roll-part-cuisine.mjs";
import { RdDPossessionV2 } from "./rdd-possession-v2.mjs";
import { Apprecier, MORAL, SITUATION_MORAL } from "./moral/apprecier.mjs";
export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
@@ -98,6 +105,8 @@ export class RdDActor extends RdDBaseActorSang {
}
isPersonnage() { return true }
isFeminin() { return this.system.sexe.length > 0 && this.system.sexe.charAt(0).toLowerCase() == 'f' }
isHautRevant() { return this.system.attributs.hautrevant.value != "" }
/* -------------------------------------------- */
@@ -123,11 +132,6 @@ export class RdDActor extends RdDBaseActorSang {
return etatGeneral
}
/* -------------------------------------------- */
getActivePoisons() {
return foundry.utils.duplicate(this.items.filter(item => item.type == 'poison' && item.system.active))
}
/* -------------------------------------------- */
getMalusArmure() {
return this.itemTypes[ITEM_TYPES.armure].filter(it => it.system.equipe)
@@ -136,12 +140,12 @@ export class RdDActor extends RdDBaseActorSang {
}
listActions({ isAttaque = false, isEquipe = false }) {
// Recupération des armes
// Recupération des attaques
const actions = this.listActionsAttaque()
.filter(it => !isEquipe || it.arme.system.equipe)
if (!isAttaque && this.system.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', initOnly: true })
actions.push({ label: "Draconic", action: 'haut-reve', initOnly: true })
}
return actions
}
@@ -158,12 +162,15 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
getDemiReve() { return this.system.reve.tmrpos.coord }
getDraconicList() { return this.itemTypes[ITEM_TYPES.competence].filter(it => it.system.categorie == 'draconic') }
getBestDraconic() { return foundry.utils.duplicate([...this.getDraconicList(), PAS_DE_DRACONIC].sort(Misc.descending(it => it.system.niveau)).find(it => true)) }
getDraconicOuPossession() {
return [...this.getDraconicList().filter(it => it.system.niveau >= 0), POSSESSION_SANS_DRACONIC]
.sort(Misc.descending(it => it.system.niveau))
.find(it => true)
getDraconics() {
return this.isHautRevant()
? this.itemTypes[ITEM_TYPES.competence].filter(it => it.system.categorie == CATEGORIES_COMPETENCES.draconic.key)
: []
}
getBestDraconic() { return foundry.utils.duplicate([...this.getDraconics(), PAS_DE_DRACONIC].sort(Misc.descending(it => it.system.niveau)).find(it => true)) }
getConjurationNaturelle() {
return new RdDItemCompetence(POSSESSION_SANS_DRACONIC)
}
isForceInsuffisante(forceRequise) {
@@ -177,13 +184,16 @@ export class RdDActor extends RdDBaseActorSang {
const actions = []
const uniques = []
const addAttaque = (arme, main) => {
const dommagesArme = RdDItemArme.valeurMain(arme.system.dommages, main)
const addAttaque = (arme, main = undefined) => {
const dommages = RdDItemArme.valeurMain(arme.system.dommages, main)
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
const ecaillesEfficacite = arme.system.magique ? arme.system.ecaille_efficacite : 0;
const comp = this.getCompetence(RdDActor.$getCompetenceAction(arme, main))
const unique = [comp.id, arme.name, dommagesArme, forceRequise, ecaillesEfficacite].join('|');
const comp = this.getCompetence(arme.getCompetenceAction(main), { onMessage: message => { console.info(message) } })
if (!comp) {
return
}
const unique = [comp.id, arme.name, dommages, forceRequise, ecaillesEfficacite].join('|');
if (uniques.includes(unique)) {
return
}
@@ -196,7 +206,7 @@ export class RdDActor extends RdDBaseActorSang {
const ajustement = (arme.parent?.getEtatGeneral() ?? 0) + ecaillesEfficacite
actions.push({
name: arme.name + (main ? ' ' + main : ''),
label: arme.name + (main ? ' ' + main : ''),
action: 'attaque',
initOnly: false,
arme: arme,
@@ -204,24 +214,21 @@ export class RdDActor extends RdDBaseActorSang {
main: main,
carac: { key: caracCode, value: caracValue },
equipe: arme.system.equipe,
dommagesArme: dommagesArme,
dommages: dommages,
forceRequise: forceRequise,
initiative: RdDInitiative.calculInitiative(niveau, caracValue, ajustement)
initiative: RdDInitiative.getRollInitiative(caracValue, niveau, ajustement)
})
}
addAttaque(RdDItemArme.empoignade(this))
addAttaque(RdDItemArme.corpsACorps(this))
addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS)
this.itemTypes[ITEM_TYPES.arme]
.filter(it => it.isAttaque())
.sort(Misc.ascending(it => it.name))
.forEach(arme => {
if (arme.system.unemain && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.UNE_MAIN) }
if (arme.system.deuxmains && arme.system.competence) { addAttaque(arme, ATTAQUE_TYPE.DEUX_MAINS) }
if (arme.system.lancer) { addAttaque(arme, ATTAQUE_TYPE.LANCER) }
if (arme.system.tir) { addAttaque(arme, ATTAQUE_TYPE.TIR) }
})
.forEach(arme => arme.getTypeAttaques().forEach(t => addAttaque(arme, t)))
addAttaque(RdDItemArme.pugilat(this), ATTAQUE_TYPE.CORPS_A_CORPS)
return actions
}
@@ -236,16 +243,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))
@@ -335,10 +332,6 @@ export class RdDActor extends RdDBaseActorSang {
await this._recupererVie(message, isMaladeEmpoisonne);
}
getMaladiesPoisons() {
return this.items.filter(item => item.type == 'maladie' || (item.type == 'poison' && item.system.active));
}
_messageRecuperationMaladiePoisons(maladiesPoisons, message) {
if (maladiesPoisons.length > 0) {
const identifies = maladiesPoisons.filter(it => it.system.identifie);
@@ -370,15 +363,15 @@ export class RdDActor extends RdDBaseActorSang {
content: ""
};
await this._recuperationSante(message);
await this._recupereMoralChateauDormant(message);
await this._recupereChance();
await this.transformerStress();
await this.retourSeuilDeReve(message);
await this.setBonusPotionSoin(0);
await this.retourSust(message);
await this.$perteReveEnchantementsChateauDormants();
await this.$suppressionLancementsSort();
await this._recuperationSante(message)
await this._recupereMoralChateauDormant(message)
await this._recupereChance()
await this.transformerStress()
await this.retourSeuilDeReve(message)
await this.setBonusPotionSoin(0)
await this.retourSust(message)
await this.$perteReveEnchantementsChateauDormants()
await this.$suppressionLancementsSort()
await RdDCoeur.applyCoeurChateauDormant(this, message);
if (message.content != "") {
message.content = `A la fin Chateau Dormant, ${message.content}<br>Un nouveau jour se lève`;
@@ -422,11 +415,13 @@ export class RdDActor extends RdDBaseActorSang {
}
async _recupereMoralChateauDormant(message) {
await this.update({ 'system.compteurs.bonmoments': [] }, { render: false })
if (!ReglesOptionnelles.isUsing("recuperation-moral")) { return }
const etatMoral = this.system.sommeil?.moral ?? 'neutre';
const jetMoral = await this._jetDeMoral(etatMoral);
message.content += ` -- le jet de moral est ${etatMoral}, le moral ` + this._messageAjustementMoral(jetMoral.ajustement);
const etatMoral = this.system.sommeil?.moral ?? MORAL.NEUTRE
const jetMoral = await this._jetDeMoral(etatMoral)
message.content += ` -- le jet de moral est ${etatMoral}, le moral ` + this._messageAjustementMoral(jetMoral.ajustement)
}
_messageAjustementMoral(ajustement) {
@@ -697,11 +692,12 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async combattreReveDeDragon(force) {
const rencontre = await game.system.rdd.rencontresTMR.getReveDeDragon(force);
let rollData = {
actor: this,
competence: this.getDraconicOuPossession(),
competence: this.getDraconicOuPossession().find(it => true),
canClose: false,
rencontre: await game.system.rdd.rencontresTMR.getReveDeDragon(force),
rencontre: rencontre,
tmr: true,
use: { libre: false, conditions: false },
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.getReveActuel() } }
@@ -732,12 +728,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async sortMisEnReserve(sort, draconic, coord, ptreve) {
await this.createEmbeddedDocuments("Item", [{
type: ITEM_TYPES.sortreserve,
name: sort.name,
img: sort.img,
system: { sortid: sort._id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
}],
await this.createEmbeddedDocuments("Item", [RdDItemSort.prepareSortEnReserve(sort, draconic, ptreve, coord)],
{ renderSheet: false });
this.tmrApp.updateTokens();
}
@@ -751,20 +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);
}
@@ -1209,17 +1198,21 @@ export class RdDActor extends RdDBaseActorSang {
}
/* -------------------------------------------- */
async jetDeMoral(situation, messageReussi = undefined, messageManque = undefined) {
const jetMoral = await this._jetDeMoral(situation);
const finMessage = (jetMoral.succes ? messageReussi : messageManque) ?? (jetMoral.ajustement == 0 ? "Vous gardez votre moral" : jetMoral.ajustement > 0 ? "Vous gagnez du moral" : "Vous perdez du moral");
async jetDeMoral(situation, bonmoment = "") {
if (bonmoment != "" && bonmoment != undefined && this.system.compteurs.bonmoments.includes(bonmoment)) {
ui.notifications.info(`${this.name} a déjà gagné du moral après avoir passé un bon moment (${bonmoment}) pendant la journée, pas de gain de moral`)
return
}
const jetMoral = await this._jetDeMoral(situation, bonmoment)
const finMessage = (jetMoral.ajustement == 0 ? "Vous gardez votre moral" : jetMoral.ajustement > 0 ? "Vous gagnez du moral" : "Vous perdez du moral");
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: `${finMessage} - jet ${jetMoral.succes ? "réussi" : "manqué"} en situation ${situation} (${jetMoral.jet}/${jetMoral.difficulte}).`
});
return jetMoral.ajustement;
content: `${finMessage} - jet ${jetMoral.succes ? "réussi" : "manqué"} en situation ${SITUATION_MORAL[situation] ?? situation} (${jetMoral.jet}/${jetMoral.difficulte}).`
})
return jetMoral.ajustement
}
async _jetDeMoral(situation) {
async _jetDeMoral(situation, bonmoment = "") {
const moralActuel = Misc.toInt(this.system.compteurs.moral.value);
const jet = await RdDDice.rollTotal("1d20");
const difficulte = 10 + moralActuel;
@@ -1231,14 +1224,18 @@ export class RdDActor extends RdDBaseActorSang {
difficulte: difficulte,
succes: succes,
ajustement: this._calculAjustementMoral(succes, moralActuel, situation)
};
await this.moralIncDec(jetMoral.ajustement);
}
await this.moralIncDec(jetMoral.ajustement, bonmoment);
return jetMoral;
}
/* -------------------------------------------- */
async moralIncDec(ajustementMoral) {
async moralIncDec(ajustementMoral, bonmoment = "") {
if (ajustementMoral != 0) {
if (ajustementMoral > 0 && bonmoment != "" && bonmoment != undefined) {
const bonmoments = [...this.system.compteurs.bonmoments, bonmoment]
await this.update({ 'system.compteurs.bonmoments': bonmoments }, { render: false })
}
const startMoral = parseInt(this.system.compteurs.moral.value)
const moralTheorique = startMoral + ajustementMoral
if (moralTheorique > 3) { // exaltation
@@ -1259,13 +1256,14 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
_calculAjustementMoral(succes, moral, situation) {
switch (situation) {
case 'heureux': case 'heureuse': return succes ? 1 : 0;
case 'malheureuse': case 'malheureux': return succes ? 0 : -1;
case 'neutre':
if (succes && moral < 0) return 1;
if (!succes && moral > 0) return -1;
case MORAL.TRESHEUREUX: return succes ? 2 : 1
case MORAL.HEUREUX: case 'heureuse': return succes ? 1 : 0
case MORAL.MALHEUREUX: case 'malheureuse': return succes ? 0 : -1
case MORAL.NEUTRE:
if (succes && moral < 0) return 1
if (!succes && moral > 0) return -1
}
return 0;
return 0
}
/* -------------------------------------------- */
@@ -1324,33 +1322,31 @@ export class RdDActor extends RdDBaseActorSang {
}
const item = this.getItem(itemId)
if (!item.getUtilisationCuisine()) {
return;
return
}
if (choix.doses > item.system.quantite) {
ui.notifications.warn(`Il n'y a pas assez de ${item.name} pour manger ${choix.doses}`)
return;
return
}
if (!this._apprecierCuisine(item, choix.seForcer)) {
ui.notifications.info(`${this.name} ne n'arrive pas à manger de ${item.name}`)
return;
}
await this.manger(item, choix.doses, { diminuerQuantite: false });
await this.boire(item, choix.doses, { diminuerQuantite: false });
await item.diminuerQuantite(choix.doses, choix);
}
async _apprecierCuisine(item, seForcer) {
const surmonteExotisme = await this._surmonterExotisme(item, seForcer);
if (surmonteExotisme) {
await this.apprecier('gout', 'cuisine', item.system.qualite, item.system.boisson ? "apprécie la boisson" : "apprécie le plat");
const onManger = [
async () => await this.manger(item, choix.doses, { diminuerQuantite: false }),
async () => await this.boire(item, choix.doses, { diminuerQuantite: false }),
async () => await item.diminuerQuantite(choix.doses, choix)
]
if (await this._surmonterExotisme(item)) {
const appreciation = item.system.appreciation;
new Apprecier(this, appreciation, item.system.qualite)
.apprecier(onManger)
}
else if (seForcer) {
await this.jetDeMoral('malheureux');
else if (choix.seForcer) {
await this.jetDeMoral(MORAL.MALHEUREUX)
await Promise.all(onManger.map(async callback => await callback()))
}
else {
return false;
ui.notifications.info(`${this.name} ne n'arrive pas à manger de ${item.name}`)
}
return true;
}
/* -------------------------------------------- */
@@ -1360,7 +1356,7 @@ export class RdDActor extends RdDBaseActorSang {
if (exotisme < 0 || qualite < 0) {
const competence = qualite > 0 ? 'cuisine' : undefined
const difficulte = Math.min(exotisme, qualite)
const rolled = await this.doRollCaracCompetence('volonte', competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` })
const rolled = await this.doRollCaracCompetence(CARACS.VOLONTE, competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` })
return rolled.isSuccess
}
return true;
@@ -1376,7 +1372,7 @@ export class RdDActor extends RdDBaseActorSang {
}
const rolled = await this.doRollCaracCompetence(carac, undefined, 0, { title });
if (rolled?.isSuccess) {
await this.jetDeMoral('heureux');
await this.jetDeMoral(MORAL.HEUREUX);
}
}
@@ -1443,7 +1439,7 @@ export class RdDActor extends RdDBaseActorSang {
ethylismeData.perteEndurance = await this.santeIncDec("endurance", -perte);
if (!ethylisme.jet_moral) {
ethylismeData.jetMoral = await this._jetDeMoral('heureuse');
ethylismeData.jetMoral = await this._jetDeMoral(MORAL.HEUREUX, "Ethylisme");
if (ethylismeData.jetMoral.ajustement == 1) {
ethylismeData.moralAlcool = 'heureux';
ethylisme.jet_moral = true;
@@ -1451,7 +1447,7 @@ export class RdDActor extends RdDBaseActorSang {
ethylismeData.jetMoral.ajustement = -1;
ethylismeData.moralAlcool = 'triste';
ethylisme.jet_moral = true;
await this.moralIncDec(-1);
await this.moralIncDec(-1, "Ethylisme");
}
}
if (ethylisme.value < 0) {
@@ -1645,7 +1641,8 @@ export class RdDActor extends RdDBaseActorSang {
return
}
hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
let xpData = await this._appliquerExperience(rollData.rolled, rollData.selectedCarac.label, rollData.competence, rollData.jetResistance);
let xpData = await this._appliquerExperience(rollData.rolled, rollData.selectedCarac.label, rollData.competence,
rollData.v2 ? rollData.type.resistance : rollData.jetResistance);
if (xpData.length) {
const content = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-gain-xp.hbs`, {
actor: this,
@@ -1682,7 +1679,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
computeDraconicAndSortIndex(sortList) {
let draconicList = this.getDraconicList();
let draconicList = this.getDraconics();
for (let sort of sortList) {
let draconicsSort = RdDItemSort.getDraconicsSort(draconicList, sort).map(it => it.name);
for (let index = 0; index < draconicList.length && sort.system.listIndex == undefined; index++) {
@@ -1703,15 +1700,18 @@ export class RdDActor extends RdDBaseActorSang {
ui.notifications.error("Une queue ou un souffle vous empèche de lancer de sort!")
return
}
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
return await this.rollUnSortV2();
}
// Duplication car les pts de reve sont modifiés dans le sort!
let sorts = foundry.utils.duplicate(this.itemTypes[ITEM_TYPES.sort].filter(it => RdDItemSort.isSortOnCoord(it, coord)))
if (sorts.length == 0) {
ui.notifications.info(`Aucun sort disponible en ${TMRUtility.getTMR(coord).label} !`);
return;
return
}
const draconicList = this.computeDraconicAndSortIndex(sorts);
const reve = foundry.utils.duplicate(this.system.carac.reve);
const draconicList = this.computeDraconicAndSortIndex(sorts)
const reve = foundry.utils.duplicate(this.system.carac.reve)
const dialog = await this.openRollDialog({
name: 'lancer-un-sort',
@@ -1734,6 +1734,27 @@ export class RdDActor extends RdDBaseActorSang {
this.tmrApp?.setTMRPendingAction(dialog);
}
async rollUnSortV2() {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [ROLL_TYPE_SORT], current: ROLL_TYPE_SORT }
};
const dialog = await RollDialog.create(rollData, {
callbacks: [async roll => {
await this.tmrApp?.restoreTMRAfterAction();
if (roll.closeTMR) {
await this.tmrApp?.close();
this.tmrApp = undefined;
}
}],
onRollDone: RollDialog.onRollDoneClose,
onClose: async () => {
await this.tmrApp?.restoreTMRAfterAction();
}
});
this.tmrApp?.setTMRPendingAction(dialog);
}
/* -------------------------------------------- */
isMauvaiseRencontre() { // Gestion queue/souffle 'Mauvaise Rencontre en Perpective'
let addMsg = "";
@@ -1805,11 +1826,7 @@ export class RdDActor extends RdDBaseActorSang {
}
else {
console.log('lancement de sort', rollData.selectedSort)
const precedents = rollData.selectedSort.system.lancements ?? []
const lancements = [...precedents, {
timestamp: game.system.rdd.calendrier.getTimestamp(),
reve: rollData.selectedSort.system.ptreve_reel
}]
const lancements = RdDItemSort.prepareSortAddLancement(rollData.selectedSort, rollData.selectedSort.system.ptreve_reel)
await this.updateEmbeddedDocuments('Item',
[{ _id: rollData.selectedSort._id, 'system.lancements': lancements }]
)
@@ -1922,6 +1939,20 @@ export class RdDActor extends RdDBaseActorSang {
}
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_COMP], current: PART_COMP },
selected: {
carac: { key: caracName },
comp: { key: compName, forced: options.forced },
diff: { value: diff ?? 0 }
}
}
return await RollDialog.create(rollData)
}
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(compName);
await this.openRollDialog({
@@ -1944,23 +1975,33 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollTache(id, options = {}) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const tacheData = this.getTache(id)
const compData = this.getCompetence(tacheData.system.competence)
compData.system.defaut_carac = tacheData.system.carac; // Patch !
const tache = this.getTache(id)
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { tache: { key: tache.id, forced: options.forced } },
type: { allowed: [PART_TACHE], current: PART_TACHE }
}
return await RollDialog.create(rollData)
}
const compData = this.getCompetence(tache.system.competence)
compData.system.defaut_carac = tache.system.carac; // Patch !
await this.openRollDialog({
name: 'jet-competence',
label: 'Jet de Tâche ' + tacheData.name,
label: 'Jet de Tâche ' + tache.name,
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.hbs',
rollData: {
competence: compData,
tache: tacheData,
diffLibre: tacheData.system.difficulte,
tache: tache,
diffLibre: tache.system.difficulte,
diffConditions: 0,
use: { libre: false, conditions: true },
carac: {
[tacheData.system.carac]: foundry.utils.duplicate(this.system.carac[tacheData.system.carac])
[tache.system.carac]: foundry.utils.duplicate(this.system.carac[tache.system.carac])
}
},
callbacks: [{ action: r => this._tacheResult(r, options) }]
@@ -1994,9 +2035,18 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollJeu(id) {
const oeuvre = this.getJeu(id);
const jeu = this.getJeu(id);
const listCarac = oeuvre.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim());
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { jeu: { key: jeu.id } },
type: { allowed: [ROLL_TYPE_JEU], current: ROLL_TYPE_JEU }
}
return await RollDialog.create(rollData)
}
const listCarac = jeu.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim());
const carac = listCarac.length > 0 ? listCarac[0] : 'chance'
const artData = {
art: 'jeu', verbe: 'Jeu',
@@ -2006,14 +2056,24 @@ export class RdDActor extends RdDBaseActorSang {
};
listCarac.forEach(c => artData.forceCarac[c] = this.system.carac[c]);
artData.competence.system.niveauReel = artData.competence.system.niveau;
artData.competence.system.niveau = Math.max(artData.competence.system.niveau, oeuvre.system.base);
await this._rollArtV1(artData, carac, oeuvre);
artData.competence.system.niveau = Math.max(artData.competence.system.niveau, jeu.system.base);
await this._rollArtV1(artData, carac, jeu);
}
/* -------------------------------------------- */
async rollMeditation(id) {
const meditation = foundry.utils.duplicate(this.getMeditation(id));
if (meditation && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { meditation: { key: id } },
type: { allowed: [ROLL_TYPE_MEDITATION], current: ROLL_TYPE_MEDITATION }
}
return await RollDialog.create(rollData)
}
const competence = foundry.utils.duplicate(this.getCompetence(meditation.system.competence));
competence.system.defaut_carac = "intellect"; // Meditation = toujours avec intellect
let meditationData = {
@@ -2053,7 +2113,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
_getSignesDraconiques(coord) {
const type = TMRUtility.getTMRType(coord);
return this.itemTypes["signedraconique"].filter(it => it.system.typesTMR.includes(type));
return this.itemTypes[ITEM_TYPES.signedraconique].filter(it => it.system.typesTMR.includes(type));
}
/* -------------------------------------------- */
@@ -2065,18 +2125,19 @@ export class RdDActor extends RdDBaseActorSang {
async rollLireSigneDraconique(coord) {
if (!this.isHautRevant()) {
ui.notifications.info("Seul un haut rêvant peut lire un signe draconique!");
return;
return
}
let signes = this._getSignesDraconiques(coord);
let signes = this._getSignesDraconiques(coord)
if (signes.length == 0) {
ui.notifications.info(`Aucun signe draconiques en ${coord} !`);
return;
ui.notifications.info(`Aucun signe draconiques en ${coord} !`)
return
}
let draconicList = this.getDraconicList()
let draconicList = this.getDraconics()
.map(draconic => {
let draconicLecture = foundry.utils.duplicate(draconic);
draconicLecture.system.defaut_carac = "intellect";
return draconicLecture;
let draconicLecture = foundry.utils.duplicate(draconic)
draconicLecture.system.defaut_carac = "intellect"
return draconicLecture
});
const intellect = this.system.carac.intellect;
@@ -2209,7 +2270,7 @@ export class RdDActor extends RdDBaseActorSang {
const xpCompetence = competence ? xp - xpCarac : 0;
if (jetResistance) {
const message = `Jet de résistance ${jetResistance}, l'expérience est limitée à 1`;
const message = `Jet de résistance, l'expérience est limitée à 1`;
ui.notifications.info(message);
console.log(message)
// max 1 xp sur jets de résistance
@@ -2350,14 +2411,15 @@ export class RdDActor extends RdDBaseActorSang {
if (this.tmrApp) {
ui.notifications.warn("Vous êtes déja dans les TMR....")
this.tmrApp.forceTMRDisplay()
return
return false
}
if (mode != 'visu' && this.isDemiReve()) {
ui.notifications.warn("Les personnage est déjà dans les Terres Médianes, elles s'affichent en visualisation")
ui.notifications.warn("Le personnage est déjà dans les Terres Médianes, elles s'affichent en visualisation")
mode = "visu"; // bascule le mode en visu automatiquement
}
if (mode == 'visu') {
await this._doDisplayTMR(mode)
return false
}
else {
const rencontre = this.getRencontreTMREnAttente();
@@ -2370,6 +2432,7 @@ export class RdDActor extends RdDBaseActorSang {
buttonLabel: 'Monter dans les TMR',
onAction: async () => await this._doDisplayTMR(mode)
})
return true
}
}
@@ -2392,7 +2455,7 @@ export class RdDActor extends RdDBaseActorSang {
let tmrFormData = {
mode: mode,
fatigue: RdDUtility.calculFatigueHtml(fatigue, endurance),
draconic: this.getDraconicList(),
draconic: this.getDraconics(),
sort: this.itemTypes['sort'],
signes: this.itemTypes['signedraconique'],
caracReve: parseInt(this.system.carac.reve.value),
@@ -2407,6 +2470,29 @@ export class RdDActor extends RdDBaseActorSang {
await this.tmrApp.onDeplacement()
}
async quitterTMR(message, viewOnly, cumulFatigue) {
if (this.tmrApp) {
this.tmrApp = undefined
const appliquerFatigue = ReglesOptionnelles.isUsing("appliquer-fatigue");
await this.santeIncDec(
appliquerFatigue ? "fatigue" : "endurance",
(appliquerFatigue ? 1 : -1) * cumulFatigue)
if (!viewOnly) {
await this.supprimerSignesDraconiques(it => it.system.ephemere && it.system.duree == '1 round', { render: false })
await this.setEffect(STATUSES.StatusDemiReve, false)
ChatUtility.tellToUserAndGM(message)
}
}
}
async supprimerSignesDraconiques(filter = it => true, options = { render: true }) {
const signes = this.itemTypes[ITEM_TYPES.signedraconique].filter(filter)
if (signes.length > 0) {
this.deleteEmbeddedDocuments("Item", signes.map(item => item.id), options)
}
}
/* -------------------------------------------- */
async rollSoins(blesse, blessureId) {
const blessure = blesse.blessuresASoigner().find(it => it.id == blessureId);
@@ -2414,14 +2500,17 @@ export class RdDActor extends RdDBaseActorSang {
if (!blessure.system.premierssoins.done) {
const tache = await this.getTacheBlessure(blesse, blessure);
return await this.rollTache(tache.id, {
onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r)
onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r),
title: 'Premiers soins',
forced: true
});
}
if (!blessure.system.soinscomplets.done) {
else if (!blessure.system.soinscomplets.done) {
const diff = blessure.system.difficulte + (blessure.system.premierssoins.bonus ?? 0);
return await this.rollCaracCompetence("dexterite", "Chirurgie", diff, {
return await this.rollCaracCompetence(CARACS.DEXTERITE, "Chirurgie", diff, {
title: "Soins complets",
onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r)
onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r),
forced: true
})
}
}
@@ -2436,6 +2525,7 @@ export class RdDActor extends RdDBaseActorSang {
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && !blessure.system.premierssoins.done) {
const tache = rollData.tache;
if (rollData.rolled.isETotal) {
@@ -2488,7 +2578,12 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
conjurerPossession(possession) {
RdDPossession.onConjurerPossession(this, possession)
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
RdDPossessionV2.rollConjurerPossession(this, possession)
}
else {
RdDPossession.onConjurerPossession(this, possession)
}
}
/* -------------------------------------------- */
@@ -2506,7 +2601,7 @@ export class RdDActor extends RdDBaseActorSang {
if (item?.isEquipable()) {
const isEquipe = !item.system.equipe;
await item.update({ "system.equipe": isEquipe });
this.computeEncTotal();
this.computeEncTotal()
if (isEquipe)
this.verifierForceMin(item);
}
@@ -2897,6 +2992,7 @@ export class RdDActor extends RdDBaseActorSang {
if (updatedEndurance && options.diff) {
await this.setEffect(STATUSES.StatusUnconscious, updatedEndurance.value == 0)
}
await super.onUpdateActor(update, options, actorId)
}
/* -------------------------------------------- */
@@ -2916,7 +3012,9 @@ export class RdDActor extends RdDBaseActorSang {
break
case ITEM_TYPES.race:
await this.onCreateOwnedRace(item, options, id)
break
}
await super.onCreateItem(item, options, id)
await item.onCreateItemTemporel(this);
await item.onCreateDecoupeComestible(this);
}
@@ -2937,9 +3035,12 @@ export class RdDActor extends RdDBaseActorSang {
break
case ITEM_TYPES.empoignade:
await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item)
// TODO: check remaining emp.
await this.setEffect(STATUSES.StatusGrappled, false)
await this.setEffect(STATUSES.StatusGrappling, false)
break
}
super.onDeleteItem(item, options, id)
await super.onDeleteItem(item, options, id)
}
/* -------------------------------------------- */
@@ -3050,24 +3151,11 @@ export class RdDActor extends RdDBaseActorSang {
async _rollArtV2(oeuvreId) {
const oeuvre = this.items.get(oeuvreId)
const rollData = {
title: `Interpretation de ${oeuvre.name} par ${this.name}`,
type: {
allowed: ["oeuvre"],
current: "oeuvre",
},
selected: {
oeuvre: { key: oeuvre.id },
},
ids: {
actorId: this.id
}
ids: { actorId: this.id },
selected: { oeuvre: { key: oeuvre.id } },
type: { allowed: [PART_OEUVRE], current: PART_OEUVRE, },
}
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
}
})
return await RollDialog.create(rollData)
}
/* -------------------------------------------- */
@@ -3163,14 +3251,25 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollRecetteCuisine(id) {
const oeuvre = this.getRecetteCuisine(id);
const recette = this.getRecetteCuisine(id);
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_CUISINE], current: PART_CUISINE },
selected: {
cuisine: { key: recette.id }
}
}
return await RollDialog.create(rollData)
}
const artData = {
verbe: 'Cuisiner',
compName: 'cuisine',
proportions: 1,
ajouterEquipement: false
};
await this._rollArtV1(artData, 'odoratgout', oeuvre, r => this._resultRecetteCuisine(r));
await this._rollArtV1(artData, 'odoratgout', recette, r => this._resultRecetteCuisine(r));
}
/* -------------------------------------------- */
@@ -3203,6 +3302,17 @@ export class RdDActor extends RdDBaseActorSang {
}
async preparerNourriture(item) {
if (item.getUtilisationCuisine() == 'brut' && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_CUISINE], current: PART_CUISINE },
selected: {
cuisine: { key: item.id }
}
}
return await RollDialog.create(rollData)
}
if (item.getUtilisationCuisine() == 'brut') {
const nourriture = {
name: 'Plat de ' + item.name,
@@ -3213,7 +3323,7 @@ export class RdDActor extends RdDBaseActorSang {
exotisme: item.system.exotisme,
ingredients: item.name
}
};
}
const artData = {
verbe: 'Préparer',
compName: 'cuisine',
+6 -4
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))
});
}
}
+189 -71
View File
@@ -1,4 +1,4 @@
import { ENTITE_INCARNE, SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { RDD_CONFIG, renderTemplate, SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js";
import { RdDResolutionTable } from "../rdd-resolution-table.js";
@@ -11,7 +11,7 @@ import { ITEM_TYPES } from "../constants.js";
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
import { Targets } from "../targets.js";
import { RdDConfirm } from "../rdd-confirm.js";
import { RdDCarac } from "../rdd-carac.js";
import { CARACS, RdDCarac } from "../rdd-carac.js";
import { RdDRollResult } from "../rdd-roll-result.js";
import { RdDItemArme } from "../item/arme.js";
@@ -22,9 +22,16 @@ import { DialogValidationEncaissement } from "../dialog-validation-encaissement.
import { RdDCombat } from "../rdd-combat.js";
import { RdDEmpoignade } from "../rdd-empoignade.js";
import { RdDPossession } from "../rdd-possession.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, POSSESSION_SANS_DRACONIC } from "../item/base-items.js";
import { RollDataAjustements } from "../rolldata-ajustements.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES_CREATURES } from "../item/base-items.js";
import { RollDataAjustements } from "../rolldata-ajustements-v1.js";
import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
import RollDialog from "../roll/roll-dialog.mjs";
import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE} from "../roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
import { PART_COMP } from "../roll/roll-part-comp.mjs";
import { RdDInitiative } from "../initiative.mjs";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDPossessionV2 } from "../rdd-possession-v2.mjs";
/**
* Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
@@ -85,7 +92,6 @@ export class RdDBaseActorReve extends RdDBaseActor {
getSConst() { return 0 }
/* -------------------------------------------- */
isSurenc() { return false }
computeMalusSurEncombrement() { return 0 }
ajustementAstrologique() { return 0 }
@@ -111,27 +117,73 @@ export class RdDBaseActorReve extends RdDBaseActor {
return this.system.carac[competence.system.defaut_carac].value;
}
getDraconics() { return [] }
getDraconicOuPossession() {
return [
...this.getDraconics(),
...this.itemTypes[ITEM_TYPES.competencecreature].filter(it => it.isCompetencePossession()),
this.getConjurationNaturelle()
].sort(Misc.descending(it => it.system.niveau));
}
getConjurations() {
return this.getDraconicOuPossession()
.map(it => this.toActionConjuration(it))
}
getConjurationNaturelle() {
return new RdDItemCompetenceCreature({
name: 'Sans compétence',
type: ITEM_TYPES.competencecreature,
img: RDD_CONFIG.icons.possession,
system: {
carac_value: this.getReveActuel(),
niveau: 0,
default_diffLibre: 0,
categorie: CATEGORIES_COMPETENCES_CREATURES.possession.key,
ispossession: true,
}
})
}
toActionConjuration(comp) {
const caracCode = this.isPersonnage() ? CARACS.REVE_ACTUEL : CARACS.REVE
const caracValue = this.getReveActuel()
const ajustement = this.getEtatGeneral()
return {
label: `Conjuration (${comp.name})`,
action: 'possession',
initOnly: false,
comp: comp,
carac: { key: caracCode, value: caracValue },
initiative: RdDInitiative.getRollInitiative(caracValue, comp.system.niveau, ajustement)
}
}
listActions({ isAttaque = false, isEquipe = false }) {
return this.itemTypes[ITEM_TYPES.competencecreature]
.filter(it => it.isAttaque())
.map(it => it.attaqueCreature())
.filter(it => it != undefined);
.filter(it => it != undefined)
}
async computeArmure(dmg) { return this.getProtectionNaturelle() }
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 }) {
await this.$finDeRoundSuppressionEffetsTermines(options);
await this.finDeRoundBlessures();
await this.$finDeRoundSupprimerObsoletes();
await this.$finDeRoundEmpoignade();
await this.finDeRoundSuppressionEffetsTermines(options)
await this.finDeRoundBlessures()
await this.finDeRoundSupprimerObsoletes()
await this.finDeRoundEmpoignade()
await this.finDeRoundPossession()
}
async $finDeRoundSuppressionEffetsTermines(options) {
async finDeRoundSuppressionEffetsTermines(options) {
for (let effect of this.getEffects()) {
if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) {
await effect.delete();
@@ -143,20 +195,22 @@ export class RdDBaseActorReve extends RdDBaseActor {
async finDeRoundBlessures() {
}
async $finDeRoundSupprimerObsoletes() {
async finDeRoundSupprimerObsoletes() {
const obsoletes = []
.concat(this.itemTypes[ITEM_TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
.concat(this.itemTypes[ITEM_TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
.map(it => it.id);
.map(it => it.id)
await this.deleteEmbeddedDocuments('Item', obsoletes);
}
async $finDeRoundEmpoignade() {
const immobilisations = this.itemTypes[ITEM_TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
immobilisations.forEach(emp => RdDEmpoignade.onImmobilisation(this,
game.actors.get(emp.system.empoigneid),
emp
))
async finDeRoundEmpoignade() {
await Promise.all(this.itemTypes[ITEM_TYPES.empoignade]
.filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id)
.map(async it => await RdDEmpoignade.onImmobilisation(this, it)))
}
async finDeRoundPossession(){
await Promise.all(this.itemTypes[ITEM_TYPES.possession]
.map(async it => await RdDPossessionV2.onPossession(this, it)))
}
async setSonne(sonne = true) { }
@@ -172,10 +226,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 => { } }) {
@@ -194,11 +249,10 @@ export class RdDBaseActorReve extends RdDBaseActor {
return false
}
getDraconicOuPossession() { return POSSESSION_SANS_DRACONIC }
getPossession(possessionId) {
return this.itemTypes[ITEM_TYPES.possession].find(it => it.system.possessionid == possessionId);
}
getEmpoignades() {
return this.itemTypes[ITEM_TYPES.empoignade];
}
@@ -222,53 +276,13 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
}
/* -------------------------------------------- */
isEffectAllowed(effectId) { return false }
getEffects(filter = e => true, forceRequise = undefined) {
const effects = this.getEmbeddedCollection("ActiveEffect")
const selected = effects.filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) {
selected.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
}
return selected
}
getEffectByStatus(statusId) {
return this.getEffects().find(it => it.statuses.has(statusId));
}
async setEffect(statusId, status) {
if (this.isEffectAllowed(statusId)) {
const effect = this.getEffectByStatus(statusId);
if (!status && effect) {
await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id]);
}
if (status && !effect) {
await this.createEmbeddedDocuments("ActiveEffect", [StatusEffects.prepareActiveEffect(statusId)]);
}
}
}
async removeEffect(id) {
this.removeEffects(it => it.id == id)
}
async removeEffects(filter = e => true) {
if (game.user.isGM) {
const effectsToRemove = this.getEffects(filter);
const ids = effectsToRemove.map(it => it.id);
await this.deleteEmbeddedDocuments('ActiveEffect', ids);
}
}
/* -------------------------------------------- */
isDemiReve() {
return this.getEffectByStatus(STATUSES.StatusDemiReve) != undefined
return this.getEffectsByStatus(STATUSES.StatusDemiReve).length > 0
}
getSurprise(isCombat = undefined) {
return StatusEffects.getSurprise(this.getEffects(), isCombat)
getSurprise(isCombat = undefined, forceRequise = undefined) {
return StatusEffects.getSurprise(this.getEffects(e => true, isCombat, forceRequise), isCombat)
}
/* -------------------------------------------- */
@@ -305,6 +319,21 @@ 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 }
}
}
return await RollDialog.create(rollData, options)
}
const competence = this.getCompetence(compName);
await this.openRollDialog({
name: 'jet-competence',
@@ -376,10 +405,47 @@ export class RdDBaseActorReve extends RdDBaseActor {
})
}
/* -------------------------------------------- */
async rollReveActuel({ diff = 0, resistance = false }) {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: {
allowed: [PART_COMP],
current: PART_COMP,
resistance: resistance
},
selected: {
carac: { key: CARACS.REVE_ACTUEL, forced: true },
comp: resistance ? { key: undefined, forced: true } : undefined,
diff: { type: DIFF.DEFAUT, value: diff }
}
}
return await RollDialog.create(rollData)
}
return this.rollCarac(CARACS.REVE_ACTUEL, { diff, resistance })
}
async rollCarac(caracName, options = {}) {
if (Grammar.equalsInsensitive(caracName, 'taille')) {
if (Grammar.equalsInsensitive(caracName, CARACS.TAILLE)) {
return
}
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: {
allowed: options.resistance ? [PART_COMP] : DEFAULT_ROLL_TYPES,
current: PART_COMP,
resistance: options.resistance
},
selected: {
carac: { key: caracName },
comp: options.resistance ? { key: undefined, forced: true } : undefined,
diff: { type: DIFF.DEFAUT, value: options.diff ?? 0 }
}
}
return await RollDialog.create(rollData, options)
}
foundry.utils.mergeObject(options, { resistance: false, diff: 0 }, { overwrite: false })
RdDEmpoignade.checkEmpoignadeEnCours(this)
let selectedCarac = this.getCaracByName(caracName)
@@ -406,10 +472,24 @@ export class RdDBaseActorReve extends RdDBaseActor {
await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-general.hbs');
}
/* -------------------------------------------- */
async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(idOrName);
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: options.arme ? ATTAQUE_ROLL_TYPES : DEFAULT_ROLL_TYPES },
selected: {
carac: competence.type == ITEM_TYPES.competencecreature ? { key: competence.name } : undefined,
comp: { key: competence.name },
diff: { type: options.arme ? DIFF.ATTAQUE : DIFF.LIBRE, value: competence.system.default_diffLibre ?? 0 },
attaque: options.arme ? { arme: { key: options.arme.id } } : undefined
}
}
return await RollDialog.create(rollData)
}
let rollData = {
carac: this.system.carac,
competence: competence,
@@ -427,7 +507,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
}
});
return;
return
}
// Transformer la competence de créature
MappingCreatureArme.setRollDataCreature(rollData)
@@ -449,6 +529,40 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
}
rollAttaque(token) {
token = token ?? RdDUtility.getSelectedToken(this)
if (Targets.hasTargets()) {
Targets.selectOneTargetToken(target => {
if (Targets.isTargetEntite(target)) {
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée!!!!`)
return
}
RdDCombat.rddCombatTarget(target, this, token).attaqueV2();
})
}
else {
return RdDConfirm.confirmer({
settingConfirmer: "confirmer-combat-sans-cible",
content: `<p>Voulez vous faire une attaque sans choisir de cible valide?
<br>Tous les jets de combats devront être gérés à la main
</p>`,
title: 'Ne pas utiliser les automatisation de combat',
buttonLabel: "Pas d'automatisation",
onAction: async () => {
const rollData = {
ids: { actorId: this.id, actorTokenId: token?.id, },
type: {
allowed: [ROLL_TYPE_ATTAQUE], current: ROLL_TYPE_ATTAQUE
}
};
return await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
}
})
}
}
/** --------------------------------------------
* @param {*} arme item d'arme/compétence de créature
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
@@ -490,6 +604,10 @@ export class RdDBaseActorReve extends RdDBaseActor {
})
}
rollPossession() {
RdDPossessionV2.rollAttaquePossession(this)
}
verifierForceMin(item) { }
/* -------------------------------------------- */
@@ -588,7 +706,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
}
+61 -18
View File
@@ -34,17 +34,17 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
getFatigueActuelle() {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
return Math.max(0, Math.min(this.getFatigueMax(), Misc.toInt(this.system.sante.fatigue?.value)))
return Math.max(0, Math.min(this.getFatigueMax(), Misc.toInt(this.system.sante.fatigue?.value)))
}
return 0;
}
isCumulFatigueCauseSommeil(cumulFatigue){
isCumulFatigueCauseSommeil(cumulFatigue) {
return ReglesOptionnelles.isUsing("appliquer-fatigue")
? (this.getFatigueRestante() <= cumulFatigue)
: (this.getEnduranceActuelle() <= cumulFatigue)
? (this.getFatigueRestante() <= cumulFatigue)
: (this.getEnduranceActuelle() <= cumulFatigue)
}
getFatigueRestante() {return this.getFatigueMax() - this.getFatigueActuelle() }
getFatigueRestante() { return this.getFatigueMax() - this.getFatigueActuelle() }
getFatigueMin() { return this.system.sante.endurance.max - this.system.sante.endurance.value }
malusFatigue() {
@@ -116,6 +116,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
blessure: blessure
});
}
/* -------------------------------------------- */
async santeIncDec(name, inc, isCritique = false) {
if (name == 'fatigue' && !ReglesOptionnelles.isUsing("appliquer-fatigue")) {
@@ -129,7 +130,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
let result = {
sonne: false,
};
let perteEndurance = 0
let minValue = name == "vie" ? -this.getSConst() - 1 : 0;
result.newValue = Math.max(minValue, Math.min(compteur.value + inc, compteur.max));
@@ -144,16 +145,10 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
if (inc > 0) { // le max d'endurance s'applique seulement à la récupération
result.newValue = Math.min(result.newValue, this._computeEnduranceMax())
}
const perte = compteur.value - result.newValue;
result.perte = perte;
if (perte > 1) {
// Peut-être sonné si 2 points d'endurance perdus d'un coup
foundry.utils.mergeObject(result, await this.jetEndurance(result.newValue));
} else if (inc > 0) {
await this.setSonne(false);
}
perteEndurance = compteur.value - result.newValue;
result.perte = perteEndurance
if (sante.fatigue && inc < 0) { // Each endurance lost -> fatigue lost
fatigue = perte;
fatigue = perteEndurance;
}
}
compteur.value = result.newValue;
@@ -161,7 +156,15 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) {
sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this.getFatigueMin());
}
await this.update({ "system.sante": sante })
await this.update({ "system.sante": sante }, { render: true })
if (perteEndurance > 1) {
// Peut-être sonné si 2 points d'endurance perdus d'un coup
foundry.utils.mergeObject(result, await this.jetEndurance(result.newValue));
} else if (name == "endurance" && inc > 0) {
await this.setSonne(false);
}
if (this.isDead()) {
await this.setEffect(STATUSES.StatusComma, true);
}
@@ -179,6 +182,46 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
}
async onCreateItem(item, options, id) {
await this.changeItemEffects(item)
await super.onCreateItem(item, options, id)
}
async onUpdateItem(item, options, id) {
await this.changeItemEffects(item);
await super.onUpdateItem(item, options, id)
}
async onDeleteItem(item, options, id) {
await this.changeItemEffects(item);
await super.onDeleteItem(item, options, id)
}
async changeItemEffects(item) {
switch (item.type) {
case ITEM_TYPES.blessure:
await this.changeStateBleeding();
break;
case ITEM_TYPES.maladie:
case ITEM_TYPES.poison:
await this.changeStateMalade();
break;
}
}
async changeStateBleeding() {
const bleeding = this.itemTypes[ITEM_TYPES.blessure].find(it => it.isBleeding())
await this.setEffect(STATUSES.StatusBleeding, bleeding ? true : false)
}
async changeStateMalade() {
const maladiePoisons = this.getMaladiesPoisons()
await this.setEffect(STATUSES.StatusMalade, maladiePoisons.length > 0)
}
getMaladiesPoisons() {
return this.items.filter(it => [ITEM_TYPES.maladie, ITEM_TYPES.poison].includes(it.type))
}
/* -------------------------------------------- */
async ajouterBlessure(encaissement, attackerToken = undefined) {
@@ -195,7 +238,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken);
if (blessure.isCritique()) {
encaissement.endurance = endActuelle;
encaissement.endurance = endActuelle
}
if (blessure.isMort()) {
@@ -292,7 +335,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
}
isSonne() {
return this.getEffectByStatus(STATUSES.StatusStunned)
return this.getEffectsByStatus(STATUSES.StatusStunned).length > 0
}
isEffectAllowed(effectId) { return true }
+2 -13
View File
@@ -49,17 +49,14 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
formData.calc = {
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
encTotal: await this.actor.computeEncTotal(),
encTotal: this.actor.getEncTotal(),
}
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature)
.forEach(it => {
const competenceCreature = new RdDItemCompetenceCreature(it.toObject(), { parent: it.parent });
it.isdommages = competenceCreature.isDommages();
})
.forEach(it => it.isdommages = it.isDommages())
return formData;
}
@@ -232,14 +229,6 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);
+100 -14
View File
@@ -1,6 +1,6 @@
import { ChatVente } from "../achat-vente/chat-vente.js";
import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js";
import { renderTemplate, SYSTEM_SOCKET_ID } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Monnaie } from "../item-monnaie.js";
import { ITEM_TYPES } from "../constants.js";
@@ -10,6 +10,8 @@ import { RdDConfirm } from "../rdd-confirm.js";
import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { RdDItem } from "../item.js";
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
import { Apprecier } from "../moral/apprecier.mjs";
export class RdDBaseActor extends Actor {
@@ -45,8 +47,10 @@ export class RdDBaseActor extends Actor {
}
static init() {
Handlebars.registerHelper('actor-isFeminin', actor => actor.isFeminin())
Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
Hooks.on("updateItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onUpdateItem(item, options, id))
Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id))
Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId))
}
@@ -212,12 +216,16 @@ 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 }
isEntiteBlurette() { return false }
isHautRevant() { return false }
isVehicule() { return false }
isPersonnage() { return false }
isFeminin() { return false }
getItem(id, type = undefined) {
const item = this.items.get(id);
if (type == undefined || (item?.type == type)) {
@@ -237,20 +245,89 @@ 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 ids = this.getEffects(filter)
.filter(it => this.canRemoveEffects(it))
.map(it => it.id)
if (ids.length > 0) {
await this.deleteEmbeddedDocuments('ActiveEffect', ids)
}
}
}
canRemoveEffects(effect) {
if (effect.statuses.has(STATUSES.StatusSurEnc)) {
return !this.isSurenc()
}
if (effect.statuses.has(STATUSES.StatusDemiReve)) {
return !this.tmrApp
}
return true
}
/* -------------------------------------------- */
async updateCarac(caracName, to) {
}
async onUpdateActor(change, options, actorId) {
const updatedCarac = change?.system?.carac
if (updatedCarac && (updatedCarac.force || updatedCarac.reve || updatedCarac.taille)) {
console.log(' onUpdateActor', change, options, actorId)
await this.setEffect(STATUSES.StatusSurEnc, this.isSurenc())
}
}
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { }
async onCreateItem(item, options, id) {
}
async onUpdateItem(item, options, id) {
}
async onDeleteItem(item, options, id) {
if (item.isInventaire()) {
await this._removeItemFromConteneur(item)
}
}
async _removeItemFromConteneur(item) {
const updates = this.items.filter(it => it.isConteneur() && it.system.contenu.includes(item.id))
.map(conteneur => {
@@ -441,10 +518,13 @@ export class RdDBaseActor extends Actor {
async acheter(item, quantite, cout, achat) {
await this.depenserSols(cout)
const createdItemId = await this.creerQuantiteItem(item, quantite)
if (achat.choix.consommer && item.type == 'nourritureboisson' && createdItemId != undefined) {
if (item.type == ITEM_TYPES.nourritureboisson && achat.choix.consommer && createdItemId != undefined) {
achat.choix.doses = achat.choix.nombreLots;
await this.consommerNourritureboisson(createdItemId, achat.choix, achat.vente.actingUserId);
}
if (item.type == ITEM_TYPES.service) {
new Apprecier(this, item.system.appreciation, item.system.qualite).apprecier()
}
}
verifierFortune(cout) {
@@ -499,16 +579,22 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
async computeEncTotal() {
if (!this.pack) {
if (this.pack) {
this.encTotal = 0
}
else {
const wasSurenc = this.isSurenc()
this.encTotal = this.items.filter(it => RdDItem.getItemTypesInventaire().includes(it.type))
.map(it => it.getEncTotal()).reduce(Misc.sum(), 0)
return this.encTotal;
const isSurenc = this.isSurenc()
if (isSurenc != wasSurenc) {
await this.setEffect(STATUSES.StatusSurEnc, isSurenc)
}
}
return 0;
}
getEncTotal() {
return Math.floor(this.encTotal ?? 0);
return Math.floor(this.encTotal ?? 0)
}
async createItem(type, name = undefined) {
@@ -559,7 +645,7 @@ export class RdDBaseActor extends Actor {
}
}
}
await this.computeEncTotal();
await this.computeEncTotal()
return result;
}
@@ -738,7 +824,7 @@ export class RdDBaseActor extends Actor {
name: this.getAlias(),
system: { description: this.system.description }
}
foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.hbs', chatData)
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.hbs', chatData)
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
}
@@ -767,13 +853,13 @@ export class RdDBaseActor extends Actor {
listActionsPossessions() {
return this.itemTypes[ITEM_TYPES.possession]
.filter(it => !it.system.possede)
.map(p => {
return {
name: p.name,
label: p.name,
action: 'possession',
possessionid: p.system.possessionid,
}
})
}
}
+1 -1
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))
)
+28 -22
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 !this.isEntiteNonIncarnee() }
isEntiteBlurette() { return this.system.definition.typeentite == ENTITE_BLURETTE }
getReveActuel() {
return Misc.toInt(this.system.carac.reve?.value)
@@ -41,28 +41,21 @@ export class RdDEntite extends RdDBaseActorReve {
getChance() { return this.getReve() }
getEnduranceMax() { return Math.max(1, this.getTaille() + this.getReve()) }
getDraconicOuPossession() {
return this.itemTypes[ITEM_TYPES.competencecreature]
.filter(it => it.system.categorie == 'possession')
.sort(Misc.descending(it => it.system.niveau))
.find(it => true);
}
async remiseANeuf() {
await this.removeEffects(e => true);
if (!this.isNonIncarnee()) {
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 +71,7 @@ export class RdDEntite extends RdDBaseActorReve {
}
async encaisser() {
if (this.isNonIncarnee()) {
if (this.isEntiteNonIncarnee()) {
return
}
await RdDEncaisser.encaisser(this)
@@ -89,24 +82,37 @@ 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))
}
return true
}
listActionsPossessions() {
if (this.isEntiteNonIncarnee()) {
return this.itemTypes[ITEM_TYPES.competencecreature]
.filter(it => it.system.ispossession)
.map(it => it.attaqueCreature())
.filter(it => it != undefined)
}
return super.listActionsPossessions()
}
/* -------------------------------------------- */
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
+1 -1
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) {
+288
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
+10 -10
View File
@@ -1,8 +1,8 @@
import { Grammar } from "../../grammar.js"
import { RdDItemArme } from "../../item/arme.js"
import { EMPOIGNADE, RdDItemArme } from "../../item/arme.js"
import { RdDItemCompetence } from "../../item-competence.js"
import { RdDItemSort } from "../../item-sort.js"
import { ITEM_TYPES } from "../../constants.js"
import { ITEM_TYPES, RDD_CONFIG } from "../../constants.js"
import { Misc } from "../../misc.js"
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
import { RdDBonus } from "../../rdd-bonus.js"
@@ -141,7 +141,7 @@ export class Mapping {
static prepareArmes(actor) {
const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme)
armes.push(RdDItemArme.corpsACorps(actor));
armes.push(RdDItemArme.pugilat(actor));
armes.push(RdDItemArme.empoignade(actor));
return armes.map(arme => [
arme.system.unemain ? Mapping.prepareArme(actor, arme, '(1 main)') : undefined,
@@ -160,7 +160,7 @@ export class Mapping {
return undefined
}
const categorie = Mapping.complementCategorie(arme, maniement)
const dommages = Mapping.dommagesArme(actor, arme, maniement)
const dommages = Mapping.dommages(actor, arme, maniement)
return {
name: arme.name + categorie,
niveau: Misc.toSignedString(competence.system.niveau),
@@ -170,12 +170,13 @@ export class Mapping {
arme: arme
}
}
static dommagesArme(actor, arme, maniement) {
static dommages(actor, arme, maniement) {
const dmgArme = RdDItemArme.dommagesReels(arme, maniement)
const dommages = Misc.toSignedString(dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme))
switch (arme.system.mortalite) {
case 'non-mortel': return `(${dommages})`
case 'empoignade': return '-'
case RDD_CONFIG.encaissement.nonmortel: return `(${dommages})`
case RDD_CONFIG.encaissement.empoignade: return '-'
}
return dommages
}
@@ -274,11 +275,10 @@ export class Mapping {
}
static getDescription(actor) {
const sexe = actor.system.sexe
const sexeFeminin = sexe.length > 0 && sexe.charAt(0).toLowerCase() == 'f' ? 'Née' : 'Né'
const naissance = actor.isFeminin() ? 'née' : 'né'
const race = ['', 'humain'].includes(Grammar.toLowerCaseNoAccent(actor.system.race)) ? '' : (actor.system.race + ' ')
const heure = actor.system.heure
const hn = `${sexeFeminin} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
const hn = `${naissance} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
const age = (actor.system.age && actor.system.age >0) ? `${actor.system.age} ans` : undefined
const taille = actor.system.taille
const poids = actor.system.poids
+19 -11
View File
@@ -20,14 +20,15 @@ const PATHS = [
const RANDOM_VALUES = {
'system.sexe': { 'masculin': 1, 'féminin': 1 },
'system.main': { 'droitier': 51, 'gaucher': 15, 'ambidextre': 6 },
'system.cheveux': { 'noirs': 2, 'bruns': 5, 'châtains clair': 5, 'blonds': 4, 'blonds très clair': 1, 'roux carotte': 1, 'roux cuivré': 3 },
'system.yeux': { 'noirs': 2, 'noisettes': 3, 'bruns vert': 4, 'verts': 3, 'bleus clair': 3, 'bleus gris': 2, 'gris': 1, 'mauves': 1, 'indigos': 1 },
'system.cheveux': { 'noirs': 2, 'bruns': 5, 'châtains': 3, 'châtain clair': 5, 'blonds': 4, 'blond platine': 1, 'roux carotte': 1, 'roux cuivré': 3, 'chauve': 1 },
'system.yeux': { 'noirs': 2, 'noisette': 3, 'brun-vert': 4, 'verts': 3, 'bleu clair': 3, 'bleu gris': 2, 'gris': 1, 'mauves': 1, 'indigos': 1 },
}
export class AppPersonnageAleatoire extends FormApplication {
static preloadHandlebars() {
foundry.applications.handlebars.loadTemplates([
'systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/random/sexe-aleatoire.hbs',
])
}
@@ -49,14 +50,14 @@ export class AppPersonnageAleatoire extends FormApplication {
this.current = foundry.utils.duplicate(actor)
this.checked = {
'name': false,
'system.sexe': true,
'system.age': true,
'system.taille': true,
'system.poids': true,
'system.main': true,
'system.heure': true,
'system.cheveux': true,
'system.yeux': true
'system.sexe': (this.actor.system.sexe ?? '') == '',
'system.age': this.actor.system.age == 0,
'system.taille': (this.actor.system.taille ?? '') == '',
'system.poids': (this.actor.system.poids ?? '') == '',
'system.main': (this.actor.system.main ?? '') == '',
'system.heure': (this.actor.system.heure ?? '') == '',
'system.cheveux': (this.actor.system.cheveux ?? '') == '',
'system.yeux': (this.actor.system.yeux ?? '') == '',
}
}
@@ -76,6 +77,8 @@ export class AppPersonnageAleatoire extends FormApplication {
this.html.find("button.button-apply").click(async event => await this.onApply())
this.html.find("input.current-value").change(async event => await this.onChange(event))
this.html.find("div.random-field[data-path='system.heure'] select.current-value").change(async event => await this.onChange(event))
this.html.find('a[data-action="sexe-masculin"]').click(async event => await this.onSexe('masculin'))
this.html.find('a[data-action="sexe-feminin"]').click(async event => await this.onSexe('féminin'))
this.html.find("a.random").click(async event => await this.onRandom(event))
this.html.find("a.reset").click(async event => await this.onReset(event))
this.html.find("a.randomize-selected").click(async event => await this.onRandomizeSelected())
@@ -96,6 +99,10 @@ export class AppPersonnageAleatoire extends FormApplication {
const fields = this.html.find(selector).parents("div.random-field:first")
return fields[0].attributes['data-path'].value
}
async onSexe(sexe) {
this.current['system.sexe'] = sexe
this.render()
}
async onChange(event) {
const path = this.getPath(event.currentTarget)
@@ -180,8 +187,9 @@ export class AppPersonnageAleatoire extends FormApplication {
const variation = Math.floor((caracTaille.poidsMax - caracTaille.poidsMin + base / 5) / 2)
const total = await RdDDice.rollTotal(`2d${variation} + ${base}`)
const cm = total % 100
const dm = cm < 10 ? '0' : ''
const m = (total - cm) / 100
return `${m}m${cm}`
return `${m}m${dm}${cm}`
}
@@ -1,3 +1,4 @@
import { renderTemplate } from "../../constants.js";
export class TextRollManager {
+31 -3
View File
@@ -1,5 +1,5 @@
import { Misc } from "./misc.js";
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
@@ -76,6 +76,15 @@ export class ChatUtility {
ChatUtility.removeMessages({ part: part });
}
static remover(chatMessage) {
const messageId = chatMessage.id;
if (messageId) {
return (..._) => ChatUtility.removeMessages({ messageId: messageId });
}
return (..._) => { }
}
static removeChatMessageId(messageId) {
if (messageId) {
ChatUtility.removeMessages({ messageId: messageId });
@@ -106,6 +115,25 @@ export class ChatUtility {
return await ChatMessage.create(messageData)
}
static tellToUser(message) {
ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id] });
}
static tellToGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getGMs()
});
}
static tellToUserAndGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getUserAndGMs()
})
}
static getOwners(document) {
return document ? game.users.filter(it => document.getUserLevel(it) == CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) : [game.user]
}
@@ -190,8 +218,8 @@ export class ChatUtility {
if (rddTimestamp) {
const timestamp = new RdDTimestamp(rddTimestamp);
const timestampData = timestamp.toCalendrier();
const dateHeure = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData);
$(html).find('header.message-header .message-sender').after(dateHeure)
const dateHeure = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData);
$(html).find('header.message-header .message-timestamp').after(dateHeure)
}
}
+3 -1
View File
@@ -1,5 +1,7 @@
import { RdDBaseActor } from "../actor/base-actor.js";
import { ChatUtility } from "../chat-utility.js";
import { renderTemplate } from "../constants.js";
import { MORAL } from "../moral/apprecier.mjs";
const INFO_COEUR = 'info-coeur';
@@ -121,7 +123,7 @@ export class RdDCoeur {
const diff = Math.abs(infoCoeur.source.jetTendre - infoCoeur.target.jetTendre)
for (let amoureux of [infoCoeur.source, infoCoeur.target]) {
const actorAmoureux = game.actors.get(amoureux.actor.id);
amoureux.situation = diff <= amoureux.coeur ? 'heureux' : 'neutre'
amoureux.situation = diff <= amoureux.coeur ? MORAL.HEUREUX : MORAL.NEUTRE
amoureux.gainMoral = await actorAmoureux.jetDeMoral(amoureux.situation)
}
const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-accepter-tendre-moment.hbs`, infoCoeur)
+57 -43
View File
@@ -9,47 +9,71 @@ export const ENTITE_INCARNE = 'incarne'
export const ENTITE_NONINCARNE = 'nonincarne'
export const ENTITE_BLURETTE = 'blurette'
export const renderTemplate = foundry.applications.handlebars.renderTemplate
export const RDD_CONFIG = {
niveauEthylisme : [
{value: "1", label: "Aucun"},
{value: "0", label: "Eméché (0)"},
{value: "-1", label: "Gris (-1)"},
{value: "-2", label: "Pinté (-2)"},
{value: "-3", label: "Pas Frais (-3)"},
{value: "-4", label: "Ivre (-4)"},
{value: "-5", label: "Bu (-5)"},
{value: "-6", label: "Complètement fait (-6)"},
{value: "-7", label: "Ivre mort (-7)"}
niveauEthylisme: [
{ value: '1', label: 'Aucun' },
{ value: '0', label: 'Eméché (0)' },
{ value: '-1', label: 'Gris (-1)' },
{ value: '-2', label: 'Pinté (-2)' },
{ value: '-3', label: 'Pas Frais (-3)' },
{ value: '-4', label: 'Ivre (-4)' },
{ value: '-5', label: 'Bu (-5)' },
{ value: '-6', label: 'Complètement fait (-6)' },
{ value: '-7', label: 'Ivre mort (-7)' }
],
categorieEntite: {
"cauchemar": "Cauchemar",
"reve": "Rêve"
'cauchemar': 'Cauchemar',
'reve': 'Rêve'
},
typeEntite: {
"incarne": "Incarnée",
"nonincarne": "Non Incarnée",
"blurette": "Blurette"
[ENTITE_INCARNE]: 'Incarnée',
[ENTITE_NONINCARNE]: 'Non Incarnée',
[ENTITE_BLURETTE]: 'Blurette'
},
heuresRdD : [
{value : "vaisseau", label: "Vaisseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd01.webp"},
{value : "sirene", label: "Sirène", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd02.webp"},
{value : "faucon", label: "Faucon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd03.webp"},
{value : "couronne", label: "Couronne", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd04.webp"},
{value : "dragon", label: "Dragon", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd05.webp"},
{value : "epees", label: "Epées", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd06.webp"},
{value : "lyre", label: "Lyre", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd07.webp"},
{value : "serpent", label: "Serpent", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd08.webp"},
{value : "poissonacrobate", label: "Poisson Acrobate", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd09.webp"},
{value : "araignee", label: "Araignée", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd10.webp"},
{value : "roseau", label: "Roseau", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd11.webp"},
{value : "chateaudormant", label: "Chateau Dormant", img: "modules/foundryvtt-reve-de-dragon/icons/heures/hd12.webp"}
heuresRdD: [
{ value: 'vaisseau', label: 'Vaisseau', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd01.webp' },
{ value: 'sirene', label: 'Sirène', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd02.webp' },
{ value: 'faucon', label: 'Faucon', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd03.webp' },
{ value: 'couronne', label: 'Couronne', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd04.webp' },
{ value: 'dragon', label: 'Dragon', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd05.webp' },
{ value: 'epees', label: 'Epées', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd06.webp' },
{ value: 'lyre', label: 'Lyre', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd07.webp' },
{ value: 'serpent', label: 'Serpent', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd08.webp' },
{ value: 'poissonacrobate', label: 'Poisson Acrobate', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd09.webp' },
{ value: 'araignee', label: 'Araignée', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd10.webp' },
{ value: 'roseau', label: 'Roseau', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd11.webp' },
{ value: 'chateaudormant', label: 'Chateau Dormant', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.webp' }
],
raretes: [
{value: "Commune", label: "Commune"},
{value: "Frequente", label: "Fréquente"},
{value: "Rare", label: "Rare"},
{value: "Rarissime", label: "Rarissime"}
]
{ value: 'Commune', label: 'Commune' },
{ value: 'Frequente', label: 'Fréquente' },
{ value: 'Rare', label: 'Rare' },
{ value: 'Rarissime', label: 'Rarissime' }
],
particuliere: {
force: { key: 'force', descr: 'en force', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-force.svg' },
finesse: { key: 'finesse', descr: 'en finesse', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-finesse.svg' },
rapidite: { key: 'rapidite', descr: 'en rapidité', img: 'systems/foundryvtt-reve-de-dragon/assets/ui/part-rapidite.svg' },
},
icons: {
armesDisparates: 'systems/foundryvtt-reve-de-dragon/assets/actions/armes-disparates.svg',
demiReve: 'systems/foundryvtt-reve-de-dragon/assets/actions/sort.svg',
empoignade: 'systems/foundryvtt-reve-de-dragon/assets/actions/empoignade.svg',
possession: 'systems/foundryvtt-reve-de-dragon/assets/actions/possession.svg',
forceWeak: 'systems/foundryvtt-reve-de-dragon/assets/actions/weak.svg',
surenc: 'systems/foundryvtt-reve-de-dragon/assets/actions/surenc.svg',
magique: 'systems/foundryvtt-reve-de-dragon/assets/actions/magique.svg',
armebrisee: 'systems/foundryvtt-reve-de-dragon/assets/actions/arme-brisee.svg',
malade: 'systems/foundryvtt-reve-de-dragon/assets/actions/malade.svg',
},
encaissement: {
mortel: 'mortel',
nonmortel: 'non-mortel',
entiteincarnee: 'entiteincarnee',
empoignade: 'empoignade'
}
}
export const ACTOR_TYPES = {
@@ -107,13 +131,3 @@ export const ITEM_TYPES = {
extraitpoetique: 'extraitpoetique',
}
export const CATEGORIES_COMPETENCES = {
"generale": { base: -4, label: "Générales" },
"particuliere": { base: -8, label: "Particulières" },
"specialisee": { base: -11, label: "Spécialisées" },
"connaissance": { base: -11, label: "Connaissances" },
"draconic": { base: -11, label: "Draconic" },
"melee": { base: -6, label: "Mêlée" },
"tir": { base: -8, label: "Tir" },
"lancer": { base: -8, label: "Lancer" }
}
+1
View File
@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js"
export class DialogChoixXpCarac extends Dialog {
+1 -1
View File
@@ -1,4 +1,4 @@
import { SYSTEM_RDD } from "./constants.js";
import { renderTemplate, SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
+1
View File
@@ -1,4 +1,5 @@
import { ChatUtility } from "./chat-utility.js";
import { renderTemplate } from "./constants.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { TMRUtility } from "./tmr-utility.js";
+1
View File
@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js";
+1
View File
@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
export class DialogConsommer extends Dialog {
+2
View File
@@ -1,7 +1,9 @@
import { renderTemplate } from "./constants.js"
export class DialogSelect extends Dialog {
static extractIdNameImg(it) { return { id: it.id, name: it.name, img: it.img } }
static async select(selectionData, onSelectChoice) {
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-select.hbs", selectionData)
+1 -1
View File
@@ -1,4 +1,4 @@
import { Misc } from "./misc.js";
import { renderTemplate } from "./constants.js";
export class DialogSplitItem extends Dialog {
+1 -1
View File
@@ -1,4 +1,4 @@
import { HIDE_DICE, SHOW_DICE } from "./constants.js";
import { HIDE_DICE, renderTemplate, SHOW_DICE } from "./constants.js";
import { RdDUtility } from "./rdd-utility.js";
/**
+1 -1
View File
@@ -1,4 +1,4 @@
import { ITEM_TYPES } from "../constants.js"
import { ITEM_TYPES, renderTemplate } from "../constants.js"
import { RdDItemSort } from "../item-sort.js"
import { Misc } from "../misc.js"
+62 -3
View File
@@ -1,7 +1,66 @@
export const MAP_PHASE = {
possession: { label: "possession", rang: 10 },
draconic: { label: "draconic", rang: 9 },
tir: { label: "tir", rang: 8 },
lancer: { label: "lancer", rang: 7 },
arme: { label: "mêlée", rang: 5 },
pugilat: { label: "pugilat", rang: 4 },
naturelle: { label: "créature", rang: 4 },
empoignade: { label: "empoignade", rang: 3 },
autre: { label: "autre action", rang: 2 },
demi: { label: "demi-surprise", rang: 0 },
totale: { label: "surprise totale", rang: -1 },
}
export const PHASE = Object.values(MAP_PHASE)
export class RdDInitiative {
static calculInitiative(niveau, caracValue, bonus = 0) {
let base = niveau + Math.floor(caracValue / 2) + bonus;
return "1d6" + (base >= 0 ? "+" : "") + base;
static getRollInitiative(caracValue, niveau, bonus = 0) {
const base = RdDInitiative.ajustementInitiative(caracValue, niveau, bonus)
return "1d6" + (base >= 0 ? "+" : "") + base
}
static ajustementInitiative(caracValue, niveau, bonus = 0) {
return niveau + Math.floor(caracValue / 2) + bonus
}
static formule(phase, carac, niveau, bonusMalus = 0) {
const ajustement = RdDInitiative.ajustementInitiative(carac, niveau, bonusMalus)
return { phase, ajustement }
}
static phaseArme(categorie, arme) {
switch (categorie) {
case "tir":
case "lancer":
return MAP_PHASE[categorie]
default:
switch (arme.system.cac) {
case "empoignade":
case "pugilat":
case "naturelle":
return MAP_PHASE[arme.system.cac]
default:
return MAP_PHASE['arme']
}
}
}
static phase(keyOrRang) {
return MAP_PHASE[keyOrRang] ?? PHASE.find(it => it.rang == keyOrRang) ?? MAP_PHASE.autre
}
static async roll(formule) {
const sign = formule.ajustement >= 0 ? "+" : ""
const roll = new Roll(`1d6 + ${sign} + ${formule.ajustement}`)
await roll.evaluate()
const value = Math.max(roll.total, 0)
return {
roll: roll,
value: value,
rang: formule.phase.rang,
init: formule.phase.rang + value / 100,
label: formule.phase.label
}
}
}
+26 -1
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;
+6 -8
View File
@@ -10,15 +10,15 @@ export class RdDItemCompetenceCreature extends RdDItem {
static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }
isAttaque() { return this.getCategorieAttaque() != undefined }
isParade() { return this.system.iscombat && (this.system.categorie_parade ?? '') != '' }
isBouclier() { return this.system.categorie_parade.includes('bouclier') }
attaqueCreature() {
const categorieAttaque = this.getCategorieAttaque()
if (categorieAttaque != undefined) {
const initative = RdDInitiative.calculInitiative(this.system.niveau, this.system.carac_value);
const initative = RdDInitiative.getRollInitiative(this.system.carac_value, this.system.niveau);
const attaque = {
name: this.name,
label: this.name,
action: this.isCompetencePossession() ? 'possession' : 'attaque',
initOnly: false,
arme: new RdDItemArme({
@@ -32,6 +32,7 @@ export class RdDItemCompetenceCreature extends RdDItem {
initiative: initative,
mortalite: this.system.mortalite,
dommages: this.system.dommages,
forceRequise: 0,
equipe: true,
resistance: 100,
penetration: 0,
@@ -43,7 +44,8 @@ export class RdDItemCompetenceCreature extends RdDItem {
carac: { key: this.name, value: this.system.carac_value },
equipe: true,
mortalite: this.system.mortalite,
dmg: this.system.dommages,
dommages: this.system.dommages,
//dmg: this.system.dommages,
initiative: initative
};
return attaque
@@ -51,10 +53,6 @@ export class RdDItemCompetenceCreature extends RdDItem {
return undefined;
}
isAttaque() {
return this.getCategorieAttaque() != undefined
}
getCategorieAttaque() {
switch (this.system.categorie) {
case "melee":
+7 -4
View File
@@ -14,6 +14,8 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
import { ItemAction } from "./item/item-actions.js";
import { SANS_COMPETENCE } from "./item/base-items.js";
import { Apprecier } from "./moral/apprecier.mjs";
/**
* Extend the basic ItemSheet for RdD specific items
@@ -89,6 +91,7 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
/* -------------------------------------------- */
async getData() {
const competences = (await SystemCompendiums.getCompetences(ACTOR_TYPES.personnage))
let formData = {
title: this.item.name,
id: this.item.id,
@@ -101,8 +104,9 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
descriptionmj: await RdDTextEditor.enrichHTML(this.item.system.descriptionmj, this.item),
isComestible: this.item.getUtilisationCuisine(),
options: RdDSheetUtility.mergeDocumentRights({}, this.item, this.isEditable),
competences: await SystemCompendiums.getCompetences(ACTOR_TYPES.personnage),
competences: [SANS_COMPETENCE, ...competences],
categories: RdDItem.getCategories(this.item.type),
isAppreciable: Apprecier.isAppreciable(this.item)
}
if (this.item.type == ITEM_TYPES.competencecreature) {
@@ -260,7 +264,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 +277,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)
}
/* -------------------------------------------- */
+29 -6
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)) {
@@ -148,10 +148,17 @@ export class RdDItemSort extends Item {
/* -------------------------------------------- */
static incrementBonusCase(actor, sort, coord) {
let bonuscase = RdDItemSort.calculBonuscase(sort, coord)
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }]);
}
static calculBonuscase(sort, coord) {
if (TMRUtility.isFleuve(coord)) {
coord = FLEUVE_COORD;
coord = FLEUVE_COORD
}
let list = RdDItemSort.stringToBonuscases(sort.system.bonuscase);
let list = RdDItemSort.stringToBonuscases(sort.system.bonuscase)
const existing = list.find(it => it.case == coord)
const bonus = Number(existing?.bonus ?? 0) + 1
if (existing) {
@@ -160,11 +167,9 @@ export class RdDItemSort extends Item {
else {
list.push({ case: coord, bonus: 1 })
}
actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': RdDItemSort.bonuscasesToString(list) }]);
return RdDItemSort.bonuscasesToString(list)
}
/* -------------------------------------------- */
static getCaseBonus(sort, coord) {
const search = TMRUtility.isFleuve(coord)
@@ -189,4 +194,22 @@ export class RdDItemSort extends Item {
.map(it => it.split(':'))
.map(it => { return { case: it[0], bonus: it[1] } });
}
static prepareSortEnReserve(sort, draconic, ptreve, coord) {
return {
type: ITEM_TYPES.sortreserve,
name: sort.name,
img: sort.img,
system: { sortid: sort._id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
};
}
static prepareSortAddLancement(sort, reveSort) {
const precedents = sort.system.lancements ?? []
const lancements = [...precedents, {
timestamp: game.system.rdd.calendrier.getTimestamp(),
reve: reveSort
}]
return lancements
}
}
+19 -14
View File
@@ -1,4 +1,4 @@
import { ITEM_TYPES } from "./constants.js";
import { ITEM_TYPES, RDD_CONFIG, renderTemplate } from "./constants.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES, CATEGORIES_COMPETENCES_CREATURES } from "./item/base-items.js";
import { ITEM_ACTIONS, DEFAULT_ACTIONS, COMMON_ACTIONS } from "./item/item-actions.js";
@@ -28,7 +28,7 @@ const typesInventaireMateriel = [
]
const typesInventaire = {
materiel: typesInventaireMateriel,
all: ['service'].concat(typesInventaireMateriel),
all: [ITEM_TYPES.service].concat(typesInventaireMateriel),
}
const typesObjetsOeuvres = [ITEM_TYPES.oeuvre, ITEM_TYPES.recettecuisine, ITEM_TYPES.musique, ITEM_TYPES.chant, ITEM_TYPES.danse, ITEM_TYPES.jeu]
@@ -36,6 +36,10 @@ const typesObjetsDraconiques = [ITEM_TYPES.queue, ITEM_TYPES.ombre, ITEM_TYPES.s
const typesObjetsConnaissance = [ITEM_TYPES.meditation, ITEM_TYPES.recettealchimique, ITEM_TYPES.sort]
const typesObjetsEffet = [ITEM_TYPES.possession, ITEM_TYPES.poison, ITEM_TYPES.maladie, ITEM_TYPES.blessure]
const typesObjetsCompetence = [ITEM_TYPES.competence, ITEM_TYPES.competencecreature]
const typesAppreciable = [
ITEM_TYPES.oeuvre, ITEM_TYPES.musique, ITEM_TYPES.chant, ITEM_TYPES.danse, ITEM_TYPES.nourritureboisson,
ITEM_TYPES.jeu, ITEM_TYPES.service
]
const typesObjetsTemporels = [ITEM_TYPES.blessure, ITEM_TYPES.poison, ITEM_TYPES.maladie, ITEM_TYPES.queue, ITEM_TYPES.ombre, ITEM_TYPES.souffle, ITEM_TYPES.signedraconique, ITEM_TYPES.rencontre]
const typesObjetsEquipable = [ITEM_TYPES.arme, ITEM_TYPES.armure, ITEM_TYPES.objet];
const typesEnvironnement = typesInventaireMateriel;
@@ -96,14 +100,14 @@ export class RdDItem extends Item {
switch (field) {
case 'quantite':
if (ITEM_TYPES.conteneur == type) {
return false;
return false
}
break;
case 'cout':
if (ITEM_TYPES.monnaie == type) {
return game.user.isGM;
}
break;
break
}
return true;
}
@@ -180,10 +184,10 @@ export class RdDItem extends Item {
isCompetencePersonnage() { return this.type == ITEM_TYPES.competence }
isCompetenceCreature() { return this.type == ITEM_TYPES.competencecreature }
isConteneur() { return this.type == ITEM_TYPES.conteneur; }
isMonnaie() { return this.type == ITEM_TYPES.monnaie; }
isConteneur() { return this.type == ITEM_TYPES.conteneur }
isMonnaie() { return this.type == ITEM_TYPES.monnaie }
isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; }
isService() { return this.type == ITEM_TYPES.service; }
isService() { return this.type == ITEM_TYPES.service }
isAttaque() { return false }
isParade() { return false }
isBouclier() { return false }
@@ -205,7 +209,7 @@ export class RdDItem extends Item {
return this.isCompetence() && ['melee', 'tir', 'lancer'].includes(this.system.categorie)
}
isCompetencePossession() { return ITEM_TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isCompetencePossession() { return ITEM_TYPES.competencecreature == this.type && this.system.categorie == CATEGORIES_COMPETENCES_CREATURES.possession.key }
isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
isDraconique() { return RdDItem.getItemTypesDraconiques().includes(this.type) }
@@ -214,6 +218,7 @@ export class RdDItem extends Item {
isConnaissance() { return typesObjetsConnaissance.includes(this.type) }
isInventaire(mode = 'materiel') { return RdDItem.getItemTypesInventaire(mode).includes(this.type); }
isAppreciable() { return typesAppreciable.includes(this.type) }
isBoisson() { return this.isNourritureBoisson() && this.system.boisson; }
isAlcool() { return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise; }
isHerbeAPotion() { return this.type == ITEM_TYPES.herbe && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
@@ -364,11 +369,11 @@ export class RdDItem extends Item {
getEnc() {
switch (this.type) {
case ITEM_TYPES.service:
return 0;
return 0
case ITEM_TYPES.herbe:
return this.getEncHerbe();
return this.getEncHerbe()
}
return Math.max(this.system.encombrement ?? 0, 0);
return Math.max(this.system.encombrement ?? 0.0, 0.0);
}
getEncContenu() {
@@ -384,9 +389,9 @@ export class RdDItem extends Item {
getEncHerbe() {
switch (this.system.categorie) {
case 'Repos': case 'Soin': case 'Alchimie':
return encBrin;
return encBrin
}
return this.system.encombrement;
return this.system.encombrement
}
@@ -629,7 +634,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()
+62 -29
View File
@@ -1,9 +1,10 @@
import { ITEM_TYPES } from "../constants.js";
import { ITEM_TYPES, RDD_CONFIG } from "../constants.js";
import { RdDItem } from "../item.js";
import { BASE_CORPS_A_CORPS } from "./base-items.js";
import { Grammar } from "../grammar.js";
import { RdDInitiative } from "../initiative.mjs";
import { MappingCreatureArme } from "./mapping-creature-arme.mjs";
import { Misc } from "../misc.js";
const nomCategorieParade = {
"sans-armes": "Sans arme",
@@ -21,10 +22,16 @@ const nomCategorieParade = {
export const ATTAQUE_TYPE = {
UNE_MAIN: '(1 main)',
DEUX_MAINS: '(2 mains)',
CORPS_A_CORPS: '(corps à corps)',
COMPETENCE: 'competence',
TIR: '(tir)',
LANCER: '(lancer)'
}
export const ATTAQUE_TYPE_MELEE = [ATTAQUE_TYPE.UNE_MAIN, ATTAQUE_TYPE.DEUX_MAINS, ATTAQUE_TYPE.CORPS_A_CORPS]
export const CORPS_A_CORPS = 'Corps à corps'
export const PUGILAT = 'pugilat'
export const EMPOIGNADE = RDD_CONFIG.encaissement.empoignade
/* -------------------------------------------- */
export class RdDItemArme extends RdDItem {
@@ -38,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() ?? ""
@@ -62,7 +79,7 @@ export class RdDItemArme extends RdDItem {
case ITEM_TYPES.competencecreature:
return MappingCreatureArme.armeCreature(arme);
}
return RdDItemArme.corpsACorps();
return RdDItemArme.pugilat();
}
static getCompetenceArme(arme, maniement) {
@@ -71,16 +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);
@@ -244,47 +271,53 @@ export class RdDItemArme extends RdDItem {
}
isAttaque() {
return this.system.resistance > 0 || this.system.portee_courte > 0
return this.system.resistance > 0 || (this.system.tir != '' && this.system.portee_courte > 0)
}
static corpsACorps(actor) {
let competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS
let melee = actor ? actor.system.carac['melee'].value : 0
isEmpoignade() {
return this.system.mortalite == RDD_CONFIG.encaissement.empoignade
}
isPossession() {
return this.system.mortalite == RDD_CONFIG.encaissement.possession
}
isUtilisableEmpoigne() {
return this.system.baseInit == 3 || this.system.baseInit == 4 || this.system.competence == "Dague"
}
static pugilat(actor) {
return RdDItemArme.$corpsACorps(actor, 'Pugilat', PUGILAT)
}
static empoignade(actor) {
return RdDItemArme.$corpsACorps(actor, 'Empoignade', RDD_CONFIG.encaissement.empoignade)
}
static $corpsACorps(actor, name, cac) {
const competence = actor?.getCompetenceCorpsACorps() ?? BASE_CORPS_A_CORPS
const melee = actor ? actor.system.carac['melee'].value : 0
return new RdDItemArme({
_id: competence.id,
name: 'Corps à corps',
_id: Misc.fakeId(cac),
name: name,
type: ITEM_TYPES.arme,
img: competence.img,
system: {
initiative: RdDInitiative.calculInitiative(competence.system.niveau, melee),
initiative: RdDInitiative.getRollInitiative(melee, competence.system.niveau),
equipe: true,
rapide: true,
force: 0,
dommages: "0",
dommagesReels: 0,
mortalite: 'non-mortel',
competence: 'Corps à corps',
mortalite: cac == RDD_CONFIG.encaissement.empoignade ? RDD_CONFIG.encaissement.empoignade : RDD_CONFIG.encaissement.nonmortel,
competence: CORPS_A_CORPS,
resistance: 1,
baseInit: 4,
cac: 'pugilat',
baseInit: cac == RDD_CONFIG.encaissement.empoignade ? 3 : 4,
cac: cac,
deuxmains: true,
categorie_parade: 'sans-armes'
}
})
}
static mainsNues(actor) {
const mainsNues = RdDItemArme.corpsACorps(actor)
mainsNues.name = 'Mains nues'
return mainsNues;
}
static empoignade(actor) {
const empoignade = RdDItemArme.corpsACorps(actor)
empoignade.name = 'Empoignade'
empoignade.system.cac = 'empoignade'
empoignade.system.baseInit = 3
empoignade.system.mortalite = 'empoignade'
return empoignade
}
}
+36 -30
View File
@@ -1,40 +1,46 @@
export const POSSESSION_SANS_DRACONIC = { name: 'Sans draconic', type: 'competence', system: { niveau: 0, defaut_carac: "reve-actuel", }, img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp' }
import { ITEM_TYPES, RDD_CONFIG } from "../constants.js"
import { CARACS } from "../rdd-carac.js"
export const CATEGORIES_COMPETENCES = {
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: { 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" },
}
export const POSSESSION_SANS_DRACONIC = {
name: 'Sans draconic',
type: ITEM_TYPES.competence,
img: RDD_CONFIG.icons.possession,
system: { niveau: 0, defaut_carac: CARACS.REVE_ACTUEL, categorie: CATEGORIES_COMPETENCES.draconic.key }
}
export const PAS_DE_DRACONIC = { name: 'Pas de draconic', type: 'competence', system: { niveau: -11, defaut_carac: "reve" }, img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp' }
export const BASE_CORPS_A_CORPS = { name: 'Corps à Corps', type: 'competence', system: { niveau: -6, defaut_carac: "melee" }, img: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp' }
export const BASE_ESQUIVE = { name: 'Esquive', type: 'competence', system: { niveau: -6, defaut_carac: "derobee" }, img: 'systems/foundryvtt-reve-de-dragon/icons/competence_esquive.webp' }
export const SANS_COMPETENCE = {
name: "Sans compétence",
type: 'competence',
type: ITEM_TYPES.competence,
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp",
system: {
niveau: 0,
default_diffLibre: 0,
base: 0,
categorie: "Aucune",
description: "",
descriptionmj: "",
defaut_carac: "",
},
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp"
}
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" }
}
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" },
categorie: CATEGORIES_COMPETENCES.generale.key,
}
}
+13 -6
View File
@@ -71,21 +71,25 @@ export class RdDItemBlessure extends RdDItem {
return 0
}
static async createBlessure(actor, gravite, localisation = '', attackerToken) {
const definition = RdDItemBlessure.getDefinition(gravite)
const blessure = {
static async createBlessure(actor, gravite, localisation = '', attackerToken = undefined) {
const blessure = RdDItemBlessure.prepareBlessure(gravite, localisation, attackerToken);
const blessures = await actor.createEmbeddedDocuments('Item', [blessure])
return blessures[0]
}
static prepareBlessure(gravite, localisation, attackerToken) {
const definition = RdDItemBlessure.getDefinition(gravite);
return {
name: definition.label,
type: 'blessure',
img: definition.icon,
system: {
gravite: gravite,
difficulte: - gravite,
difficulte: -gravite,
localisation: localisation,
origine: attackerToken?.name ?? ""
}
}
const blessures = await actor.createEmbeddedDocuments('Item', [blessure])
return blessures[0]
}
static async createTacheSoinBlessure(actor, gravite) {
@@ -209,6 +213,9 @@ export class RdDItemBlessure extends RdDItem {
isCritique() {
return this.system.gravite > 4 && this.system.gravite <= 6
}
isBleeding() {
return this.system.gravite > 2 && !this.system.premierssoins.done
}
isMort() {
return this.system.gravite > 6
}
+3 -2
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 = {
+1 -2
View File
@@ -3,7 +3,6 @@ import { RdDInitiative } from "../initiative.mjs";
export class MappingCreatureArme {
/* -------------------------------------------- */
static setRollDataCreature(rollData) {
const code = Grammar.toLowerCaseNoAccentNoSpace(rollData.competence.name);
@@ -26,7 +25,7 @@ export class MappingCreatureArme {
competence: item.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: item.system.niveau,
initiative: RdDInitiative.calculInitiative(item.system.niveau, item.system.carac_value),
initiative: RdDInitiative.getRollInitiative(item.system.carac_value, item.system.niveau),
equipe: true,
resistance: 100,
dommagesReels: item.system.dommages,
+1 -1
View File
@@ -76,7 +76,7 @@ export class RdDItemRace extends RdDItem {
ui.notifications.warn("La Taille est inférieur au minimum racial")
return min
}
const raceMax = this.system.carac.taille.max;
const raceMax = this.system.carac.taille.max == undefined ? 15 + this.system.carac.taille.value : this.system.carac.taille.max
const max = raceMax < 0 ? taille + 1 : raceMax
if (max < taille) {
ui.notifications.warn("La Taille est supérieure au maximum racial")
+4 -4
View File
@@ -6,8 +6,8 @@ const tableEffets = [
{ code: "passeur", resultat: "succes", description: "Déplacer le demi-rêve à (force) cases", method: EffetsRencontre.passeur},
{ code: "reve+f", resultat: "succes", description: "Gain de (force) points de rêve" , method: EffetsRencontre.reve_plus_force},
{ code: "teleport", resultat: "succes", description: "Déplacer le demi-rêve (même type)", method: EffetsRencontre.teleportation_typecase },
{ code: "part+tete", resultat: "succes", description: "Tête de dragon sur réussite particulière", method: EffetsRencontre.rdd_part_tete },
{ code: "part+xp", resultat: "succes", description: "Expérience sur réussite particulière", method: EffetsRencontre.experience_particuliere },
{ code: "part+tete", resultat: "succes", description: "Tête de dragon sur particulière", method: EffetsRencontre.rdd_part_tete },
{ code: "part+xp", resultat: "succes", description: "Expérience sur particulière", method: EffetsRencontre.experience_particuliere },
{ code: "seuil", resultat: "succes", description: "Récupération de seuil de rêve", method: EffetsRencontre.regain_seuil },
{ code: "reve-1", resultat: "echec", description: "Perte de 1 point de rêve", method: EffetsRencontre.reve_moins_1 },
@@ -19,7 +19,7 @@ const tableEffets = [
{ code: "aleatoire", resultat: "echec", description: "Déplacement aléatoire", method: EffetsRencontre.deplacement_aleatoire },
{ code: "sort-aleatoire", resultat: "echec", description: "Déclenche un sort en réserve aléatoire", method: EffetsRencontre.sort_aleatoire },
{ code: "rompu", resultat: "echec", description: "Demi-rêve interrompu", method: EffetsRencontre.demireve_rompu },
{ code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon sur échec", method: EffetsRencontre.rdd_echec_queue },
{ code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon", method: EffetsRencontre.rdd_echec_queue },
{ code: "reve+1", resultat: "succes", description: "Gain de 1 point de rêve", method: EffetsRencontre.reve_plus_1 },
{ code: "vie-f", resultat: "echec", description: "Perte de (force) points de vie", method: EffetsRencontre.vie_moins_force },
@@ -68,7 +68,7 @@ export class RdDRencontre extends RdDItem {
}
static async appliquer(codes, tmrDialog, rencData) {
for(const effet of RdDRencontre.mapEffets(codes)){
for(let effet of RdDRencontre.mapEffets(codes)){
await effet.method(tmrDialog, rencData);
}
}
+37 -2
View File
@@ -8,6 +8,7 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
import { VOIES_DRACONIC } from "./item-sort.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { APPRECIATION } from "./moral/apprecier.mjs";
class Migration {
get code() { return "sample"; }
@@ -33,7 +34,6 @@ class Migration {
await Item.updateDocuments(itemUpdates);
}
}
}
class _1_5_34_migrationPngWebp {
@@ -662,6 +662,37 @@ class _13_0_7_FixNiveauOeuvres extends Migration {
}
}
class _13_0_21_AjoutAppreciation extends Migration {
get code() { return "ajout-appreciation" }
get version() { return "13.0.21" }
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => it.isAppreciable())
.filter(it => this.getBaseAppreciation(it))
.map(it => {
return { _id: it.id, 'system.appreciation': this.getBaseAppreciation(it) }
})
)
}
getBaseAppreciation(item) {
switch (item.type) {
case ITEM_TYPES.nourritureboisson: return APPRECIATION.CUISINE
case ITEM_TYPES.service: return APPRECIATION.SERVICE
case ITEM_TYPES.musique: return APPRECIATION.MUSIQUE
case ITEM_TYPES.chant: return APPRECIATION.CHANT
case ITEM_TYPES.danse: return APPRECIATION.DANSE
case ITEM_TYPES.oeuvre: {
const appreciation = foundry.utils.duplicate(APPRECIATION.OEUVRE)
appreciation.competence = item.system.competence
return appreciation
}
}
return undefined
}
}
export class Migrations {
static getMigrations() {
return [
@@ -687,6 +718,7 @@ export class Migrations {
new _12_0_38_TachesEcriture(),
new _13_0_4_FixReveActuel(),
new _13_0_7_FixNiveauOeuvres(),
new _13_0_21_AjoutAppreciation(),
];
}
@@ -707,7 +739,10 @@ export class Migrations {
}
if (foundry.utils.isNewerVersion(game.system.version, currentVersion)) {
// if (true) { /* comment previous and uncomment here to test before upgrade */
const migrations = Migrations.getMigrations().filter(m => foundry.utils.isNewerVersion(m.version, currentVersion));
const migrations = Migrations.getMigrations().filter(m => foundry.utils.isNewerVersion(m.version, currentVersion)
/* uncomment and set the version to migrate to to force a migration */
// ||m.version == "13.0.21"
)
if (migrations.length > 0) {
migrations.sort((a, b) => this.compareVersions(a, b));
migrations.forEach(async (m) => {
+21 -1
View File
@@ -34,9 +34,13 @@ export class Misc {
return ((n % m) + m) % m;
}
static inRange(value, min,max){
static inRange(value, min, max) {
if (min > max) {
return Misc.inRange(value, max, min)
}
return Math.max(min, Math.min(value, max))
}
static sum() {
return (a, b) => Number(a) + Number(b);
}
@@ -61,6 +65,10 @@ export class Misc {
return 0;
}
static fakeId(base) {
return (base + foundry.utils.randomID(16)).substring(0, 16)
}
static typeName(type, subType) {
return subType ? game.i18n.localize(`TYPES.${type}.${subType}`)
: '';
@@ -111,6 +119,10 @@ export class Misc {
list.forEach(it => addToObj(obj, it))
return obj;
}
static indexed(list, index = 'index') {
let i = 0;
return list.map(it => { it[index] = i++; return it })
}
static concat(lists) {
return lists.reduce((a, b) => a.concat(b), []);
@@ -217,6 +229,14 @@ export class Misc {
return undefined
}
static async doIfOwner(document, action, orElse = async it => { ui.notifications.warn(`Vous n'avez pas le droit d'agir pour ${it.name}`) }) {
if (Misc.isOwnerPlayer(document)) {
return await action(document)
} else {
return await orElse(document)
}
}
static isOwnerPlayer(document) {
return document.testUserPermission && document.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
}
+177
View File
@@ -0,0 +1,177 @@
import { ITEM_TYPES } from "../constants.js"
import { SANS_COMPETENCE } from "../item/base-items.js"
import { Misc } from "../misc.js"
import { CARACS } from "../rdd-carac.js"
import { RdDUtility } from "../rdd-utility.js"
import { DIFF } from "../roll/roll-constants.mjs"
import RollDialog from "../roll/roll-dialog.mjs"
import { PART_COMP } from "../roll/roll-part-comp.mjs"
export const MORAL = {
MALHEUREUX: "malheureux",
NEUTRE: "neutre",
HEUREUX: "heureux",
TRESHEUREUX: "très heureux"
}
export const SITUATION_MORAL = {
[MORAL.MALHEUREUX]: "malheureuse",
[MORAL.NEUTRE]: "neutre",
[MORAL.HEUREUX]: "heureuse",
[MORAL.TRESHEUREUX]: "très heureuse",
}
export const APPRECIATION = {
CUISINE: {
bonmoment: "Cuisine", carac: CARACS.ODORATGOUT, competence: "Cuisine",
moral: MORAL.HEUREUX, jetComp: false, jetQualite: false, compMinimum: true
},
MUSIQUE: {
bonmoment: "Musique", carac: CARACS.OUIE, competence: "Musique",
moral: MORAL.HEUREUX, jetComp: false, jetQualite: false, compMinimum: true
},
CHANT: {
bonmoment: "Musique", carac: CARACS.OUIE, competence: "Chant",
moral: MORAL.HEUREUX, jetComp: false, jetQualite: false, compMinimum: true
},
DANSE: {
bonmoment: "Spectacle", carac: CARACS.EMPATHIE, competence: "Danse",
moral: MORAL.HEUREUX, jetComp: false, jetQualite: false, compMinimum: true
},
JEU: {
bonmoment: "Jeu", carac: CARACS.EMPATHIE, competence: "Jeu",
moral: MORAL.HEUREUX, jetComp: false, jetQualite: false, compMinimum: true
},
OEUVRE: {
bonmoment: "Spectacle", carac: CARACS.EMPATHIE, competence: "",
moral: MORAL.HEUREUX, jetComp: false, jetQualite: false, compMinimum: true
},
SERVICE: {
bonmoment: "Confort", carac: CARACS.EMPATHIE, competence: "",
moral: MORAL.HEUREUX, jetComp: false, jetQualite: false, compMinimum: false
},
}
export class Apprecier {
static isAppreciable(item) {
switch (item.type) {
case ITEM_TYPES.nourritureboisson:
case ITEM_TYPES.service:
return item.system.qualite > 0
case ITEM_TYPES.chant:
case ITEM_TYPES.musique:
case ITEM_TYPES.danse:
case ITEM_TYPES.oeuvre:
case ITEM_TYPES.jeu:
return true
}
return false
}
static qualite(qualite, roll = undefined, comp = undefined) {
if (roll && comp) {
return roll.rolled.isSuccess ? qualite : Math.min(qualite, comp.system.niveau)
}
return
}
static getAppreciation(qualite, appreciable, roll = undefined, comp = undefined) {
return {
qualite: (roll?.rolled.ptQualite ?? 0) + Apprecier.qualite(qualite, roll, comp),
appreciation: foundry.utils.duplicate(appreciable.system.appreciation),
messages: []
}
}
static onClickApprecier(roll) {
const appreciation = roll.result.appreciation
if (appreciation.moral == "") {
return
}
RdDUtility.doWithSelectedActor(
actor => new Apprecier(actor, appreciation, roll.result.qualite).apprecier(),
actor => actor.isPersonnage())
}
constructor(actor, appreciation, qualite) {
this.actor = actor
this.appreciation = foundry.utils.duplicate(appreciation)
this.appreciation.situation = SITUATION_MORAL[appreciation.moral]
this.qualite = qualite
this.raisons = []
}
apprecier(callbacks = []) {
if (this.qualite <= 0) {
this.raisons.push(`la qualité ${this.qualite} est négative.`)
}
if (this.qualite <= this.actor.getMoralTotal()) {
this.raisons.push(`la qualité de ${this.qualite} est inférieure au moral de ${this.actor.getMoralTotal()}.`)
}
const competence = this.getCompetenceAppreciation()
if (this.appreciation.compMinimum && this.qualite <= competence.system.niveau && competence.system.niveau > 0 && competence.id) {
this.raisons.push(`la qualité ${this.qualite} est insuffisante pour le niveau ${competence.system.niveau} en ${competence.name}`)
}
const bonmoment = this.appreciation.bonmoment
if (!["", undefined].includes(bonmoment) && this.actor.system.compteurs.bonmoments.includes(bonmoment)) {
this.raisons.push(`du moral a déjà été gagné pour cause de ${bonmoment}`)
}
if (this.appreciation.carac != "") {
this.rollAppreciation(callbacks)
}
else {
this.rollMoral()
}
}
getCompetenceAppreciation() {
return [SANS_COMPETENCE.name, ""].includes(this.appreciation.competence) ? SANS_COMPETENCE : this.actor.getCompetence(this.appreciation.competence)
}
rollAppreciation(rollCallbacks = []) {
const competence = (this.appreciation.jetComp && this.appreciation.competence) ? this.appreciation.competence : ""
const rollData = {
ids: { actorId: this.actor.id },
type: { allowed: [PART_COMP], current: PART_COMP, appreciation: true },
selected: {
carac: { key: this.appreciation.carac, forced: true },
comp: { key: competence, forced: true },
diff: { type: DIFF.IMPOSEE },
apprecier: {
appreciation: this.appreciation,
qualite: this.qualite,
raisons: this.raisons
}
}
}
RollDialog.create(rollData, {
callbacks: [
async r => await this.onRollAppreciation(r),
...rollCallbacks
]
})
}
async onRollAppreciation(roll) {
if (roll.rolled.isSuccess) {
await this.rollMoral()
}
else {
if (this.appreciation.moral == MORAL.TRESHEUREUX) {
await this.rollMoral(MORAL.HEUREUX)
}
}
}
async rollMoral(moral = undefined) {
if (this.raisons.length > 0) {
ui.notifications.info('Pas de jet de moral:' + Misc.concat(this.raisons.map(r => `<br> - ${r}`)))
return
}
moral = moral ?? this.appreciation.moral
// TODO: jet de moral
await this.actor.jetDeMoral(moral, this.appreciation.bonmoment)
}
}
+18 -24
View File
@@ -1,3 +1,4 @@
import { RDD_CONFIG } from "./constants.js";
import { RdDItemArme } from "./item/arme.js";
import { RdDPossession } from "./rdd-possession.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
@@ -35,7 +36,7 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static dmg(rollData, actor, isEntiteIncarnee = false) {
static dmg(rollData, actor) {
const diff = rollData.diffLibre;
const dmgArme = RdDBonus.dmgArme(rollData.arme, rollData.arme?.system.dommagesReels)
const forceRequise = rollData.arme ? RdDItemArme.valeurMain(rollData.arme.system.force ?? 0, RdDItemArme.getMainAttaque(rollData.competence)) : 0
@@ -47,33 +48,33 @@ export class RdDBonus {
penetration: RdDBonus._peneration(rollData),
dmgTactique: RdDBonus.dmgBonus(rollData.tactique),
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used),
mortalite: RdDBonus._calculMortalite(rollData, isEntiteIncarnee),
dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris?.used),
mortalite: RdDBonus.mortalite(rollData.dmg?.mortalite, rollData.arme?.system.mortalite),
dmgActor: RdDBonus.bonusDmg(actor, rollData.selectedCarac?.label.toLowerCase(), dmgArme),
dmgForceInsuffisante: Math.min(0, actor.getForce() - forceRequise)
}
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante
return dmg;
return dmg
}
static dmgRollV2(rollData, current) {
static dmgRollV2(rollData, attaque) {
const actor = rollData.active.actor
const attaque = current.attaque
const arme = attaque.arme
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommagesArme)
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommages)
const dmg = {
total: 0,
dmgArme: dmgArme,
penetration: arme.penetration(),
penetration: arme?.system.penetration ?? 0,
diff: attaque.diff,
dmgTactique: current.tactique?.dmg ?? 0,
dmgParticuliere: 0, // TODO RdDBonus._dmgParticuliere(rollData),
dmgTactique: attaque.tactique?.dmg ?? 0,
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0,
mortalite: RdDBonus.mortalite(current.dmg?.mortalite, arme.system.mortalite, rollData.opponent?.actor?.isEntite()),
mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme?.system.mortalite),
dmgActor: RdDBonus.bonusDmg(actor, attaque.carac.key, dmgArme, attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - (attaque.forceRequise ?? 0)),
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0
}
dmg.isEmpoignade = dmg.mortalite == RDD_CONFIG.encaissement.empoignade
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante + dmg.dmgDiffLibre
return dmg
}
@@ -94,21 +95,14 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static _calculMortalite(rollData, isEntiteIncarnee) {
return RdDBonus.mortalite(rollData.dmg?.mortalite, rollData.arme?.system.mortalite, isEntiteIncarnee)
}
static mortalite(mortaliteSelect, mortaliteArme, isEntiteIncarnee) {
return isEntiteIncarnee ? "entiteincarnee"
: mortaliteSelect
?? mortaliteArme
?? "mortel";
static mortalite(mortaliteSelect, mortaliteArme) {
return mortaliteSelect ?? mortaliteArme ?? RDD_CONFIG.encaissement.mortel
}
/* -------------------------------------------- */
static dmgArme(arme, dommagesMain) {
static dmgArme(arme, dommages) {
if (arme) {
let dmgBase = dommagesMain ?? Number(arme.system.dommages ?? 0);
let dmgBase = dommages ?? Number(arme.system.dommages ?? 0);
//Le bonus dégats magiques ne peut pas faire dépasser le bonus de l'arme (cf p.278)
return dmgBase + Math.min(dmgBase, arme.system.magique ? arme.system.ecaille_efficacite : 0);
}
+224 -116
View File
@@ -1,5 +1,5 @@
import { ChatUtility } from "./chat-utility.js";
import { ENTITE_BLURETTE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { HIDE_DICE, renderTemplate, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
@@ -10,15 +10,15 @@ import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { Targets } from "./targets.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDItemArme } from "./item/arme.js";
import { EMPOIGNADE, RdDItemArme } from "./item/arme.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDInitiative } from "./initiative.mjs";
import { MAP_PHASE, RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs";
import { PART_DEFENSE } from "./roll/roll-part-defense.mjs";
import { RollDialogAdapter } from "./roll/roll-dialog-adapter.mjs";
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js";
import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs";
import { RollBasicParts } from "./roll/roll-basic-parts.mjs";
/* -------------------------------------------- */
const premierRoundInit = [
@@ -56,6 +56,12 @@ export class RdDCombatManager extends Combat {
}
}
static getCombatant(actorId, tokenId) {
return game.combat?.combatants.find(it => it.actor.id == actorId &&
it.token.id == tokenId
)
}
/* -------------------------------------------- */
async nextRound() {
await this.finDeRound();
@@ -67,7 +73,7 @@ export class RdDCombatManager extends Combat {
if (Misc.isFirstConnectedGM()) {
await this.finDeRound({ terminer: true })
ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
game.messages.filter(m => ChatUtility.getMessageData(m, 'rollData') != undefined && ChatUtility.getMessageData(m, 'rollData') != undefined)
.forEach(it => it.delete())
RdDEmpoignade.deleteAllEmpoignades()
}
@@ -99,21 +105,25 @@ export class RdDCombatManager extends Combat {
}
else if (!combatant.actor.isActorCombat()) {
if (options.warning) {
ui.notifications.warn(`${combatant.name} ne peut pas combattre!`)
ui.notifications.warn(`L'acteur ${combatant.name} ne peut pas combattre!`)
}
return undefined
}
return combatant.actor
}
static calculAjustementInit(actor, arme) {
const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
const etatGeneral = actor.getEtatGeneral() ?? 0
return efficacite + etatGeneral
static bonusArme(arme) {
return (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
}
/************************************************************************************/
static etatGeneral(actor) {
return actor.getEtatGeneral() ?? 0;
}
/** ***********************************************************************************
* @override lance l'initiative de plusieurs combattants (tous/ous les PNJs) en une fois
*/
async rollInitiative(ids, messageOptions = {}) {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, messageOptions)
ids = typeof ids === "string" ? [ids] : ids
@@ -121,18 +131,17 @@ export class RdDCombatManager extends Combat {
return this
}
async rollInitRdD(id, formula, messageOptions = {}) {
async rollInitRdD(id, formule, messageOptions = {}) {
const combatant = this.combatants.get(id);
const actor = RdDCombatManager.getActorCombatant(combatant)
if (actor) {
const rollFormula = formula ?? RdDCombatManager.getFirstInitRollFormula(actor)
const roll = combatant.getInitiativeRoll(rollFormula);
if (!roll.total) {
await roll.evaluate();
}
const total = Math.max(roll.total, 0.00);
console.log("Compute init for", rollFormula, roll, total, combatant);
await this.updateEmbeddedDocuments("Combatant", [{ _id: combatant._id || combatant.id, initiative: total }]);
formule = formule ?? RdDCombatManager.getFirstInitRollFormula(actor)
const init = await RdDInitiative.roll(formule)
await this.updateEmbeddedDocuments("Combatant", [{
_id: combatant._id || combatant.id,
initiative: init.init, 'system.init': init
}])
// Send a chat message
let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode");
@@ -144,10 +153,10 @@ export class RdDCombatManager extends Combat {
alias: combatant.token?.name,
sound: CONFIG.sounds.dice,
},
flavor: `${combatant.token?.name} a fait son jet d'Initiative (${messageOptions.info})<br>`
flavor: `${combatant.token?.name} a une initiatyive de ${init.value} : ${messageOptions.info}<br>`
},
messageOptions);
roll.toMessage(messageData, { rollMode, create: true });
init.roll.toMessage(messageData, { rollMode, create: true });
RdDCombatManager.processPremierRoundInit();
}
@@ -159,16 +168,11 @@ export class RdDCombatManager extends Combat {
if (actions.length > 0) {
const action = actions[0]
const init = RdDCombatManager.getInitData(actor, action)
const ajustement = RdDCombatManager.calculAjustementInit(actor, action)
return RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement);
const ajustement = RdDCombatManager.bonusArme(action.arme) + RdDCombatManager.etatGeneral(actor)
return RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement);
}
let ajustement = RdDCombatManager.calculAjustementInit(actor, undefined);
return RdDCombatManager.formuleInitiative(2, 10, 0, ajustement);
}
static formuleInitiative(rang, carac, niveau, bonusMalus) {
return `${rang} +( (${RdDInitiative.calculInitiative(niveau, carac, bonusMalus)} )/100)`;
return RdDInitiative.formule(MAP_PHASE['autre'], 10, 0, actor.getEtatGeneral() ?? 0);
}
/* -------------------------------------------- */
@@ -180,7 +184,6 @@ export class RdDCombatManager extends Combat {
for (let combatant of game.combat.combatants) {
if (combatant.initiativeData?.arme?.type == "arme") {
// TODO: get init data premier round
const initiativeData = combatant.initiativeData;
const action = combatant.initiativeData.arme;
const fromArme = Grammar.toLowerCaseNoAccentNoSpace(action.system.initpremierround)
const initData = premierRoundInit.find(it => fromArme.includes(initData.pattern))
@@ -200,10 +203,27 @@ export class RdDCombatManager extends Combat {
}
/* -------------------------------------------- */
static incDecInit(combatantId, incDecValue) {
const combatant = game.combat.combatants.get(combatantId);
let initValue = combatant.initiative + incDecValue;
game.combat.setInitiative(combatantId, initValue);
static applyInitiativeCommand(combatantId, command, commandValue) {
switch (command) {
case 'delta': return RdDCombatManager.incDecInit(combatantId, commandValue);
case 'autre': return RdDCombatManager.rollInitiativeAction(combatantId,
{ name: "Autre action", action: 'autre', system: { initOnly: true, competence: "Autre action" } });
}
}
static async incDecInit(combatantId, incDecValue) {
const combatant = game.combat.combatants.get(combatantId)
if (combatant?.initiative && incDecValue != 0) {
const value = combatant.system.init.value + incDecValue
const newInit = combatant.initiative + incDecValue / 100;
await game.combat.updateEmbeddedDocuments("Combatant", [{
_id: combatantId,
initiative: newInit,
'system.init.value': value,
'system.init.init': newInit,
}])
}
}
/* -------------------------------------------- */
@@ -220,56 +240,42 @@ export class RdDCombatManager extends Combat {
}
}
options = [
{ name: "Incrémenter initiative", condition: true, icon: '<i class="fa-solid fa-plus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +0.01); } },
{ name: "Décrémenter initiative", condition: true, icon: '<i class="fa-solid fa-minus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), -0.01); } }
{ name: "Incrémenter initiative", condition: true, icon: '<i class="fa-solid fa-plus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +1); } },
{ name: "Décrémenter initiative", condition: true, icon: '<i class="fa-solid fa-minus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), -1); } }
].concat(options);
}
/* -------------------------------------------- */
static async rollInitiativeAction(combatantId, action) {
const combatant = game.combat.combatants.get(combatantId)
const actor = RdDCombatManager.getActorCombatant(combatant)
if (actor == undefined) { return [] }
combatant.initiativeData = { arme: action } // pour reclasser l'init au round 0
if (actor == undefined) { return }
const init = RdDCombatManager.getInitData(actor, action)
const ajustement = RdDCombatManager.calculAjustementInit(actor, action.arme)
const rollFormula = RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement);
const ajustement = RdDCombatManager.bonusArme(actor, action.arme) + RdDCombatManager.etatGeneral(actor)
const formule = RdDInitiative.formule(init.phase, init.carac, init.niveau, ajustement);
await game.combat.rollInitRdD(combatantId, rollFormula, init);
combatant.initiativeData
await game.combat.rollInitRdD(combatantId, formule, init);
combatant.initiativeData = { action, formule } // pour reclasser l'init au round 0
}
static getInitData(actor, action) {
if (actor.getSurprise() == "totale") { return { offset: -1, info: "Surprise Totale", carac: 0, niveau: 0 } }
if (actor.getSurprise() == "demi") { return { offset: 0, info: "Demi Surprise", carac: 0, niveau: 0 } }
if (action.action == 'autre') { return { offset: 2, info: "Autre Action", carac: 0, niveau: 0 } }
if (action.action == 'possession') { return { offset: 10, info: "Possession", carac: actor.getReveActuel(), niveau: 0 } }
if (action.action == 'haut-reve') { return { offset: 9, info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } }
if (actor.getSurprise() == "totale") { return { phase: MAP_PHASE['totale'], info: "Surprise Totale", carac: 0, niveau: 0 } }
if (actor.getSurprise() == "demi") { return { phase: MAP_PHASE['demi'], info: "Demi Surprise", carac: 0, niveau: 0 } }
if (action.action == 'autre') { return { phase: MAP_PHASE['autre'], info: "Autre Action", carac: 0, niveau: 0 } }
if (action.action == 'possession') { return { phase: MAP_PHASE['possession'], info: "Possession", carac: actor.getReveActuel(), niveau: 0 } }
if (action.action == 'haut-reve') { return { phase: MAP_PHASE['draconic'], info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } }
const comp = action.comp
return {
offset: RdDCombatManager.initOffset(comp?.system.categorie, action.arme),
info: action.name + " / " + comp.name,
phase: RdDInitiative.phaseArme(comp?.system.categorie, action.arme),
info: action.label,
carac: actor.getCaracInit(comp),
niveau: comp?.system.niveau ?? (['(lancer)', '(tir)'].includes(action.main) ? -8 : -6)
}
}
static initOffset(categorie, arme) {
switch (categorie) {
case "tir": return 8
case "lancer": return 7
default:
switch (arme.system.cac) {
case "empoignade": return 3
case "pugilat": return 4
case "naturelle": return 4
default: return 5
}
}
}
/* -------------------------------------------- */
static displayInitiativeMenu(html, combatantId) {
const combatant = game.combat.combatants.get(combatantId)
@@ -297,13 +303,8 @@ export class RdDCombatManager extends Combat {
? possessions
: actor.listActions({ isEquipe: true })
for (let index = 0; index < actions.length; index++) {
actions[index].index = index
}
return actions
return Misc.indexed(actions)
}
}
/* -------------------------------------------- */
@@ -314,6 +315,7 @@ export class RdDCombat {
switch (sockmsg.msg) {
case "msg_encaisser": return RdDCombat.onMsgEncaisser(sockmsg.data);
case "msg_defense": return RdDCombat.onMsgDefense(sockmsg.data);
case "msg_defense_v2": return RdDCombat.onMsgDefenseV2(sockmsg.data);
}
}
@@ -366,6 +368,18 @@ export class RdDCombat {
return new RdDCombat(attacker, attackerTokenId, defender, defenderTokenId, target)
}
static rddCombatForAttackV2(attackerRoll) {
return RdDCombat.rddCombatForDefenseV2(RollBasicParts.prepareDefense(attackerRoll))
}
static rddCombatForDefenseV2(defenderRoll) {
let defenderToken = canvas.tokens.get(defenderRoll.active.tokenId)
if (defenderToken) {
return RdDCombat.rddCombatForAttackerAndDefender(defenderRoll.opponent.id, defenderRoll.opponent.tokenId, defenderRoll.active.tokenId)
}
}
/* -------------------------------------------- */
static onMsgEncaisser(msg) {
let defender = canvas.tokens.get(msg.defenderToken.id).actor;
@@ -383,8 +397,13 @@ export class RdDCombat {
let defenderToken = canvas.tokens.get(msg.defenderToken.id)
if (defenderToken && Misc.isFirstConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id)
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme)
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
rddCombat?.removeChatMessageActionsPasseArme(msg.paramChatDefense.attackerRoll.passeArme)
if (msg.defenderRoll.v2) {/* TODO: delete roll V1 */
RollDialog.loadRollData(msg.paramChatDefense)
rddCombat?._chatMessageDefenseV2(msg.paramChatDefense)
} else {
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
}
}
}
@@ -402,7 +421,6 @@ export class RdDCombat {
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of [
'.button-defense',
'.button-parade',
'.button-esquive',
'.button-encaisser',
@@ -464,8 +482,8 @@ export class RdDCombat {
/* -------------------------------------------- */
async onEvent(button, event) {
const chatMessage = ChatUtility.getChatMessage(event);
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll');
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'rollData');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'rollData');
console.log('RdDCombat', attackerRoll, defenderRoll);
const armeParadeId = event.currentTarget.attributes['data-armeid']?.value;
@@ -474,8 +492,6 @@ export class RdDCombat {
switch (button) {
case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '.button-defense': return this.defenseV2(attackerRoll);
case '.button-parade': return this.parade(attackerRoll, armeParadeId);
case '.button-esquive': return this.esquive(attackerRoll, compId, competence);
case '.button-encaisser': return this.encaisser(attackerRoll, defenderRoll);
@@ -560,7 +576,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static isEchecTotal(rollData) {
if (rollData.ids /* roll V2*/) {
if (rollData.v2 /* roll V2*/) {
// TODO: en cas de demi-surprise à l'attaque, tout échec est un echec total.
// TODO: en cas de demi-surprise en défense, pas de changement à la règle de base
return rollData.rolled.isETotal
@@ -574,7 +590,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static isParticuliere(rollData) {
if (rollData.ids /* roll V2*/) {
if (rollData.v2 /* roll V2*/) {
return rollData.rolled.isPart
}
if (rollData.attackerRoll || !rollData.ajustements.surprise.used) {
@@ -585,7 +601,7 @@ export class RdDCombat {
/* -------------------------------------------- */
static isReussite(rollData) {
if (rollData.ids /* roll V2*/) {
if (rollData.v2 /* roll V2*/) {
return rollData.rolled.isSuccess
}
if (!rollData.ajustements.surprise.used) {
@@ -601,7 +617,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async proposerAjustementTirLancer(rollData) {
if (['tir', 'lancer'].includes(rollData.competence.system.categorie)) {
if (this.defender.isEntite([ENTITE_BLURETTE])) {
if (this.defender.isEntiteBlurette()) {
ChatMessage.create({
content: `<strong>La cible est une blurette, l'arme à distance sera perdue dans le blurêve`,
whisper: ChatUtility.getGMs()
@@ -665,12 +681,110 @@ export class RdDCombat {
return { msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3 };
}
async attaqueV2() {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return
}
await this.doRollAttaque({
ids: {
actorId: this.attackerId,
actorTokenId: this.attackerTokenId,
opponentId: this.defender.id,
opponentTokenId: this.defenderTokenId,
},
type: { allowed: ['attaque'], current: 'attaque' },
passeArme: foundry.utils.randomID(16),
})
}
async doRollAttaque(rollData, callbacks = []) {
// TODO V2 await this.proposerAjustementTirLancer(rollData)
return await RollDialog.create(rollData, {
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
async (roll) => await this.onAttaqueV2(roll),
...callbacks
]
})
}
async onAttaqueV2(attackerRoll) {
if (!this.defender || !attackerRoll.rolled.isSuccess || attackerRoll.particulieres?.length > 1) {
return
}
if (!await this.attacker.accorder(this.defender, 'avant-defense')) {
return;
}
RollDialog.loadRollData(attackerRoll)
const surpriseDefender = this.defender.getSurprise(true);
const paramChatDefense = {
attackerRoll: attackerRoll,
isPossession: this.isPossession(attackerRoll),
defender: this.defender,
attacker: this.attacker,
attackerId: this.attackerId,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
surprise: surpriseDefender,
}
if (Misc.isFirstConnectedGM()) {
await this._chatMessageDefenseV2(paramChatDefense);
}
else {
this._socketSendMessageDefenseV2(paramChatDefense);
}
}
_socketSendMessageDefenseV2(paramChatDefense) {
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_defense_v2", data: {paramChatDefense} })
}
static async onMsgDefenseV2(msg) {
if (Misc.isFirstConnectedGM()) {
const paramChatDefense = msg.paramChatDefense
RollBasicParts.restore(paramChatDefense.attackerRoll)
const rddCombat = RdDCombat.rddCombatForAttackV2(paramChatDefense.attackerRoll)
rddCombat?.removeChatMessageActionsPasseArme(paramChatDefense.attackerRoll.passeArme)
await rddCombat?._chatMessageDefenseV2(paramChatDefense)
}
}
async _chatMessageDefenseV2(paramDemandeDefense) {
const attackerRoll = paramDemandeDefense.attackerRoll
RollBasicParts.loadSurprises(attackerRoll)
attackerRoll.dmg = RdDBonus.dmgRollV2(attackerRoll, attackerRoll.current.attaque)
const defenseData = RollBasicParts.prepareDefense(attackerRoll)
const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', defenseData)
});
// flag pour garder les jets d'attaque/defense
ChatUtility.setMessageData(choixDefense, 'demande-defense', true)
ChatUtility.setMessageData(choixDefense, 'rollData', {
ids: defenseData.ids,
attackerRoll: RollDialog.saveParts(attackerRoll),
passeArme: defenseData.passeArme
})
}
/* -------------------------------------------- */
async attaque(competence, arme) {
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
return
}
if (arme.system.cac == 'empoignade') {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
return this.attacker.rollCompetence(competence.name, { arme: arme })
}
if (arme.system.cac == EMPOIGNADE) {
RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender)
return
}
@@ -714,7 +828,7 @@ export class RdDCombat {
essais: {}
};
if (this.attacker.isCreatureEntite()) {
if (this.attacker.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(rollData);
}
else if (arme) {
@@ -723,9 +837,9 @@ export class RdDCombat {
}
else {
// sans armes: à mains nues
rollData.arme = RdDItemArme.corpsACorps(this.attacker)
rollData.arme = RdDItemArme.pugilat(this.attacker)
rollData.arme.system.niveau = competence.system.niveau
rollData.arme.system.initiative = RdDInitiative.calculInitiative(competence.system.niveau, this.attacker.system.carac['melee'].value);
rollData.arme.system.initiative = RdDInitiative.getRollInitiative(this.attacker.system.carac['melee'].value, competence.system.niveau);
}
return rollData;
}
@@ -776,14 +890,14 @@ export class RdDCombat {
passeArme: rollData.passeArme
})
});
ChatUtility.setMessageData(choixParticuliere, 'attacker-roll', rollData);
ChatUtility.setMessageData(choixParticuliere, 'rollData', rollData);
}
/* -------------------------------------------- */
async _onAttaqueNormale(attackerRoll) {
console.log("RdDCombat.onAttaqueNormale >>>", attackerRoll);
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite());
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker);
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
attackerRoll.show = {
cible: this.defender?.getAlias() ?? 'la cible',
@@ -802,7 +916,10 @@ export class RdDCombat {
/* -------------------------------------------- */
isPossession(attackerRoll) {
return attackerRoll.selectedCarac.label.toLowerCase() == 'possession';
const carac = attackerRoll.v2
? attackerRoll.current.carac?.label
: attackerRoll.selectedCarac.label
return carac?.toLowerCase() == 'possession';
}
/* -------------------------------------------- */
@@ -852,10 +969,10 @@ export class RdDCombat {
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', paramDemandeDefense),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense-v1.hbs', paramDemandeDefense),
});
// flag pour garder les jets d'attaque/defense
ChatUtility.setMessageData(choixDefense, 'defender-roll', defenderRoll);
ChatUtility.setMessageData(choixDefense, 'rollData', defenderRoll);
}
/* -------------------------------------------- */
@@ -869,7 +986,7 @@ export class RdDCombat {
defenderToken: this.defenderToken,
defenderRoll: defenderRoll,
paramChatDefense: paramChatDefense,
rollMode: true
rollMode: true,
}
});
}
@@ -906,7 +1023,7 @@ export class RdDCombat {
essais: attackerRoll.essais
})
});
ChatUtility.setMessageData(choixEchecTotal, 'attacker-roll', attackerRoll);
ChatUtility.setMessageData(choixEchecTotal, 'rollData', attackerRoll);
}
/* -------------------------------------------- */
@@ -966,7 +1083,7 @@ export class RdDCombat {
dialog.render(true);
}
async defenseV2(attackerRoll) {
async defenseV2(attackerRoll, callbacks = []) {
// this._prepareParade(attackerRoll, arme, competence);
await this.doRollDefense({
ids: {
@@ -975,19 +1092,15 @@ export class RdDCombat {
opponentTokenId: this.attackerTokenId,
opponentId: this.attackerId,
},
type: { allowed: ['defense'], current: 'defense' },
attackerRoll: RollDialogAdapter.mapActionAttaque(attackerRoll),
type: { allowed: [ROLL_TYPE_DEFENSE], current: ROLL_TYPE_DEFENSE },
attackerRoll: attackerRoll,
passeArme: attackerRoll.passeArme,
})
}, callbacks)
}
async doRollDefense(rollData, callbacks = []) {
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
},
customChatMessage: true,
return await RollDialog.create(rollData, {
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
async (roll) => {
this.removeChatMessageActionsPasseArme(roll.passeArme);
@@ -1021,7 +1134,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(defenderRoll);
}
@@ -1034,22 +1147,17 @@ export class RdDCombat {
if (RdDCombat.isReussite(rollData)) {
if (isParade) {
await this.computeDeteriorationArme(rollData)
if (RdDCombat.isParticuliere(rollData)) {
await this.infoAttaquantDesarme(rollData)
}
}
if (RdDCombat.isParticuliere(rollData)) {
await this._onDefenseParticuliere(rollData, isEsquive)
}
}
this.removeChatMessageActionsPasseArme(rollData.passeArme)
}
async _onDefenseParticuliere(rollData, isEsquive) {
if (isEsquive) {
ChatUtility.createChatWithRollMode(
{ content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" },
this.defender)
}
else if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) {
async infoAttaquantDesarme(rollData) {
if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) {
// TODO: attaquant doit jouer résistance et peut être désarmé p132
ChatUtility.createChatWithRollMode(
{ content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` },
@@ -1153,7 +1261,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreatureOuEntite()) {
MappingCreatureArme.setRollDataCreature(rollData);
}
return rollData;
+4 -8
View File
@@ -19,6 +19,7 @@ import { TMRUtility } from "./tmr-utility.js";
import { DialogFatigueVoyage } from "./voyage/dialog-fatigue-voyage.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { renderTemplate } from "./constants.js";
const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
@@ -126,7 +127,7 @@ export class RdDCommands {
<br><strong>/rdd 15 -2</strong> effectue un jet 15 à -2
<br><strong>/rdd 15 0 s</strong> effectue un jet 15 à 0, avec significative requise
<br><strong>/rdd Vue Vigilance -2</strong> effectue un jet de Vue/Vigilance à -2 pour les tokens sélectionnés
<br><strong>/rdd vol déser +2</strong> effectue un jet de Volonté/Survie en désert à +2 pour les tokens sélectionnés
<br><strong>/rdd vol déser +2</strong> effectue un jet de Volonté/Survie en Désert à +2 pour les tokens sélectionnés
`
});
this.registerCommand({
@@ -136,7 +137,7 @@ export class RdDCommands {
<br><strong>/jet Vue</strong> poste une demande de jet de VUE à 0
<br><strong>/jet Vue -2</strong> poste une demande de jet de VUE à -2
<br><strong>/jet Vue Vigilance -2</strong> poste une demande de jet de VUE / Vigilance à -2
<br><strong>/jet vol déser +2</strong> poste une demande de jet de VOLONTÉ / Survie en désert à +2
<br><strong>/jet vol déser +2</strong> poste une demande de jet de VOLONTÉ / Survie en Désert à +2
`
});
this.registerCommand({ path: ["/ddr"], func: (content, msg, params) => this.rollDeDraconique(msg), descr: "Lance un Dé Draconique" });
@@ -460,12 +461,7 @@ export class RdDCommands {
async supprimerSignesDraconiquesEphemeres() {
if (game.user.isGM) {
game.actors.forEach(actor => {
const ephemeres = actor.items.filter(item => item.type = 'signedraconique' && item.system.ephemere);
if (ephemeres.length > 0) {
actor.deleteEmbeddedDocuments("Item", ephemeres.map(item => item.id));
}
});
game.actors.forEach(actor => actor.supprimerSignesDraconiques(it => it.system.ephemere))
}
else {
ui.notifications.warn("Seul le MJ est autorisé à utiliser la commande /signe");
+152 -55
View File
@@ -1,9 +1,11 @@
import { STATUSES } from "./settings/status-effects.js";
import { ITEM_TYPES } from "./constants.js";
import { ITEM_TYPES, renderTemplate } from "./constants.js";
import { ChatUtility } from "./chat-utility.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDRoll } from "./rdd-roll.js";
import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs";
import { MAP_PHASE } from "./initiative.mjs";
import { RdDCombatManager } from "./rdd-combat.js";
/* -------------------------------------------- */
export class RdDEmpoignade {
@@ -12,6 +14,58 @@ 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) {
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
if (empoignade?.system.empoigneurid == defender.id) {
let empoignade = RdDEmpoignade.getEmpoignade(defender, attacker)
return await RdDEmpoignade.$ajustementEmpoignade(empoignade, defender, attacker, - adjust);
}
return await RdDEmpoignade.$ajustementEmpoignade(empoignade, attacker, defender, adjust);
}
static async $ajustementEmpoignade(empoignade, attacker, defender, adjust) {
const empId = empoignade?.system.empoignadeid ?? foundry.utils.randomID(16);
const empFin = (empoignade?.system.pointsemp ?? 0) + adjust
if (empoignade) {
empoignade.system.pointsemp = empFin;
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: empFin,
empoigneurname: attacker.name,
empoignename: defender.name
}
}, attacker, defender);
}
if (adjust != 0 && empFin == 2) {
await RdDEmpoignade.proposerEntrainerAuSol(attacker, defender, empoignade);
}
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 => {
@@ -35,20 +89,20 @@ export class RdDEmpoignade {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.entrainerAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
ChatUtility.remover(chatMessage)()
});
$(html).on("click", '.empoignade-projeter-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.projeterAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
ChatUtility.remover(chatMessage)()
});
$(html).on("change", '.empoignade-perte-endurance', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
if (event.currentTarget.value && event.currentTarget.value != "none") {
RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value)
ChatUtility.removeChatMessageId(chatMessage.id)
ChatUtility.remover(chatMessage)()
}
});
}
@@ -93,8 +147,7 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static getEmpoignadeById(actor, id) {
let emp = actor.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.empoignadeid == id)
return emp && foundry.utils.duplicate(emp) || undefined;
return actor.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.empoignadeid == id)
}
/* -------------------------------------------- */
@@ -184,16 +237,28 @@ 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) {
if (!empoignade.system.ausol) {
let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.hbs');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
await RdDEmpoignade.proposerEntrainerAuSol(attacker, defender, empoignade)
} else {
await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle);
await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle)
}
}
static async proposerEntrainerAuSol(attacker, defender, empoignade) {
if (!empoignade.system.ausol) {
const mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer"
const rollData = {
mode, empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetenceCorpsACorps(),
selectedCarac: attacker.system.carac.melee,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
const msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.hbs');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
}
@@ -204,7 +269,14 @@ export class RdDEmpoignade {
await this.onAttaqueEmpoignadeValidee(attacker, defender)
}
static async onImmobilisation(attacker, defender, empoignade) {
static async onImmobilisation(attacker, empoignade) {
const defender = game.actors.get(empoignade.system.empoigneid)
const empDefenseur = defender.itemTypes[ITEM_TYPES.empoignade]
.find(it => it.system.empoignadeid == empoignade.system.empoignadeid);
await defender.updateEmbeddedDocuments('Item', [{
_id: empDefenseur.id,
'system.immobilise': true
}])
const rollData = {
mode: "immobilise",
empoignade, attacker, defender,
@@ -237,10 +309,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 +328,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,27 +386,48 @@ 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) {
const belligerants = [
attacker ?? game.actors.get(empoignade.system.empoigneurid),
defender ?? game.actors.get(empoignade.system.empoigneid)]
const removeEmp = empoignade.system.pointsemp == 0
if (removeEmp) {
const emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
return await attacker.deleteEmbeddedDocuments('Item', [emp.id])
}
else {
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
}])
}))
}
}
/* -------------------------------------------- */
static async $deleteEmpoignade(empoignade) {
console.log("DELETE Empoignade", empoignade)
let defender = game.actors.get(empoignade.system.empoigneid)
let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
await defender.deleteEmbeddedDocuments('Item', [emp._id])
const defender = game.actors.get(empoignade.system.empoigneid)
const emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
await defender.deleteEmbeddedDocuments('Item', [emp.id])
}
/* -------------------------------------------- */
@@ -347,7 +437,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 +456,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)
@@ -379,29 +469,28 @@ export class RdDEmpoignade {
static async perteEndurance(rollData, perteMode) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
if (perteMode == "none" || !RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
//console.log("Perte d'endurance :!!!", perteMode)
let endValue = defender.system.sante.endurance.value
if (perteMode == "end0") {
await defender.santeIncDec("endurance", -endValue);
}
if (perteMode == "end1") {
await defender.santeIncDec("endurance", -(endValue - 1));
}
if (perteMode == "endmoitie") {
await defender.santeIncDec("endurance", -Math.floor(endValue / 2));
}
if (perteMode == "endquart") {
await defender.santeIncDec("endurance", -(3 * Math.floor(endValue / 4)));
}
let msg = await RdDRollResult.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.hbs');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
const perteEndurance = await RdDEmpoignade.$calcPerteEnd(perteMode, defender.system.sante.endurance.value)
await defender.santeIncDec("endurance", -perteEndurance)
await RdDRollResult.displayRollData({ attacker, defender, perteEndurance },
attacker,
'chat-empoignade-perte-endurance.hbs')
}
static async $calcPerteEnd(perteMode, endValue) {
switch (perteMode) {
case "none": return 0
case "end0": return endValue
case "end1": return (endValue - 1)
case "3/4": return Math.floor(3 * endValue / 4)
case "1/2": return Math.floor(endValue / 2)
case "1/4": return Math.floor(endValue / 4)
}
const rolled = await (new Roll(perteMode).evaluate())
return rolled.total
}
/* -------------------------------------------- */
static async deleteAllEmpoignades() {
for (let actor of game.actors) {
@@ -416,16 +505,24 @@ export class RdDEmpoignade {
let actor = game.actors.get(actorDeleteId)
let emp = this.getEmpoignadeById(actor, empoignade.system.empoignadeid)
if (emp) {
await actor.deleteEmbeddedDocuments('Item', [emp._id])
await actor.deleteEmbeddedDocuments('Item', [emp.id])
}
}
/* -------------------------------------------- */
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
+8 -8
View File
@@ -1,4 +1,4 @@
import { ATTAQUE_TYPE, RdDItemArme } from "./item/arme.js";
import { ATTAQUE_TYPE, EMPOIGNADE, PUGILAT, RdDItemArme } from "./item/arme.js";
import { ITEM_TYPES } from "./constants.js";
export class RdDHotbar {
@@ -28,8 +28,8 @@ export class RdDHotbar {
return ' ' + maniement
case 'tir': return ' (tir)';
case 'lancer': return ' (lancer)';
case 'pugilat': return ' (pugilat)';
case 'empoignade': return ' (empoignade)';
case PUGILAT: return ' (pugilat)';
case EMPOIGNADE: return ' (empoignade)';
}
return ''
}
@@ -63,8 +63,8 @@ export class RdDHotbar {
case ITEM_TYPES.competence:
await this.createItemMacro(item, slot++, 'competence')
if (item.isCorpsACorps()) {
await this.createItemMacro(item, slot++, 'pugilat')
await this.createItemMacro(item, slot++, 'empoignade')
await this.createItemMacro(item, slot++, PUGILAT)
await this.createItemMacro(item, slot++, EMPOIGNADE)
}
else if (item.isCompetenceArme()) {
ui.notifications.info(`${item.name} est une compétence d'arme, la macro n'est pas liée à un arme.<br>
@@ -121,9 +121,9 @@ export class RdDHotbar {
case ITEM_TYPES.competence:
if (item.isCorpsACorps()) {
switch (categorieArme) {
case 'pugilat':
return actor.rollArme(RdDItemArme.corpsACorps(actor));
case 'empoignade':
case PUGILAT:
return actor.rollArme(RdDItemArme.pugilat(actor));
case EMPOIGNADE:
return actor.rollArme(RdDItemArme.empoignade(actor));
}
}
+2
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()
}
+1
View File
@@ -1,4 +1,5 @@
import { ChatUtility } from "./chat-utility.js"
import { renderTemplate } from "./constants.js"
const vents = [
{ min: 0, max: 0, valeur: 'Calme' },
+1
View File
@@ -1,5 +1,6 @@
import { RdDBaseActor } from "./actor/base-actor.js";
import { ChatUtility } from "./chat-utility.js";
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
+261
View File
@@ -0,0 +1,261 @@
import { ITEM_TYPES, RDD_CONFIG } from "./constants.js";
import { RollBasicParts } from "./roll/roll-basic-parts.mjs";
import { ChatUtility } from "./chat-utility.js";
import RollDialog from "./roll/roll-dialog.mjs";
import { Targets } from "./targets.js";
import { RdDUtility } from "./rdd-utility.js";
import { ROLL_TYPE_POSSESSION } from "./roll/roll-constants.mjs";
import { TokenActor } from "./technical/actor-token.mjs";
export const ACTIONS_POSSESSION = {
ATTAQUE: 'attaque',
POSSEDER: 'posseder',
CONJURER: 'conjurer',
DEFENSE: 'defense',
DEFENSE_POSSESSION: 'defense-possession',
DEFENSE_CONJURATION: 'defense-conjuration'
}
export class RdDPossessionV2 {
static init() {
}
static $isInverse(entite, victime) {
return !entite.actor.isEntiteNonIncarnee() && victime.actor.isEntiteNonIncarnee()
}
/* -------------------------------------------- */
static findPossession(entite, victime) {
if (RdDPossessionV2.$isInverse(entite, victime)) {
return RdDPossessionV2.findPossession(victime, entite)
}
return victime.actor.itemTypes[ITEM_TYPES.possession].find(it => it.system.entiteid == entite.id)
}
static async createPossessionIfMissing(entite, victime) {
if (RdDPossessionV2.$isInverse(entite, victime)) {
return await RdDPossessionV2.createPossessionIfMissing(victime, entite)
}
const existing = RdDPossessionV2.findPossession(entite, victime)
if (!existing) {
await victime.actor.createEmbeddedDocuments('Item', [{
name: `Possession de ${entite.name}`,
type: ITEM_TYPES.possession,
img: RDD_CONFIG.icons.possession,
system: {
entiteid: entite.actor.id,
entitetokenid: entite.token.id,
victimeid: victime.actor.id,
victimetokenid: victime.token.id,
compteur: 0
}
}])
}
}
static getTypePossession(active, opponent) {
const itemPossession = RdDPossessionV2.findPossession(active, opponent)
const isEntite = active.actor.isEntiteNonIncarnee() && !opponent.actor.isEntiteNonIncarnee()
const compteur = itemPossession?.system.compteur ?? 0
return {
isEntite: isEntite,
isPersonnage: active.actor.isPersonnage(),
isCompteurPossession: Math.sign(compteur) >= 0,
compteur: Math.abs(compteur)
}
}
static getTypePossessionAction(active, opponent, action) {
const possession = RdDPossessionV2.getTypePossession(active, opponent)
possession.isAttaque = RdDPossessionV2.isAttaque(action)
possession.action = RdDPossessionV2.$getAction(possession.isAttaque, possession.isEntite)
return possession
}
static $getAction(isAttaque, isEntite) {
if (isAttaque) {
return isEntite ? ACTIONS_POSSESSION.POSSEDER : ACTIONS_POSSESSION.CONJURER
}
return isEntite ? ACTIONS_POSSESSION.DEFENSE_CONJURATION : ACTIONS_POSSESSION.DEFENSE_POSSESSION
}
static actionTitle(action) {
switch (action) {
case ACTIONS_POSSESSION.POSSEDER: return "tente de posséder"
case ACTIONS_POSSESSION.CONJURER: return "tente de conjurer"
case ACTIONS_POSSESSION.DEFENSE_POSSESSION: return "résiste à la possession de"
case ACTIONS_POSSESSION.DEFENSE_CONJURATION: return "résiste à la conjuration de"
case ACTIONS_POSSESSION.DEFENSE: return "résiste à "
}
return "lutte contre"
}
static isDefense(action) {
return [ACTIONS_POSSESSION.DEFENSE, ACTIONS_POSSESSION.DEFENSE_POSSESSION, ACTIONS_POSSESSION.DEFENSE_CONJURATION].includes(action)
}
static isAttaque(action) {
return [ACTIONS_POSSESSION.ATTAQUE, ACTIONS_POSSESSION.POSSEDER, ACTIONS_POSSESSION.CONJURER].includes(action)
}
static async rollAttaquePossession(actor) {
const selectedToken = RdDUtility.getSelectedToken(actor)
Targets.selectOneTargetToken(async target => {
RollDialog.create(
{
ids: {
actorId: actor.id,
actorTokenId: selectedToken.id,
opponentId: target.actor.id,
opponentTokenId: target.id
},
passeArme: foundry.utils.randomID(16),
type: { allowed: [ROLL_TYPE_POSSESSION], current: ROLL_TYPE_POSSESSION, possession: { action: ACTIONS_POSSESSION.ATTAQUE } },
},
{
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
async (roll) => await RdDPossessionV2.createPossessionIfMissing(roll.active, roll.opponent),
async (roll) => RdDPossessionV2.$setParticuliereFinesse(roll),
async (roll) => await RdDPossessionV2.chatMessageDefensePossession(roll)
]
})
})
}
static async rollConjurerPossession(actor, possession) {
if (possession.system.possede){
ui.notifications.warn(`${actor.name} est totalement possédé, impossible de conjurer l'entité`)
return
}
RollDialog.create(
{
ids: {
actorId: possession.system.victimeid,
actorTokenId: possession.system.victimetokenid,
opponentId: possession.system.entiteid,
opponentTokenId: possession.system.entitetokenid
},
passeArme: foundry.utils.randomID(16),
type: { allowed: [ROLL_TYPE_POSSESSION], current: ROLL_TYPE_POSSESSION, possession: { action: ACTIONS_POSSESSION.ATTAQUE } },
},
{
onRollDone: RollDialog.onRollDoneClose,
callbacks: [
async (roll) => await RdDPossessionV2.createPossessionIfMissing(roll.active, roll.opponent),
async (roll) => RdDPossessionV2.$setParticuliereFinesse(roll),
async (roll) => await RdDPossessionV2.chatMessageDefensePossession(roll)
]
})
}
static $setParticuliereFinesse(roll) {
if (roll.rolled.isPart) {
roll.particuliere = RDD_CONFIG.particuliere.finesse.key
}
}
static async rollDefensePossession(savedRoll, chatMessage) {
RollBasicParts.restore(savedRoll)
const attackerRoll = savedRoll.attackerRoll
RollDialog.create(
{
ids: savedRoll.ids,
passeArme: attackerRoll.passeArme,
type: { allowed: [ROLL_TYPE_POSSESSION], current: ROLL_TYPE_POSSESSION, possession: { action: ACTIONS_POSSESSION.DEFENSE } },
selected: { diff: { value: attackerRoll.selected?.diff?.value ?? 0 } },
attackerRoll: attackerRoll
},
{
onRollDone: RollDialog.onRollDoneClose, callbacks: [
async roll => await RdDPossessionV2.onRollDefense(roll),
ChatUtility.remover(chatMessage)
]
});
}
static async onRollDefense(defense) {
if (defense.rolled.isEchec) {
await RdDPossessionV2.addPointPossession(defense.opponent.actor, defense.active.actor)
}
RdDPossessionV2.resetPossession(defense)
}
static async onMarquerPointPossession(roll) {
roll.type = {}
await RdDPossessionV2.addPointPossession(roll.opponent, roll.active)
RdDPossessionV2.resetPossession(roll)
await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker({ actor: roll.active.actor, token: roll.active.token }),
alias: roll.opponent.name,
whisper: ChatUtility.getOwners(roll.active.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/possession/chat-message-marquer.hbs`, roll)
})
}
static resetPossession(roll) {
roll.type.possession = foundry.utils.mergeObject(
roll.type.possession ?? {},
RdDPossessionV2.getTypePossession(roll.opponent, roll.active))
}
static async addPointPossession(entite, victime, points = 1) {
if (RdDPossessionV2.$isInverse(entite, victime)) {
return await RdDPossessionV2.addPointPossession(victime, entite, - points)
}
const existing = RdDPossessionV2.findPossession(entite, victime)
if (existing) {
const compteur = (existing.system.compteur ?? 0) + points
await victime.actor.updateEmbeddedDocuments('Item', [{ _id: existing.id, 'system.compteur': compteur }])
}
}
static async chatMessageDefensePossession(attackerRoll) {
const defense = RollBasicParts.prepareDefense(attackerRoll)
defense.type = {
possession: RdDPossessionV2.getTypePossessionAction(defense.active, defense.opponent, ACTIONS_POSSESSION.DEFENSE)
}
const chatDemandeDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker({ actor: defense.active.actor, token: defense.active.token }),
alias: attackerRoll.active.name,
whisper: ChatUtility.getOwners(defense.active.actor),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/possession/chat-demande-defense.hbs', defense)
});
ChatUtility.setMessageData(chatDemandeDefense, 'demande-defense', true)
// // flag pour garder les jets d'attaque/defense
ChatUtility.setMessageData(chatDemandeDefense, 'rollData', {
ids: defense.ids,
attackerRoll: RollDialog.saveParts(attackerRoll),
passeArme: defense.passeArme
})
}
static async onPossession(actor, possession) {
if (Math.abs(possession.system.compteur) >= 2) {
await ChatMessage.create({
alias: actor.getAlias(),
whisper: ChatUtility.getOwners(actor),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/possession/chat-fin-possession.hbs', {
possession,
active: TokenActor.fromActor(actor),
opponent: TokenActor.fromActor(game.actors.get(possession.system.entiteid))
})
})
if (possession.system.compteur <= -2) {
await actor.deleteEmbeddedDocuments('Item', [possession.id])
}
if (possession.system.compteur >= 2) {
await actor.updateEmbeddedDocuments('Item', [{ _id: possession.id, 'system.possede': true }])
}
}
}
}
+14 -11
View File
@@ -18,9 +18,9 @@ export class RdDPossession {
/* -------------------------------------------- */
static searchPossessionFromEntite(attacker, defender) {
let poss = attacker.items.find(poss => poss.type == ITEM_TYPES.possession && poss.system.victime.actorid == defender.id);
let poss = attacker.itemTypes[ITEM_TYPES.possession].find(poss => poss.system.victime.actorid == defender.id);
if (!poss) {
poss = defender.items.find(poss => poss.type == ITEM_TYPES.possession && poss.system.victime.actorid == defender.id);
poss = defender.itemTypes[ITEM_TYPES.possession].find(poss => poss.system.victime.actorid == defender.id);
}
return poss && foundry.utils.duplicate(poss) || undefined;
}
@@ -94,8 +94,8 @@ export class RdDPossession {
}
static selectCompetenceDraconicOuPossession(rollData, rollingActor) {
rollData.competence = rollingActor.getDraconicOuPossession();
if (rollingActor.isCreatureEntite()) {
rollData.competence = rollingActor.getDraconicOuPossession().find(it => true);
if (rollingActor.isCreatureOuEntite()) {
const carac = rollingActor.system.carac
rollData.carac = carac
rollData.competence.system.defaut_carac = 'reve'
@@ -132,7 +132,7 @@ export class RdDPossession {
}
const possession = (rollData.isECNIDefender ? rollData.attacker : rollData.defender).getPossession(rollData.possession.system.possessionid)
RdDPossession.storePossessionAttaque(possession, rollData)
await RdDRollResult.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.hbs');
await RdDRollResult.displayRollData(rollData, rollData.defender, 'chat-resultat-possession-v1.hbs');
}
/* -------------------------------------------- */
@@ -172,7 +172,7 @@ export class RdDPossession {
rollData.possession = possession
RdDPossession.$updateEtatPossession(rollData.possession)
await RdDRollResult.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.hbs')
await RdDRollResult.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession-v1.hbs')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
// conjuration
await victime.deleteEmbeddedDocuments("Item", [rollData.possession._id])
@@ -226,15 +226,18 @@ export class RdDPossession {
/* -------------------------------------------- */
static async createPossession(attacker, defender) {
return await Item.create({
name: "Possession en cours de " + attacker.name, type: 'possession',
img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
name: `Possession de ${entite.name}`,
type: ITEM_TYPES.possession,
img: RDD_CONFIG.icons.possession,
system: {
description: "", typepossession: attacker.name,
typepossession: attacker.name,
possede: false,
possessionid: foundry.utils.randomID(16),
entiteid: attacker.id,
victimeid: defender.id,
compteur: 0,
entite: { actorid: attacker.id },
victime: { actorid: defender.id },
compteur: 0
victime: { actorid: defender.id }
}
},
{
+17 -7
View File
@@ -1,3 +1,4 @@
import { renderTemplate } from "./constants.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
@@ -15,17 +16,20 @@ const levelDown = [
];
const levelImpossible = { score: 0, norm: 0, sign: 0, part: 0, epart: 0, etotal: 1 };
const reussiteNormale = { code: "norm", isPart: false, isSign: false, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 1, ptQualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.norm) };
const reussiteSignificative = { code: "sign", isPart: false, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 2, ptQualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) };
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
const reussites = [
{ code: "etotal", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: -4, ptQualite: -6, quality: "Echec total", condition: (target, roll) => roll >= target.etotal && roll <= 100 },
{ code: "epart", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: false, ptTache: -2, ptQualite: -4, quality: "Echec particulier", condition: (target, roll) => (roll >= target.epart && roll < target.etotal) },
{ code: "echec", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Echec normal", condition: (target, roll) => (roll > target.norm && roll < target.etotal) },
{ code: "norm", isPart: false, isSign: false, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 1, ptQualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.norm) },
{ code: "sign", isPart: false, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 2, ptQualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) },
reussiteNormale,
reussiteSignificative,
{ code: "part", isPart: true, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 3, ptQualite: 2, quality: "Réussite Particulière!", condition: (target, roll) => (roll > 0 && roll <= target.part) },
{ code: "error", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: 0, ptQualite: 0, quality: "Jet de dés invalide", condition: (target, roll) => (roll <= 0 || roll > 100) }
];
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
/* -------------------------------------------- */
const CARAC_MAXIMUM_RESOLUTION = 40;
/* -------------------------------------------- */
@@ -66,9 +70,9 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static _computeCell(niveau, percentage) {
static _computeCell(level, percentage) {
return {
niveau: niveau,
level: level,
score: percentage,
norm: Math.min(99, percentage),
sign: this._reussiteSignificative(percentage),
@@ -155,6 +159,11 @@ export class RdDResolutionTable {
}
}
/* -------------------------------------------- */
static replaceParticuliereDemiSurprise(chances) {
foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'part'), { overwrite: true });
}
/* -------------------------------------------- */
static significativeRequise(chances) {
chances.roll = Math.min(chances.part + 1, chances.sign)
@@ -188,9 +197,10 @@ export class RdDResolutionTable {
static computeReussite(chances, roll, diviseur) {
const reussite = reussites.find(x => x.condition(chances, roll))
if (diviseur > 1 && reussite.isSuccess) {
if (chances > roll * diviseur) {
if (chances.norm < roll * diviseur) {
return reussiteInsuffisante
}
return reussiteSignificative
}
return reussite
}
@@ -236,7 +246,7 @@ export class RdDResolutionTable {
maxCarac = Math.min(maxCarac, minCarac + 20);
minLevel = Math.max(minLevel, -10);
maxLevel = Math.max(Math.min(maxLevel, 30), minLevel + colonnes);
return await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.hbs', {
return await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.hbs', {
carac: carac,
difficulte: level,
min: minLevel,

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