Compare commits
59 Commits
foundryvtt
...
v10
Author | SHA1 | Date | |
---|---|---|---|
54df875451 | |||
253a1bd433 | |||
e58d88fab6 | |||
41d2404de2 | |||
631ee0b801 | |||
ed9c574cd2 | |||
bb624e8e96 | |||
40f2ac8714 | |||
804fa3b784 | |||
d35e47824d | |||
37d3fa5bc5 | |||
8a12eb865c | |||
5baa94b3f0 | |||
37c2b6432d | |||
fc63835a71 | |||
d82a543860 | |||
2e76961ba7 | |||
a9f50bbc5e | |||
8ba3476d7b | |||
4e8f6e8872 | |||
727701bdcd | |||
dcc0f0acfd | |||
61eee66ebe | |||
c75d10f69b | |||
333bb051c1 | |||
1bf247db33 | |||
49fc2c9b0a | |||
9013376096 | |||
972459a08d | |||
1607629365 | |||
8f7efdad87 | |||
2dbe0dea4a | |||
5fc455fbad | |||
8a7e4d3a9e | |||
c10195f753 | |||
4d7317b964 | |||
7b1fa009bb | |||
71bb8a14e1 | |||
5444bc7fd8 | |||
debaf1da34 | |||
69e2ab9000 | |||
24218ed062 | |||
da4e18dc3c | |||
154a9b7a37 | |||
fb8189606b | |||
cfbfad27bd | |||
f541849306 | |||
d2d1891838 | |||
5580b6d59c | |||
40c45c30de | |||
1b6c5256cc | |||
c0e6759164 | |||
a096590a07 | |||
9883048fb5 | |||
7bec42c4cf | |||
6708dc0da9 | |||
96c2da0d05 | |||
2b19250e8b | |||
957eabcac7 |
302
changelog.md
302
changelog.md
@ -1,10 +1,298 @@
|
||||
==================================================================
|
||||
v0.9.2 - 05/09/2020
|
||||
# v10.7 - L'os de Sémolosse
|
||||
|
||||
Erreur de calcul sur points de vie
|
||||
Gestion différente des compétences "troncs"
|
||||
## v10.7.20 - la poigne de Sémolosse
|
||||
- correction de méthodes qui filtrent les items
|
||||
- recherche de cases TMR
|
||||
- recherche de tâches de lecture
|
||||
- recherche d'armure (pour le malus armure)
|
||||
- recherche de potions
|
||||
|
||||
==================================================================
|
||||
v0.9.1 - 03/09/2020
|
||||
## v10.7.20 - la poigne de Sémolosse
|
||||
- correction de l'empoignade
|
||||
- les items d'empoignade sont ajoutés par le MJ quand nécessaire
|
||||
- seul le joueur propriétaire du personnage peut effectuer ses choix et actions d'empoignade
|
||||
- les caractéristiques du défenseur sont utilisées pour la défense
|
||||
- la difficulté d'attaque est imposée au défenseur
|
||||
- les attaques particulières sont en finesse (p133)
|
||||
- on peut entraîner au sol dès 2 points d'empoignade
|
||||
- les actions liée à l'immobilisation sont proposées en fin de round
|
||||
|
||||
Initial official release
|
||||
## v10.7.19 - les fantômes de Sémolosse
|
||||
- les créatures ont maintenant le droit d'avoir des compétences de tir, lancer, mêlée, armes naturelles, parade.
|
||||
- les créatures armées utilisent la bonne phase d'initiative
|
||||
- correction des possessions
|
||||
- la difficulté de la défense est imposée par l'attaque
|
||||
- une attaque particulière de possession est en finesse
|
||||
- le rêve actuel des personnages est bien utilisé
|
||||
- correction des achats par le MJ sans acteur sélectionné
|
||||
|
||||
## v10.7.18 - le repos de Sémolosse
|
||||
- correction des dates de blessures qui ne marchaient plus
|
||||
|
||||
## v10.7.17 - le doigt du destin de Sémolosse
|
||||
- correction de la validation d'encaissement par le MJ
|
||||
|
||||
## v10.7.16 - la morsure de Sémolosse
|
||||
- correction de l'affichage des objets suite à confusion
|
||||
- correction de liens dans la liste des équipements
|
||||
|
||||
## v10.7.14 - l'expérience de Sémolosse
|
||||
- Affichage des personnages accordés sur les fiches des entités
|
||||
- Refonte du journal d'expérience
|
||||
- disponible pour les personnages des joueurs
|
||||
- explication "comptable" des changements (dépense ou ajout, changements de niveaux, ...)
|
||||
- tri alphabétique des différentes listes (sorts, recettes, oeuvres, ...)
|
||||
|
||||
## v10.7.13 - l'armure de Sémolosse
|
||||
- Fix: en cas d'armure variable, la détérioration diminue le dé d'armure
|
||||
|
||||
## v10.7.12
|
||||
- Fix: si le MJ gère les changements de jours, l'option "sieste" de la fenêtre de repos est prise par défaut si chateau dormant n'est pas passé
|
||||
|
||||
## v10.7.11 - Le Pugilat de Sémolosse
|
||||
- Fix sur la projection au sol.
|
||||
|
||||
## v10.7.10 - Le Pugilat de Sémolosse
|
||||
- Gestion de l'empoignade
|
||||
- Corrections sur l'initiative
|
||||
- Correction sur l'equipement des vêtements et bijoux
|
||||
|
||||
## v10.7.9 - Le Pugilat de Sémolosse
|
||||
|
||||
- Gestion assistée de l'empoignade
|
||||
1. On selectionne sa cible (ie le token qui va être empoigné)
|
||||
2. On lance une attaque avec l'"arme" _Empoignade_
|
||||
3. A ce stade, si la victime a une arme, on rappelle le point de règle d'engagement
|
||||
(page 134), et un bouton permet de confirmer l'empoignade
|
||||
4. L'empoigneur fait son jet
|
||||
5. Si réussite, l'empoigné peut se défendre, avec gestion du premier round d'engagement
|
||||
(ie Esquive autorisée ou pas)
|
||||
- 4 bis. et 5 bis. L'empoigné, à son tour, peut tenter de se libérer, toujours en cliquant sur l'action "Empoignade"
|
||||
6. Selon le résultat, incrément/décrément des points d'emp
|
||||
7. Retour en 4., ou si 2 points d'Emp, alors 8.
|
||||
8. Affichage des options disponibles pour l'empoigneur : perte d'endurance, projection au
|
||||
sol ou entrainer au sol. Ces 3 options sont gérées automatiquement ensuite, selon le
|
||||
bouton cliqué par l'empoigneur.
|
||||
|
||||
Les empoignades sont des "items" supprimées à la fin d'un combat, qui peuvent aussi être
|
||||
gérés par le MJ au cas ou. Hors combat, penser à les supprimer (ou commencer et
|
||||
arrêter un combat).
|
||||
|
||||
## v10.7.7 - Les bobos de Sémolosse
|
||||
- Mise à jour du texte de l'heure pour les joueurs
|
||||
- L'horloge n'empêche plus de sélectionner les tokens dessous
|
||||
- _Lecture & Détection d'Aura_ sous Hypnos sont des rituels
|
||||
- _Lire les étoiles_ pour les joueurs de nouveau fonctionnel
|
||||
- Ajout de logs pour comprendre un cas d'échec des achatVente
|
||||
|
||||
## v10.7.6 - L'origine des maux de Sémolosse
|
||||
- Calendrier
|
||||
- fix du ré-affichage de l'horloge qui ne marchait pas pour les joueurs
|
||||
- l'horloge ne se ferme plus sur Escape
|
||||
- amélioration d'affichage
|
||||
- couleurs jour/nuit plus marquées
|
||||
- Divers
|
||||
- correction de l'affichage de quantités diminuées d'herbes dans les contenants ouvert
|
||||
- ajout d'un bouton pour diminuer les quantités dans l'équipement (si quantité > 1)
|
||||
- ajout de la signature de l'acteur sur les blessures qu'il a causées
|
||||
|
||||
- Magie
|
||||
- correction des bonus de cases pour les sorts en Fleuve
|
||||
|
||||
## v10.7.5 - La montre-gousset de Sémolosse
|
||||
- Amélioration de la fenêtre calendrier
|
||||
* plus compacte
|
||||
* horloge analogique (optionnelle)
|
||||
* minimizable (juste la barre de titre)
|
||||
* normalement compatible pop-out
|
||||
|
||||
## v10.7.4 - Les ligatures de Sémolosse
|
||||
- Corrections diverses
|
||||
- Correction des boutons pour déclencher un sort en réserve avec réserve en sécurité ou réserve extensible
|
||||
- le lien pour les jets de vie suite à une blessure critique est remplacé par un bouton
|
||||
- déplacement des tâches et boutons de chirurgie dans l'onglet savoirs et tâches
|
||||
- correction de l'affichage des bonus de cases des sorts
|
||||
- corrections des queues non-refoulables dans le compendium
|
||||
|
||||
## v10.7.3 - Les tisanes de Sémolosse
|
||||
- Soins
|
||||
- on peut de nouveau boire une potion de soins enchantée
|
||||
- les potions non enchantées donnent de nouveau un bonus au prochain jet de récupération
|
||||
- Une fois les soins complets faits, le bonus aux soins complets fournis par les premiers soins est masqué
|
||||
|
||||
- Horloge
|
||||
- A l'heure de Couronne pile, les aiguilles des heures et des minutes pointent sur couronne (comme une montre) au lieu d'avoir l'aiguille des heures 15° à gauche
|
||||
|
||||
## v10.7.2 - les maux de dents de Sémolosse
|
||||
- correction des récupérations de blessures
|
||||
- la fin de château dormant se passe normalement
|
||||
|
||||
## v10.7.1 - L'os de Sémolosse
|
||||
- Fix rapide sur les jets de carac qui n'étaient plus possibles
|
||||
|
||||
## v10.7.0 - L'os de Sémolosse
|
||||
- gestion des blessures en items
|
||||
- soins du token ciblé par menu contextuel (comme le combat)
|
||||
- automatisation des soins et de l'affichage de l'avancement des soins
|
||||
- support des changements d'opérants
|
||||
|
||||
---
|
||||
# v10.6 - Les recherches de Pralinor le Goûteux
|
||||
|
||||
## v10.6.25 - Fix sur l'astrologie
|
||||
|
||||
## v10.6.22 - le nuage de lait dans le thé de Pralinor
|
||||
- Amélioration de l'affichage de l'horloge
|
||||
- Fix: affichage des points de guérison dans les potions
|
||||
|
||||
## v10.6.21 - La théière de Pralinor
|
||||
- Astrologie
|
||||
- le thème astral est directement dans la fenêtre d'astrologie
|
||||
- la roue des heures sert d'horloge
|
||||
- sélectionner un personnage ajuste le thème astral pour son heure de naissance
|
||||
- sélectionner le nombre astral d'un jour ajuste le thème astral
|
||||
|
||||
- Fix: les PNJs peuvent de nouveau dormir
|
||||
|
||||
## v10.6.20 - Les Oracles de Pralinor: vous mangerez à Couronne
|
||||
- Ajout de la fenêtre pour effectuer un thème astral
|
||||
|
||||
## v10.6.19 - La cerise de Pralinor
|
||||
- les joueurs peuvent chercher dans les commerces avec un droit limité/observateur
|
||||
- simplifications des fins de tours et nombre d'utilisations
|
||||
- ajout du _Haubert d'Oniros_ dans le compendium de sorts
|
||||
|
||||
## v10.6.17 - Les désordres de Pralinor
|
||||
- le contenu des casseroles et autres contenants est maintenant trié dans l'ordre alphabétique
|
||||
- les objets dupliqués du compendium d'équipement sont de nouveaux uniques
|
||||
|
||||
## v0.6.16 - Le pardon de Pralinor
|
||||
- Ajout d'un commerce _Liste d'équipement_ dans les archétypes de PNJs
|
||||
- Séparations d'équipements groupés et corrections de quelques objets & herbes
|
||||
- On peut éditer les armes stockées dans un commerce
|
||||
|
||||
## v10.6.15 - les digestifs de Pralinor
|
||||
- amélioration des messages de sommeil (nombre d'heure dormies, uniquement les
|
||||
récupérations de rêve en dessous du seuil, affichage de la récupération d'endurance
|
||||
qui avait disparu, meilleur message sur le jet de moral)
|
||||
- les insomnies ne durent bien que 12h draconique à partir du prochain
|
||||
Chateau Dormant (elles pouvaient durer 3 nuits suite à une erreur).
|
||||
- la recherche dans l'équipement affiche correctement les conteneurs dans lesquels les
|
||||
objets trouvés sont rangés
|
||||
|
||||
## v10.6.14 - la digestion de Pralinor
|
||||
- Chateau dormant
|
||||
- la situation du jet de moral peut être choisie lorsque l'on dort
|
||||
- les queues de dragon "insomnie" empêchent de dormir, et de rêver
|
||||
- ajout d'une option pour meilleure gestion de Chateau Dormant par le MJ
|
||||
- avec cette option, à la fin Chateau Dormant, une fenêtre permet au gardien de
|
||||
positionner pour chaque joueur:
|
||||
- le stress de la journée
|
||||
- les heures de sommeil
|
||||
- la situation du jet de moral (neutre/heureux/malheureux)
|
||||
|
||||
- l'affichage des heures Chateau Dormant et Poisson Acrobate est correct
|
||||
- le jet de moral en situation neutre fait maintenant retourner le moral vers 0, et
|
||||
n'affecte plus un moral à 0.
|
||||
## v10.6.13 - la cave de Pralinor
|
||||
- on peut maintenant chercher dans l'inventaire des commerces
|
||||
- l'inventaire est correctement affiché en entier après suppression de la recherche
|
||||
- le message de chateau dormant reflète correctement un jet de moral neutre qui passe le moral de 0 à +1
|
||||
|
||||
## v10.6.12 - l'index de Pralinor
|
||||
- On peut désormais chercher dans l'inventaire comme dans les compétences
|
||||
|
||||
## v10.6.11 - l'empoisonnement de Pralinor
|
||||
- La récupération est bloquée par les maladies. Pas de récupération de vie ou de blessures possibles sous l'effet d'un poison ou d'une maladie
|
||||
- ajout d'un "poison" pour bloquer la récupération sous Griffe Morbide de Thanatos.
|
||||
Ajout du lien vers l'objet du compendium dans la description MJ,, qui pourra donc
|
||||
ajouter ce "poison" à la victime pour empêcher ses guérisons de vie ou blessure.
|
||||
|
||||
## v10.6.10
|
||||
- Correction de l'édition des description
|
||||
- Amélioration des descriptions d'alchimie:
|
||||
- difficulté calculée automatiquement
|
||||
- Température pour les couleurs
|
||||
- La sustentation n'est plus concaténée dans certains cas (ce qui donnait 2+2=22)
|
||||
|
||||
## v10.6.8 : les bon mots de Pralinor
|
||||
- Dans la fenêtre de _recherche et tirages_, possibilité de chercher sur le nom des objets en plus des autres critères
|
||||
|
||||
## v10.6.7 : les grumelés de Pralinor
|
||||
- les objets peuvent être utilisés depuis la fenêtre d'un conteneur
|
||||
- dans les fenêtres de contenants, le contenu est correctement indenté
|
||||
- la présentation du contenu d'un sac est améliorée
|
||||
- le bouton Nouvel Objet n'est affiché que si on est propriétaire de l'acteur
|
||||
- la fenêtre de vente permet de nouveau de choisir les quantités à vendre
|
||||
|
||||
## v10.6.6
|
||||
- Corrections d'armes rudimentaires
|
||||
- Inversion: Taille puis poids
|
||||
- Suppression d'une ligne de caractéristique vide (causée par la beauté)
|
||||
- Les messages liés aux compétences troncs deviennent des notifications
|
||||
## v10.6.5
|
||||
- Le +dom est de nouveau affiché
|
||||
- L'édition de caractéristiques des créatures fonctionne de nouveau
|
||||
|
||||
## v10.6.4 - La sénilité de Pralinor
|
||||
- Fenêtre _Recherches et tirages_
|
||||
- les résultats de recherches sur plusieurs compendiums sont triés
|
||||
- lors de recherches avec un ou des milieux sélectionnés:
|
||||
- le filtre sur la rareté utilise la rareté dans ces milieux
|
||||
- les tirages se basent sur la fréquence la plus élevées dans ces milieux
|
||||
- les filtres par utilisation prennent les potions en compte
|
||||
- les remèdes ont une catégorie de potion 'Remède' (et correspondent à une utilisation médicale)
|
||||
- ajout d'un filtre d'utilisation 'cuisine'
|
||||
|
||||
|
||||
# Divers
|
||||
- fix du cas où la transformation de 0 points de stress était concaténé, (passage de 29 à 290 avec 0 points transformés)
|
||||
- suppression du compendium de taches courantes, désormais inutile
|
||||
|
||||
## v10.6.3 - le baba-brandevin de Pralinor
|
||||
- les tâches de Soins sont maintenant déplacées à côté des blessures
|
||||
- on peut créer les tâches de soins directement avec un bouton par gravité.
|
||||
|
||||
- le round n'est plus bloqué si un acteur est sonné
|
||||
- un rare cas d'initiative négative pouvait empêcher de faire une initiative (à cause de l'état général)
|
||||
- dans une circonstance inconnue, une rencontre pouvait disparaître lors de la maîtrise. Ajout d'un message pour essayer d'obtenir des détails sur ce cas, et ajout d'une sécurité pour retrouver la rencontre (qui est conservée par la fenêtre de choix d'action).
|
||||
- les objets temporels (queues, souffles, poisons, maladies...) créés avant la gestion temporelle ne pouvaient pas être édités.
|
||||
- les particulières sur les jets de résistance de rêve actuel ne rapportent qu'un point d'expérience (p191)
|
||||
- pour lutter contre l'alcoolisme, les jets d'éthylisme sont considérés comme des jets de résistance, et n'apportent qu'un point d'expérience.
|
||||
## v10.6.2 - Le méli-mélo de Pralinor
|
||||
- Fenêtre _Recherches et tirages_
|
||||
- support de la recherche dans les compendiums choisis
|
||||
- suppression des commandes `/table milieu` et `/tirer milieu` (remplacées par la fenêtre de recherche)
|
||||
- ajout de fréquences à tous les équipements
|
||||
## v10.6.1 - Les recherches de Pralinor
|
||||
- Fenêtre _Recherches et tirages_
|
||||
- Amélioration des filtres de cuisine/utilisation
|
||||
- Ajout de catégories pour les poisons, urtiquants, ...
|
||||
- Bouton "Effacer les filtres" plus clair
|
||||
- Drag&drop depuis la recherche
|
||||
- Reprise du compendium
|
||||
- pour les plantes vénéneuses
|
||||
- pour les plantes venimeuses
|
||||
- ajout de sust pour les champignons et autres plantes comestibles
|
||||
|
||||
- Affichage de l'image du token pour les commerces non liés
|
||||
- Les pièces d'or sont appelées 'Dragon'
|
||||
|
||||
## v10.6.0 - Les recherches de Pralinor le Goûteux
|
||||
- Fenêtre _Recherches et tirages_
|
||||
- ajout de la fenêtre _Recherches et tirages_ avec filtres paramétrables
|
||||
- ouverture de la fenêtre: commande `/tirage` ou macro disponible dans les macros du système
|
||||
- support des équipements, faune & flore (depuis les compendiums configurés par défaut)
|
||||
- nombreux choix à activer
|
||||
- possibilité de montrer les objets correspondant à la sélection
|
||||
- possibilité de faire un tirage parmi ces objets (en prenant en compte la fréquence)
|
||||
|
||||
- Plantes & pèche
|
||||
- séparation des ingrédients et plantes comestibles
|
||||
- retour des poissons dans les compendiums
|
||||
- ajout d'un lien depuis les plantes toxiques/dangereuses vers la maladie/poison correspondante
|
||||
|
||||
- On peut de nouveau ouvrir les conteneurs dans une fenêtre séparée
|
||||
- Les jets de volontés d'éthylisme calculent correctement la difficulté liée au moral (ie: 0 au lieu de -22)
|
||||
- si le journal de chronologie est supprimée, on peut en choisir un autre
|
||||
- la taille du calendrier est ajustée pour éviter une présentation bancale quand le nom du mois est court
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { RdDActorSheet } from "./actor-sheet.js";
|
||||
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
|
||||
import { RdDUtility } from "./rdd-utility.js";
|
||||
|
||||
export class RdDActorEntiteSheet extends RdDActorSheet {
|
||||
|
||||
@ -13,6 +15,12 @@ export class RdDActorEntiteSheet extends RdDActorSheet {
|
||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
|
||||
});
|
||||
}
|
||||
async getData() {
|
||||
let formData = await super.getData();
|
||||
formData.resonances = this.actor.system.sante.resonnance.actors.map(actorId => game.actors.get(actorId))
|
||||
.map(actor => { return { id: actor.id, name: actor.name, img: actor.img } })
|
||||
return formData
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** @override */
|
||||
@ -35,6 +43,23 @@ export class RdDActorEntiteSheet extends RdDActorSheet {
|
||||
let compName = event.currentTarget.attributes.compname.value;
|
||||
this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value));
|
||||
});
|
||||
this.html.find('.resonance-delete').click(async event => {
|
||||
const li = RdDSheetUtility.getEventElement(event);
|
||||
const actorId = li.data("actor-id");
|
||||
if (actorId) {
|
||||
const actorResonance = game.actors.get(actorId);
|
||||
RdDUtility.confirmerSuppressionSubacteur(this, actorResonance, li, () => {
|
||||
console.log('Delete : ', actorId);
|
||||
this.removeSubacteur(actorId);
|
||||
RdDUtility.slideOnDelete(this, li);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async removeSubacteur(actorId) {
|
||||
let newResonances = this.actor.system.sante.resonnance.actors.filter(id => id != actorId);
|
||||
await this.actor.update({ 'system.sante.resonnance.actors': newResonances }, { renderSheet: false });
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import { MAINS_DIRECTRICES } from "./actor.js";
|
||||
import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js";
|
||||
import { RdDItem } from "./item.js";
|
||||
import { RdDItemBlessure } from "./item/blessure.js";
|
||||
import { RdDEmpoignade } from "./rdd-empoignade.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
@ -83,6 +84,7 @@ export class RdDActorSheet extends RdDBaseActorSheet {
|
||||
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac);
|
||||
formData.esquives = this.actor.getCompetences("Esquive");
|
||||
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
|
||||
formData.empoignades = this.actor.getEmpoignades();
|
||||
|
||||
this.armesList = formData.combat;
|
||||
|
||||
@ -127,7 +129,11 @@ export class RdDActorSheet extends RdDBaseActorSheet {
|
||||
const actorId = li.data("actor-id");
|
||||
if (actorId) {
|
||||
const subActor = game.actors.get(actorId);
|
||||
RdDUtility.confirmerSuppressionSubacteur(this, subActor, li);
|
||||
RdDUtility.confirmerSuppressionSubacteur(this, subActor, li, () => {
|
||||
console.log('Delete : ', subActor.id);
|
||||
this.actor.removeSubacteur(subActor.id);
|
||||
RdDUtility.slideOnDelete(this, li);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.html.find('.experiencelog-delete').click(async event => {
|
||||
@ -140,6 +146,13 @@ export class RdDActorSheet extends RdDBaseActorSheet {
|
||||
const key = Number(li.data("key") ?? -1);
|
||||
await this.actor.deleteExperienceLog(0, key + 1);
|
||||
});
|
||||
this.html.find("input.derivee-value[name='system.compteurs.stress.value']").change(async event => {
|
||||
this.actor.updateCompteurValue("stress", parseInt(event.target.value));
|
||||
});
|
||||
this.html.find("input.derivee-value[name='system.compteurs.experience.value']").change(async event => {
|
||||
this.actor.updateCompteurValue("experience", parseInt(event.target.value));
|
||||
});
|
||||
|
||||
this.html.find('.encaisser-direct').click(async event => {
|
||||
this.actor.encaisser();
|
||||
})
|
||||
@ -254,6 +267,11 @@ export class RdDActorSheet extends RdDBaseActorSheet {
|
||||
this.actor.rollCarac('reve-actuel', true);
|
||||
});
|
||||
|
||||
// Suite empoignade
|
||||
this.html.find('.empoignade-label a').click(async event => {
|
||||
let emp = RdDSheetUtility.getItem(event, this.actor)
|
||||
RdDEmpoignade.onAttaqueEmpoignadeFromItem(emp)
|
||||
});
|
||||
// Roll Weapon1
|
||||
this.html.find('.arme-label a').click(async event => {
|
||||
let arme = this._getEventArmeCombat(event);
|
||||
@ -438,9 +456,9 @@ export class RdDActorSheet extends RdDBaseActorSheet {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async selectTypeOeuvreToCreate() {
|
||||
let typeObjets = RdDItem.getTypesOeuvres();
|
||||
let types = RdDItem.getTypesOeuvres();
|
||||
let content = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
|
||||
for (let typeName of typeObjets) {
|
||||
for (let typeName of types) {
|
||||
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
|
||||
}
|
||||
content += '</select>';
|
||||
|
446
module/actor.js
446
module/actor.js
@ -36,13 +36,16 @@ import { RdDBaseActor } from "./actor/base-actor.js";
|
||||
import { RdDTimestamp } from "./time/rdd-timestamp.js";
|
||||
import { RdDItemBlessure } from "./item/blessure.js";
|
||||
import { AppAstrologie } from "./sommeil/app-astrologie.js";
|
||||
import { RdDEmpoignade } from "./rdd-empoignade.js";
|
||||
import { ExperienceLog, XP_TOPIC } from "./actor/experience-log.js";
|
||||
import { TYPES } from "./item.js";
|
||||
|
||||
const POSSESSION_SANS_DRACONIC = {
|
||||
img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp',
|
||||
name: 'Sans draconic',
|
||||
system: {
|
||||
niveau: 0,
|
||||
defaut_carac: "reve",
|
||||
defaut_carac: "reve-actuel",
|
||||
}
|
||||
};
|
||||
|
||||
@ -97,7 +100,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async cleanupConteneurs() {
|
||||
let updates = this.listItems('conteneur')
|
||||
let updates = this.itemTypes['conteneur']
|
||||
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
|
||||
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
|
||||
if (updates.length > 0) {
|
||||
@ -107,7 +110,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
canReceive(item) {
|
||||
if (this.isCreature()) {
|
||||
return item.type == 'competencecreature' || item.isInventaire();
|
||||
return item.type == TYPES.competencecreature || item.isInventaire();
|
||||
}
|
||||
if (this.isEntite()) {
|
||||
return item.type == 'competencecreature';
|
||||
@ -231,7 +234,6 @@ export class RdDActor extends RdDBaseActor {
|
||||
getCompetences(name) {
|
||||
return RdDItemCompetence.findCompetences(this.items, name)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getTache(id) {
|
||||
return this.findItemLike(id, 'tache');
|
||||
@ -269,31 +271,34 @@ export class RdDActor extends RdDBaseActor {
|
||||
}
|
||||
|
||||
getDraconicOuPossession() {
|
||||
const possessions = this.items.filter(it => it.type == 'competencecreature' && it.system.ispossession)
|
||||
.sort(Misc.descending(it => it.system.niveau));
|
||||
if (possessions.length > 0) {
|
||||
return duplicate(possessions[0]);
|
||||
const possession = this.items.filter(it => it.type == TYPES.competencecreature && it.system.categorie == 'possession')
|
||||
.sort(Misc.descending(it => it.system.niveau))
|
||||
.find(it => true);
|
||||
if (possession) {
|
||||
return possession.clone();
|
||||
}
|
||||
const draconics = [...this.getDraconicList().filter(it => it.system.niveau >= 0),
|
||||
const draconics = [...this.getDraconicList().filter(it => it.system.niveau >= 0).map(it => it.clone()),
|
||||
POSSESSION_SANS_DRACONIC]
|
||||
.sort(Misc.descending(it => it.system.niveau));
|
||||
return duplicate(draconics[0]);
|
||||
return draconics[0];
|
||||
}
|
||||
|
||||
getPossession(possessionId) {
|
||||
return this.items.find(it => it.type == 'possession' && it.system.possessionid == possessionId);
|
||||
return this.items.find(it => it.type == TYPES.possession && it.system.possessionid == possessionId);
|
||||
}
|
||||
getPossessions() {
|
||||
return this.items.filter(it => it.type == 'possession');
|
||||
return this.items.filter(it => it.type == TYPES.possession);
|
||||
}
|
||||
getEmpoignades() {
|
||||
return this.items.filter(it => it.type == TYPES.empoignade);
|
||||
}
|
||||
|
||||
getDemiReve() {
|
||||
return this.system.reve.tmrpos.coord;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async verifierPotionsEnchantees() {
|
||||
let potionsEnchantees = this.filterItems(it => it.type == 'potion' && it.system.categorie.toLowerCase().includes('enchant'));
|
||||
let potionsEnchantees = this.filterItems(it => it.system.categorie.toLowerCase().includes('enchant'), 'potion');
|
||||
for (let potion of potionsEnchantees) {
|
||||
if (!potion.system.prpermanent) {
|
||||
console.log(potion);
|
||||
@ -329,8 +334,15 @@ export class RdDActor extends RdDBaseActor {
|
||||
return '';
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
hasArmeeMeleeEquipee() { // Return true si l'acteur possède au moins 1 arme de mêlée équipée
|
||||
return this.itemTypes['arme'].find(it => it.system.equipe && it.system.competence != "")
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async roll() {
|
||||
RdDEmpoignade.checkEmpoignadeEnCours(this)
|
||||
|
||||
const carac = mergeObject(duplicate(this.system.carac),
|
||||
{
|
||||
'reve-actuel': this.getCaracReveActuel(),
|
||||
@ -768,7 +780,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
async combattreReveDeDragon(force) {
|
||||
let rollData = {
|
||||
actor: this,
|
||||
competence: duplicate(this.getDraconicOuPossession()),
|
||||
competence: this.getDraconicOuPossession(),
|
||||
canClose: false,
|
||||
rencontre: await game.system.rdd.rencontresTMR.getReveDeDragon(force),
|
||||
tmr: true,
|
||||
@ -812,31 +824,40 @@ export class RdDActor extends RdDBaseActor {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCarac(caracName, caracValue) {
|
||||
async updateCarac(caracName, to) {
|
||||
if (caracName == "force") {
|
||||
if (Number(caracValue) > this.getTaille() + 4) {
|
||||
if (Number(to) > this.getTaille() + 4) {
|
||||
ui.notifications.warn("Votre FORCE doit être au maximum de TAILLE+4");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (caracName == "reve") {
|
||||
if (caracValue > Misc.toInt(this.system.reve.seuil.value)) {
|
||||
this.setPointsDeSeuil(caracValue);
|
||||
if (to > Misc.toInt(this.system.reve.seuil.value)) {
|
||||
this.setPointsDeSeuil(to);
|
||||
}
|
||||
}
|
||||
if (caracName == "chance") {
|
||||
if (caracValue > Misc.toInt(this.system.compteurs.chance.value)) {
|
||||
this.setPointsDeChance(caracValue);
|
||||
if (to > Misc.toInt(this.system.compteurs.chance.value)) {
|
||||
this.setPointsDeChance(to);
|
||||
}
|
||||
}
|
||||
await this.update({ [`system.carac.${caracName}.value`]: caracValue });
|
||||
let selectedCarac = RdDActor._findCaracByName(this.system.carac, caracName);
|
||||
const from = selectedCarac.value
|
||||
await this.update({ [`system.carac.${caracName}.value`]: to });
|
||||
await ExperienceLog.add(this, XP_TOPIC.CARAC, from, to, caracName);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCaracXP(caracName, caracXP) {
|
||||
async updateCaracXP(caracName, to) {
|
||||
if (caracName == 'Taille') {
|
||||
return;
|
||||
}
|
||||
let selectedCarac = RdDActor._findCaracByName(this.system.carac, caracName);
|
||||
if (!selectedCarac.derivee) {
|
||||
const from = Number(selectedCarac.xp);
|
||||
await this.update({ [`system.carac.${caracName}.xp`]: to });
|
||||
await ExperienceLog.add(this, XP_TOPIC.XPCARAC, from, to, caracName);
|
||||
}
|
||||
this.checkCaracXP(caracName);
|
||||
}
|
||||
|
||||
@ -848,16 +869,19 @@ export class RdDActor extends RdDBaseActor {
|
||||
let carac = RdDActor._findCaracByName(this.system.carac, caracName);
|
||||
if (carac) {
|
||||
carac = duplicate(carac);
|
||||
let xp = Number(carac.xp);
|
||||
let value = Number(carac.value);
|
||||
while (xp >= RdDCarac.getCaracNextXp(value) && xp > 0) {
|
||||
xp -= RdDCarac.getCaracNextXp(value);
|
||||
value++;
|
||||
const fromXp = Number(carac.xp);
|
||||
const fromValue = Number(carac.value);
|
||||
let toXp = fromXp;
|
||||
let toValue = fromValue;
|
||||
while (toXp >= RdDCarac.getCaracNextXp(toValue) && toXp > 0) {
|
||||
toXp -= RdDCarac.getCaracNextXp(toValue);
|
||||
toValue++;
|
||||
}
|
||||
carac.xp = xp;
|
||||
carac.value = value;
|
||||
carac.xp = toXp;
|
||||
carac.value = toValue;
|
||||
await this.update({ [`system.carac.${caracName}`]: carac });
|
||||
this.updateExperienceLog("Carac +", xp, caracName + " passée à " + value);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XPCARAC, fromXp, toXp, caracName);
|
||||
await ExperienceLog.add(this, XP_TOPIC.CARAC, fromValue, toValue, caracName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -865,17 +889,20 @@ export class RdDActor extends RdDBaseActor {
|
||||
async updateCompetenceXPAuto(idOrName) {
|
||||
let competence = this.getCompetence(idOrName);
|
||||
if (competence) {
|
||||
let xp = Number(competence.system.xp);
|
||||
let niveau = Number(competence.system.niveau);
|
||||
while (xp >= RdDItemCompetence.getCompetenceNextXp(niveau) && xp > 0) {
|
||||
xp -= RdDItemCompetence.getCompetenceNextXp(niveau);
|
||||
niveau++;
|
||||
const fromXp = Number(competence.system.xp);
|
||||
const fromNiveau = Number(competence.system.niveau);
|
||||
let toXp = fromXp;
|
||||
let toNiveau = fromNiveau;
|
||||
while (toXp >= RdDItemCompetence.getCompetenceNextXp(toNiveau) && toXp > 0) {
|
||||
toXp -= RdDItemCompetence.getCompetenceNextXp(toNiveau);
|
||||
toNiveau++;
|
||||
}
|
||||
await competence.update({
|
||||
"system.xp": xp,
|
||||
"system.niveau": niveau,
|
||||
"system.xp": toXp,
|
||||
"system.niveau": toNiveau,
|
||||
});
|
||||
this.updateExperienceLog("Compétence +", xp, competence.name + " passée à " + niveau);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XP, fromXp, toXp, competence.name);
|
||||
await ExperienceLog.add(this, XP_TOPIC.NIVEAU, fromNiveau, toNiveau, competence.name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,29 +911,33 @@ export class RdDActor extends RdDBaseActor {
|
||||
if (!competence) {
|
||||
return;
|
||||
}
|
||||
const stress = this.system.compteurs.experience.value;
|
||||
const niveau = Number(competence.system.niveau);
|
||||
const xpSuivant = RdDItemCompetence.getCompetenceNextXp(niveau);
|
||||
const xpRequis = xpSuivant - competence.system.xp;
|
||||
if (stress <= 0 || niveau >= competence.system.niveau_archetype) {
|
||||
const fromXp = competence.system.xp;
|
||||
const fromXpStress = this.system.compteurs.experience.value;
|
||||
const fromNiveau = Number(competence.system.niveau);
|
||||
const xpSuivant = RdDItemCompetence.getCompetenceNextXp(fromNiveau);
|
||||
const xpRequis = xpSuivant - fromXp;
|
||||
if (fromXpStress <= 0 || fromNiveau >= competence.system.niveau_archetype) {
|
||||
ui.notifications.info(`La compétence ne peut pas augmenter!
|
||||
stress disponible: ${stress}
|
||||
stress disponible: ${fromXpStress}
|
||||
expérience requise: ${xpRequis}
|
||||
niveau : ${niveau}
|
||||
niveau : ${fromNiveau}
|
||||
archétype : ${competence.system.niveau_archetype}`);
|
||||
return;
|
||||
}
|
||||
const xpUtilise = Math.max(0, Math.min(stress, xpRequis));
|
||||
const xpUtilise = Math.max(0, Math.min(fromXpStress, xpRequis));
|
||||
const gainNiveau = (xpUtilise >= xpRequis || xpRequis <= 0) ? 1 : 0;
|
||||
const nouveauNiveau = niveau + gainNiveau;
|
||||
const nouveauXp = gainNiveau > 0 ? Math.max(competence.system.xp - xpSuivant, 0) : (competence.system.xp + xpUtilise);
|
||||
const toNiveau = fromNiveau + gainNiveau;
|
||||
const newXp = gainNiveau > 0 ? Math.max(fromXp - xpSuivant, 0) : (fromXp + xpUtilise);
|
||||
await competence.update({
|
||||
"system.xp": nouveauXp,
|
||||
"system.niveau": nouveauNiveau,
|
||||
"system.xp": newXp,
|
||||
"system.niveau": toNiveau,
|
||||
});
|
||||
const stressTransformeRestant = Math.max(0, stress - xpUtilise);
|
||||
await this.update({ "system.compteurs.experience.value": stressTransformeRestant });
|
||||
this.updateExperienceLog('Dépense stress', xpUtilise, `Stress en ${competence.name} ${gainNiveau ? "pour passer à " + nouveauNiveau : ""}`);
|
||||
const toXpStress = Math.max(0, fromXpStress - xpUtilise);
|
||||
await this.update({ "system.compteurs.experience.value": toXpStress });
|
||||
|
||||
await ExperienceLog.add(this, XP_TOPIC.TRANSFORM, fromXpStress, toXpStress, `Dépense stress`);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XP, fromXp, newXp, competence.name);
|
||||
await ExperienceLog.add(this, XP_TOPIC.NIVEAU, fromNiveau, toNiveau, competence.name);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -930,51 +961,59 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCompetence(idOrName, compValue) {
|
||||
const competence = this.getCompetence(idOrName);
|
||||
if (competence) {
|
||||
const toNiveau = compValue ?? RdDItemCompetence.getNiveauBase(competence.system.categorie, competence.getCategories());
|
||||
this.notifyCompetencesTronc(competence, toNiveau);
|
||||
const fromNiveau = competence.system.niveau;
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: competence.id, 'system.niveau': toNiveau }]);
|
||||
await ExperienceLog.add(this, XP_TOPIC.NIVEAU, fromNiveau, toNiveau, competence.name, true);
|
||||
} else {
|
||||
console.log("Competence not found", idOrName);
|
||||
}
|
||||
}
|
||||
|
||||
notifyCompetencesTronc(competence, toNiveau) {
|
||||
const listTronc = RdDItemCompetence.getListTronc(competence.name).filter(it => {
|
||||
const autreComp = this.getCompetence(it);
|
||||
const niveauTr = autreComp?.system.niveau ?? 0;
|
||||
return niveauTr < 0 && niveauTr < toNiveau;
|
||||
});
|
||||
if (listTronc.length > 0) {
|
||||
ui.notifications.info(
|
||||
"Vous avez modifié une compétence 'tronc'. Vérifiez que les compétences suivantes évoluent ensemble jusqu'au niveau 0 : <br>"
|
||||
+ Misc.join(listTronc, '<br>'));
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCompetenceXP(idOrName, toXp) {
|
||||
let competence = this.getCompetence(idOrName);
|
||||
if (competence) {
|
||||
let nouveauNiveau = compValue ?? RdDItemCompetence.getNiveauBase(competence.system.categorie);
|
||||
const tronc = RdDItemCompetence.getListTronc(competence.name).filter(it => {
|
||||
const comp = this.getCompetence(it);
|
||||
const niveauTr = competence ? competence.system.niveau : 0;
|
||||
return niveauTr < 0 && niveauTr < nouveauNiveau;
|
||||
});
|
||||
if (tronc.length > 0) {
|
||||
let message = "Vous avez modifié une compétence 'tronc'. Vérifiez que les compétences suivantes évoluent ensemble jusqu'au niveau 0 : ";
|
||||
for (let troncName of tronc) {
|
||||
message += "<br>" + troncName;
|
||||
}
|
||||
ui.notifications.info(message);
|
||||
if (isNaN(toXp) || typeof (toXp) != 'number') toXp = 0;
|
||||
const fromXp = competence.system.xp;
|
||||
this.checkCompetenceXP(idOrName, toXp);
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: competence.id, 'system.xp': toXp }]);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XP, fromXp, toXp, competence.name, true);
|
||||
if (toXp > fromXp) {
|
||||
RdDUtility.checkThanatosXP(idOrName);
|
||||
}
|
||||
const update = { _id: competence.id, 'system.niveau': nouveauNiveau };
|
||||
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
|
||||
} else {
|
||||
console.log("Competence not found", idOrName);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCompetenceXP(idOrName, newXp) {
|
||||
async updateCompetenceXPSort(idOrName, toXpSort) {
|
||||
let competence = this.getCompetence(idOrName);
|
||||
if (competence) {
|
||||
if (isNaN(newXp) || typeof (newXp) != 'number') newXp = 0;
|
||||
this.checkCompetenceXP(idOrName, newXp);
|
||||
const update = { _id: competence.id, 'system.xp': newXp };
|
||||
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
|
||||
this.updateExperienceLog("XP", newXp, "XP modifié en " + competence.name);
|
||||
} else {
|
||||
console.log("Competence not found", idOrName);
|
||||
}
|
||||
RdDUtility.checkThanatosXP(idOrName);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCompetenceXPSort(idOrName, compValue) {
|
||||
let competence = this.getCompetence(idOrName);
|
||||
if (competence) {
|
||||
if (isNaN(compValue) || typeof (compValue) != 'number') compValue = 0;
|
||||
const update = { _id: competence.id, 'system.xp_sort': compValue };
|
||||
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
|
||||
this.updateExperienceLog("XP Sort", compValue, "XP modifié en sort de " + competence.name);
|
||||
if (isNaN(toXpSort) || typeof (toXpSort) != 'number') toXpSort = 0;
|
||||
const fromXpSort = competence.system.xp_sort;
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: competence.id, 'system.xp_sort': toXpSort }]);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XPSORT, fromXpSort, toXpSort, competence.name, true);
|
||||
if (toXpSort > fromXpSort) {
|
||||
RdDUtility.checkThanatosXP(idOrName);
|
||||
}
|
||||
} else {
|
||||
console.log("Competence not found", idOrName);
|
||||
}
|
||||
@ -984,26 +1023,12 @@ export class RdDActor extends RdDBaseActor {
|
||||
async updateCompetenceArchetype(idOrName, compValue) {
|
||||
let competence = this.getCompetence(idOrName);
|
||||
if (competence) {
|
||||
compValue = compValue ?? 0;
|
||||
const update = { _id: competence.id, 'system.niveau_archetype': compValue };
|
||||
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: competence.id, 'system.niveau_archetype': Math.max(compValue ?? 0, 0) }]);
|
||||
} else {
|
||||
console.log("Competence not found", idOrName);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateExperienceLog(modeXP, valeurXP, raisonXP = 'Inconnue') {
|
||||
let d = new Date();
|
||||
let expLog = duplicate(this.system.experiencelog);
|
||||
expLog.push({
|
||||
mode: Misc.upperFirst(modeXP), valeur: valeurXP, raison: Misc.upperFirst(raisonXP),
|
||||
daterdd: game.system.rdd.calendrier.dateCourante(),
|
||||
datereel: `${d.getDate()}/${d.getMonth() + 1}/${d.getFullYear()}`
|
||||
});
|
||||
await this.update({ [`system.experiencelog`]: expLog });
|
||||
}
|
||||
|
||||
async deleteExperienceLog(from, count) {
|
||||
if (from >= 0 && count > 0) {
|
||||
let expLog = duplicate(this.system.experiencelog);
|
||||
@ -1012,24 +1037,27 @@ export class RdDActor extends RdDBaseActor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async updateCompteurValue(fieldName, fieldValue, raison = 'Inconnue') {
|
||||
await this.update({ [`system.compteurs.${fieldName}.value`]: fieldValue });
|
||||
await this.addStressExperienceLog(fieldName, fieldValue, 'forcé: ' + raison);
|
||||
async updateCompteurValue(fieldName, to) {
|
||||
const from = this.system.compteurs[fieldName].value
|
||||
await this.update({ [`system.compteurs.${fieldName}.value`]: to });
|
||||
await this.addStressExperienceLog(fieldName, from, to, fieldName, true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async addCompteurValue(fieldName, fieldValue, raison = 'Inconnue') {
|
||||
let oldValue = this.system.compteurs[fieldName].value;
|
||||
await this.update({ [`system.compteurs.${fieldName}.value`]: Number(oldValue) + Number(fieldValue) });
|
||||
await this.addStressExperienceLog(fieldName, fieldValue, raison);
|
||||
async addCompteurValue(fieldName, add, raison) {
|
||||
let from = this.system.compteurs[fieldName].value;
|
||||
const to = Number(from) + Number(add);
|
||||
await this.update({ [`system.compteurs.${fieldName}.value`]: to });
|
||||
await this.addStressExperienceLog(fieldName, from, to, raison);
|
||||
}
|
||||
|
||||
async addStressExperienceLog(fieldName, fieldValue, raison) {
|
||||
switch (fieldName) {
|
||||
case 'stress': case 'experience':
|
||||
await this.updateExperienceLog(fieldName, fieldValue, raison);
|
||||
async addStressExperienceLog(topic, from, to, raison, manuel) {
|
||||
switch (topic) {
|
||||
case 'stress':
|
||||
return await ExperienceLog.add(this, XP_TOPIC.STRESS, from, to, raison, manuel)
|
||||
case 'experience':
|
||||
return await ExperienceLog.add(this, XP_TOPIC.TRANSFORM, from, to, raison, manuel)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1086,21 +1114,17 @@ export class RdDActor extends RdDBaseActor {
|
||||
/* -------------------------------------------- */
|
||||
computeIsHautRevant() {
|
||||
if (this.isPersonnage()) {
|
||||
this.system.attributs.hautrevant.value = this.hasItemNamed('tete', 'don de haut-reve')
|
||||
this.system.attributs.hautrevant.value = this.itemTypes['tete'].find(it => Grammar.equalsInsensitive(it.name, 'don de haut-reve'))
|
||||
? "Haut rêvant"
|
||||
: "";
|
||||
}
|
||||
}
|
||||
|
||||
hasItemNamed(type, name) {
|
||||
name = Grammar.toLowerCaseNoAccent(name);
|
||||
return this.listItems(type).find(it => Grammar.toLowerCaseNoAccent(it.name) == name);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async computeMalusArmure() {
|
||||
if (this.isPersonnage()) {
|
||||
const malusArmure = this.filterItems(it => it.type == 'armure' && it.system.equipe)
|
||||
const malusArmure = this.filterItems(it => it.system.equipe, 'armure')
|
||||
.map(it => it.system.malus ?? 0)
|
||||
.reduce(Misc.sum(), 0);
|
||||
// Mise à jour éventuelle du malus armure
|
||||
@ -1273,8 +1297,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
buildTMRInnaccessible() {
|
||||
const tmrInnaccessibles = this.filterItems(it => Draconique.isCaseTMR(it) &&
|
||||
EffetsDraconiques.isInnaccessible(it));
|
||||
const tmrInnaccessibles = this.filterItems(it => Draconique.isCaseTMR(it) && EffetsDraconiques.isInnaccessible(it));
|
||||
return tmrInnaccessibles.map(it => it.system.coord);
|
||||
}
|
||||
|
||||
@ -1338,21 +1361,46 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async finDeRound(options = { terminer: false }) {
|
||||
await this.$finDeRoundSuppressionEffetsTermines(options);
|
||||
await this.$finDeRoundBlessuresGraves();
|
||||
await this.$finDeRoundSupprimerObsoletes();
|
||||
await this.$finDeRoundEmpoignade();
|
||||
}
|
||||
|
||||
async $finDeRoundSuppressionEffetsTermines(options) {
|
||||
for (let effect of this.getEffects()) {
|
||||
if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) {
|
||||
await effect.delete();
|
||||
ChatMessage.create({ content: `${this.name} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async $finDeRoundBlessuresGraves() {
|
||||
if (this.isPersonnage() || this.isCreature()) {
|
||||
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length
|
||||
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length;
|
||||
if (nbGraves > 0) {
|
||||
// Gestion blessure graves : -1 pt endurance par blessure grave
|
||||
await this.santeIncDec("endurance", - nbGraves);
|
||||
await this.santeIncDec("endurance", -nbGraves);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async $finDeRoundSupprimerObsoletes() {
|
||||
const obsoletes = []
|
||||
.concat(this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
|
||||
.concat(this.itemTypes[TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
|
||||
.map(it => it.id);
|
||||
await this.deleteEmbeddedDocuments('Item', obsoletes);
|
||||
}
|
||||
|
||||
async $finDeRoundEmpoignade(){
|
||||
const immobilisations = this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
|
||||
immobilisations.forEach(emp => RdDEmpoignade.onImmobilisation(this,
|
||||
game.actors.get(emp.system.empoigneid),
|
||||
emp
|
||||
))
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
async setSonne(sonne = true) {
|
||||
if (this.isEntite()) {
|
||||
@ -1657,9 +1705,9 @@ export class RdDActor extends RdDBaseActor {
|
||||
await this.rollTache(tache.id);
|
||||
}
|
||||
}
|
||||
async actionHerbe(item, onActionItem = async () => {}) {
|
||||
async actionHerbe(item, onActionItem = async () => { }) {
|
||||
if (item.isHerbeAPotion()) {
|
||||
return DialogFabriquerPotion.create(this, item, onActionItem);
|
||||
return DialogFabriquerPotion.create(this, item, onActionItem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1838,14 +1886,14 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async transformerStress() {
|
||||
const stress = Number(this.system.compteurs.stress.value);
|
||||
if (this.system.sommeil?.insomnie || stress <= 0) {
|
||||
const fromStress = Number(this.system.compteurs.stress.value);
|
||||
if (this.system.sommeil?.insomnie || fromStress <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stressRoll = await this._stressRoll(this.getReveActuel());
|
||||
|
||||
const conversion = Math.floor(stress * stressRoll.factor / 100);
|
||||
const conversion = Math.floor(fromStress * stressRoll.factor / 100);
|
||||
let dissolution = Math.max(0, Number(this.system.compteurs.dissolution.value));
|
||||
let exaltation = Math.max(0, Number(this.system.compteurs.exaltation.value));
|
||||
const annule = Math.min(dissolution, exaltation);
|
||||
@ -1857,8 +1905,8 @@ export class RdDActor extends RdDBaseActor {
|
||||
alias: this.name,
|
||||
selectedCarac: this.system.carac.reve,
|
||||
rolled: stressRoll,
|
||||
stress: stress,
|
||||
perte: Math.min(conversion, stress),
|
||||
stress: fromStress,
|
||||
perte: Math.min(conversion, fromStress),
|
||||
convertis: conversion - perteDissolution,
|
||||
xp: conversion - perteDissolution + exaltation,
|
||||
dissolution: dissolution,
|
||||
@ -1870,15 +1918,18 @@ export class RdDActor extends RdDBaseActor {
|
||||
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-transformer-stress.html`, stressRollData)
|
||||
});
|
||||
|
||||
const toStress = Math.max(fromStress - stressRollData.perte - 1, 0);
|
||||
const fromXpSress = Number(this.system.compteurs.experience.value);
|
||||
const toXpStress = fromXpSress + Number(stressRollData.xp);
|
||||
const updates = {
|
||||
"system.compteurs.stress.value": Math.max(stress - stressRollData.perte - 1, 0),
|
||||
"system.compteurs.experience.value": Number(this.system.compteurs.experience.value) + Number(stressRollData.xp),
|
||||
"system.compteurs.stress.value": toStress,
|
||||
"system.compteurs.experience.value": toXpStress,
|
||||
"system.compteurs.dissolution.value": dissolution - perteDissolution,
|
||||
"system.compteurs.exaltation.value": 0
|
||||
}
|
||||
await this.update(updates);
|
||||
|
||||
this.updateExperienceLog('XP', stressRollData.xp, "Transformation du stress");
|
||||
await ExperienceLog.add(this, XP_TOPIC.STRESS, fromStress, toStress, 'Transformation')
|
||||
await ExperienceLog.add(this, XP_TOPIC.TRANSFORM, fromXpSress, toXpStress, 'Transformation')
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -2061,12 +2112,13 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollUnSort(coord) {
|
||||
RdDEmpoignade.checkEmpoignadeEnCours(this)
|
||||
if (EffetsDraconiques.isSortImpossible(this)) {
|
||||
ui.notifications.error("Une queue ou un souffle vous empèche de lancer de sort!");
|
||||
return;
|
||||
}
|
||||
// Duplication car les pts de reve sont modifiés dans le sort
|
||||
let sorts = duplicate(this.$filterSortList(this.getSortList(), coord));
|
||||
let sorts = duplicate(this.$filterSortList(this.itemTypes['sort'], coord));
|
||||
if (sorts.length == 0) {
|
||||
ui.notifications.info(`Aucun sort disponible en ${TMRUtility.getTMR(coord).label} !`);
|
||||
return;
|
||||
@ -2200,6 +2252,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollCarac(caracName, jetResistance = undefined) {
|
||||
RdDEmpoignade.checkEmpoignadeEnCours(this)
|
||||
let selectedCarac = this.getCaracByName(caracName)
|
||||
await this._openRollDialog({
|
||||
name: 'jet-' + caracName,
|
||||
@ -2267,18 +2320,19 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollCompetence(idOrName, options = { tryTarget: true }) {
|
||||
RdDEmpoignade.checkEmpoignadeEnCours(this)
|
||||
let rollData = {
|
||||
carac: this.system.carac,
|
||||
competence: this.getCompetence(idOrName)
|
||||
}
|
||||
if (rollData.competence.type == 'competencecreature') {
|
||||
if (rollData.competence.system.iscombat && options.tryTarget && Targets.hasTargets()) {
|
||||
if (rollData.competence.type == TYPES.competencecreature) {
|
||||
const arme = RdDItemCompetenceCreature.armeCreature(rollData.competence)
|
||||
if (arme && options.tryTarget && Targets.hasTargets()) {
|
||||
Targets.selectOneToken(target => {
|
||||
if (rollData.competence.system.ispossession) {
|
||||
if (arme.action == "possession") {
|
||||
RdDPossession.onAttaquePossession(target, this, rollData.competence)
|
||||
}
|
||||
else {
|
||||
const arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence)
|
||||
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
|
||||
}
|
||||
});
|
||||
@ -2308,8 +2362,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
/* -------------------------------------------- */
|
||||
async creerTacheDepuisLivre(item, options = { renderSheet: true }) {
|
||||
const nomTache = "Lire " + item.name;
|
||||
const filterTacheLecture = it => it.type == 'tache' && it.name == nomTache;
|
||||
let tachesExistantes = this.filterItems(filterTacheLecture);
|
||||
let tachesExistantes = findTache(nomTache);
|
||||
if (tachesExistantes.length == 0) {
|
||||
const tache = {
|
||||
name: nomTache, type: 'tache',
|
||||
@ -2325,20 +2378,24 @@ export class RdDActor extends RdDBaseActor {
|
||||
}
|
||||
}
|
||||
await this.createEmbeddedDocuments('Item', [tache], options);
|
||||
tachesExistantes = this.filterItems(filterTacheLecture);
|
||||
tachesExistantes = findTache(nomTache)
|
||||
}
|
||||
return tachesExistantes.length > 0 ? tachesExistantes[0] : undefined;
|
||||
|
||||
function findTache(name) {
|
||||
return this.filterItems(it => it.name == name, 'tache');
|
||||
}
|
||||
}
|
||||
|
||||
blessuresASoigner() {
|
||||
// TODO or not TODO: filtrer les blessures poour lesquels on ne peut plus faire de premiers soins?
|
||||
// TODO or not TODO: filtrer les blessures pour lesquelles on ne peut plus faire de premiers soins?
|
||||
return this.filterItems(it => it.system.gravite > 0 && it.system.gravite <= 6 && !(it.system.premierssoins.done && it.system.soinscomplets.done), 'blessure')
|
||||
}
|
||||
|
||||
async getTacheBlessure(blesse, blessure) {
|
||||
const gravite = blessure?.system.gravite ?? 0;
|
||||
if (gravite > 0) {
|
||||
const tache = this.listItems('tache').find(it => it.system.itemId == blessure.id)
|
||||
const tache = this.itemTypes['tache'].find(it => it.system.itemId == blessure.id)
|
||||
?? await RdDItemBlessure.createTacheSoinBlessure(this, gravite);
|
||||
await blessure?.updateTacheSoinBlessure(tache);
|
||||
return tache
|
||||
@ -2347,6 +2404,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
}
|
||||
|
||||
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
|
||||
RdDEmpoignade.checkEmpoignadeEnCours(this)
|
||||
const competence = this.getCompetence(compName);
|
||||
await this._openRollDialog({
|
||||
name: 'jet-competence',
|
||||
@ -2367,6 +2425,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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 !
|
||||
@ -2633,7 +2692,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
/* -------------------------------------------- */
|
||||
_getSignesDraconiques(coord) {
|
||||
const type = TMRUtility.getTMRType(coord);
|
||||
return this.listItems("signedraconique").filter(it => it.system.typesTMR.includes(type));
|
||||
return this.itemTypes["signedraconique"].filter(it => it.system.typesTMR.includes(type));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -2699,8 +2758,10 @@ export class RdDActor extends RdDBaseActor {
|
||||
}
|
||||
rollData.xpSort = RdDItemSigneDraconique.getXpSortSigneDraconique(rollData.rolled.code, rollData.signe);
|
||||
if (rollData.xpSort > 0) {
|
||||
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': Misc.toInt(compData.system.xp_sort) + rollData.xpSort }]);
|
||||
await this.updateExperienceLog("XP Sort", rollData.xpSort, "Signe draconique en " + rollData.competence.name);
|
||||
const fromXp = Number(compData.system.xp_sort);
|
||||
const toXp = fromXp + rollData.xpSort;
|
||||
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': toXp }]);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XPSORT, fromXp, toXp, `${rollData.competence.name} : signe draconique`);
|
||||
}
|
||||
await this.deleteEmbeddedDocuments("Item", [rollData.signe._id]);
|
||||
await RdDResolutionTable.displayRollData(rollData, this.name, 'chat-resultat-lecture-signedraconique.html');
|
||||
@ -2814,11 +2875,12 @@ export class RdDActor extends RdDBaseActor {
|
||||
/* -------------------------------------------- */
|
||||
async _xpCompetence(xpData) {
|
||||
if (xpData.competence) {
|
||||
const newXp = Misc.toInt(xpData.competence.system.xp) + xpData.xpCompetence;
|
||||
let update = { _id: xpData.competence._id, 'system.xp': newXp };
|
||||
const from = Number(xpData.competence.system.xp);
|
||||
const to = from + xpData.xpCompetence;
|
||||
let update = { _id: xpData.competence._id, 'system.xp': to };
|
||||
await this.updateEmbeddedDocuments('Item', [update]);
|
||||
xpData.checkComp = await this.checkCompetenceXP(xpData.competence.name, undefined, false);
|
||||
this.updateExperienceLog("XP", xpData.xpCompetence, "XP gagné en " + xpData.competence.name);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XP, from, to, xpData.competence.name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2828,10 +2890,12 @@ export class RdDActor extends RdDBaseActor {
|
||||
let carac = duplicate(this.system.carac);
|
||||
let selectedCarac = RdDActor._findCaracByName(carac, xpData.caracName);
|
||||
if (!selectedCarac.derivee) {
|
||||
selectedCarac.xp = Misc.toInt(selectedCarac.xp) + xpData.xpCarac;
|
||||
const from = Number(selectedCarac.xp);
|
||||
const to = from + xpData.xpCarac;
|
||||
selectedCarac.xp = to;
|
||||
await this.update({ "system.carac": carac });
|
||||
xpData.checkCarac = await this.checkCaracXP(selectedCarac.label, false);
|
||||
this.updateExperienceLog("XP", xpData.xpCarac, "XP gagné en " + xpData.caracName);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XPCARAC, from, to, xpData.caracName);
|
||||
} else {
|
||||
xpData.caracRepartitionManuelle = true;
|
||||
}
|
||||
@ -2840,8 +2904,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async resetNombresAstraux() {
|
||||
let toDelete = this.listItems('nombreastral');
|
||||
const deletions = toDelete.map(it => it._id);
|
||||
const deletions = this.itemTypes['nombreastral'].map(it => it._id);
|
||||
await this.deleteEmbeddedDocuments("Item", deletions);
|
||||
}
|
||||
|
||||
@ -2865,7 +2928,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
async supprimerAnciensNombresAstraux() {
|
||||
const calendrier = game.system.rdd.calendrier;
|
||||
if (calendrier) {
|
||||
const toDelete = this.listItems('nombreastral')
|
||||
const toDelete = this.itemTypes['nombreastral']
|
||||
.filter(it => calendrier.isAfterIndexDate(it.system.jourindex))
|
||||
.map(it => it._id);
|
||||
await this.deleteEmbeddedDocuments("Item", toDelete);
|
||||
@ -2923,11 +2986,6 @@ export class RdDActor extends RdDBaseActor {
|
||||
return entry && entry.length > 0 ? carac[entry[0]] : undefined;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getSortList() {
|
||||
return this.listItems("sort");
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
countMonteeLaborieuse() { // Return +1 par queue/ombre/souffle Montée Laborieuse présente
|
||||
let countMonteeLaborieuse = EffetsDraconiques.countMonteeLaborieuse(this);
|
||||
@ -2987,8 +3045,8 @@ export class RdDActor extends RdDBaseActor {
|
||||
mode: mode,
|
||||
fatigue: RdDUtility.calculFatigueHtml(fatigue, endurance),
|
||||
draconic: this.getDraconicList(),
|
||||
sort: this.getSortList(),
|
||||
signes: this.listItems("signedraconique"),
|
||||
sort: this.itemTypes['sort'],
|
||||
signes: this.itemTypes['signedraconique'],
|
||||
caracReve: this.system.carac.reve.value,
|
||||
pointsReve: this.getReveActuel(),
|
||||
isRapide: isRapide,
|
||||
@ -3024,7 +3082,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
|
||||
const competence = this.getCompetence(arme.system.competence)
|
||||
if (competence.system.ispossession) {
|
||||
if (competence.isCompetencePossession()) {
|
||||
return RdDPossession.onAttaquePossession(target, this, competence);
|
||||
}
|
||||
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme);
|
||||
@ -3104,9 +3162,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
conjurerPossession(possession) {
|
||||
// TODO: choix de la compétence de draconic ou de possession
|
||||
let draconic = this.getDraconicOuPossession();
|
||||
RdDPossession.onConjurerPossession(this, draconic, possession)
|
||||
RdDPossession.onConjurerPossession(this, possession)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -3128,7 +3184,7 @@ export class RdDActor extends RdDBaseActor {
|
||||
/* -------------------------------------------- */
|
||||
async equiperObjet(itemID) {
|
||||
let item = this.getEmbeddedDocument('Item', itemID);
|
||||
if (item && ['arme', 'armure'].includes(item.type)) {
|
||||
if (item?.isEquipable()) {
|
||||
const isEquipe = !item.system.equipe;
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: item.id, "system.equipe": isEquipe }]);
|
||||
this.computeEncTotal(); // Mise à jour encombrement
|
||||
@ -3145,8 +3201,8 @@ export class RdDActor extends RdDBaseActor {
|
||||
const armures = this.items.filter(it => it.type == "armure" && it.system.equipe);
|
||||
for (const armure of armures) {
|
||||
protection += await RdDDice.rollTotal(armure.system.protection.toString());
|
||||
if (dmg > 0) {
|
||||
this._deteriorerArmure(armure, dmg);
|
||||
if (dmg > 0 && attackerRoll.dmg.encaisserSpecial != "noarmure") {
|
||||
armure.deteriorerArmure(dmg);
|
||||
dmg = 0;
|
||||
}
|
||||
}
|
||||
@ -3164,35 +3220,6 @@ export class RdDActor extends RdDBaseActor {
|
||||
return protection;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
_deteriorerArmure(armure, dmg) {
|
||||
armure = duplicate(armure);
|
||||
if (!ReglesOptionelles.isUsing('deteriorationArmure') || armure.system.protection == '0') {
|
||||
return;
|
||||
}
|
||||
armure.system.deterioration = (armure.system.deterioration ?? 0) + dmg;
|
||||
if (armure.system.deterioration >= 10) {
|
||||
armure.system.deterioration -= 10;
|
||||
let res = /(\d+)?d(\d+)(\-\d+)?/.exec(armure.system.protection);
|
||||
if (res) {
|
||||
let malus = Misc.toInt(res[3]) - 1;
|
||||
let armure = Misc.toInt(res[2]);
|
||||
if (armure + malus <= 0) {
|
||||
armure.system.protection = 0;
|
||||
} else {
|
||||
armure.system.protection = '' + (res[1] ?? '1') + 'd' + armure + malus;
|
||||
}
|
||||
}
|
||||
else if (/\d+/.exec(armure.system.protection)) {
|
||||
armure.system.protection = "1d" + armure.system.protection;
|
||||
}
|
||||
else {
|
||||
ui.notifications.warn(`La valeur d'armure de votre ${armure.name} est incorrecte`);
|
||||
}
|
||||
ChatMessage.create({ content: "Votre armure s'est détériorée, elle protège maintenant de " + armure.system.protection });
|
||||
}
|
||||
this.updateEmbeddedDocuments('Item', [armure]);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async encaisser() {
|
||||
@ -3204,25 +3231,31 @@ export class RdDActor extends RdDBaseActor {
|
||||
if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
|
||||
return;
|
||||
}
|
||||
const attackerId = attacker?.id;
|
||||
if (ReglesOptionelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
|
||||
RdDBaseActor.remoteActorCall({
|
||||
actorId: this.id,
|
||||
method: 'validerEncaissement',
|
||||
args: [rollData, show]
|
||||
method: 'appliquerEncaissement',
|
||||
args: [rollData, show, attackerId]
|
||||
});
|
||||
return;
|
||||
}
|
||||
await this.appliquerEncaissement(rollData, show, attackerId);
|
||||
}
|
||||
|
||||
async appliquerEncaissement(rollData, show, attackerId) {
|
||||
const armure = await this.computeArmure(rollData);
|
||||
if (ReglesOptionelles.isUsing('validation-encaissement-gr')) {
|
||||
DialogValidationEncaissement.validerEncaissement(this, rollData, armure, show, (encaissement, show) => this._appliquerEncaissement(encaissement, show, attacker));
|
||||
DialogValidationEncaissement.validerEncaissement(this, rollData, armure, show, attackerId, (encaissement, show, attackerId) => this._appliquerEncaissement(encaissement, show, attackerId));
|
||||
}
|
||||
else {
|
||||
let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
|
||||
await this._appliquerEncaissement(encaissement, show, attacker)
|
||||
await this._appliquerEncaissement(encaissement, show, attackerId);
|
||||
}
|
||||
}
|
||||
|
||||
async _appliquerEncaissement(encaissement, show, attacker) {
|
||||
async _appliquerEncaissement(encaissement, show, attackedId) {
|
||||
const attacker = attackedId ? game.actors.get(attackedId) : undefined
|
||||
let santeOrig = duplicate(this.system.sante);
|
||||
|
||||
const blessure = await this.ajouterBlessure(encaissement, attacker); // Will update the result table
|
||||
@ -3754,6 +3787,9 @@ export class RdDActor extends RdDBaseActor {
|
||||
case 'casetmr':
|
||||
await this.onDeleteOwnedCaseTmr(item, options, id);
|
||||
break;
|
||||
case 'empoignade':
|
||||
await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@ import { Misc } from "../misc.js";
|
||||
import { DialogSplitItem } from "../dialog-split-item.js";
|
||||
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
|
||||
import { Monnaie } from "../item-monnaie.js";
|
||||
import { RdDItem } from "../item.js";
|
||||
import { RdDItem, TYPES } from "../item.js";
|
||||
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
@ -50,16 +51,18 @@ export class RdDBaseActorSheet extends ActorSheet {
|
||||
encTotal: await this.actor.computeEncTotal(),
|
||||
}
|
||||
|
||||
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
|
||||
this._appliquerRechercheObjets(formData.objets, formData.conteneurs);
|
||||
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 == TYPES.competencecreature)
|
||||
.forEach(it => it.isdommages = RdDItemCompetenceCreature.isDommages(it))
|
||||
return formData;
|
||||
}
|
||||
|
||||
_appliquerRechercheObjets(objets, conteneurs) {
|
||||
_appliquerRechercheObjets(conteneurs, inventaires) {
|
||||
if (this.options.recherche?.text) {
|
||||
const recherche = this.options.recherche;
|
||||
const allVisible = objets.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id);
|
||||
const allVisible = inventaires.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id);
|
||||
let addVisible = conteneurs.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id)
|
||||
do {
|
||||
allVisible.push(...addVisible)
|
||||
@ -67,11 +70,11 @@ export class RdDBaseActorSheet extends ActorSheet {
|
||||
addVisible = parentsIds.filter(id => !allVisible.includes(id))
|
||||
}
|
||||
while (addVisible.length > 0)
|
||||
objets.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
|
||||
inventaires.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
|
||||
conteneurs.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
|
||||
}
|
||||
else {
|
||||
objets.forEach(it => it.system.isHidden = false)
|
||||
inventaires.forEach(it => it.system.isHidden = false)
|
||||
conteneurs.forEach(it => it.system.isHidden = false)
|
||||
}
|
||||
}
|
||||
@ -118,33 +121,34 @@ export class RdDBaseActorSheet extends ActorSheet {
|
||||
formData.herbes = Misc.arrayOrEmpty(itemTypes['herbe']);
|
||||
formData.nourritureboissons = Misc.arrayOrEmpty(itemTypes['nourritureboisson']);
|
||||
formData.gemmes = Misc.arrayOrEmpty(itemTypes['gemme']);
|
||||
formData.monnaie = Misc.arrayOrEmpty(itemTypes['monnaie']).sort(Monnaie.triValeurEntiere());
|
||||
formData.monnaies = Misc.arrayOrEmpty(itemTypes['monnaie']).sort(Monnaie.triValeurEntiere());
|
||||
formData.objets = Misc.arrayOrEmpty(itemTypes['objet'])
|
||||
|
||||
formData.objets = RdDItem.getItemTypesInventaire('all')
|
||||
formData.inventaires = RdDItem.getItemTypesInventaire('all')
|
||||
.map(t => Misc.arrayOrEmpty(itemTypes[t]))
|
||||
.reduce((a, b) => a.concat(b), [])
|
||||
.sort(Misc.ascending(it => it.name));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */ /** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
this.html = html;
|
||||
|
||||
this.html.find('.conteneur-name a').click(async event => {
|
||||
RdDUtility.toggleAfficheContenu(this.getItemId(event));
|
||||
this.render(true);
|
||||
});
|
||||
this.html.find('.item-edit').click(async event => this.getItem(event)?.sheet.render(true))
|
||||
this.html.find('.item-montrer').click(async event => this.getItem(event)?.postItemToChat());
|
||||
this.html.find('.actor-montrer').click(async event => this.actor.postActorToChat());
|
||||
this.html.find('.recherche')
|
||||
.each((index, field) => {
|
||||
this._rechercheSelectArea(field);
|
||||
})
|
||||
.keyup(async event => this._rechercherKeyup(event))
|
||||
.change(async event => this._rechercherKeyup(event));
|
||||
this.html.find('.recherche').prop( "disabled", false );
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */ /** @override */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
this.html = html;
|
||||
|
||||
this.html.find('.conteneur-name a').click(async event => {
|
||||
RdDUtility.toggleAfficheContenu(this.getItemId(event));
|
||||
this.render(true);
|
||||
});
|
||||
this.html.find('.item-edit').click(async event => this.getItem(event)?.sheet.render(true))
|
||||
this.html.find('.item-montrer').click(async event => this.getItem(event)?.postItemToChat());
|
||||
this.html.find('.actor-montrer').click(async event => this.actor.postActorToChat());
|
||||
this.html.find('.recherche')
|
||||
.each((index, field) => {
|
||||
this._rechercheSelectArea(field);
|
||||
})
|
||||
.keyup(async event => this._rechercherKeyup(event))
|
||||
.change(async event => this._rechercherKeyup(event));
|
||||
this.html.find('.recherche').prop("disabled", false);
|
||||
// Everything below here is only needed if the sheet is editable
|
||||
if (!this.options.editable) return;
|
||||
|
||||
@ -230,9 +234,9 @@ export class RdDBaseActorSheet extends ActorSheet {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async selectObjetTypeToCreate() {
|
||||
let typeObjets = this.getTypesInventaire().sort(Misc.ascending(type => Misc.typeName('Item', type)));
|
||||
let types = this.getTypesInventaire().sort(Misc.ascending(type => Misc.typeName('Item', type)));
|
||||
let content = `<span class="competence-label">Selectionnez le type d'équipement</span><select class="item-type">`;
|
||||
for (let typeName of typeObjets) {
|
||||
for (let typeName of types) {
|
||||
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
|
||||
}
|
||||
content += '</select>';
|
||||
|
@ -3,6 +3,7 @@ import { SYSTEM_SOCKET_ID } from "../constants.js";
|
||||
import { Monnaie } from "../item-monnaie.js";
|
||||
import { Misc } from "../misc.js";
|
||||
import { RdDAudio } from "../rdd-audio.js";
|
||||
import { RdDConfirm } from "../rdd-confirm.js";
|
||||
import { RdDUtility } from "../rdd-utility.js";
|
||||
import { SystemCompendiums } from "../settings/system-compendiums.js";
|
||||
import { APP_ASTROLOGIE_REFRESH } from "../sommeil/app-astrologie.js";
|
||||
@ -32,7 +33,7 @@ export class RdDBaseActor extends Actor {
|
||||
case "msg_refresh_nombre_astral":
|
||||
Hooks.callAll(APP_ASTROLOGIE_REFRESH);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static remoteActorCall(callData, userId = undefined) {
|
||||
@ -109,7 +110,6 @@ export class RdDBaseActor extends Actor {
|
||||
isEntite() { return this.type == 'entite'; }
|
||||
isPersonnage() { return this.type == 'personnage'; }
|
||||
isVehicule() { return this.type == 'vehicule'; }
|
||||
|
||||
getItem(id, type = undefined) {
|
||||
const item = this.items.get(id);
|
||||
if (type == undefined || (item?.type == type)) {
|
||||
@ -119,7 +119,7 @@ export class RdDBaseActor extends Actor {
|
||||
}
|
||||
|
||||
listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
|
||||
filterItems(filter, type = undefined) { return this.listItems(type)?.filter(filter) ?? []; }
|
||||
filterItems(filter, type = undefined) { return (type ? this.itemTypes[type] : this.items)?.filter(filter); }
|
||||
findItemLike(idOrName, type) {
|
||||
return this.getItem(idOrName, type)
|
||||
?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
|
||||
@ -144,6 +144,18 @@ export class RdDBaseActor extends Actor {
|
||||
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
|
||||
}
|
||||
|
||||
async creerObjetParMJ(object){
|
||||
if (!Misc.isUniqueConnectedGM()) {
|
||||
RdDBaseActor.remoteActorCall({
|
||||
actorId: this.id,
|
||||
method: 'creerObjetParMJ',
|
||||
args: [object]
|
||||
});
|
||||
return;
|
||||
}
|
||||
await this.createEmbeddedDocuments('Item', [object])
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
getFortune() {
|
||||
return Monnaie.getFortune(this.itemTypes['monnaie']);
|
||||
@ -244,14 +256,16 @@ export class RdDBaseActor extends Actor {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const cout = Number(achat.prixTotal ?? 0);
|
||||
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
|
||||
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
|
||||
const vente = achat.vente;
|
||||
const quantite = (achat.choix.nombreLots ?? 1) * (vente.tailleLot);
|
||||
const itemVendu = vendeur?.getItem(vente.item._id);
|
||||
if (!this.verifierQuantite(vendeur, itemVendu, quantite)) {
|
||||
const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
|
||||
const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
|
||||
if (!itemVendu) {
|
||||
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !`: `Impossible de retrouver: ${achat.vente.item.name} !`);
|
||||
return;
|
||||
}
|
||||
if (vendeur && !this.verifierQuantite(itemVendu, quantite)) {
|
||||
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
|
||||
return
|
||||
}
|
||||
@ -262,13 +276,13 @@ export class RdDBaseActor extends Actor {
|
||||
await this.decrementerVente(vendeur, itemVendu, quantite, cout);
|
||||
if (acheteur) {
|
||||
await acheteur.depenserSols(cout);
|
||||
const createdItemId = await acheteur.creerQuantiteItem(vente.item, quantite);
|
||||
await acheteur.consommerNourritureAchetee(achat, vente, createdItemId);
|
||||
const createdItemId = await acheteur.creerQuantiteItem(itemVendu, quantite);
|
||||
await acheteur.consommerNourritureAchetee(achat, achat.vente, createdItemId);
|
||||
}
|
||||
if (cout > 0) {
|
||||
RdDAudio.PlayContextAudio("argent");
|
||||
}
|
||||
const chatAchatItem = duplicate(vente);
|
||||
const chatAchatItem = duplicate(achat.vente);
|
||||
chatAchatItem.quantiteTotal = quantite;
|
||||
ChatMessage.create({
|
||||
user: achat.userId,
|
||||
@ -277,16 +291,16 @@ export class RdDBaseActor extends Actor {
|
||||
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem)
|
||||
});
|
||||
|
||||
if (!vente.quantiteIllimite) {
|
||||
if (vente.quantiteNbLots <= achat.choix.nombreLots) {
|
||||
if (!achat.vente.quantiteIllimite) {
|
||||
if (achat.vente.quantiteNbLots <= achat.choix.nombreLots) {
|
||||
ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
|
||||
}
|
||||
else if (achat.chatMessageIdVente) {
|
||||
vente["properties"] = itemVendu.getProprietes();
|
||||
vente.quantiteNbLots -= achat.choix.nombreLots;
|
||||
vente.jsondata = JSON.stringify(vente.item);
|
||||
achat.vente.properties = itemVendu.getProprietes();
|
||||
achat.vente.quantiteNbLots -= achat.choix.nombreLots;
|
||||
achat.vente.jsondata = JSON.stringify(achat.vente.item);
|
||||
const messageVente = game.messages.get(achat.chatMessageIdVente);
|
||||
messageVente.update({ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente) });
|
||||
messageVente.update({ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', achat.vente) });
|
||||
messageVente.render(true);
|
||||
}
|
||||
}
|
||||
@ -303,9 +317,9 @@ export class RdDBaseActor extends Actor {
|
||||
return this.getFortune() >= cout;
|
||||
}
|
||||
|
||||
verifierQuantite(vendeur, item, quantiteTotal) {
|
||||
const disponible = vendeur?.getQuantiteDisponible(item);
|
||||
return disponible == undefined || disponible >= quantiteTotal;
|
||||
verifierQuantite(item, quantiteDemande) {
|
||||
const disponible = item?.getQuantite();
|
||||
return disponible == undefined || disponible >= quantiteDemande;
|
||||
}
|
||||
|
||||
async consommerNourritureAchetee(achat, vente, createdItemId) {
|
||||
|
@ -22,7 +22,7 @@ export class RdDCommerce extends RdDBaseActor {
|
||||
}
|
||||
|
||||
getQuantiteDisponible(item) {
|
||||
return this.system.illimite || item.isService() ? undefined : item.getQuantite();
|
||||
return (this.system.illimite || item?.isService()) ? undefined : item.getQuantite();
|
||||
}
|
||||
|
||||
verifierFortune(cout) {
|
||||
|
39
module/actor/experience-log.js
Normal file
39
module/actor/experience-log.js
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
export const XP_TOPIC = {
|
||||
XP: { code: 'xp', label: 'xp' },
|
||||
XPSORT: { code: 'xpsort', label: 'xp sort' },
|
||||
NIVEAU: { code: 'niveau', label: 'Niveau' },
|
||||
XPCARAC: { code: 'xpcarac', label: 'xp carac' },
|
||||
CARAC: { code: 'carac', label: 'Carac' },
|
||||
STRESS: { code: 'stress', label: 'Stress' },
|
||||
TRANSFORM: { code: 'xps', label: 'Transformé' },
|
||||
}
|
||||
|
||||
export class ExperienceLog {
|
||||
|
||||
static async add(actor, topic, from, to, raison, manuel = false) {
|
||||
if (!actor.hasPlayerOwner || !actor.isPersonnage()) {
|
||||
return
|
||||
}
|
||||
if (from == to) {
|
||||
return
|
||||
}
|
||||
const newXpLog = {
|
||||
mode: topic?.code ?? topic,
|
||||
raison: (manuel ? '(manuel) ' : '') + raison,
|
||||
from: from,
|
||||
to: to,
|
||||
valeur: to - from,
|
||||
daterdd: game.system.rdd.calendrier.dateCourante(),
|
||||
datereel: game.system.rdd.calendrier.dateReel().replace('T', ' ')
|
||||
};
|
||||
console.log('ExperienceLog.add', newXpLog)
|
||||
const newExperienceLog = (actor.system.experiencelog ?? []).concat([newXpLog]);
|
||||
await actor.update({ [`system.experiencelog`]: newExperienceLog });
|
||||
}
|
||||
|
||||
static labelTopic(topic) {
|
||||
const xpt = Object.values(XP_TOPIC).find(it => it.code == topic);
|
||||
return xpt?.label ?? xpt?.code ?? topic;
|
||||
}
|
||||
}
|
@ -78,11 +78,7 @@ export class ChatUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async createChatWithRollMode(name, chatOptions) {
|
||||
return await ChatUtility.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async createChatMessage(name, rollMode, chatOptions) {
|
||||
let rollMode = game.settings.get("core", "rollMode")
|
||||
switch (rollMode) {
|
||||
case "blindroll": // GM only
|
||||
if (!game.user.isGM) {
|
||||
|
@ -25,23 +25,13 @@ export class DialogChronologie extends Dialog {
|
||||
journalId: game.settings.get(SYSTEM_RDD, LATEST_USED_JOURNAL_ID),
|
||||
journaux: game.journal.filter(it => it.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)),
|
||||
timestamp: game.system.rdd.calendrier.timestamp,
|
||||
dateReel: DialogChronologie.getCurrentDateTime()
|
||||
dateReel: game.system.rdd.calendrier.dateReel()
|
||||
};
|
||||
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-chronologie.html", dialogData);
|
||||
const dialog = new DialogChronologie(html, dialogData);
|
||||
dialog.render(true);
|
||||
}
|
||||
|
||||
static getCurrentDateTime() {
|
||||
return new Date().toLocaleString("sv-SE", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
}).replace(" ", "T");
|
||||
}
|
||||
|
||||
constructor(html, dialogData) {
|
||||
const options = {
|
||||
classes: ["DialogChronologie"],
|
||||
@ -124,7 +114,7 @@ export class DialogChronologie extends Dialog {
|
||||
heure: RdDTimestamp.definition(this.html.find("form.rdddialogchrono :input[name='chronologie.heure']").val()),
|
||||
minute: this.html.find("form.rdddialogchrono :input[name='chronologie.minute']").val(),
|
||||
},
|
||||
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val().replace('T', ' ')
|
||||
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ export class DialogItemAchat extends Dialog {
|
||||
}
|
||||
|
||||
return {
|
||||
item: (json ? JSON.parse(json) : undefined),
|
||||
item: JSON.parse(json),
|
||||
vendeur,
|
||||
acheteur,
|
||||
nbLots: parseInt(chatButton.attributes['data-quantiteNbLots']?.value),
|
||||
@ -34,7 +34,6 @@ export class DialogItemAchat extends Dialog {
|
||||
const venteData = {
|
||||
item,
|
||||
actingUserId: game.user.id,
|
||||
vendeurId: vendeur?.id,
|
||||
vendeur,
|
||||
acheteur,
|
||||
tailleLot,
|
||||
|
@ -7,7 +7,7 @@ import { RdDUtility } from "./rdd-utility.js";
|
||||
*/
|
||||
export class DialogValidationEncaissement extends Dialog {
|
||||
|
||||
static async validerEncaissement(actor, rollData, armure, show, onEncaisser) {
|
||||
static async validerEncaissement(actor, rollData, armure, show, attackerId, onEncaisser) {
|
||||
let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: HIDE_DICE });
|
||||
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.html', {
|
||||
actor: actor,
|
||||
@ -15,12 +15,12 @@ export class DialogValidationEncaissement extends Dialog {
|
||||
encaissement: encaissement,
|
||||
show: show
|
||||
});
|
||||
const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, show, onEncaisser);
|
||||
const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, show, attackerId, onEncaisser);
|
||||
dialog.render(true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
constructor(html, actor, rollData, armure, encaissement, show, onEncaisser) {
|
||||
constructor(html, actor, rollData, armure, encaissement, show, attackerId, onEncaisser) {
|
||||
// Common conf
|
||||
let buttons = {
|
||||
"valider": { label: "Valider", callback: html => this.onValider() },
|
||||
@ -48,6 +48,7 @@ export class DialogValidationEncaissement extends Dialog {
|
||||
this.armure = armure;
|
||||
this.encaissement = encaissement;
|
||||
this.show = show;
|
||||
this.attackerId = attackerId;
|
||||
this.onEncaisser = onEncaisser;
|
||||
this.forceDiceResult = {total: encaissement.roll.result };
|
||||
}
|
||||
@ -66,6 +67,6 @@ export class DialogValidationEncaissement extends Dialog {
|
||||
|
||||
async onValider() {
|
||||
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
|
||||
this.onEncaisser(this.encaissement, this.show)
|
||||
this.onEncaisser(this.encaissement, this.show, this.attackerId)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
|
||||
import { TYPES } from "./item.js";
|
||||
import { RdDCombatManager } from "./rdd-combat.js";
|
||||
|
||||
const nomCategorieParade = {
|
||||
@ -19,7 +20,7 @@ const nomCategorieParade = {
|
||||
export class RdDItemArme extends Item {
|
||||
|
||||
static isArme(item) {
|
||||
return (item.type == 'competencecreature' && item.system.iscombat) || item.type == 'arme';
|
||||
return RdDItemCompetenceCreature.getCategorieAttaque(item) || item.type == 'arme';
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -27,7 +28,7 @@ export class RdDItemArme extends Item {
|
||||
switch (arme ? arme.type : '') {
|
||||
case 'arme': return arme;
|
||||
case 'competencecreature':
|
||||
return RdDItemCompetenceCreature.armeNaturelle(arme);
|
||||
return RdDItemCompetenceCreature.armeCreature(arme);
|
||||
}
|
||||
return RdDItemArme.mainsNues();
|
||||
}
|
||||
@ -165,7 +166,7 @@ export class RdDItemArme extends Item {
|
||||
let corpsACorps = competences.find(it => it.name == 'Corps à corps') ?? { system: { niveau: -6 } };
|
||||
let init = RdDCombatManager.calculInitiative(corpsACorps.system.niveau, carac['melee'].value);
|
||||
armes.push(RdDItemArme.mainsNues({ niveau: corpsACorps.system.niveau, initiative: init }));
|
||||
//armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init }));
|
||||
armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init }));
|
||||
}
|
||||
|
||||
static corpsACorps(mainsNuesActor) {
|
||||
|
@ -23,7 +23,7 @@ const limitesArchetypes = [
|
||||
];
|
||||
|
||||
/* -------------------------------------------- */
|
||||
const categorieCompetences = {
|
||||
const categoriesCompetences = {
|
||||
"generale": { base: -4, label: "Générales" },
|
||||
"particuliere": { base: -8, label: "Particulières" },
|
||||
"specialisee": { base: -11, label: "Spécialisées" },
|
||||
@ -49,16 +49,16 @@ const competence_xp_cumul = _buildCumulXP();
|
||||
|
||||
export class RdDItemCompetence extends Item {
|
||||
/* -------------------------------------------- */
|
||||
static getCategorieCompetences() {
|
||||
return categorieCompetences;
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static getNiveauBase(category) {
|
||||
return categorieCompetences[category].base;
|
||||
static getCategories() {
|
||||
return categoriesCompetences;
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static getLabelCategorie(category) {
|
||||
return categorieCompetences[category].label;
|
||||
return categoriesCompetences[category].label;
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static getNiveauBase(category, categories = categoriesCompetences) {
|
||||
return categories[category]?.base ?? 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@ -192,7 +192,7 @@ export class RdDItemCompetence extends Item {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static isNiveauBase(item) {
|
||||
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie);
|
||||
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.getCategories());
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -1,51 +1,97 @@
|
||||
|
||||
import { RdDItem, TYPES } from "./item.js";
|
||||
import { RdDCombatManager } from "./rdd-combat.js";
|
||||
|
||||
const categories = {
|
||||
"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" },
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class RdDItemCompetenceCreature extends Item {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static setRollDataCreature(rollData) {
|
||||
rollData.competence = rollData.competence
|
||||
rollData.carac = { "carac_creature": { label: rollData.competence.name, value: rollData.competence.system.carac_value } }
|
||||
rollData.competence.system.defaut_carac = "carac_creature"
|
||||
rollData.competence.system.categorie = "creature"
|
||||
rollData.selectedCarac = rollData.carac.carac_creature
|
||||
if (rollData.competence.system.iscombat) {
|
||||
rollData.arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence);
|
||||
}
|
||||
static getCategories() {
|
||||
return categories;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static armeNaturelle(competencecreature) {
|
||||
if (RdDItemCompetenceCreature.isCompetenceAttaque(competencecreature)) {
|
||||
// si c'est un Item compétence: cloner pour ne pas modifier lma compétence
|
||||
let arme = (competencecreature instanceof Item) ? competencecreature.clone(): competencecreature;
|
||||
mergeObject(arme.system,
|
||||
static setRollDataCreature(rollData) {
|
||||
rollData.carac = { "carac_creature": { label: rollData.competence.name, value: rollData.competence.system.carac_value } }
|
||||
rollData.competence.system.defaut_carac = "carac_creature"
|
||||
rollData.selectedCarac = rollData.carac.carac_creature
|
||||
rollData.arme = RdDItemCompetenceCreature.armeCreature(rollData.competence);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static armeCreature(item) {
|
||||
const categorieAttaque = RdDItemCompetenceCreature.getCategorieAttaque(item)
|
||||
if (categorieAttaque != undefined) {
|
||||
// si c'est un Item compétence: cloner pour ne pas modifier la compétence
|
||||
let arme = item.clone();
|
||||
mergeObject(arme,
|
||||
{
|
||||
competence: arme.name,
|
||||
initiative: RdDCombatManager.calculInitiative(competencecreature.system.niveau, competencecreature.system.carac_value),
|
||||
niveau: competencecreature.system.niveau,
|
||||
equipe: true,
|
||||
resistance: 100,
|
||||
dommagesReels: arme.system.dommages,
|
||||
penetration: 0,
|
||||
force: 0,
|
||||
rapide: true,
|
||||
cac: competencecreature.system.isnaturelle ? "naturelle" : "",
|
||||
action: 'attaque'
|
||||
action: item.isCompetencePossession() ? 'possession' : 'attaque',
|
||||
system: {
|
||||
competence: arme.name,
|
||||
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
|
||||
niveau: item.system.niveau,
|
||||
initiative: RdDCombatManager.calculInitiative(item.system.niveau, item.system.carac_value),
|
||||
equipe: true,
|
||||
resistance: 100,
|
||||
dommagesReels: arme.system.dommages,
|
||||
penetration: 0,
|
||||
force: 0,
|
||||
rapide: true,
|
||||
}
|
||||
});
|
||||
return arme;
|
||||
}
|
||||
console.error("RdDItemCompetenceCreature.toActionArme(", competencecreature, ") : impossible de transformer l'Item en arme");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static isCompetenceAttaque(item) {
|
||||
return item.type == 'competencecreature' && item.system.iscombat;
|
||||
static getCategorieAttaque(item) {
|
||||
if (item.type == TYPES.competencecreature) {
|
||||
switch (item.system.categorie) {
|
||||
case "melee":
|
||||
case "tir":
|
||||
case "lancer":
|
||||
case "naturelle":
|
||||
case "possession":
|
||||
return item.system.categorie
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
static isDommages(item) {
|
||||
if (item.type == TYPES.competencecreature) {
|
||||
switch (item.system.categorie) {
|
||||
case "melee":
|
||||
case "tir":
|
||||
case "lancer":
|
||||
case "naturelle":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
static isParade(item) {
|
||||
if (item.type == TYPES.competencecreature) {
|
||||
switch (item.system.categorie) {
|
||||
case "melee":
|
||||
case "naturelle":
|
||||
case "parade":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static isCompetenceParade(item) {
|
||||
return item.type == 'competencecreature' && item.system.categorie_parade !== "";
|
||||
|
@ -11,6 +11,8 @@ import { RdDSheetUtility } from "./rdd-sheet-utility.js";
|
||||
import { SystemCompendiums } from "./settings/system-compendiums.js";
|
||||
import { Misc } from "./misc.js";
|
||||
import { RdDTimestamp } from "./time/rdd-timestamp.js";
|
||||
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
|
||||
import { TYPES } from "./item.js";
|
||||
|
||||
/**
|
||||
* Extend the basic ItemSheet for RdD specific items
|
||||
@ -98,9 +100,13 @@ export class RdDItemSheet extends ItemSheet {
|
||||
isComestible: this.item.getUtilisationCuisine(),
|
||||
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable)
|
||||
}
|
||||
if (this.item.type == TYPES.competencecreature) {
|
||||
formData.isparade = RdDItemCompetenceCreature.isParade(this.item)
|
||||
formData.isdommages = RdDItemCompetenceCreature.isDommages(this.item)
|
||||
}
|
||||
|
||||
const competences = await SystemCompendiums.getCompetences('personnage');
|
||||
formData.categorieCompetences = RdDItemCompetence.getCategorieCompetences()
|
||||
formData.categories = this.item.getCategories()
|
||||
if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') {
|
||||
formData.caracList = duplicate(game.system.model.Actor.personnage.carac)
|
||||
formData.caracList["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve)
|
||||
@ -249,7 +255,8 @@ export class RdDItemSheet extends ItemSheet {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.item.isCompetence()) {
|
||||
let level = RdDItemCompetence.getNiveauBase(event.currentTarget.value);
|
||||
const categorie = event.currentTarget.value;
|
||||
const level = RdDItemCompetence.getNiveauBase(categorie, this.item.getCategories());
|
||||
this.item.system.base = level;
|
||||
this.html.find('[name="system.base"]').val(level);
|
||||
}
|
||||
|
182
module/item.js
182
module/item.js
@ -6,34 +6,83 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
|
||||
import { RdDUtility } from "./rdd-utility.js";
|
||||
import { SystemCompendiums } from "./settings/system-compendiums.js";
|
||||
import { RdDRaretes } from "./item/raretes.js";
|
||||
import { RdDItemCompetence } from "./item-competence.js";
|
||||
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
|
||||
|
||||
export const TYPES = {
|
||||
competence: 'competence',
|
||||
competencecreature: 'competencecreature',
|
||||
empoignade: 'empoignade',
|
||||
possession: 'possession',
|
||||
blessure: 'blessure',
|
||||
maladie: 'maladie',
|
||||
poison: 'poison',
|
||||
arme: 'arme',
|
||||
armure: 'armure',
|
||||
conteneur: 'conteneur',
|
||||
objet: 'objet',
|
||||
monnaie: 'monnaie',
|
||||
gemme: 'gemme',
|
||||
munition: 'munition',
|
||||
nourritureboisson: 'nourritureboisson',
|
||||
herbe: 'herbe',
|
||||
plante: 'plante',
|
||||
ingredient: 'ingredient',
|
||||
faune: 'faune',
|
||||
livre: 'livre',
|
||||
potion: 'potion',
|
||||
service: 'service',
|
||||
musique: 'musique',
|
||||
danse: 'danse',
|
||||
chant: 'chant',
|
||||
jeu: 'jeu',
|
||||
recettecuisine: 'recettecuisine',
|
||||
oeuvre: 'oeuvre',
|
||||
recettealchimique: 'recettealchimique',
|
||||
tache: 'tache',
|
||||
sort: 'sort',
|
||||
sortreserve: 'sortreserve',
|
||||
rencontre: 'rencontre',
|
||||
queue: 'queue',
|
||||
ombre: 'ombre',
|
||||
souffle: 'souffle',
|
||||
tete: 'tete',
|
||||
casetmr: 'casetmr',
|
||||
meditation: 'meditation',
|
||||
signedraconique: 'signedraconique',
|
||||
tarot: 'tarot',
|
||||
nombreastral: 'nombreastral',
|
||||
extraitpoetique: 'extraitpoetique',
|
||||
}
|
||||
|
||||
const typesInventaireMateriel = [
|
||||
"arme",
|
||||
"armure",
|
||||
"conteneur",
|
||||
"faune",
|
||||
"gemme",
|
||||
"herbe",
|
||||
"plante",
|
||||
"ingredient",
|
||||
"livre",
|
||||
"monnaie",
|
||||
"munition",
|
||||
"nourritureboisson",
|
||||
"objet",
|
||||
"potion",
|
||||
TYPES.arme,
|
||||
TYPES.armure,
|
||||
TYPES.conteneur,
|
||||
TYPES.faune,
|
||||
TYPES.gemme,
|
||||
TYPES.herbe,
|
||||
TYPES.plante,
|
||||
TYPES.ingredient,
|
||||
TYPES.livre,
|
||||
TYPES.monnaie,
|
||||
TYPES.munition,
|
||||
TYPES.nourritureboisson,
|
||||
TYPES.objet,
|
||||
TYPES.potion,
|
||||
]
|
||||
const typesInventaire = {
|
||||
materiel: typesInventaireMateriel,
|
||||
all: ['service'].concat(typesInventaireMateriel),
|
||||
}
|
||||
|
||||
const typesObjetsOeuvres = ["oeuvre", "recettecuisine", "musique", "chant", "danse", "jeu"]
|
||||
const typesObjetsDraconiques = ["queue", "ombre", "souffle", "tete", "signedraconique", "sortreserve", "rencontre"]
|
||||
const typesObjetsConnaissance = ["meditation", "recettealchimique", "sort"]
|
||||
const typesObjetsEffet = ["possession", "poison", "maladie", "blessure"]
|
||||
const typesObjetsCompetence = ["competence", "competencecreature"]
|
||||
const typesObjetsTemporels = ["blessure", "poison", "maladie", "queue", "ombre", "souffle", "signedraconique", "rencontre"]
|
||||
const typesObjetsOeuvres = [TYPES.oeuvre, TYPES.recettecuisine, TYPES.musique, TYPES.chant, TYPES.danse, TYPES.jeu]
|
||||
const typesObjetsDraconiques = [TYPES.queue, TYPES.ombre, TYPES.souffle, TYPES.tete, TYPES.signedraconique, TYPES.sortreserve, TYPES.rencontre]
|
||||
const typesObjetsConnaissance = [TYPES.meditation, TYPES.recettealchimique, TYPES.sort]
|
||||
const typesObjetsEffet = [TYPES.possession, TYPES.poison, TYPES.maladie, TYPES.blessure]
|
||||
const typesObjetsCompetence = [TYPES.competence, TYPES.competencecreature]
|
||||
const typesObjetsTemporels = [TYPES.blessure, TYPES.poison, TYPES.maladie, TYPES.queue, TYPES.ombre, TYPES.souffle, TYPES.signedraconique, TYPES.rencontre]
|
||||
const typesObjetsEquipable = [TYPES.arme, TYPES.armure, TYPES.objet];
|
||||
const typesEnvironnement = typesInventaireMateriel;
|
||||
const encBrin = 0.00005; // un brin = 1 décigramme = 1/10g = 1/10000kg = 1/20000 enc
|
||||
const encPepin = 0.0007; /* un pépin de gemme = 1/10 cm3 = 1/1000 l = 3.5/1000 kg = 7/2000 kg = 7/1000 enc
|
||||
@ -75,11 +124,16 @@ export const defaultItemImg = {
|
||||
sortreserve: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp",
|
||||
extraitpoetique: "systems/foundryvtt-reve-de-dragon/icons/competence_ecriture.webp",
|
||||
tarot: "systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp",
|
||||
empoignade: "systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp"
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class RdDItem extends Item {
|
||||
|
||||
static get defaultIcon() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static getDefaultImg(itemType) {
|
||||
return game.system.rdd.itemClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
|
||||
}
|
||||
@ -87,12 +141,12 @@ export class RdDItem extends Item {
|
||||
static isFieldInventaireModifiable(type, field) {
|
||||
switch (field) {
|
||||
case 'quantite':
|
||||
if (['conteneur'].includes(type)) {
|
||||
if ([TYPES.conteneur].includes(type)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'cout':
|
||||
if (['monnaie'].includes(type)) {
|
||||
if ([TYPES.monnaie].includes(type)) {
|
||||
return game.user.isGM;
|
||||
}
|
||||
break;
|
||||
@ -138,49 +192,58 @@ export class RdDItem extends Item {
|
||||
super(docData, context);
|
||||
}
|
||||
|
||||
static get defaultIcon() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getUniteQuantite() {
|
||||
switch (this.type) {
|
||||
case "monnaie": return "(Pièces)"
|
||||
case "herbe":
|
||||
case TYPES.monnaie: return "(Pièces)"
|
||||
case TYPES.herbe:
|
||||
switch (this.system.categorie) {
|
||||
case 'Alchimie': case 'Repos': case 'Soin':
|
||||
return "(Brins)"
|
||||
case 'Cuisine': return '';
|
||||
}
|
||||
return '';
|
||||
case "ingredient": return "(Pépins ou Brins)"
|
||||
case TYPES.ingredient: return "(Pépins ou Brins)"
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
isCompetencePersonnage() { return this.type == 'competence' }
|
||||
isCompetenceCreature() { return this.type == 'competencecreature' }
|
||||
isConteneur() { return this.type == 'conteneur'; }
|
||||
isMonnaie() { return this.type == 'monnaie'; }
|
||||
isPotion() { return this.type == 'potion'; }
|
||||
isNourritureBoisson() { return this.type == 'nourritureboisson'; }
|
||||
isService() { return this.type == 'service'; }
|
||||
isEquipable() {
|
||||
return typesObjetsEquipable.includes(this.type)
|
||||
}
|
||||
|
||||
isCompetencePersonnage() { return this.type == TYPES.competence }
|
||||
isCompetenceCreature() { return this.type == TYPES.competencecreature }
|
||||
isConteneur() { return this.type == TYPES.conteneur; }
|
||||
isMonnaie() { return this.type == TYPES.monnaie; }
|
||||
isPotion() { return this.type == TYPES.potion; }
|
||||
isNourritureBoisson() { return this.type == TYPES.nourritureboisson; }
|
||||
isService() { return this.type == TYPES.service; }
|
||||
|
||||
isCompetence() { return typesObjetsCompetence.includes(this.type) }
|
||||
isCompetencePossession() { return TYPES.competencecreature == this.type && this.system.categorie == "possession" }
|
||||
isTemporel() { return typesObjetsTemporels.includes(this.type) }
|
||||
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
|
||||
isDraconique() { return RdDItem.getItemTypesDraconiques().includes(this.type) }
|
||||
isQueueDragon() { return [TYPES.queue, TYPES.ombre].includes(this.type) }
|
||||
isEffet() { return typesObjetsEffet.includes(this.type) }
|
||||
isConnaissance() { return typesObjetsConnaissance.includes(this.type) }
|
||||
|
||||
isInventaire(mode = 'materiel') { return RdDItem.getItemTypesInventaire(mode).includes(this.type); }
|
||||
isBoisson() { return this.isNourritureBoisson() && this.system.boisson; }
|
||||
isAlcool() { return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise; }
|
||||
isHerbeAPotion() { return this.type == 'herbe' && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
|
||||
isBlessure() { return this.type == 'blessure' }
|
||||
isHerbeAPotion() { return this.type == TYPES.herbe && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
|
||||
isBlessure() { return this.type == TYPES.blessure }
|
||||
|
||||
isPresentDansMilieux(milieux) {
|
||||
return this.getEnvironnements(milieux).length > 0
|
||||
}
|
||||
getCategories() {
|
||||
switch (this.type) {
|
||||
case TYPES.competence: return RdDItemCompetence.getCategories()
|
||||
case TYPES.competencecreature: return RdDItemCompetenceCreature.getCategories()
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
getEnvironnements(milieux = undefined) {
|
||||
const environnements = this.isInventaire() ? this.system.environnement : undefined;
|
||||
@ -261,15 +324,15 @@ export class RdDItem extends Item {
|
||||
|
||||
getUtilisation() {
|
||||
switch (this.type) {
|
||||
case 'potion':
|
||||
case TYPES.potion:
|
||||
switch (this.system.categorie) {
|
||||
case 'Alchimie': case 'AlchimieEnchante': case 'AlchimieAutre': return 'alchimie'
|
||||
case 'Cuisine': return 'cuisine'
|
||||
case 'Remede': case 'Repos': case 'ReposEnchante': case 'Soin': case 'SoinEnchante': return 'soins'
|
||||
}
|
||||
return '';
|
||||
case 'nourritureboisson': return 'cuisine';
|
||||
case 'herbe': case 'faune': case 'ingredient': case 'plante':
|
||||
case TYPES.nourritureboisson: return 'cuisine';
|
||||
case TYPES.herbe: case TYPES.faune: case TYPES.ingredient: case TYPES.plante:
|
||||
switch (this.system.categorie) {
|
||||
case 'Cuisine': return 'cuisine';
|
||||
case 'Toxique': case 'Poison': return 'poison';
|
||||
@ -284,9 +347,9 @@ export class RdDItem extends Item {
|
||||
getUtilisationCuisine() {
|
||||
if (this.getUtilisation() == 'cuisine') {
|
||||
switch (this.type) {
|
||||
case 'nourritureboisson':
|
||||
case TYPES.nourritureboisson:
|
||||
return 'pret';
|
||||
case 'herbe': case 'faune': case 'ingredient': case 'plante':
|
||||
case TYPES.herbe: case TYPES.faune: case TYPES.ingredient: case TYPES.plante:
|
||||
return 'brut';
|
||||
}
|
||||
}
|
||||
@ -294,7 +357,7 @@ export class RdDItem extends Item {
|
||||
}
|
||||
|
||||
isCristalAlchimique() {
|
||||
return this.type == 'objet' && Grammar.toLowerCaseNoAccent(this.name) == 'cristal alchimique' && this.system.quantite > 0;
|
||||
return this.type == TYPES.objet && Grammar.includesLowerCaseNoAccent(this.name, 'cristal alchimique') && this.system.quantite > 0;
|
||||
}
|
||||
|
||||
isMagique() {
|
||||
@ -317,16 +380,16 @@ export class RdDItem extends Item {
|
||||
}
|
||||
|
||||
getEncTotal() {
|
||||
return (this.isService() ? 0 : this.getQuantite()) * this.getEnc();
|
||||
return (this.getQuantite() ?? 0) * this.getEnc();
|
||||
}
|
||||
|
||||
getEnc() {
|
||||
switch (this.type) {
|
||||
case 'service':
|
||||
case TYPES.service:
|
||||
return 0;
|
||||
case 'herbe':
|
||||
case TYPES.herbe:
|
||||
return this.getEncHerbe();
|
||||
case 'gemme':
|
||||
case TYPES.gemme:
|
||||
return encPepin * this.system.taille;
|
||||
}
|
||||
return Math.max(this.system.encombrement ?? 0, 0);
|
||||
@ -366,6 +429,7 @@ export class RdDItem extends Item {
|
||||
}
|
||||
this.system.actionPrincipale = this.getActionPrincipale({ warnIfNot: false });
|
||||
}
|
||||
this.equipable = this.isEquipable();
|
||||
}
|
||||
|
||||
prepareDataPotion() {
|
||||
@ -381,7 +445,7 @@ export class RdDItem extends Item {
|
||||
|
||||
getActionPrincipale(options = { warnIfNot: true }) {
|
||||
switch (this.type) {
|
||||
case 'conteneur': return 'Ouvrir';
|
||||
case TYPES.conteneur: return 'Ouvrir';
|
||||
}
|
||||
if (this.actor?.isPersonnage()) {
|
||||
const warn = options.warnIfNot;
|
||||
@ -389,11 +453,11 @@ export class RdDItem extends Item {
|
||||
return 'Utiliser';
|
||||
}
|
||||
switch (this.type) {
|
||||
case 'nourritureboisson': return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
|
||||
case 'potion': return this._actionOrWarnQuantiteZero('Boire', warn);
|
||||
case 'livre': return this._actionOrWarnQuantiteZero('Lire', warn);
|
||||
case 'herbe': return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
|
||||
case 'queue': case 'ombre': return this.system.refoulement > 0 ? 'Refouler' : undefined;
|
||||
case TYPES.nourritureboisson: return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
|
||||
case TYPES.potion: return this._actionOrWarnQuantiteZero('Boire', warn);
|
||||
case TYPES.livre: return this._actionOrWarnQuantiteZero('Lire', warn);
|
||||
case TYPES.herbe: return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
|
||||
case TYPES.queue: case TYPES.ombre: return this.system.refoulement > 0 ? 'Refouler' : undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@ -408,11 +472,11 @@ export class RdDItem extends Item {
|
||||
return;
|
||||
}
|
||||
switch (this.type) {
|
||||
case 'potion': return await actor.consommerPotion(this, onActionItem);
|
||||
case 'livre': return await actor.actionLire(this);
|
||||
case 'conteneur': return await this.sheet.render(true);
|
||||
case 'herbe': return await actor.actionHerbe(this, onActionItem);
|
||||
case 'queue': case 'ombre': return await actor.actionRefoulement(this);
|
||||
case TYPES.potion: return await actor.consommerPotion(this, onActionItem);
|
||||
case TYPES.livre: return await actor.actionLire(this);
|
||||
case TYPES.conteneur: return await this.sheet.render(true);
|
||||
case TYPES.herbe: return await actor.actionHerbe(this, onActionItem);
|
||||
case TYPES.queue: case TYPES.ombre: return await actor.actionRefoulement(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
51
module/item/armure.js
Normal file
51
module/item/armure.js
Normal file
@ -0,0 +1,51 @@
|
||||
import { RdDItem } from "../item.js";
|
||||
import { Misc } from "../misc.js";
|
||||
import { ReglesOptionelles } from "../settings/regles-optionelles.js";
|
||||
|
||||
export class RdDItemArmure extends RdDItem {
|
||||
|
||||
static get defaultIcon() {
|
||||
return "systems/foundryvtt-reve-de-dragon/icons/armes_armures/armure_plaques.webp";
|
||||
}
|
||||
|
||||
deteriorerArmure(dmg) {
|
||||
if (!ReglesOptionelles.isUsing('deteriorationArmure') || this.system.protection == '0') {
|
||||
return;
|
||||
}
|
||||
let deterioration = (this.system.deterioration ?? 0) + dmg;
|
||||
let protection = this.system.protection;
|
||||
|
||||
if (deterioration >= 10) {
|
||||
deterioration -= 10;
|
||||
protection = this.calculProtectionDeterioree();
|
||||
ChatMessage.create({ content: `Votre armure ${this.name} s'est détériorée, elle protège maintenant de ${protection}` });
|
||||
}
|
||||
this.update({
|
||||
system: {
|
||||
deterioration: deterioration,
|
||||
protection: protection
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
calculProtectionDeterioree() {
|
||||
const protectionCourante = this.system.protection;
|
||||
let res = /(\d+)?d(\d+)(\-\d+)?/.exec(protectionCourante);
|
||||
if (res) {
|
||||
let protection = Misc.toInt(res[2]);
|
||||
let malus = Misc.toInt(res[3]) - 1;
|
||||
if (protection + malus <= 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return `1d${protection}${malus}`;
|
||||
}
|
||||
}
|
||||
else if (/\d+/.exec(protectionCourante)) {
|
||||
return `1d${protectionCourante}`;
|
||||
}
|
||||
else {
|
||||
ui.notifications.warn(`La valeur d'armure de votre ${this.name} est incorrecte`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ export class RdDConteneurItemSheet extends RdDItemInventaireSheet {
|
||||
/* -------------------------------------------- */
|
||||
prepareConteneurData(formData) {
|
||||
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
|
||||
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
|
||||
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
|
||||
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { RdDBaseActor } from "./actor/base-actor.js";
|
||||
import { LOG_HEAD, SYSTEM_RDD } from "./constants.js";
|
||||
import { Grammar } from "./grammar.js";
|
||||
import { Monnaie } from "./item-monnaie.js";
|
||||
import { RdDItem } from "./item.js";
|
||||
import { RdDItem, TYPES } from "./item.js";
|
||||
import { RdDTimestamp } from "./time/rdd-timestamp.js";
|
||||
import { RdDRaretes } from "./item/raretes.js";
|
||||
|
||||
@ -13,7 +13,7 @@ class Migration {
|
||||
|
||||
async applyItemsUpdates(computeUpdates) {
|
||||
await game.actors.forEach(async (actor) => {
|
||||
const actorItemUpdates = computeUpdates(actor.items);
|
||||
const actorItemUpdates = computeUpdates(actor.items).filter(it => it != undefined);
|
||||
if (actorItemUpdates.length > 0) {
|
||||
console.log(
|
||||
this.code,
|
||||
@ -24,7 +24,7 @@ class Migration {
|
||||
}
|
||||
});
|
||||
|
||||
const itemUpdates = computeUpdates(game.items);
|
||||
const itemUpdates = computeUpdates(game.items).filter(it => it != undefined);
|
||||
if (itemUpdates.length > 0) {
|
||||
console.log(this.code, "Applying updates on items", itemUpdates);
|
||||
await Item.updateDocuments(itemUpdates);
|
||||
@ -65,7 +65,6 @@ class _1_5_34_migrationPngWebp {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _10_0_16_MigrationSortsReserve extends Migration {
|
||||
get code() { return "creation-item-sort-reserve"; }
|
||||
get version() { return "10.0.16"; }
|
||||
@ -370,6 +369,7 @@ class _10_4_6_ServicesEnCommerces extends Migration {
|
||||
return itemToCreate;
|
||||
}
|
||||
}
|
||||
|
||||
class _10_5_0_UpdatePeriodicite extends Migration {
|
||||
get code() { return "migration-periodicite-poisons-maladies"; }
|
||||
get version() { return "10.5.0"; }
|
||||
@ -458,6 +458,62 @@ class _10_7_0_MigrationBlessures extends Migration {
|
||||
}
|
||||
}
|
||||
|
||||
class _10_7_19_CategorieCompetenceCreature extends Migration {
|
||||
get code() { return "categorie-competence-creature"; }
|
||||
get version() { return "10.7.19"; }
|
||||
|
||||
async migrate() {
|
||||
await this.applyItemsUpdates(items => items
|
||||
.filter(it => TYPES.competencecreature == it.type)
|
||||
.map(it => this.migrateCompetenceCreature(it))
|
||||
);
|
||||
}
|
||||
|
||||
migrateCompetenceCreature(it) {
|
||||
const categorie = this.getCategorie(it)
|
||||
if (categorie == it.system.categorie) {
|
||||
return undefined
|
||||
}
|
||||
return { _id: it.id, 'system.categorie': categorie }
|
||||
}
|
||||
|
||||
getCategorie(it) {
|
||||
if (it.system.ispossession) {
|
||||
return 'possession'
|
||||
}
|
||||
switch (it.system.categorie) {
|
||||
case "melee":
|
||||
if (it.system.isnaturelle) {
|
||||
return 'naturelle'
|
||||
}
|
||||
return 'melee'
|
||||
case "particuliere": case "specialisee": case "connaissance":
|
||||
return "generale"
|
||||
default:
|
||||
return it.system.categorie
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _10_7_19_PossessionsEntiteVictime extends Migration {
|
||||
get code() { return "possessions-entite-victime"; }
|
||||
get version() { return "10.7.19"; }
|
||||
|
||||
async migrate() {
|
||||
await this.applyItemsUpdates(items => items
|
||||
.filter(it => TYPES.possession == it.type)
|
||||
.map(it => this.migratePossession(it))
|
||||
);
|
||||
}
|
||||
|
||||
migratePossession(it) {
|
||||
return { _id: it.id,
|
||||
'system.entite.actorid': it.system.possesseurid,
|
||||
'system.victime.actorid': it.system.possedeid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Migrations {
|
||||
static getMigrations() {
|
||||
return [
|
||||
@ -474,6 +530,8 @@ export class Migrations {
|
||||
new _10_4_6_ServicesEnCommerces(),
|
||||
new _10_5_0_UpdatePeriodicite(),
|
||||
new _10_7_0_MigrationBlessures(),
|
||||
new _10_7_19_CategorieCompetenceCreature(),
|
||||
new _10_7_19_PossessionsEntiteVictime(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -490,7 +548,7 @@ export class Migrations {
|
||||
migrate() {
|
||||
const currentVersion = game.settings.get(SYSTEM_RDD, "systemMigrationVersion");
|
||||
if (isNewerVersion(game.system.version, currentVersion)) {
|
||||
//if (true) { /* comment previous and uncomment here to test before upgrade */
|
||||
// if (true) { /* comment previous and uncomment here to test before upgrade */
|
||||
const migrations = Migrations.getMigrations().filter(m => isNewerVersion(m.version, currentVersion));
|
||||
if (migrations.length > 0) {
|
||||
migrations.sort((a, b) => this.compareVersions(a, b));
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { RdDCarac } from "./rdd-carac.js";
|
||||
import { RdDPossession } from "./rdd-possession.js";
|
||||
|
||||
const conditionsTactiques = [
|
||||
{ type: '', descr: '', dmg: 0, attaque: 0, parade: 0, esquive: true },
|
||||
@ -24,6 +25,12 @@ export class RdDBonus {
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static isDefenseAttaqueFinesse(rollData) {
|
||||
if (rollData.isEmpoignade && rollData.rolled?.isPart) {
|
||||
return true
|
||||
}
|
||||
if (RdDPossession.isDefensePossession(rollData)) {
|
||||
return RdDPossession.isPossessionFinesse(rollData)
|
||||
}
|
||||
return rollData.attackerRoll?.particuliere == 'finesse';
|
||||
}
|
||||
|
||||
@ -71,7 +78,7 @@ export class RdDBonus {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static _dmgArme(rollData) {
|
||||
if ( rollData.arme) {
|
||||
if (rollData.arme) {
|
||||
let dmgBase = rollData.arme.system.dommagesReels ?? Number(rollData.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, rollData.arme.system.magique ? rollData.arme.system.ecaille_efficacite : 0);
|
||||
|
@ -58,15 +58,6 @@ export class RdDCarac {
|
||||
selectedCarac?.label.match(/(Apparence|Force|Agilité|Dextérité|Vue|Ouïe|Odorat-Goût|Empathie|Dérobée|Mêlée|Tir|Lancer)/);
|
||||
}
|
||||
|
||||
static isIgnoreEtatGeneral(rollData) {
|
||||
const selectedCarac = rollData.selectedCarac;
|
||||
return !selectedCarac ||
|
||||
rollData.ethylisme ||
|
||||
RdDCarac.isChance(selectedCarac) ||
|
||||
(RdDCarac.isReve(selectedCarac) && !rollData.competence);
|
||||
}
|
||||
|
||||
|
||||
static computeTotal(carac, beaute = undefined) {
|
||||
const total = Object.values(carac ?? {}).filter(c => !c.derivee)
|
||||
.map(it => parseInt(it.value))
|
||||
|
@ -12,6 +12,7 @@ import { RdDRollTables } from "./rdd-rolltables.js";
|
||||
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
|
||||
import { STATUSES } from "./settings/status-effects.js";
|
||||
import { Targets } from "./targets.js";
|
||||
import { RdDEmpoignade } from "./rdd-empoignade.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
const premierRoundInit = [
|
||||
@ -57,6 +58,7 @@ export class RdDCombatManager extends Combat {
|
||||
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)
|
||||
.forEach(it => it.delete());
|
||||
RdDEmpoignade.deleteAllEmpoignades()
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,19 +87,32 @@ export class RdDCombatManager extends Combat {
|
||||
let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, 0);
|
||||
if (!formula) {
|
||||
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
|
||||
const competence = combatant.actor.items.find(it => it.system.iscombat)
|
||||
const competence = combatant.actor.items.find(it => RdDItemCompetenceCreature.getCategorieAttaque(it))
|
||||
if (competence) {
|
||||
rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, 0);
|
||||
}
|
||||
} else {
|
||||
const armeCombat = combatant.actor.itemTypes['arme'].find(it => it.system.equipe)
|
||||
const compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.system.competence;
|
||||
let compName = "Corps à corps"
|
||||
if (armeCombat) {
|
||||
if (armeCombat.system.competence != "") {
|
||||
compName = armeCombat.system.competence
|
||||
}
|
||||
if (armeCombat.system.lancer != "") {
|
||||
compName = armeCombat.system.lancer
|
||||
}
|
||||
if (armeCombat.system.tir != "") {
|
||||
compName = armeCombat.system.tir
|
||||
}
|
||||
}
|
||||
const competence = RdDItemCompetence.findCompetence(combatant.actor.items, compName);
|
||||
if (competence) {
|
||||
if (competence && competence.system.defaut_carac) {
|
||||
const carac = combatant.actor.system.carac[competence.system.defaut_carac].value;
|
||||
const niveau = competence.system.niveau;
|
||||
const bonusEcaille = (armeCombat?.system.magique) ? armeCombat.system.ecaille_efficacite : 0;
|
||||
rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, bonusEcaille);
|
||||
} else {
|
||||
ui.notifications.warn(`Votre arme ${armeCombat.name} n'a pas de compétence renseignée`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,15 +230,15 @@ export class RdDCombatManager extends Combat {
|
||||
}
|
||||
|
||||
static listActionsCreature(competences) {
|
||||
return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
|
||||
.map(it => RdDItemCompetenceCreature.armeNaturelle(it));
|
||||
return competences.map(it => RdDItemCompetenceCreature.armeCreature(it))
|
||||
.filter(it => it != undefined);
|
||||
}
|
||||
|
||||
static listActionsPossessions(actor) {
|
||||
return RdDCombatManager._indexActions(actor.getPossessions().map(p => {
|
||||
return {
|
||||
name: p.name,
|
||||
action: 'conjurer',
|
||||
action: 'possession',
|
||||
system: {
|
||||
competence: p.name,
|
||||
possessionid: p.system.possessionid,
|
||||
@ -240,15 +255,15 @@ export class RdDCombatManager extends Combat {
|
||||
return actions;
|
||||
}
|
||||
if (actor.isCreatureEntite()) {
|
||||
actions = actions.concat(RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']));
|
||||
actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']);
|
||||
} else if (actor.isPersonnage()) {
|
||||
// Recupération des items 'arme'
|
||||
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
|
||||
//.concat(RdDItemArme.empoignade())
|
||||
.concat(RdDItemArme.empoignade())
|
||||
.concat(RdDItemArme.mainsNues());
|
||||
|
||||
const competences = actor.itemTypes['competence'];
|
||||
actions = actions.concat(RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac));
|
||||
actions = RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac);
|
||||
|
||||
if (actor.system.attributs.hautrevant.value) {
|
||||
actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } });
|
||||
@ -336,7 +351,7 @@ export class RdDCombatManager extends Combat {
|
||||
} else if (combatant.actor.getSurprise() == "demi") {
|
||||
initOffset = 0;
|
||||
initInfo = "Demi Surprise"
|
||||
} else if (action.action == 'conjurer') {
|
||||
} else if (action.action == 'possession') {
|
||||
initOffset = 10;
|
||||
caracForInit = combatant.actor.getReveActuel();
|
||||
initInfo = "Possession"
|
||||
@ -737,17 +752,11 @@ export class RdDCombat {
|
||||
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
|
||||
return;
|
||||
}
|
||||
if (arme.system.cac == 'empoignade' && this.attacker.isCombatTouche()) {
|
||||
ChatMessage.create({
|
||||
alias: this.attacker.name,
|
||||
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
|
||||
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-actor-perte-empoignade.html', {
|
||||
attacker: this.attacker,
|
||||
competence: competence
|
||||
})
|
||||
});
|
||||
if (arme.system.cac == 'empoignade') {
|
||||
RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender)
|
||||
return;
|
||||
}
|
||||
RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
|
||||
|
||||
let rollData = this._prepareAttaque(competence, arme);
|
||||
console.log("RdDCombat.attaque >>>", rollData);
|
||||
@ -781,7 +790,7 @@ export class RdDCombat {
|
||||
passeArme: randomID(16),
|
||||
mortalite: arme?.system.mortalite,
|
||||
coupsNonMortels: false,
|
||||
competence: competence,
|
||||
competence: competence.clone(),
|
||||
surprise: this.attacker.getSurprise(true),
|
||||
surpriseDefenseur: this.defender.getSurprise(true),
|
||||
targetToken: Targets.extractTokenData(this.target),
|
||||
@ -1036,7 +1045,7 @@ export class RdDCombat {
|
||||
passeArme: attackerRoll.passeArme,
|
||||
diffLibre: attackerRoll.diffLibre,
|
||||
attackerRoll: attackerRoll,
|
||||
competence: this.defender.getCompetence(competenceParade),
|
||||
competence: this.defender.getCompetence(competenceParade).clone(),
|
||||
arme: armeParade,
|
||||
surprise: this.defender.getSurprise(true),
|
||||
needParadeSignificative: ReglesOptionelles.isUsing('categorieParade') && RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade),
|
||||
@ -1117,7 +1126,7 @@ export class RdDCombat {
|
||||
passeArme: attackerRoll.passeArme,
|
||||
diffLibre: attackerRoll.diffLibre,
|
||||
attackerRoll: attackerRoll,
|
||||
competence: competence,
|
||||
competence: competence.clone(),
|
||||
surprise: this.defender.getSurprise(true),
|
||||
surpriseDefenseur: this.defender.getSurprise(true),
|
||||
carac: this.defender.system.carac,
|
||||
|
436
module/rdd-empoignade.js
Normal file
436
module/rdd-empoignade.js
Normal file
@ -0,0 +1,436 @@
|
||||
/* -------------------------------------------- */
|
||||
import { RdDResolutionTable } from "./rdd-resolution-table.js";
|
||||
import { RdDRoll } from "./rdd-roll.js";
|
||||
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
|
||||
import { ChatUtility } from "./chat-utility.js";
|
||||
import { STATUSES } from "./settings/status-effects.js";
|
||||
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
|
||||
import { TYPES } from "./item.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class RdDEmpoignade {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static init() {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static registerChatCallbacks(html) {
|
||||
html.on("click", '.defense-empoignade-cac', event => {
|
||||
const chatMessage = ChatUtility.getChatMessage(event);
|
||||
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
|
||||
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
|
||||
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Corps à corps", "melee")
|
||||
});
|
||||
html.on("click", '.defense-empoignade-esquive', event => {
|
||||
const chatMessage = ChatUtility.getChatMessage(event);
|
||||
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
|
||||
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
|
||||
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Esquive", "derobee")
|
||||
});
|
||||
html.on("click", '.empoignade-poursuivre', event => {
|
||||
let attackerId = event.currentTarget.attributes['data-attackerId'].value
|
||||
let defenderId = event.currentTarget.attributes['data-defenderId'].value
|
||||
RdDEmpoignade.onAttaqueEmpoignadeValidee(game.actors.get(attackerId), game.actors.get(defenderId))
|
||||
});
|
||||
html.on("click", '.empoignade-entrainer-sol', event => {
|
||||
const chatMessage = ChatUtility.getChatMessage(event);
|
||||
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
|
||||
RdDEmpoignade.entrainerAuSol(rollData)
|
||||
ChatUtility.removeChatMessageId(chatMessage.id)
|
||||
});
|
||||
html.on("click", '.empoignade-projeter-sol', event => {
|
||||
const chatMessage = ChatUtility.getChatMessage(event);
|
||||
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
|
||||
RdDEmpoignade.projeterAuSol(rollData)
|
||||
ChatUtility.removeChatMessageId(chatMessage.id)
|
||||
});
|
||||
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)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static checkEmpoignadeEnCours(actor) {
|
||||
// TODO: autoriser la perception? la comédie/séduction?
|
||||
if (RdDEmpoignade.isEmpoignadeEnCours(actor)) {
|
||||
ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.")
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static $storeRollEmpoignade(msg, rollData) {
|
||||
RdDEmpoignade.$reduceActorToIds(rollData);
|
||||
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData);
|
||||
}
|
||||
|
||||
static $reduceActorToIds(rollData) {
|
||||
rollData.attacker = { id: rollData.attacker.id };
|
||||
rollData.defender = { id: rollData.defender.id };
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static $readRollEmpoignade(msg) {
|
||||
const rollData = ChatUtility.getMessageData(msg, 'empoignade-roll-data');
|
||||
RdDEmpoignade.$replaceIdsWithActors(rollData);
|
||||
return rollData
|
||||
}
|
||||
|
||||
static $replaceIdsWithActors(rollData) {
|
||||
rollData.attacker = game.actors.get(rollData.attacker.id);
|
||||
rollData.defender = game.actors.get(rollData.defender.id);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static isEmpoignadeEnCours(actor) {
|
||||
return actor.itemTypes[TYPES.empoignade].find(it => it.system.pointsemp > 0)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getEmpoignadeById(actor, id) {
|
||||
let emp = actor.itemTypes[TYPES.empoignade].find(it => it.system.empoignadeid == id)
|
||||
return emp && duplicate(emp) || undefined;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getEmpoignade(attacker, defender) {
|
||||
let emp = attacker.itemTypes[TYPES.empoignade].find(it =>
|
||||
(it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
|
||||
(it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
|
||||
)
|
||||
if (emp) {
|
||||
return duplicate(emp);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getMalusTaille(emp, attacker, defender) {
|
||||
// Si pas empoigné, alors 0
|
||||
if (emp.system.pointsemp == 0) {
|
||||
return 0
|
||||
}
|
||||
// p135: Malus de -1 par point de taille de différence de taille au delà de 1 (donc -2 pour une différence de 3, ...)
|
||||
const diffTaille = attacker.system.carac.taille.value - defender.system.carac.taille.value;
|
||||
const diffTailleAbs = Math.abs(diffTaille)
|
||||
const signDiff = diffTaille > 0 ? 1 : -1
|
||||
|
||||
if (diffTailleAbs > 2) {
|
||||
return signDiff * (diffTailleAbs - 1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
static isActionAutorisee(mode, attacker, defender) {
|
||||
const acting = RdDEmpoignade.isActionDefenseur(mode) ? defender : attacker;
|
||||
if (acting.getUserLevel(game.user) < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
|
||||
ui.notifications.warn(`Vous n'êtes pas autorisé à choisir l'action de ${acting.name}`)
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static isActionDefenseur(mode) {
|
||||
switch (mode) {
|
||||
case "liberer":
|
||||
case "contrer-empoigner":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async onAttaqueEmpoignade(attacker, defender) {
|
||||
if (!RdDEmpoignade.isActionAutorisee("empoigner", attacker, defender)) {
|
||||
return
|
||||
}
|
||||
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
|
||||
const isNouvelle = empoignade == undefined;
|
||||
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
|
||||
//console.log("W.", empoignade, defender.hasArmeeMeleeEquipee())
|
||||
if ((isNouvelle || empoignade.system.pointsemp == 0) && defender.hasArmeeMeleeEquipee()) {
|
||||
ChatUtility.createChatWithRollMode(attacker.name, {
|
||||
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.html`, { attacker: attacker, defender: defender })
|
||||
})
|
||||
} else {
|
||||
await this.onAttaqueEmpoignadeValidee(attacker, defender)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async onAttaqueEmpoignadeValidee(attacker, defender) {
|
||||
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
|
||||
const isNouvelle = empoignade == undefined;
|
||||
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
|
||||
|
||||
let mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer"
|
||||
|
||||
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
|
||||
return
|
||||
}
|
||||
|
||||
let rollData = {
|
||||
mode, empoignade, attacker, defender,
|
||||
isEmpoignade: true,
|
||||
competence: attacker.getCompetence("Corps à corps").clone(),
|
||||
selectedCarac: attacker.system.carac.melee,
|
||||
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
|
||||
}
|
||||
if (attacker.isCreatureEntite()) {
|
||||
RdDItemCompetenceCreature.setRollDataCreature(rollData)
|
||||
}
|
||||
if (empoignade.system.pointsemp >= 2) {
|
||||
if (!empoignade.system.ausol) {
|
||||
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.html');
|
||||
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
|
||||
}
|
||||
} else {
|
||||
await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async onAttaqueEmpoignadeFromItem(empoignade) {
|
||||
let attacker = game.actors.get(empoignade.system.empoigneurid)
|
||||
let defender = game.actors.get(empoignade.system.empoigneid)
|
||||
await this.onAttaqueEmpoignadeValidee(attacker, defender)
|
||||
}
|
||||
|
||||
static async onImmobilisation(attacker, defender, empoignade) {
|
||||
const rollData = {
|
||||
mode: "immobilise",
|
||||
empoignade, attacker, defender,
|
||||
isEmpoignade: true,
|
||||
competence: attacker.getCompetence("Corps à corps").clone()
|
||||
}
|
||||
const msg = await ChatMessage.create({
|
||||
whisper: ChatUtility.getWhisperRecipientsAndGMs(attacker.name),
|
||||
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.html`, rollData)
|
||||
})
|
||||
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $rollAttaqueEmpoignade(attacker, rollData, isNouvelle = false) {
|
||||
const dialog = await RdDRoll.create(attacker, rollData,
|
||||
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
|
||||
{
|
||||
name: 'jet-empoignade',
|
||||
label: 'Empoigner',
|
||||
callbacks: [{ action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, isNouvelle) },]
|
||||
});
|
||||
dialog.render(true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $onRollEmpoignade(rollData, isNouvelle = false) {
|
||||
let attacker = game.actors.get(rollData.attacker.id)
|
||||
let defender = game.actors.get(rollData.defender.id)
|
||||
|
||||
|
||||
if (rollData.rolled.isSuccess && isNouvelle) {
|
||||
const objectEmpoignade = rollData.empoignade.toObject();
|
||||
// Creer l'empoignade sur attaquant/defenseur
|
||||
attacker.creerObjetParMJ(objectEmpoignade);
|
||||
defender.creerObjetParMJ(objectEmpoignade);
|
||||
}
|
||||
|
||||
rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
|
||||
if (rollData.rolled.isPart) {
|
||||
rollData.particuliere = "finesse";
|
||||
}
|
||||
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-resultat.html');
|
||||
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async onDefenseEmpoignade(attackerRoll, mode, competenceName = "Corps à corps", carac = "melee") {
|
||||
let attacker = game.actors.get(attackerRoll.attacker.id)
|
||||
let defender = game.actors.get(attackerRoll.defender.id)
|
||||
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
|
||||
return
|
||||
}
|
||||
|
||||
let empoignade = this.getEmpoignade(attacker, defender)
|
||||
|
||||
if (!empoignade) {
|
||||
ui.notifications.warn("Une erreur s'est produite : Aucune empoignade trouvée !!")
|
||||
return
|
||||
}
|
||||
|
||||
empoignade = duplicate(empoignade)
|
||||
let defenderRoll = {
|
||||
mode, attacker, defender, empoignade, attackerRoll,
|
||||
diffLibre: attackerRoll.diffLibre,
|
||||
attaqueParticuliere: attackerRoll.particuliere,
|
||||
competence: defender.getCompetence(competenceName).clone(),
|
||||
surprise: defender.getSurprise(true),
|
||||
carac: defender.system.carac,
|
||||
selectedCarac: defender.system.carac[carac],
|
||||
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, defender, attacker),
|
||||
show: {}
|
||||
};
|
||||
await RdDEmpoignade.$rollDefenseEmpoignade(defender, defenderRoll);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $rollDefenseEmpoignade(defender, defenderRoll) {
|
||||
const dialog = await RdDRoll.create(defender, defenderRoll,
|
||||
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-empoignade.html' },
|
||||
{
|
||||
name: 'empoignade',
|
||||
label: 'Contrer',
|
||||
callbacks: [
|
||||
{ action: async (r) => await RdDEmpoignade.$onRollContrerLiberer(r) }
|
||||
]
|
||||
}
|
||||
);
|
||||
dialog.render(true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $onRollContrerLiberer(rollData) {
|
||||
let empoignade = rollData.empoignade
|
||||
|
||||
if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) {
|
||||
empoignade.system.pointsemp++
|
||||
RdDEmpoignade.$updateEtatEmpoignade(empoignade)
|
||||
}
|
||||
if (rollData.mode == "contrer-liberer" && !rollData.rolled.isSuccess) {
|
||||
empoignade.system.pointsemp--
|
||||
RdDEmpoignade.$updateEtatEmpoignade(empoignade)
|
||||
}
|
||||
|
||||
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html')
|
||||
if (empoignade.system.pointsemp >= 2) {
|
||||
let msg = await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-empoignade-entrainer.html');
|
||||
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $updateEtatEmpoignade(empoignade) {
|
||||
console.log("UPDATE 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])
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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])
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async entrainerAuSol(rollData) {
|
||||
let attacker = game.actors.get(rollData.attacker.id)
|
||||
let defender = game.actors.get(rollData.defender.id)
|
||||
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
|
||||
return
|
||||
}
|
||||
let empoignade = this.getEmpoignade(attacker, defender)
|
||||
|
||||
empoignade.system.ausol = true
|
||||
await this.$updateEtatEmpoignade(empoignade)
|
||||
|
||||
await attacker.setEffect(STATUSES.StatusProne, true);
|
||||
await defender.setEffect(STATUSES.StatusProne, true);
|
||||
|
||||
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html');
|
||||
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async projeterAuSol(rollData) {
|
||||
let attacker = game.actors.get(rollData.attacker.id)
|
||||
let defender = game.actors.get(rollData.defender.id)
|
||||
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
|
||||
return
|
||||
}
|
||||
let empoignade = this.getEmpoignade(attacker, defender)
|
||||
|
||||
await defender.setEffect(STATUSES.StatusProne, true);
|
||||
await this.$deleteEmpoignade(empoignade)
|
||||
|
||||
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html');
|
||||
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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)) {
|
||||
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 RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html');
|
||||
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async deleteAllEmpoignades() {
|
||||
for (let actor of game.actors) {
|
||||
let empIds = actor.itemTypes["empoignade"].map(it => it.id)
|
||||
await actor.deleteEmbeddedDocuments('Item', empIds)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async deleteLinkedEmpoignade(actorId, empoignade) {
|
||||
let actorDeleteId = (actorId == empoignade.system.empoigneurid) ? empoignade.system.empoigneid : empoignade.system.empoigneurid
|
||||
let actor = game.actors.get(actorDeleteId)
|
||||
let emp = this.getEmpoignadeById(actor, empoignade.system.empoignadeid)
|
||||
if (emp) {
|
||||
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',
|
||||
img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
|
||||
system: { description: "", empoignadeid: randomID(16), compteempoigne: 0, empoigneurid: attacker.id, empoigneid: defender.id, ptsemp: 0, empoigneurname: attacker.name, empoignename: defender.name }
|
||||
},
|
||||
{
|
||||
temporary: true
|
||||
})
|
||||
}
|
||||
}
|
@ -58,6 +58,7 @@ import { RdDConteneurItemSheet } from "./item/sheet-conteneur.js";
|
||||
import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js";
|
||||
import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js";
|
||||
import { AppAstrologie } from "./sommeil/app-astrologie.js";
|
||||
import { RdDItemArmure } from "./item/armure.js";
|
||||
|
||||
/**
|
||||
* RdD system
|
||||
@ -76,6 +77,7 @@ export class SystemReveDeDragon {
|
||||
this.RdDUtility = RdDUtility;
|
||||
this.RdDHotbar = RdDHotbar;
|
||||
this.itemClasses = {
|
||||
armure: RdDItemArmure,
|
||||
blessure: RdDItemBlessure,
|
||||
maladie: RdDItemMaladie,
|
||||
ombre: RdDItemOmbre,
|
||||
@ -174,7 +176,7 @@ export class SystemReveDeDragon {
|
||||
"recettealchimique", "musique", "chant", "danse", "jeu", "recettecuisine", "oeuvre",
|
||||
"meditation", "queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve",
|
||||
"nombreastral", "tache", "maladie", "poison", "possession",
|
||||
"tarot", "extraitpoetique"
|
||||
"tarot", "extraitpoetique", "empoignade"
|
||||
], makeDefault: true
|
||||
});
|
||||
CONFIG.Combat.documentClass = RdDCombatManager;
|
||||
|
@ -1,9 +1,9 @@
|
||||
/* -------------------------------------------- */
|
||||
import { RdDCombat } from "./rdd-combat.js";
|
||||
import { RdDResolutionTable } from "./rdd-resolution-table.js";
|
||||
import { RdDRoll } from "./rdd-roll.js";
|
||||
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
|
||||
import { Targets } from "./targets.js";
|
||||
import { TYPES } from "./item.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/* On part du principe qu'une entité démarre tjs
|
||||
@ -20,9 +20,9 @@ export class RdDPossession {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static searchPossessionFromEntite(attacker, defender) {
|
||||
let poss = attacker.items.find(poss => poss.type == 'possession' && poss.system.possedeid == defender.id);
|
||||
let poss = attacker.items.find(poss => poss.type == TYPES.possession && poss.system.victime.actorid == defender.id);
|
||||
if (!poss) {
|
||||
poss = defender.items.find(poss => poss.type == 'possession' && poss.system.possedeid == defender.id);
|
||||
poss = defender.items.find(poss => poss.type == TYPES.possession && poss.system.victime.actorid == defender.id);
|
||||
}
|
||||
return poss && duplicate(poss) || undefined;
|
||||
}
|
||||
@ -31,39 +31,40 @@ export class RdDPossession {
|
||||
static async onAttaquePossession(target, attacker, competence, suitePossession = undefined) {
|
||||
const defender = target.actor;
|
||||
const fromEntite = RdDPossession.searchPossessionFromEntite(attacker, defender);
|
||||
const isNouvelle = !suitePossession && ! fromEntite;
|
||||
const isNouvelle = !suitePossession && !fromEntite;
|
||||
const possession = (suitePossession ?? fromEntite ?? (await RdDPossession.createPossession(attacker, defender)));
|
||||
|
||||
|
||||
RdDPossession.$updateEtatPossession(possession)
|
||||
|
||||
let rollData = {
|
||||
mode: "possession",
|
||||
mode: "attaque",
|
||||
isECNIDefender: false,
|
||||
competence: competence,
|
||||
competence: competence.clone(),
|
||||
possession: possession,
|
||||
attacker: attacker,
|
||||
defender: defender,
|
||||
targetToken: Targets.extractTokenData(target)
|
||||
};
|
||||
if (attacker.isCreatureEntite()) {
|
||||
RdDItemCompetenceCreature.setRollDataCreature(rollData)
|
||||
}
|
||||
|
||||
RdDPossession.selectCompetenceDraconicOuPossession(rollData, attacker)
|
||||
|
||||
await RdDPossession.$rollAttaquePossession(attacker, rollData, isNouvelle);
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async onConjurerPossession(attacker, competence, possession) {
|
||||
static async onConjurerPossession(attacker, possession) {
|
||||
possession = duplicate(possession);
|
||||
RdDPossession.$updateEtatPossession(possession)
|
||||
|
||||
const defender = game.actors.get(possession.system.entite.actorid);
|
||||
let rollData = {
|
||||
mode: "possession",
|
||||
mode: "attaque",
|
||||
isECNIDefender: true,
|
||||
competence: competence,
|
||||
possession: possession,
|
||||
attacker: attacker,
|
||||
defender: game.actors.get(possession.system.possesseurid)
|
||||
defender: defender,
|
||||
};
|
||||
RdDPossession.selectCompetenceDraconicOuPossession(rollData, attacker)
|
||||
|
||||
await RdDPossession.$rollAttaquePossession(attacker, rollData);
|
||||
}
|
||||
|
||||
@ -71,7 +72,7 @@ export class RdDPossession {
|
||||
static async onDefensePossession(attackerId, defenderId, possessionId) {
|
||||
let attacker = game.actors.get(attackerId)
|
||||
let possession = attacker?.getPossession(possessionId)
|
||||
defenderId = defenderId ?? possession?.system.possesseurid ?? undefined
|
||||
defenderId = defenderId ?? possession?.system.entite.actorid ?? undefined
|
||||
let defender = game.actors.get(defenderId)
|
||||
possession = possession ?? defender?.getPossession(possessionId) ?? undefined;
|
||||
|
||||
@ -82,19 +83,29 @@ export class RdDPossession {
|
||||
possession = duplicate(possession)
|
||||
// Update for draconic roll
|
||||
let rollData = {
|
||||
mode: "conjuration",
|
||||
mode: "defense",
|
||||
isECNIDefender: defender.type == "entite",
|
||||
possession: possession,
|
||||
attacker: attacker,
|
||||
defender: defender,
|
||||
competence: defender.getDraconicOuPossession(),
|
||||
selectedCarac: defender.system.carac.reve,
|
||||
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } }
|
||||
}
|
||||
rollData.competence.system.defaut_carac = 'reve-actuel'
|
||||
|
||||
RdDPossession.selectCompetenceDraconicOuPossession(rollData, defender)
|
||||
rollData.diffLibre = RdDPossession.getInfoAttaque(rollData).diffLibre
|
||||
|
||||
await RdDPossession.$rollDefensePossession(defender, rollData);
|
||||
}
|
||||
|
||||
static selectCompetenceDraconicOuPossession(rollData, rollingActor) {
|
||||
rollData.competence = rollingActor.getDraconicOuPossession();
|
||||
if (rollingActor.isCreatureEntite()) {
|
||||
RdDItemCompetenceCreature.setRollDataCreature(rollData)
|
||||
}
|
||||
else {
|
||||
rollData.selectedCarac = rollingActor.system.carac.reve
|
||||
rollData.forceCarac = { 'reve-actuel': { label: "Rêve Actuel", value: rollingActor.getReveActuel() } }
|
||||
rollData.competence.system.defaut_carac = 'reve-actuel'
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $rollAttaquePossession(attacker, rollData, isNouvelle = false) {
|
||||
@ -104,21 +115,22 @@ export class RdDPossession {
|
||||
name: 'jet-possession',
|
||||
label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession',
|
||||
callbacks: [
|
||||
{ condition: r => (r.rolled.isSuccess), action: async (r) => await RdDPossession.$onRollPossession(r, true, isNouvelle) },
|
||||
{ condition: r => (r.rolled.isEchec), action: async (r) => await RdDPossession.$onRollPossession(r, false, isNouvelle) },
|
||||
{ action: async (r) => await RdDPossession.$onRollPossession(r, isNouvelle) },
|
||||
]
|
||||
});
|
||||
dialog.render(true);
|
||||
dialog.render(true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $onRollPossession(rollData, isSuccess, isNouvelle = false) {
|
||||
rollData.possession.isSuccess = isSuccess;
|
||||
RdDPossession.$updateEtatPossession(rollData.possession);
|
||||
static async $onRollPossession(rollData, isNouvelle = false) {
|
||||
rollData.possession.isSuccess = rollData.rolled.isSuccess;
|
||||
RdDPossession.$updateEtatPossession(rollData.possession, rollData);
|
||||
if (isNouvelle) {
|
||||
// Creer la possession sur le defenseur
|
||||
rollData.defender.createEmbeddedDocuments('Item', [rollData.possession.toObject()])
|
||||
await rollData.defender.createEmbeddedDocuments('Item', [rollData.possession.toObject()])
|
||||
}
|
||||
const possession = (rollData.isECNIDefender ? rollData.attacker : rollData.defender).getPossession(rollData.possession.system.possessionid)
|
||||
RdDPossession.storePossessionAttaque(possession, rollData)
|
||||
await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html');
|
||||
}
|
||||
|
||||
@ -127,35 +139,42 @@ export class RdDPossession {
|
||||
const dialog = await RdDRoll.create(defender, rollData,
|
||||
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html' },
|
||||
{
|
||||
name: 'conjurer',
|
||||
name: 'possession',
|
||||
label: 'Conjurer une Possession',
|
||||
callbacks: [
|
||||
{ action: async (r) => await RdDPossession.$onRollConjuration(r) }
|
||||
]
|
||||
}
|
||||
);
|
||||
]
|
||||
}
|
||||
);
|
||||
dialog.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async $onRollConjuration(rollData) {
|
||||
let actor = game.actors.get(rollData.possession.system.possedeid)
|
||||
let victime = game.actors.get(rollData.possession.system.victime.actorid)
|
||||
let compteur = rollData.possession.system.compteur
|
||||
if (!rollData.rolled.isSuccess) {
|
||||
if (rollData.isECNIDefender) {
|
||||
rollData.possession.system.compteur--
|
||||
compteur--
|
||||
} else {
|
||||
rollData.possession.system.compteur++
|
||||
compteur++
|
||||
}
|
||||
let update = { _id: rollData.possession._id, "system.compteur": rollData.possession.system.compteur }
|
||||
await actor.updateEmbeddedDocuments('Item', [update])
|
||||
}
|
||||
|
||||
const possession = victime.getPossession(rollData.possession.system.possessionid)
|
||||
await possession.update({
|
||||
system: {
|
||||
compteur: compteur,
|
||||
entite: { diffLibre: 0, finesse: false },
|
||||
victime: { diffLibre: 0, finesse: false }
|
||||
}
|
||||
})
|
||||
rollData.possession = possession
|
||||
RdDPossession.$updateEtatPossession(rollData.possession)
|
||||
|
||||
await RdDResolutionTable.displayRollData(rollData,rollData.defender, 'chat-resultat-possession.html')
|
||||
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.html')
|
||||
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
|
||||
// conjuration
|
||||
actor.deleteEmbeddedDocuments("Item", [rollData.possession._id])
|
||||
victime.deleteEmbeddedDocuments("Item", [rollData.possession._id])
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,13 +199,43 @@ export class RdDPossession {
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static isPossessionFinesse(rollData) {
|
||||
return RdDPossession.getInfoAttaque(rollData).finesse
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static getInfoAttaque(rollData) {
|
||||
return rollData.possession.system[rollData.isECNIDefender ? 'victime' : 'entite'];
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static isDefensePossession(rollData) {
|
||||
return rollData.possession && rollData.mode == "defense"
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static storePossessionAttaque(possession, rollData = undefined) {
|
||||
const attaquant = rollData?.isECNIDefender ? 'victime' : 'entite'
|
||||
possession.update({
|
||||
[`system.${attaquant}`]: {
|
||||
diffLibre: rollData?.diffLibre ?? 0,
|
||||
finesse: rollData?.rolled.isPart ?? false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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",
|
||||
system: { description: "", typepossession: attacker.name, possede: false, possessionid: randomID(16), possesseurid: attacker.id, possedeid: defender.id, date: 0, compteur: 0 }
|
||||
},
|
||||
name: "Possession en cours de " + attacker.name, type: 'possession',
|
||||
img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
|
||||
system: {
|
||||
description: "", typepossession: attacker.name,
|
||||
possede: false,
|
||||
possessionid: randomID(16),
|
||||
entite: { actorid: attacker.id },
|
||||
victime: { actorid: defender.id },
|
||||
compteur: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
temporary: true
|
||||
})
|
||||
|
@ -91,13 +91,17 @@ export class RdDResolutionTable {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
|
||||
return await ChatUtility.createChatWithRollMode(actor?.userName ?? game.user.name, {
|
||||
content: await RdDResolutionTable.buildRollDataHtml(rollData, actor, template)
|
||||
return await ChatUtility.createChatWithRollMode(RdDResolutionTable.actorChatName(actor), {
|
||||
content: await RdDResolutionTable.buildRollDataHtml(rollData, template)
|
||||
});
|
||||
}
|
||||
|
||||
static actorChatName(actor) {
|
||||
return actor?.userName ?? game.user.name;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async buildRollDataHtml(rollData, actor, template = 'chat-resultat-general.html') {
|
||||
static async buildRollDataHtml(rollData, template = 'chat-resultat-general.html') {
|
||||
rollData.show = rollData.show || {};
|
||||
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
|
||||
}
|
||||
|
@ -314,8 +314,8 @@ export class RdDRoll extends Dialog {
|
||||
HtmlUtility.showControlWhen(this.html.find(".use-encTotal"), rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac));
|
||||
HtmlUtility.showControlWhen(this.html.find(".use-surenc"), rollData.ajustements.surenc.visible && RdDCarac.isActionPhysique(rollData.selectedCarac));
|
||||
HtmlUtility.showControlWhen(this.html.find(".utilisation-moral"), rollData.use.appelAuMoral);
|
||||
HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
|
||||
HtmlUtility.showControlWhen(this.html.find(".divAppelAuMoral"), rollData.use.appelAuMoral);
|
||||
HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
|
||||
|
||||
// Mise à jour valeurs
|
||||
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));
|
||||
|
@ -75,8 +75,8 @@ export class RdDTokenHud {
|
||||
(event) => {
|
||||
const actionIndex = event.currentTarget.attributes['data-action-index']?.value;
|
||||
const action = hudData.actions[actionIndex];
|
||||
if (action.action == 'conjurer') {
|
||||
const possession = combatant.actor.getPossession(action.system.possessionid);
|
||||
const possession = action.action == 'possession' ? combatant.actor.getPossession(action.system.possessionid) : undefined;
|
||||
if (possession) {
|
||||
combatant.actor.conjurerPossession(possession);
|
||||
}
|
||||
else {
|
||||
|
@ -15,6 +15,8 @@ import { RdDItemCompetence } from "./item-competence.js";
|
||||
import { RdDResolutionTable } from "./rdd-resolution-table.js";
|
||||
import { RdDTimestamp } from "./time/rdd-timestamp.js";
|
||||
import { RdDRaretes } from "./item/raretes.js";
|
||||
import { RdDEmpoignade } from "./rdd-empoignade.js";
|
||||
import { ExperienceLog } from "./actor/experience-log.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
// This table starts at 0 -> niveau -10
|
||||
@ -135,6 +137,7 @@ export class RdDUtility {
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/blessure.hbs',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/maladies-poisons.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/possessions.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/resonances.hbs',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/taches.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/taches.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.html',
|
||||
@ -179,7 +182,7 @@ export class RdDUtility {
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-caracteristiques.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-aspect-tarot.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-competence.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-categories.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-ingredient.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html',
|
||||
@ -282,6 +285,9 @@ export class RdDUtility {
|
||||
Handlebars.registerHelper('uniteQuantite', (itemId, actorId) => RdDUtility.getItem(itemId, actorId)?.getUniteQuantite());
|
||||
Handlebars.registerHelper('isFieldInventaireModifiable', (type, field) => RdDItem.isFieldInventaireModifiable(type, field));
|
||||
Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field));
|
||||
|
||||
Handlebars.registerHelper('experienceLog-topic', topic => ExperienceLog.labelTopic(topic));
|
||||
|
||||
return loadTemplates(templatePaths);
|
||||
}
|
||||
|
||||
@ -333,13 +339,13 @@ export class RdDUtility {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static buildArbreDeConteneurs(conteneurs, objets) {
|
||||
static buildArbreDeConteneurs(conteneurs, inventaires) {
|
||||
let objetVersConteneur = {};
|
||||
// Attribution des objets aux conteneurs
|
||||
for (let conteneur of conteneurs) {
|
||||
conteneur.subItems = [];
|
||||
for (let id of conteneur.system.contenu ?? []) {
|
||||
let objet = objets.find(objet => (id == objet._id));
|
||||
let objet = inventaires.find(objet => (id == objet._id));
|
||||
if (objet) {
|
||||
objet.estContenu = true; // Permet de filtrer ce qui est porté dans le template
|
||||
objetVersConteneur[id] = conteneur._id;
|
||||
@ -348,20 +354,20 @@ export class RdDUtility {
|
||||
}
|
||||
}
|
||||
for (let conteneur of conteneurs) {
|
||||
conteneur.system.encTotal = RdDUtility.calculEncContenu(conteneur, objets);
|
||||
conteneur.system.encTotal = RdDUtility.calculEncContenu(conteneur, inventaires);
|
||||
}
|
||||
return objetVersConteneur;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static calculEncContenu(conteneur, objets) {
|
||||
static calculEncContenu(conteneur, inventaires) {
|
||||
const contenus = (conteneur.system.contenu ?? []).filter(id => id != undefined)
|
||||
.map(id => objets.find(it => (id == it.id)))
|
||||
.map(id => inventaires.find(it => (id == it.id)))
|
||||
.filter(it => it);
|
||||
let enc = Number(conteneur.system.encombrement ?? 0) * Number(conteneur.system.quantite ?? 1);
|
||||
for (let contenu of contenus) {
|
||||
if (contenu.type == 'conteneur') {
|
||||
enc += RdDUtility.calculEncContenu(contenu, objets);
|
||||
enc += RdDUtility.calculEncContenu(contenu, inventaires);
|
||||
}
|
||||
else {
|
||||
enc += Number(contenu.system.encombrement ?? 0) * Number(contenu.system.quantite ?? 1)
|
||||
@ -662,6 +668,7 @@ export class RdDUtility {
|
||||
/* -------------------------------------------- */
|
||||
static async chatListeners(html) {
|
||||
RdDCombat.registerChatCallbacks(html);
|
||||
RdDEmpoignade.registerChatCallbacks(html);
|
||||
|
||||
// Gestion spécifique message passeurs
|
||||
html.on("click", '.tmr-passeur-coord a', event => {
|
||||
@ -820,17 +827,13 @@ export class RdDUtility {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static confirmerSuppressionSubacteur(sheet, subActor, htmlToDelete) {
|
||||
static confirmerSuppressionSubacteur(sheet, subActor, htmlToDelete, onSuppression = ()=>{}) {
|
||||
RdDConfirm.confirmer({
|
||||
settingConfirmer: "confirmation-supprimer-lien-acteur",
|
||||
content: `<p>Etes vous certain de vouloir supprimer le lien vers ${subActor.name} ?</p>`,
|
||||
title: 'Confirmer la suppression',
|
||||
buttonLabel: 'Supprimer le lien',
|
||||
onAction: () => {
|
||||
console.log('Delete : ', subActor.id);
|
||||
sheet.actor.removeSubacteur(subActor.id);
|
||||
RdDUtility.slideOnDelete(sheet, htmlToDelete);
|
||||
}
|
||||
onAction: onSuppression
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { RdDItemSort } from "./item-sort.js";
|
||||
import { Misc } from "./misc.js";
|
||||
import { RdDBonus } from "./rdd-bonus.js";
|
||||
import { RdDCarac } from "./rdd-carac.js";
|
||||
import { RdDPossession } from "./rdd-possession.js";
|
||||
import { RdDUtility } from "./rdd-utility.js";
|
||||
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
|
||||
|
||||
@ -30,7 +31,7 @@ export const referenceAjustements = {
|
||||
},
|
||||
diffLibre: {
|
||||
isUsed: (rollData, actor) => rollData.diffLibre != undefined,
|
||||
getLabel: (rollData, actor) => rollData.selectedSort?.name ?? rollData.attackerRoll ? 'Imposée' : 'Libre',
|
||||
getLabel: (rollData, actor) => rollData.selectedSort?.name ?? rollData.attackerRoll ?? RdDPossession.isDefensePossession(rollData) ? 'Imposée' : 'Libre',
|
||||
getValue: (rollData, actor) => rollData.selectedSort
|
||||
? RdDItemSort.getDifficulte(rollData.selectedSort, rollData.diffLibre)
|
||||
: rollData.diffLibre ?? rollData.competence?.system.default_diffLibre ?? 0
|
||||
@ -51,7 +52,7 @@ export const referenceAjustements = {
|
||||
getValue: (rollData, actor) => RdDBonus.find(rollData.surpriseDefenseur).attaque,
|
||||
},
|
||||
etat: {
|
||||
isUsed: (rollData, actor) => !RdDCarac.isIgnoreEtatGeneral(rollData),
|
||||
isUsed: (rollData, actor) => !RollDataAjustements.isIgnoreEtatGeneral(rollData),
|
||||
getLabel: (rollData, actor) => 'Etat général',
|
||||
getValue: (rollData, actor) => actor.getEtatGeneral({ ethylisme: rollData.forceAlcool != undefined })
|
||||
},
|
||||
@ -139,6 +140,12 @@ export const referenceAjustements = {
|
||||
isUsed: (rollData, actor) => rollData.ethylisme != undefined,
|
||||
getLabel: (rollData, actor) => "Ethylisme - " + RdDUtility.getNomEthylisme(rollData.ethylisme),
|
||||
getValue: (rollData, actor) => rollData.ethylisme,
|
||||
},
|
||||
tailleempoignade: {
|
||||
isVisible: (rollData, actor) => rollData.isEmpoignade,
|
||||
isUsed: (rollData, actor) => rollData.isEmpoignade,
|
||||
getLabel: (rollData, actor) => "Malus de taille",
|
||||
getValue: (rollData, actor) => rollData.malusTaille,
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,4 +178,11 @@ export class RollDataAjustements {
|
||||
return sum;
|
||||
}
|
||||
|
||||
static isIgnoreEtatGeneral(rollData) {
|
||||
const selectedCarac = rollData.selectedCarac;
|
||||
return !selectedCarac ||
|
||||
rollData.ethylisme ||
|
||||
RdDCarac.isChance(selectedCarac) ||
|
||||
(RdDCarac.isReve(selectedCarac) && !rollData.competence);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SYSTEM_RDD } from "../constants.js";
|
||||
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
|
||||
import { Misc } from "../misc.js";
|
||||
import { RdDTimestamp } from "../time/rdd-timestamp.js";
|
||||
|
||||
@ -57,7 +57,7 @@ export class AppAstrologie extends Application {
|
||||
if (this.actor) {
|
||||
return {
|
||||
actor: this.actor,
|
||||
nombres: this._organizeNombresAstraux(this.actor.listItems('nombreastral')),
|
||||
nombres: this._organizeNombresAstraux(this.actor.itemTypes['nombreastral']),
|
||||
ajustements: CONFIG.RDD.difficultesLibres,
|
||||
etat: this.actor.getEtatGeneral(),
|
||||
astrologie: this.actor.getCompetence('Astrologie')
|
||||
|
@ -116,6 +116,7 @@ export class RdDCalendrier extends Application {
|
||||
if (setting.key == SYSTEM_RDD + '.' + WORLD_TIMESTAMP_SETTING) {
|
||||
this.timestamp = RdDTimestamp.getWorldTime();
|
||||
this.positionAiguilles()
|
||||
this.render(false);
|
||||
Hooks.callAll(APP_ASTROLOGIE_REFRESH);
|
||||
}
|
||||
}
|
||||
@ -181,6 +182,16 @@ export class RdDCalendrier extends Application {
|
||||
return this.timestamp.formatDate();
|
||||
}
|
||||
|
||||
dateReel() {
|
||||
return new Date().toLocaleString("sv-SE", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
});
|
||||
}
|
||||
|
||||
isAfterIndexDate(indexDate) {
|
||||
// TODO: standardize
|
||||
return indexDate < this.timestamp.indexDate;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Misc } from "../misc.js";
|
||||
import { TYPES } from "../item.js";
|
||||
import { TMRUtility } from "../tmr-utility.js";
|
||||
import { PixiTMR } from "./pixi-tmr.js";
|
||||
|
||||
@ -9,10 +9,10 @@ const registeredEffects = [
|
||||
* Définition des informations d'une "draconique" (queue, ombre, tête, souffle) qui influence les TMR
|
||||
*/
|
||||
export class Draconique {
|
||||
static isCaseTMR(item) { return item.type == 'casetmr'; }
|
||||
static isQueueDragon(item) { return item.type == 'queue' || item.type == 'ombre'; }
|
||||
static isSouffleDragon(item) { return item.type == 'souffle'; }
|
||||
static isTeteDragon(item) { return item.type == 'tete'; }
|
||||
static isCaseTMR(item) { return item.type == TYPES.casetmr; }
|
||||
static isQueueDragon(item) { return item.isQueueDragon(); }
|
||||
static isSouffleDragon(item) {return item.type == TYPES.souffle; }
|
||||
static isTeteDragon(item) { return item.type == TYPES.tete; }
|
||||
static isQueueSouffle(item) { return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item); }
|
||||
|
||||
tmrLabel(linkData) { return TMRUtility.getTMRLabel(linkData.system.coord); }
|
||||
|
@ -18,6 +18,7 @@ import { Periple } from "./periple.js";
|
||||
import { UrgenceDraconique } from "./urgence-draconique.js";
|
||||
import { Grammar } from "../grammar.js";
|
||||
import { AugmentationSeuil } from "./augmentation-seuil.js";
|
||||
import { TYPES } from "../item.js";
|
||||
|
||||
|
||||
export class EffetsDraconiques {
|
||||
@ -114,46 +115,57 @@ export class EffetsDraconiques {
|
||||
);
|
||||
}
|
||||
|
||||
static filterItems(actor, filter, name) {
|
||||
return actor.filterItems(filter)
|
||||
.filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
|
||||
static tetesDragon(actor, name) {
|
||||
return actor.itemTypes[TYPES.tete].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
|
||||
}
|
||||
|
||||
static soufflesDragon(actor, name) {
|
||||
return actor.itemTypes[TYPES.souffle].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
|
||||
}
|
||||
|
||||
static queuesDragon(actor, name) {
|
||||
return actor.filterItems(it => it.isQueueDragon() && Grammar.includesLowerCaseNoAccent(it.name, name));
|
||||
}
|
||||
|
||||
static queuesSoufflesDragon(actor, name) {
|
||||
return actor.filterItems(it => [TYPES.queue, TYPES.ombre, TYPES.souffle].includes(it.type) && Grammar.includesLowerCaseNoAccent(it.name, name));
|
||||
}
|
||||
|
||||
static countAugmentationSeuil(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'Augmentation du seuil de rêve').length;
|
||||
return EffetsDraconiques.tetesDragon(actor, 'Augmentation du seuil de rêve').length;
|
||||
}
|
||||
|
||||
static isDonDoubleReve(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'Don de double-rêve').length>0;
|
||||
return EffetsDraconiques.tetesDragon(actor, 'Don de double-rêve').length > 0;
|
||||
}
|
||||
|
||||
static isConnaissanceFleuve(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'connaissance du fleuve').length>0;
|
||||
return EffetsDraconiques.tetesDragon(actor, 'connaissance du fleuve').length > 0;
|
||||
}
|
||||
|
||||
static isReserveEnSecurite(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, 'réserve en sécurité').length>0;
|
||||
return EffetsDraconiques.tetesDragon(actor, 'réserve en sécurité').length > 0;
|
||||
}
|
||||
|
||||
static isDeplacementAccelere(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isTeteDragon, ' déplacement accéléré').length>0;
|
||||
return EffetsDraconiques.tetesDragon(actor, 'déplacement accéléré').length > 0;
|
||||
}
|
||||
|
||||
static isDoubleResistanceFleuve(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isSouffleDragon, 'résistance du fleuve').length>0;
|
||||
return EffetsDraconiques.soufflesDragon(actor, 'résistance du fleuve').length > 0;
|
||||
}
|
||||
|
||||
static countInertieDraconique(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isQueueDragon, 'inertie draconique').length;
|
||||
return EffetsDraconiques.queuesDragon(actor, 'inertie draconique').length;
|
||||
}
|
||||
|
||||
static countMonteeLaborieuse(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isQueueSouffle, 'montée laborieuse').length;
|
||||
return EffetsDraconiques.queuesSoufflesDragon(actor, 'montée laborieuse').length;
|
||||
}
|
||||
|
||||
static mauvaiseRencontre(actor) {
|
||||
const mauvaisesRencontres = EffetsDraconiques.filterItems(actor, Draconique.isQueueSouffle, 'mauvaise rencontre');
|
||||
return mauvaisesRencontres.length>0 ? mauvaisesRencontres[0] : undefined;
|
||||
const mauvaisesRencontres = EffetsDraconiques.queuesSoufflesDragon(actor, 'mauvaise rencontre');
|
||||
return mauvaisesRencontres.length > 0 ? mauvaisesRencontres[0] : undefined;
|
||||
}
|
||||
|
||||
static isPontImpraticable(actor) {
|
||||
@ -165,11 +177,11 @@ export class EffetsDraconiques {
|
||||
}
|
||||
|
||||
static isSujetInsomnie(actor) {
|
||||
return actor.items.find(it => ['queue', 'ombre'].includes(it.type) && Grammar.includesLowerCaseNoAccent(it.name, 'Insomnie')) ? true : false;
|
||||
return EffetsDraconiques.queuesDragon(actor, 'Insomnie').length > 0 ? true : false;
|
||||
}
|
||||
|
||||
static isPeage(actor) {
|
||||
return EffetsDraconiques.filterItems(actor, Draconique.isSouffleDragon, 'péage').length > 0;
|
||||
return EffetsDraconiques.soufflesDragon(actor, 'péage').length > 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ExperienceLog, XP_TOPIC } from "../actor/experience-log.js";
|
||||
import { ChatUtility } from "../chat-utility.js";
|
||||
import { Poetique } from "../poetique.js";
|
||||
import { RdDDice } from "../rdd-dice.js";
|
||||
@ -53,9 +54,10 @@ export class EffetsRencontre {
|
||||
static xp_sort_force = async (dialog, context) => {
|
||||
let competence = context.competence;
|
||||
if (competence) {
|
||||
const xpSort = Misc.toInt(competence.system.xp_sort) + context.rencontre.system.force;
|
||||
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': xpSort }]);
|
||||
await this.updateExperienceLog("XP Sort", xpSort, `Rencontre d'un ${context.rencontre.name} en TMR`);
|
||||
const fromXpSort = Number(competence.system.xp_sort);
|
||||
const toXpSort = fromXpSort + context.rencontre.system.force;
|
||||
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': toXpSort }]);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XPSORT, fromXpSort, toXpSort, `${competence.name} - ${context.rencontre.name} en TMR`);
|
||||
}
|
||||
}
|
||||
|
||||
|
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
@ -23,7 +23,7 @@
|
||||
{"_id":"UQYy9WjsKqqrjLc7","name":"Guerrier Sorde","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.UQYy9WjsKqqrjLc7"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"system":{"description":"<p>@JournalEntry[vid6uJc66QFgHSUr]{Note sur les invocations de créatures}</p>\n<p>Le Guerrier Sorde a l’apparence d’un humanoïde entièrement revêtu d’une armure de plaques, visière du heaume toujours baissée, dissimulant son visage. Il est armé d’une épée sorde, d’un bouclier moyen et d’une dague. La seule tâche que l’on puisse lui demander est de faire usage de ses armes. Une fois invoqué, il attaque toute créature que le haut-rêvant lui désigne expressément, et se bat contre elle jusqu’à ce qu’il l’extermine ou reçoive un contre-ordre. On peut alors lui ordonner de commencer un autre combat, à condition que le délai entre deux combats n’excède pas 10 rounds, faute de quoi il considère sa tâche accomplie et se dématérialise. S’il est invoqué alors qu’il n’y a pas de créature à combattre immédiatement, il ne patiente que jusqu’à la fin de l’heure en cours, après quoi il s’estime dérangé pour rien et se dématérialise. Durant le délai, il peut accompagner le haut-rêvant où qu’il aille, mais sans pouvoir s’éloigner de lui de plus de E1 mètres. Sa vitesse est limitée à 12 m/round, il ne court, n’escalade ni ne nage jamais. Il n’obéit qu’au haut-rêvant qui l’a invoqué. Le rituel peut être répété pour invoquer plusieurs Guerriers Sordes dans un même combat. Tous ont les mêmes caractéristiques. La PERCEPTION indiquée tient compte des malus dus au heaume. Les Guerriers Sordes sont normalement affectés par les suggestions et illusions d’Hypnos, avec un JR standard r-8, ainsi que par les sorts individuels de Thananatos.</p>\n<p>@JournalEntry[R3q4vUTEfyxYgmGr]{Communication avec les créatures invoquées}</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Tâche","JR":"Aucun","cible":"","difficulte":"-8","portée":"","caseTMR":"special","caseTMRspeciale":"Cité Sordide D13","ptreve":"7","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426561,"modifiedTime":1667260032848,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"VG89vfk7KsO01eJv","name":"Secouriste blanc","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.VG89vfk7KsO01eJv"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"system":{"description":"<p>@JournalEntry[vid6uJc66QFgHSUr]{Note sur les invocations de créatures}</p>\n<p>Le Secouriste blanc ne peut être invoqué que pendant un combat ou au terme de celui-ci. Sa tâche consiste à prendre les blessés en charge, premiers soins et soins complets, ce qu’il fait spontanément. En plein combat, il peut même s’approcher de la mêlée pour tirer en arrière d’éventuels tombés à terre. Quand il a plusieurs blessés à soigner, il commence par le plus proche de lui. Mais l’invocateur peut lui désigner un blessé à soigner en priorité.</p>\n<p>Le Secouriste possède son propre matériel de chirurgie, mais il n’a aucun consommable (eau, chiffons) qu’il faut lui fournir.</p>\n<p>Pour la résolution des soins, le Secouriste ne joue pas de jet de Chirurgie. À la place, c’est le blessé qui joue des jets de CHANCE, pour déterminer les points de tâche obtenus :</p>\n<table style=\"height: 102px; width: 260px;\" border=\"0\">\n<tbody>\n<tr style=\"height: 17px;\">\n<td style=\"height: 17px; width: 119px;\">Particulière :</td>\n<td style=\"height: 17px; width: 134px;\">4 points</td>\n</tr>\n<tr style=\"height: 17px;\">\n<td style=\"width: 119px; height: 17px;\">Significative :</td>\n<td style=\"width: 134px; height: 17px;\">3 points</td>\n</tr>\n<tr style=\"height: 17px;\">\n<td style=\"width: 119px; height: 17px;\">Normale :</td>\n<td style=\"width: 134px; height: 17px;\">2 points</td>\n</tr>\n<tr style=\"height: 17px;\">\n<td style=\"width: 119px; height: 17px;\">Échec :</td>\n<td style=\"width: 134px; height: 17px;\">1 pt</td>\n</tr>\n<tr style=\"height: 17px;\">\n<td style=\"width: 119px; height: 17px;\">Échec particulier :</td>\n<td style=\"width: 134px; height: 17px;\">0 pt</td>\n</tr>\n<tr style=\"height: 17px;\">\n<td style=\"width: 119px; height: 17px;\">Échec total :</td>\n<td style=\"width: 134px; height: 17px;\">0 pt (et sans malus)</td>\n</tr>\n</tbody>\n</table>\n<p>Le Secouriste disparaît dès que le combat est terminé ET que tous les blessés ont été soignés.</p>\n<p>@JournalEntry[R3q4vUTEfyxYgmGr]{Communication avec les créatures invoquées}</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Tâche ou fin HN","JR":"Aucun","cible":"","difficulte":"-8","portée":"","caseTMR":"special","caseTMRspeciale":"Sanctuaire Blanc G4","ptreve":"7","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426561,"modifiedTime":1667260032851,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"WvTkEYb216X0XiJc","name":"Voix d'Hypnos","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.WvTkEYb216X0XiJc"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"system":{"description":"<p>Les rituels de lecture d’Hypnos sont d’étonnantes démonstrations d’auto-suggestion. Aidé par un support, le haut-rêvant se persuade qu’il peut voir ou entendre à distance, et il le fait. Couplé à l’ultime forme d’illusion que sont les invocations, il peut pareillement projeter son image ou sa voix à distance. Tous obéissent aux règles usuelles d’application des rituels. Si un rituel de lecture est paramétré sur une personne et que celle-ci est morte ou a changé de rêve, aucun effet ne se produit, mais les points de rêve sont tout de même dépensés.</p>\n<p>Le rituel de Voix d’Hypnos permet de détecter le mensonge. Il n’y a pas de véritable ciblage, le rituel opère sur le haut-rêvant directement sans passer par un support. Lors du paramétrage, le haut-rêvant se reporte à une certaine conversation de son choix, datant au maximum de 12 heures. La conversation peut avoir eu plusieurs interlocuteurs, mais Voix d’Hypnos ne fonctionne que sur un seul d’entre eux à la fois.</p>\n<p>Pour les détecter tous, il faut recommencer autant de fois le rituel. Puis, le sort étant ciblé sur lui-même, le haut-rêvant se plonge dans un état hypnotique dans lequel il réentend toute la conversation, comme si on repassait la bande. La durée de réécoute est d’un round, quelle qu’ait été la conversation, le temps mental du haut-rêvant devenant élastique. Tant que son interlocuteur dit la vérité, sa voix est mélodieuse ; dès qu’il ment <em>volontairement</em>, elle devient horrible et grinçante. On ne peut ainsi détecter que les mensonges volontaires, pas les mensonges inconscients ou par omission.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"1 round","JR":"Aucun","cible":"Soi-même","difficulte":"-4","portée":"","caseTMR":"desert","caseTMRspeciale":"","ptreve":"4","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426562,"modifiedTime":1667260032852,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"Y4r9kTN2brWC2N0n","name":"Lecture d'aura","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.Y4r9kTN2brWC2N0n"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_vigilance.webp","effects":[],"system":{"description":"<p>Les rituels de Lecture d’Aura peuvent indifféremment être accomplis par n’importe laquelle des quatre voies.</p>\n<p>Ce rituel permet d’approfondir les informations fournies par Détection d’Aura. Pratiquer Lecture d’Aura quand il n’y a pas d’aura revient à demander une magie impossible et crée immédiatement une déchirure du rêve.</p>\n<p>Lecture d’Aura est effectuée en plusieurs étapes, toutes étant de difficulté R-3 et coûtant 3 points de rêve. La première a toujours lieu dans un sanctuaire et ne fait que révéler dans quel(s) autre(s) genre(s) de case(s) le haut-rêvant doit se rendre pour continuer sa lecture. Là, il apprend quel genre de magie a été produit ou à quel type de rêve il a affaire, de même que les cases spécifiques concernées. Enfin dans les cases spécifiques, le haut-rêvant peut apprendre la force du rêve ou de la magie en cours, c’est-à-dire pratiquement la difficulté et le nombre de points de rêve impliqués, information indispensable dans l’optique d’une annulation de magie.</p>\n<p>Lecture d’Aura révèle également la couleur de l’aura (fixe ou pulsative) comme Détection d’Aura. Pour les créatures vivantes, on peut donc sauter l’étape de Détection d’Aura et commencer directement par la lecture, puisqu’on est sûr de trouver une aura. Dans les autres cas, il est plus prudent de commencer par la détection si, en l’absence finale d’une aura, on ne veut pas créer de magie impossible. Effectuée sur une créature non soumise à un effet magique ni sous l’emprise d’une entité, Lecture d’Aura indique toujours le Fleuve. Là, dans n’importe quelle case du Fleuve, le haut-rêvant se contente d’apprendre qu’il a affaire à une créature vivante et douée de rêve.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Instantanée","JR":"Aucun","cible":"","difficulte":"-3","portée":"","caseTMR":"special","caseTMRspeciale":"Sanctuaire / variable","ptreve":"3","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0,"portee":""},"ownership":{"default":0,"Q2G6GTdrotKzYGUC":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426562,"modifiedTime":1667260032849,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"Y4r9kTN2brWC2N0n","name":"Lecture d'aura","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.Y4r9kTN2brWC2N0n"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_vigilance.webp","effects":[],"system":{"description":"<p>Les rituels de Lecture d’Aura peuvent indifféremment être accomplis par n’importe laquelle des quatre voies.</p>\n<p>Ce rituel permet d’approfondir les informations fournies par Détection d’Aura. Pratiquer Lecture d’Aura quand il n’y a pas d’aura revient à demander une magie impossible et crée immédiatement une déchirure du rêve.</p>\n<p>Lecture d’Aura est effectuée en plusieurs étapes, toutes étant de difficulté R-3 et coûtant 3 points de rêve. La première a toujours lieu dans un sanctuaire et ne fait que révéler dans quel(s) autre(s) genre(s) de case(s) le haut-rêvant doit se rendre pour continuer sa lecture. Là, il apprend quel genre de magie a été produit ou à quel type de rêve il a affaire, de même que les cases spécifiques concernées. Enfin dans les cases spécifiques, le haut-rêvant peut apprendre la force du rêve ou de la magie en cours, c’est-à-dire pratiquement la difficulté et le nombre de points de rêve impliqués, information indispensable dans l’optique d’une annulation de magie.</p>\n<p>Lecture d’Aura révèle également la couleur de l’aura (fixe ou pulsative) comme Détection d’Aura. Pour les créatures vivantes, on peut donc sauter l’étape de Détection d’Aura et commencer directement par la lecture, puisqu’on est sûr de trouver une aura. Dans les autres cas, il est plus prudent de commencer par la détection si, en l’absence finale d’une aura, on ne veut pas créer de magie impossible. Effectuée sur une créature non soumise à un effet magique ni sous l’emprise d’une entité, Lecture d’Aura indique toujours le Fleuve. Là, dans n’importe quelle case du Fleuve, le haut-rêvant se contente d’apprendre qu’il a affaire à une créature vivante et douée de rêve.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Instantanée","JR":"Aucun","cible":"","difficulte":"-3","portée":"","caseTMR":"special","caseTMRspeciale":"Sanctuaire / variable","ptreve":"3","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0,"portee":""},"ownership":{"default":0,"Q2G6GTdrotKzYGUC":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.7.6","coreVersion":"10.291","createdTime":1667259426562,"modifiedTime":1681495249423,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"YOJsOLpHTQYreZ6i","name":"Soufflet","type":"sort","img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.YOJsOLpHTQYreZ6i"}},"system":{"description":"<p>Le Soufflet peut être dirigé contre toute créature, humanoïde ou animale. Son effet, instantané, est celui d’une gifle magistrale, causant ses dommages sur la table des Coups non mortels. Le +dom de l’agression est égal au nombre de points de rêve dépensés. Sauf pour les animaux qui peuvent faire jouer entièrement leur protection naturelle, la protection applicable peut être au maximum de 2 points.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Instantanée","JR":"Humanoïde selon HN, animal r-8","cible":"Toutes créatures","difficulte":"-6","portée":"","caseTMR":"gouffre","caseTMRspeciale":"","ptreve":"1+","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0,"portee":""},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426562,"modifiedTime":1667260032851,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"aYOfXEuDp6xGDO4N","name":"Égarement","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.aYOfXEuDp6xGDO4N"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"system":{"description":"<p>L’effet d’Égarement ne s’applique qu’aux humanoïdes. L’apport massif de pseudo-souvenirs, informulés et insaisissables, empêche l’humanoïde visé de se concentrer sur son activité intellectuelle, manuelle ou verbale. Il ne fait plus ou ne dit plus que des <em>bêtises</em>, en termes de jeu des échecs totaux. Un intellectuel devient incapable de lire ou d’écrire, un artisan se tape sur les doigts, un musicien rate tous ses accords, un orateur bafouille, etc. L’état d’égarement dure jusqu’à la fin de l’heure en cours + une heure complète, ou se dissipe de lui-même dès qu’il y a stress, par exemple une agression. Ce sort est donc totalement inutile et inefficace en combat.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Une heure","JR":"Selon HN","cible":"Humanoïde","difficulte":"-4","portée":"","caseTMR":"desolation","caseTMRspeciale":"","ptreve":"4","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426562,"modifiedTime":1667260032847,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"cghxHRstw7cXLEm4","name":"Invoquer son image","type":"sort","img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.cghxHRstw7cXLEm4"}},"system":{"description":"<p>Les rituels de lecture d’Hypnos sont d’étonnantes démonstrations d’auto-suggestion. Aidé par un support, le haut-rêvant se persuade qu’il peut voir ou entendre à distance, et il le fait. Couplé à l’ultime forme d’illusion que sont les invocations, il peut pareillement projeter son image ou sa voix à distance. Tous obéissent aux règles usuelles d’application des rituels. Si un rituel de lecture est paramétré sur une personne et que celle-ci est morte ou a changé de rêve, aucun effet ne se produit, mais les points de rêve sont tout de même dépensés.</p>\n<p>Ce rituel est comme le négatif de @Item[Ew5JzQ2lzcpGoF11]{Miroir d'Hypnos}. Les conditions de ciblage et de paramétrage en sont exactement les mêmes. Lorsqu’un mouvement apparaît au centre du miroir, provoquant l’état hypnotique, le haut-rêvant peut commencer à effectuer des gestes, des mimiques, ou montrer ostensiblement un objet qu’il tient sur lui, mais sans pouvoir se déplacer. Dans l’instant même, un hologramme de lui-même, grandeur nature et fidèle jusqu’au moindre geste, prend naissance près de la personne ou au centre du lieu choisi.</p>\n<p>Les spectateurs peuvent se déplacer à travers l’hologramme, ce n’est qu’une illusion sans substance. Par ce rituel, le haut-rêvant ne peut communiquer aucun son, et lui-même n’entend ni ne voit rien. Il ne peut pas savoir comment est accueillie sa \"visite\". La communication est de 1 round par point de rêve dépensé.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Selon r dépensé","JR":"Aucun","cible":"Un miroir","difficulte":"-6","portée":"","caseTMR":"sanctuaire","caseTMRspeciale":"","ptreve":"1+","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0,"portee":""},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426562,"modifiedTime":1667260032849,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
@ -39,7 +39,7 @@
|
||||
{"_id":"qqcLydulFkL25Ipc","name":"Conjurer l'oubli","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.qqcLydulFkL25Ipc"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"system":{"description":"<p>Ce rituel permet de faire renaître chez la cible un souvenir oublié, la cause de l’oubli pouvant être magique ou naturelle. Le souvenir oublié peut appartenir à une précédente incarnation si le gardien des rêves l’estime possible ou pertinent. Dans tous les cas, le souvenir ne peut revenir que sous la forme d’une <em>réponse </em>à une <em>question </em>précise. Et l’accomplissement du rituel ne permet qu’une seule question-réponse.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Une question","JR":"Aucun","cible":"Humanoïde","difficulte":"-4","portée":"","caseTMR":"lac","caseTMRspeciale":"","ptreve":"4","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426564,"modifiedTime":1667260032846,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"rrSE9c7KKsqcKueo","name":"Nonechalepasse","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.rrSE9c7KKsqcKueo"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"system":{"description":"<p>@JournalEntry[vid6uJc66QFgHSUr]{Note sur les invocations de créatures}</p>\n<p>Le Nonechalepasse a la même apparence physique et le même armement que le Guerrier Sorde. C’est en fait une variante de ce dernier. Il est invoqué pour garder ou veiller sur quelque chose : une porte, un coffre, un pont, etc. L’ayant invoqué, le haut-rêvant doit lui indiquer expressément sur quoi il doit veiller, et le Nonechalepasse ne laissera personne d’autre que l’invocateur franchir la limite indiquée, c’est-à-dire pas même ses compagnons. La garde peut avoir lieu en la présence du haut-rêvant, ou en son absence s’il désire vaquer à d’autres affaires, et dure jusqu’à la fin de son heure de naissance. Dès qu’une créature est en voie d’enfreindre la consigne donnée, le Nonechalepasse l’en prévient en clamant son propre nom à plusieurs reprises ; et si la créature insiste, il la combat jusqu’à ce qu’il l’extermine ou qu’elle recule et s’enfuie. Les Nonechalepasses ont tous les mêmes caractéristiques que les Guerriers Sordes et sont comme eux affectés par les sorts.</p>\n<p>@JournalEntry[R3q4vUTEfyxYgmGr]{Communication avec les créatures invoquées}</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Tâche","JR":"Aucun","cible":"","difficulte":"-8","portée":"","caseTMR":"special","caseTMRspeciale":"Cité Jalouse M1","ptreve":"7","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426564,"modifiedTime":1667260032850,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"sVA94h9Reimmfw5B","name":"Suggestion","type":"sort","img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.sVA94h9Reimmfw5B"}},"system":{"description":"<p>L’effet de Suggestion ne s’applique qu’aux humanoïdes. Comme l’indique le titre, c’est la suggestion à l’état pur. Il permet de donner un ordre bref à la victime, et cette dernière ne pourra pas s’empêcher d’y obéir machinalement. Il est impératif que la victime puisse obéir à l’ordre de façon <em>immédiate</em>, dans la seconde qui suit l’ordre, et que l’action soit uniquement <em>physique</em>, ni mentale ni réfléchie. Si la Suggestion est telle qu’elle oblige la victime à une autre action préalable ou si l’action demandée ne peut être qu’une action réfléchie, pensée, la Suggestion avorte automatiquement.</p>\n<p>Un passant a sa bourse accrochée à la ceinture. On lui donne l’ordre de suggestion : \"donne ta bourse !\" L’effet avorte automatiquement. En effet, le passant doit d’abord en dénouer les cordons, action préalable, et qui plus est réfléchie. Si le passant avait déjà sa bourse à la main, l’ordre : \"donne ta bourse !\" ou \"donne\"! tout court, pouvant être obéi de façon <em>immédiate</em>, aurait été accepté.</p>\n<p>Des ordres tels que \"réponds à la question \" ou \" dis la vérité\" sont pareillement inacceptables (actions mentales). Si la victime est au bord d’un gouffre, et qu’on lui suggère : \" saute dans le gouffre !\", elle saute. Si elle est à trois mètres du gouffre, la suggestion avorte. Il faut d’abord qu’elle y coure. Des ordres tels que : \"Fuis, saute, plonge, assieds-toi, agenouille-toi, lève les bras, ferme les yeux, hurle, donne (ce que la victime a déjà en main), mange ou bois (ce que la victime a déjà à portée de ses lèvres), lâche (ce qu’elle tient en main), etc.\" sont possibles. Des ordres tels que : \"endors-toi, suicide-toi, va faire ceci, déshabille-toi (actions multiples), écris ceci, avoue, lance tel sort, etc.\" sont impossibles.</p>\n<p>L’ordre donné dans la suggestion doit être unique, c’est-à-dire pratiquement ne comporter qu’un seul verbe. \"Cours et saute !\" est impossible. Quand l’action implique une durée, elle est obéie pendant un round. Si par exemple l’ordre donné est \"cours !\" ou \"fuis !\", la victime courra, fuira, pendant un round. À ce moment, toutefois, l’ordre pourra être donné une seconde fois, et la victime obéira pour la durée d’un nouveau round.</p>\n<p>L’ordre contenu dans la suggestion doit être paramétré lors du lancer. Mais le ciblage de la victime ne le déclenche pas aussitôt. La victime étant maintenant sous l’effet du sort, il faut que l’ordre soit donné réellement, <em>verbalement</em>. La victime doit pouvoir l’entendre et le comprendre (parler la même langue). Peu importe qui donne l’ordre verbal, le haut-rêvant ou quelqu’un d’autre. Chaque 3r dépensés permet verbalement de réitérer l’ordre une fois. Si par exemple 9 points de rêve ont été dépensés, l’ordre \"cours !\" pourra être donné trois fois. Il n’y a aucune limite de temps entre le ciblage et le moment où le premier ordre est donné verbalement, ni non plus entre chaque ordre. Tant que le dernier ordre n’a pas été donné, la victime est sous l’influence du sort, influence qui peut être détectée et lue par Lecture d’Aura. Le libellé de l’ordre est également révélé dans la case spécifique par Lecture d’Aura, et le sort peut être annulé dans cette même case. Dès que le dernier ordre est donné, l’effet se dissipe totalement.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Spéciale","JR":"Selon HN","cible":"Humanoïde","difficulte":"-9","portée":"","caseTMR":"desert","caseTMRspeciale":"","ptreve":"3+","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0,"portee":""},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426564,"modifiedTime":1667260032851,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"xOicgRMCUxJNmVzF","name":"Détection d'aura","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.xOicgRMCUxJNmVzF"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_vigilance.webp","effects":[],"system":{"description":"<p>Les rituels de Détection d’Aura peuvent indifféremment être accomplis par n’importe laquelle des quatre voies.</p>\n<p>Toutes les créatures vivantes animées (humains, humanoïdes, animaux) ont une caractéristique RÊVE. Les objets enchantés possèdent des points de rêve, de même que les produits de magie naturelle, comme certaines pierres de chance. Les entités de cauchemar, incarnées ou non, en ont également. Quelle qu’elle soit, la présence de rêve émet une aura, laquelle est détectable par Détection d’Aura. Parallèlement, toute cible d’un sort ou d’un rituel, émet une aura propre, quand bien même ladite cible ne possède pas de points de rêve (centre de zone, objet ou plante soumis à une illusion d’Hypnos). Cette aura est également détectable par Détection d’Aura.</p>\n<p>L’aura de présence de rêve se traduit par un halo bleuté constant ; l’aura résultant d’un effet magique par un halo parcouru de pulsations. Quand les deux auras sont présentes conjointement, le halo est pulsatif et d’un bleu plus foncé. On peut toujours effectuer Détection d’Aura sans aucun risque, il y a toujours une réponse. Soit une aura est perçue, constante ou pulsative, et l’on peut tenter une Lecture d’Aura pour en savoir plus ; soit aucune aura n’est perçue et il s’agit de matière inerte, sans rêve, non soumise à un sort.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Instantanée","JR":"Aucun","cible":"","difficulte":"-3","portée":"","caseTMR":"sanctuaire","caseTMRspeciale":"","ptreve":"1","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0,"portee":""},"ownership":{"default":0,"Q2G6GTdrotKzYGUC":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426564,"modifiedTime":1667260032847,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"xOicgRMCUxJNmVzF","name":"Détection d'aura","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.xOicgRMCUxJNmVzF"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_vigilance.webp","effects":[],"system":{"description":"<p>Les rituels de Détection d’Aura peuvent indifféremment être accomplis par n’importe laquelle des quatre voies.</p>\n<p>Toutes les créatures vivantes animées (humains, humanoïdes, animaux) ont une caractéristique RÊVE. Les objets enchantés possèdent des points de rêve, de même que les produits de magie naturelle, comme certaines pierres de chance. Les entités de cauchemar, incarnées ou non, en ont également. Quelle qu’elle soit, la présence de rêve émet une aura, laquelle est détectable par Détection d’Aura. Parallèlement, toute cible d’un sort ou d’un rituel, émet une aura propre, quand bien même ladite cible ne possède pas de points de rêve (centre de zone, objet ou plante soumis à une illusion d’Hypnos). Cette aura est également détectable par Détection d’Aura.</p>\n<p>L’aura de présence de rêve se traduit par un halo bleuté constant ; l’aura résultant d’un effet magique par un halo parcouru de pulsations. Quand les deux auras sont présentes conjointement, le halo est pulsatif et d’un bleu plus foncé. On peut toujours effectuer Détection d’Aura sans aucun risque, il y a toujours une réponse. Soit une aura est perçue, constante ou pulsative, et l’on peut tenter une Lecture d’Aura pour en savoir plus ; soit aucune aura n’est perçue et il s’agit de matière inerte, sans rêve, non soumise à un sort.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Instantanée","JR":"Aucun","cible":"","difficulte":"-3","portée":"","caseTMR":"sanctuaire","caseTMRspeciale":"","ptreve":"1","xp":0,"bonuscase":"","isrituel":true,"coutseuil":0,"portee":""},"ownership":{"default":0,"Q2G6GTdrotKzYGUC":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.7.6","coreVersion":"10.291","createdTime":1667259426564,"modifiedTime":1681495242408,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"yNMa8DlBaZyTGFSr","name":"Oubli","type":"sort","img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.yNMa8DlBaZyTGFSr"}},"system":{"description":"<p>L’effet d’Oubli ne s’applique qu’aux humanoïdes. C’est une des plus puissantes suggestions hypnotiques. L’effet d’amnésie ne survient pas aussitôt le sort ciblé, mais intervient à la fin de l’heure en cours. À ce moment la victime perd tout souvenir de ce qu’elle a vécu, de ce qu’elle a pu dire ou faire, entre le moment présent et celui où le sort a été ciblé. Pratiquement, cette période est comme un grand trou noir dans sa tête, et aucun moyen normal ne peut lui restituer ses souvenirs. Chaque point de rêve dépensé en plus des 6 de base augmente la durée d’une heure. Soit un haut-rêvant lançant ce sort au cours de l’heure du Dragon et dépensant 8 points : à la fin de l’heure de la Lyre, la victime se retrouve brusquement amnésique de ce qu’elle a pu faire depuis la mi-Dragon jusqu’à maintenant, sans comprendre comment elle est arrivée dans le lieu où elle se trouve actuellement, comme si elle venait de se réveiller d’une période de sommeil noir, encore plus opaque que le gris rêve. Une Lecture d’Aura révèle la présence d’un sort d’Oubli en train d’œuvrer, et Annulation de Magie peut l’annuler selon les règles normales. À défaut, le rituel de Conjurer l’Oubli peut être utilisé, mais ne restitue les souvenirs que sélectivement, en réponse à une question précise. Quand c’est un personnage de joueur qui lance ce sort sur un PNJ, sa mise en œuvre est sans problème. L’inverse est plus délicat. Le mieux est alors de faire sortir de la salle le joueur du personnage victime, et de le faire rentrer au moment où l’amnésie opère. De cette façon, il ne se souvient effectivement de rien. Entre temps, si besoin est par rapport aux autres joueurs, jouer ce personnage comme un PNJ.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Selo n r dépensé","JR":"Selon HN","cible":"Humanoïde","difficulte":"-8","portée":"","caseTMR":"fleuve","caseTMRspeciale":"","ptreve":"6+","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0,"portee":""},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426564,"modifiedTime":1667260032850,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"yhw8f7HKrmfzAxmj","name":"Sérénité","type":"sort","img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.yhw8f7HKrmfzAxmj"}},"system":{"description":"<p>L’effet de sérénité ne s’applique qu’aux humanoïdes. La suggestion d’une intense satisfaction est source d’une sérénité réelle. Pour chaque 3r dépensés, le personnage visé regagne 1 point de moral jusqu’à concurrence de zéro. Neuf points de rêve permettent ainsi de remonter un moral de -3 à zéro. Si trop de points sont dépensés, l’excédent est perdu, le moral ne pouvant dépasser zéro par l’influence de ce sort.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Instantanée","JR":"Selon HN","cible":"Humanoïde","difficulte":"-3","portée":"","caseTMR":"collines","caseTMRspeciale":"","ptreve":"3+","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0,"portee":""},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426564,"modifiedTime":1667260032851,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
{"_id":"ze53LdwhuqUFMvqw","name":"Fou-rire","type":"sort","flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.sorts-hypnos.ze53LdwhuqUFMvqw"}},"img":"systems/foundryvtt-reve-de-dragon/icons/competence_hypnos.webp","effects":[],"system":{"description":"<p>L’effet de Fou-Rire ne s’applique qu’aux humanoïdes. La suggestion d’une idée drolatique plonge la cible dans un irrépressible fou-rire, automatique le premier round. Puis pour les rounds suivants, la cible doit réussir un jet de VOLONTÉ/ <em>moins </em>moral à -5, ou continuer à rire. Tant que la cible rit, elle est considérée en <em>demi-surprise</em>.</p>","descriptionmj":"","draconic":"Voie d'Hypnos","duree":"Instantanée","JR":"Selon HN","cible":"Humanoïde","difficulte":"-5","portée":"","caseTMR":"cite","caseTMRspeciale":"","ptreve":"5","xp":0,"bonuscase":"","isrituel":false,"coutseuil":0},"ownership":{"default":0,"jOzRscDxoXZWpGS6":3},"folder":null,"sort":0,"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.0.32","coreVersion":"10.288","createdTime":1667259426564,"modifiedTime":1667260032847,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
|
||||
|
@ -1609,14 +1609,18 @@ table.table-nombres-astraux tr:hover {
|
||||
|
||||
|
||||
.window-app.calendar {
|
||||
display: inline-block;
|
||||
background: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
pointer-events: none;
|
||||
max-height: fit-content;
|
||||
}
|
||||
|
||||
.window-app.calendar header.window-header {
|
||||
min-width: fit-content;
|
||||
height: 1.4rem;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@ -1632,7 +1636,14 @@ table.table-nombres-astraux tr:hover {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.window-app.calendar .window-content > div {
|
||||
.window-app.calendar .window-content div:is(.calendar-boutons-heure, .horloge-digitale) {
|
||||
pointer-events: all;
|
||||
}
|
||||
.window-app.calendar .window-content div.horloge-analogique {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.window-app.calendar .window-content div.horloge-analogique div.horloge-roue {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
@ -1783,15 +1794,12 @@ div.horloge-roue div img {
|
||||
div.horloge-analogique {
|
||||
border: none;
|
||||
margin: 0;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
div.horloge-analogique.horloge-analogique-hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
div.horloge-digitale {
|
||||
color: #CCC;
|
||||
background: hsla(0, 0%, 20%, 1);
|
||||
|
@ -1,12 +1,13 @@
|
||||
{
|
||||
"id": "foundryvtt-reve-de-dragon",
|
||||
"title": "Rêve de Dragon",
|
||||
"version": "10.7.6",
|
||||
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-10.7.6.zip",
|
||||
"version": "10.7.21",
|
||||
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-10.7.21.zip",
|
||||
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v10/system.json",
|
||||
"compatibility": {
|
||||
"minimum": "10",
|
||||
"verified": "10"
|
||||
"verified": "10",
|
||||
"maximum": "10"
|
||||
},
|
||||
"description": "Rêve de Dragon RPG for FoundryVTT",
|
||||
"authors": [
|
||||
@ -379,4 +380,4 @@
|
||||
"flags": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -538,7 +538,7 @@
|
||||
"service",
|
||||
"meditation", "rencontre", "queue", "ombre", "souffle", "tete", "casetmr", "signedraconique", "sort", "sortreserve",
|
||||
"nombreastral", "tache", "blessure", "maladie", "poison", "possession",
|
||||
"tarot", "extraitpoetique"
|
||||
"tarot", "extraitpoetique", "empoignade"
|
||||
],
|
||||
"templates": {
|
||||
"description": {
|
||||
@ -586,25 +586,42 @@
|
||||
},
|
||||
"competencecreature": {
|
||||
"templates": ["description"],
|
||||
"categorie_parade": "",
|
||||
"carac_value": 0,
|
||||
"niveau": 0,
|
||||
"default_diffLibre": 0,
|
||||
"categorie": "",
|
||||
"carac_value": 0,
|
||||
"categorie_parade": "",
|
||||
"iscombat": false,
|
||||
"isnaturelle": true,
|
||||
"ispossession": false,
|
||||
"dommages": 0
|
||||
},
|
||||
"empoignade": {
|
||||
"templates": ["description"],
|
||||
"empoignadeid": "",
|
||||
"empoigneurid": "",
|
||||
"empoigneid": "",
|
||||
"pointsemp": 0,
|
||||
"empoigneurname": "",
|
||||
"empoignename": "",
|
||||
"ausol": false
|
||||
},
|
||||
"possession": {
|
||||
"templates": ["description"],
|
||||
"typepossession": "",
|
||||
"possede": false,
|
||||
"possessionid": "",
|
||||
"possesseurid": "",
|
||||
"possedeid": "",
|
||||
"compteur": 0,
|
||||
"date": 0
|
||||
"entite": {
|
||||
"actorid": "",
|
||||
"diffLibre": 0,
|
||||
"finesse": false
|
||||
},
|
||||
"victime": {
|
||||
"actorid": "",
|
||||
"diffLibre": 0,
|
||||
"finesse": false
|
||||
},
|
||||
"compteur": 0
|
||||
},
|
||||
"blessure": {
|
||||
"templates": ["temporel"],
|
||||
@ -682,7 +699,7 @@
|
||||
"capacite": 0
|
||||
},
|
||||
"objet": {
|
||||
"templates": ["description", "inventaire"]
|
||||
"templates": ["description", "equipement", "inventaire"]
|
||||
},
|
||||
"monnaie": {
|
||||
"templates": ["description", "inventaire"]
|
||||
|
@ -41,6 +41,9 @@
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/comp-creature.html"}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/comp-possession.html"}}
|
||||
</div>
|
||||
<div>
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/resonances.hbs"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group large-editor">
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{#if recettesAlchimiques.length}}
|
||||
<h3>Recettes Alchimiques</h3>
|
||||
<ul class="item-list alterne-list">
|
||||
{{#each recettesAlchimiques as |recette id|}}
|
||||
{{#each (trier recettesAlchimiques) as |recette id|}}
|
||||
<li class="item flexrow list-item" data-item-id="{{recette._id}}"><span class="competence-title recette-label item-edit"><a>{{recette.name}}</a></span>
|
||||
<div class="item-controls flex-shrink">
|
||||
<a class="item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||
|
@ -40,4 +40,29 @@
|
||||
<span class="initiative-value"></span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<ul class="item-list alterne-list">
|
||||
<li class="competence-header flexrow">
|
||||
<span class="competence-title competence-label">Empoignades</span>
|
||||
<span class="competence-title competence-value">Points d'Emp</span>
|
||||
</li>
|
||||
{{#each empoignades as |emp key|}}
|
||||
<li class="item flexrow list-item"
|
||||
data-item-id="{{emp._id}}" data-arme-name="{{emp.name}}">
|
||||
<span class="empoignade-label">
|
||||
<a>
|
||||
{{#if emp.img}}
|
||||
<img class="sheet-competence-img" src="{{emp.img}}"/>
|
||||
{{/if}}
|
||||
<span>{{emp.name}}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="competence-value">{{emp.system.pointsemp}}</span>
|
||||
<div class="item-controls">
|
||||
<a class="item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
@ -61,7 +61,7 @@
|
||||
{{/unless}}
|
||||
</label>
|
||||
</li>
|
||||
{{#each system.services as |service key|}}
|
||||
{{#each (trier system.services) as |service key|}}
|
||||
<li class="item flexrow list-item" data-key="{{key}}">
|
||||
<input {{@root.disabled}} type="text" name="services[{{key}}].name" value="{{service.name}}" data-dtype="String" />
|
||||
<input {{@root.disabled}} type="checkbox" name="services[{{key}}].system.moral" {{#if service.system.moral}}checked{{/if}} />
|
||||
|
@ -25,14 +25,14 @@
|
||||
<span class="equipement-detail">Prix (sols)</span>
|
||||
<span class="equipement-actions">Actions</span>
|
||||
</li>
|
||||
{{#each objets as |item id|}}
|
||||
{{#each (trier inventaires) as |item id|}}
|
||||
{{#unless item.estContenu}}
|
||||
{{#if (ne item.type 'conteneur')}}
|
||||
{{buildLigneInventaire item ../options}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
{{#each conteneurs as |conteneur id|}}
|
||||
{{#each (trier conteneurs) as |conteneur id|}}
|
||||
{{buildLigneInventaire conteneur ../options}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
@ -9,18 +9,24 @@
|
||||
<input class="competence-carac creature-carac" type="text" compname="{{comp.name}}" name="{{comp._id}}.carac"
|
||||
value="{{comp.system.carac_value}}" data-dtype="number"
|
||||
{{#unless @root.options.vueDetaillee}}disabled{{/unless}}/>
|
||||
<input class="competence-value creature-niveau" type="text" compname="{{comp.name}}" name="{{comp._id}}.niveau"
|
||||
value="{{numberFormat comp.system.niveau decimals=0 sign=true}}" data-dtype="number"
|
||||
{{#unless @root.options.vueDetaillee}}disabled{{/unless}}/>
|
||||
<input class="competence-damage creature-dommages" type="text" compname="{{comp.name}}" name="{{comp._id}}.dommages"
|
||||
value="{{numberFormat comp.system.dommages decimals=0 sign=true}}" data-dtype="number"
|
||||
{{#unless @root.options.vueDetaillee}}disabled{{/unless}}/>
|
||||
{{#if @root.options.vueDetaillee}}
|
||||
<div class="item-controls">
|
||||
<a class="item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
{{/if}}
|
||||
<input class="competence-value creature-niveau" type="text" data-dtype="number"
|
||||
compname="{{comp.name}}" name="{{comp._id}}.niveau"
|
||||
value="{{numberFormat comp.system.niveau decimals=0 sign=true}}"
|
||||
{{#unless @root.options.vueDetaillee}}disabled{{/unless}}
|
||||
/>
|
||||
<input class="competence-damage creature-dommages" type="text" data-dtype="number"
|
||||
{{#if comp.isdommages}}
|
||||
compname="{{comp.name}}" name="{{comp._id}}.dommages"
|
||||
value="{{numberFormat comp.system.dommages decimals=0 sign=true}}"
|
||||
{{#unless @root.options.vueDetaillee}}disabled{{/unless}}
|
||||
{{else}}disabled{{/if}}
|
||||
/>
|
||||
{{#if @root.options.vueDetaillee}}
|
||||
<div class="item-controls">
|
||||
<a class="item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||
<a class="item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{#if meditations.length}}
|
||||
<h3>Méditations</h3>
|
||||
<ul class="item-list">
|
||||
{{#each meditations as |meditation key|}}
|
||||
{{#each (trier meditations) as |meditation key|}}
|
||||
<li class="item flexrow" data-item-id="{{meditation._id}}" data-attribute="{{key}}">
|
||||
<img class="sheet-competence-img" src="{{meditation.img}}" />
|
||||
<span class="meditation-label flex-grow"><a data-item-id="{{meditation._id}}">{{meditation.name}} - {{meditation.system.competence}}</a></span>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{#if sortsReserve.length}}
|
||||
<h3>Sorts en Réserve</h3>
|
||||
<ul class="item-list alterne-list">
|
||||
{{#each sortsReserve as |sort key|}}
|
||||
{{#each (trier sortsReserve) as |sort key|}}
|
||||
<li class="item list-item flexrow" data-item-id="{{sort._id}}" data-attribute="{{key}}">
|
||||
<img class="sheet-competence-img" src="{{sort.img}}" />
|
||||
<span class="item-edit"><a>{{#if sort.system.echectotal}}Echec total: {{/if}}{{sort.name}} r{{sort.system.ptreve}}</a></span>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{#if sorts.length}}
|
||||
<h3>Sorts</h3>
|
||||
<ul class="item-list alterne-list">
|
||||
{{#each sorts as |sort key|}}
|
||||
{{#each (trier sorts) as |sort key|}}
|
||||
<li class="item list-item flexrow" data-item-id="{{sort._id}}" data-attribute="{{key}}">
|
||||
<img class="sheet-competence-img" src="{{sort.img}}" />
|
||||
<span class="item-edit flex-grow-2">
|
||||
|
@ -28,7 +28,7 @@
|
||||
<span class="equipement-actions item-controls">
|
||||
{{#if options.isOwner}}
|
||||
{{#unless item.estContenu}}
|
||||
{{#if (or (eq item.type 'arme') (eq item.type 'armure') )}}
|
||||
{{#if item.equipable}}
|
||||
<a class="item-equip" title="Equiper">{{#if item.system.equipe}}<i class="fas fa-hand-rock"></i>{{else}}<i class="far fa-hand-paper"></i>{{/if}}</a>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{#if @root.options.isObserver}}
|
||||
<span class="item-name"><h4>Argent et Monnaies (fortune: {{calc.fortune.sols}} sols {{calc.fortune.deniers}} deniers)</h4></span>
|
||||
<ul class="item-list alterne-list">
|
||||
{{#each monnaie as |piece id|}}
|
||||
{{#each monnaies as |piece id|}}
|
||||
<li class="item flexrow list-item" data-item-id="{{piece._id}}">
|
||||
<img class="sheet-competence-img" src="{{piece.img}}" title="{{piece.name}}"/>
|
||||
<span class="equipement-nom">{{piece.name}}</span>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<span class="equipement-detail">Enc.</span>
|
||||
<span class="equipement-actions">Actions</span>
|
||||
</li>
|
||||
{{#each objets as |item id|}}
|
||||
{{#each inventaires as |item id|}}
|
||||
{{#unless item.estContenu}}
|
||||
{{#if (ne item.type 'conteneur')}}
|
||||
{{buildLigneInventaire item @root.options}}
|
||||
|
@ -1,18 +1,18 @@
|
||||
<h3>Oeuvres diverses</h3><a class="chat-card-button creer-une-oeuvre">Créer une oeuvre</a>
|
||||
<ul class="item-list alterne-list">
|
||||
{{#each chants as |chant id|}}
|
||||
{{#each (trier chants) as |chant id|}}
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.html" oeuvre=chant typeOeuvre="Chant" classOeuvre="chant-label"}}
|
||||
{{/each}}
|
||||
{{#each musiques as |musique id|}}
|
||||
{{#each (trier musiques) as |musique id|}}
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.html" oeuvre=musique typeOeuvre="Musique" classOeuvre="musique-label"}}
|
||||
{{/each}}
|
||||
{{#each danses as |danse id|}}
|
||||
{{#each (trier danses) as |danse id|}}
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.html" oeuvre=danse typeOeuvre="Danse" classOeuvre="danse-label"}}
|
||||
{{/each}}
|
||||
{{#each oeuvres as |oeuvre id|}}
|
||||
{{#each (trier oeuvres) as |oeuvre id|}}
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.html" oeuvre=oeuvre typeOeuvre=oeuvre.system.competence classOeuvre="oeuvre-label"}}
|
||||
{{/each}}
|
||||
{{#each recettescuisine as |recette id|}}
|
||||
{{#each (trier recettescuisine) as |recette id|}}
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.html" oeuvre=recette typeOeuvre="Recette de cuisine" classOeuvre="recettecuisine-label"}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
12
templates/actor/resonances.hbs
Normal file
12
templates/actor/resonances.hbs
Normal file
@ -0,0 +1,12 @@
|
||||
<span class="item-name"><h4>Personnages accordés</h4></span>
|
||||
<ul class="item-list alterne-list">
|
||||
{{#each resonances as |actor|}}
|
||||
<li class="item flexrow list-item" data-actor-id="{{actor.id}}">
|
||||
<img class="sheet-competence-img" src="{{actor.img}}" title="{{actor.name}}"/>
|
||||
<span class="subacteur-label"><a>{{actor.name}}</a></span>
|
||||
<div class="flex-shrink">
|
||||
<a class="resonance-delete" title="Supprimer"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
@ -3,7 +3,7 @@
|
||||
<li class="item flexrow">
|
||||
<label class="derivee-label" for="system.compteurs.experience.value">Stress transformé</label>
|
||||
{{#if options.vueDetaillee}}
|
||||
<input type="number" name="system.compteurs.experience.value" value="{{system.compteurs.experience.value}}" data-dtype="number" size="3"/>
|
||||
<input class="derivee-value" type="number" name="system.compteurs.experience.value" value="{{system.compteurs.experience.value}}" data-dtype="number" size="3"/>
|
||||
{{else}}
|
||||
<label name="system.compteurs.experience.value">{{system.compteurs.experience.value}}</label>
|
||||
{{/if}}
|
||||
|
@ -1,14 +1,18 @@
|
||||
<h3>Journal d'Experience</h3>
|
||||
<div class="form-group large-editor">
|
||||
<ul class="item-list alterne-list">
|
||||
{{#each system.experiencelog as |xp key|}}
|
||||
{{#each system.experiencelog as |xplog key|}}
|
||||
<li class="experiencelog flexrow list-item" data-key="{{key}}">
|
||||
<label class="flex-grow">{{xp.valeur}} {{xp.raison}}</label>
|
||||
<label class="flex-shrink">{{xp.mode}}</label>
|
||||
<label class="flex-shrink">{{xp.daterdd}}</label>
|
||||
<label class="flex-shrink">{{xp.datereel}}</label>
|
||||
<label class="flex-shrink">{{xplog.valeur}} {{experienceLog-topic xplog.mode}} </label>
|
||||
{{#if (or xplog.from xplog.to)}}
|
||||
<label class="flex-grow">{{xplog.raison}}: de {{xplog.from}} à {{xplog.to}}</label>
|
||||
{{else}}
|
||||
<label class="flex-grow">{{xplog.raison}}</label>
|
||||
{{/if}}
|
||||
<label class="flex-shrink">{{xplog.daterdd}}</label>
|
||||
<label class="flex-shrink">{{xplog.datereel}}</label>
|
||||
{{#if @root.options.isGM}}
|
||||
<span>
|
||||
<span class="flex-shrink">
|
||||
<a class="experiencelog-delete" title="Supprimer">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
|
8
templates/chat-empoignade-entrainer-sol.html
Normal file
8
templates/chat-empoignade-entrainer-sol.html
Normal file
@ -0,0 +1,8 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{attacker.name}} a entraîné {{defender.name}} au sol
|
||||
</h4>
|
||||
<hr>
|
||||
<div>
|
||||
L'empoignade peut continuer.
|
||||
</div>
|
16
templates/chat-empoignade-entrainer.html
Normal file
16
templates/chat-empoignade-entrainer.html
Normal file
@ -0,0 +1,16 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{attacker.name}} a empoigné {{defender.name}}
|
||||
</h4>
|
||||
<hr>
|
||||
<div>
|
||||
<span class='chat-card-button-area'>
|
||||
{{attacker.name}} vient d'obtenir 2 points d'Emp, et peut
|
||||
entraîner {{defender.name}} au sol. Les deux protagonistes
|
||||
restent empoignés.
|
||||
<br>
|
||||
<a class='empoignade-entrainer-sol chat-card-button'>
|
||||
Entraîner au sol
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
35
templates/chat-empoignade-immobilise.html
Normal file
35
templates/chat-empoignade-immobilise.html
Normal file
@ -0,0 +1,35 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{attacker.name}} a empoigné {{defender.name}}
|
||||
</h4>
|
||||
<hr>
|
||||
<div>
|
||||
<span class='chat-card-button-area'>
|
||||
<p>
|
||||
{{attacker.name}} a obtenu 2 points d'Emp à la fin du round précédent, et peut:
|
||||
|
||||
<ul><li>
|
||||
faire perdre des points d'Endurance à {{defender.name}}
|
||||
<br>
|
||||
<a class='empoignade-perte-endurance chat-card-button'>
|
||||
<select class='empoignade-perte-endurance'>
|
||||
<option value="none">Faire perdre de l'endurance (selectionnez)</option>
|
||||
<option value="end0">Endurance à 0</option>
|
||||
<option value="end1">Endurance à 1</option>
|
||||
<option value="endmoitie">La moitié de l'endurance</option>
|
||||
<option value="endquart">Le quart de l'endurance</option>
|
||||
</select>
|
||||
</a>
|
||||
{{#if empoignade.system.ausol}}
|
||||
{{else}}
|
||||
</li><li>
|
||||
projeter {{defender.name}} au sol. Les deux protagonistes ne sont plus empoignés.
|
||||
<br>
|
||||
<a class='empoignade-projeter-sol chat-card-button'>
|
||||
Projeter au sol
|
||||
</a>
|
||||
{{/if}}
|
||||
</li></ul>
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
8
templates/chat-empoignade-perte-endurance.html
Normal file
8
templates/chat-empoignade-perte-endurance.html
Normal file
@ -0,0 +1,8 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{attacker.name}} a fait perdre de l'endurance à {{defender.name}}
|
||||
</h4>
|
||||
<hr>
|
||||
<div>
|
||||
{{defender.name}} reste immobilisé. L'empoignade peut continuer.
|
||||
</div>
|
8
templates/chat-empoignade-projeter-sol.html
Normal file
8
templates/chat-empoignade-projeter-sol.html
Normal file
@ -0,0 +1,8 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{attacker.name}} a projeté {{defender.name}} au sol
|
||||
</h4>
|
||||
<hr>
|
||||
<div>
|
||||
L'empoignade est terminée et a été supprimée.
|
||||
</div>
|
79
templates/chat-empoignade-resultat.html
Normal file
79
templates/chat-empoignade-resultat.html
Normal file
@ -0,0 +1,79 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{log 'rollData' this}}
|
||||
{{#if (eq mode "empoigner")}}
|
||||
{{attacker.name}} tente d'empoigner {{defender.name}}
|
||||
{{/if}}
|
||||
{{#if (eq mode "contrer-empoigner")}}
|
||||
{{defender.name}} tente de contrer l'empoignade de {{attacker.name}}
|
||||
{{/if}}
|
||||
{{#if (eq mode "liberer")}}
|
||||
{{attacker.name}} tente de se libérer de l'empoignade de {{defender.name}}
|
||||
{{/if}}
|
||||
{{#if (eq mode "contrer-liberer")}}
|
||||
{{defender.name}} tente de contrer la libération de {{attacker.name}}
|
||||
{{/if}}
|
||||
</h4>
|
||||
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
|
||||
|
||||
<hr>
|
||||
<div>
|
||||
|
||||
{{#if (gte empoignade.system.pointsemp 2)}}
|
||||
|
||||
<br><strong>{{defender.name}} est empoigné et immobilisé par {{attacker.name}} !</strong>
|
||||
|
||||
{{else}}
|
||||
<span class='chat-card-button-area'>
|
||||
<br>
|
||||
|
||||
{{#if (eq mode "empoigner")}}
|
||||
{{#if empoignade.isSuccess}}
|
||||
<a class='defense-empoignade-cac chat-card-button'
|
||||
data-diff-libre='{{diffLibre}}'
|
||||
data-defense-mode="contrer-empoigner">
|
||||
Contrer l'empoignade (Corps à Corps)
|
||||
</a>
|
||||
{{#if (eq empoignade.system.pointsemp 0)}}
|
||||
<a class='defense-empoignade-esquive chat-card-button'
|
||||
data-diff-libre='{{diffLibre}}'
|
||||
data-defense-mode="contrer-empoigner">
|
||||
Contrer l'empoignade (Esquive)
|
||||
</a>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
La Tentative d'empoignade a échoué !
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq mode "liberer")}}
|
||||
{{#if empoignade.isSuccess}}
|
||||
<a class='defense-empoignade-cac chat-card-button'
|
||||
data-diff-libre='{{diffLibre}}'
|
||||
data-defense-mode="contrer-liberer">
|
||||
Contrer la libération (Corps à Corps)
|
||||
</a>
|
||||
{{else}}
|
||||
La Tentative de libération a échouée !
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq mode "contrer-empoigner")}}
|
||||
{{#if rolled.isSuccess}}
|
||||
La tentative de contrer l'empoignade est un succès!
|
||||
{{else}}
|
||||
La tentative de contrer l'empoignade est un échec!
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq mode "contrer-liberer")}}
|
||||
{{#if rolled.isSuccess}}
|
||||
La tentative de contrer la libération est un succès!
|
||||
{{else}}
|
||||
La tentative de contrer la libération est un échec!
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
<br>Points d'Emp: {{empoignade.system.pointsemp}}
|
||||
{{/if}}
|
||||
</div>
|
19
templates/chat-empoignade-valider.html
Normal file
19
templates/chat-empoignade-valider.html
Normal file
@ -0,0 +1,19 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{attacker.name}} tente d'empoigner {{defender.name}}
|
||||
</h4>
|
||||
<hr>
|
||||
<div>
|
||||
<span class='chat-card-button-area'>
|
||||
<br>
|
||||
<strong>{{attacker.name}} tente d'empoigner {{defender.name}}, qui est équipé d'une arme de mêlée. {{defender.name}}
|
||||
a automatiquement l'initiative sur {{attacker.name}}, et bénéficie d'un bonus de +4 à son attaque. Assurez vous
|
||||
d'avoir effectué cette attaque avant de poursuivre l'empoignade. Ce cas s'applique également si {{defender.name}}
|
||||
combat à mains nues.</strong>
|
||||
|
||||
<a class='empoignade-poursuivre chat-card-button' data-attackerId='{{attacker.id}}'
|
||||
data-defenderId='{{defender.id}}'>
|
||||
Poursuivre l'empoignade
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
<img class="chat-icon" src="{{competence.img}}" />
|
||||
<h4>
|
||||
{{#if (eq mode "possession")}}
|
||||
{{#if (eq mode "attaque")}}
|
||||
{{attacker.name}} tente de {{#if isECNIDefender}}conjurer la possession de{{else}}posséder{{/if}} {{defender.name}}
|
||||
{{else}}
|
||||
{{defender.name}} tente de {{#if isECNIDefender}}résister à{{else}}conjurer la possession de{{/if}} {{attacker.name}}
|
||||
@ -12,7 +12,7 @@
|
||||
<div>
|
||||
<span class='chat-card-button-area'>
|
||||
<br>
|
||||
{{#if (eq mode "possession")}}
|
||||
{{#if (eq mode "attaque")}}
|
||||
{{#if possession.isSuccess}}
|
||||
<a class='defense-possession chat-card-button'
|
||||
data-attackerId='{{attacker.id}}'
|
||||
|
22
templates/dialog-roll-defense-empoignade.html
Normal file
22
templates/dialog-roll-defense-empoignade.html
Normal file
@ -0,0 +1,22 @@
|
||||
<form class="skill-roll-dialog">
|
||||
<h2>
|
||||
{{defender.name}} tente de contrer l'empoignade de {{attacker.name}}
|
||||
</h2>
|
||||
<div class="grid grid-2col">
|
||||
<div class="flex-group-left">
|
||||
<img class="chat-icon" src="{{competence.img}}" alt="{{competence.name}}"/>
|
||||
<div class="grid grid-2col">
|
||||
<label for="carac">{{selectedCarac.label}}:</label><label class="flex-grow" name="carac">{{selectedCarac.value}}</label>
|
||||
<label for="competence">{{competence.name}}:</label><label class="flex-grow" name="competence">{{numberFormat competence.system.niveau decimals=0 sign=true}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-group-left">
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffFixe.html"}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffCondition.html"}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-forcer.html"}}
|
||||
<div class="placeholder-ajustements" class="flexrow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="placeholder-resolution"></div>
|
||||
</form>
|
3
templates/enum-categorie-competencecreature.html
Normal file
3
templates/enum-categorie-competencecreature.html
Normal file
@ -0,0 +1,3 @@
|
||||
{{#each @root.categoriesCompetencesCreature as |categorie key|}}
|
||||
<option value="{{@key}}">{{categorie.label}}</option>
|
||||
{{/each}}
|
@ -1,3 +1,3 @@
|
||||
{{#each @root.categorieCompetences as |categorie key|}}
|
||||
{{#each @root.categories as |categorie key|}}
|
||||
<option value="{{@key}}">{{categorie.label}}</option>
|
||||
{{/each}}
|
@ -18,7 +18,7 @@
|
||||
<label for="system.categorie">Catégorie </label>
|
||||
<select name="system.categorie" class="categorie" data-dtype="String">
|
||||
{{#select system.categorie}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-categorie-competence.html"}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-categories.html"}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -6,50 +6,38 @@
|
||||
<label for="system.categorie">Catégorie</label>
|
||||
<select name="system.categorie" class="categorie" data-dtype="String">
|
||||
{{#select system.categorie}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-categorie-competence.html"}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-categories.html"}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="niveau">Valeur de Caractéristique </label>
|
||||
<label for="niveau">Valeur de Caractéristique</label>
|
||||
<input class="attribute-value" type="text" name="system.carac_value" value="{{system.carac_value}}" data-dtype="Number"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="niveau">Niveau </label>
|
||||
<label for="niveau">Niveau</label>
|
||||
<input class="attribute-value" type="text" name="system.niveau" value="{{system.niveau}}" data-dtype="Number"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="default_diffLibre">Difficulté libre par défaut</label>
|
||||
<input class="attribute-value" type="text" name="system.default_diffLibre" value="{{system.default_diffLibre}}" data-dtype="Number"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="system.ispossession">Attaque de possession</label>
|
||||
<input class="attribute-value" type="checkbox" name="system.ispossession" {{#if system.ispossession}}checked{{/if}}/>
|
||||
</div>
|
||||
{{#unless system.ispossession}}
|
||||
<div class="form-group">
|
||||
<label for="system.iscombat">Compétence d'attaque</label>
|
||||
<input class="attribute-value" type="checkbox" name="system.iscombat" {{#if system.iscombat}}checked{{/if}}/>
|
||||
</div>
|
||||
{{#if system.iscombat}}
|
||||
<div class="form-group">
|
||||
<label for="system.isnaturelle">Arme naturelle</label>
|
||||
<input class="attribute-value" type="checkbox" name="system.isnaturelle" {{#if system.isnaturelle}}checked{{/if}}/>
|
||||
</div>
|
||||
{{#if isdommages}}
|
||||
<div class="form-group">
|
||||
<label for="niveau">Dommages (+dom)</label>
|
||||
<input class="attribute-value" type="text" name="system.dommages" value="{{system.dommages}}" data-dtype="Number"/>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if isparade}}
|
||||
<div class="form-group">
|
||||
<label>Catégorie parade </label>
|
||||
<label>Catégorie parade</label>
|
||||
<select name="system.categorie_parade" id="categorie_parade" data-dtype="String">
|
||||
{{#select system.categorie_parade}}
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html"}}
|
||||
{{/select}}
|
||||
</select>
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-item-description.html"}}
|
||||
</section>
|
||||
|
11
templates/item-empoignade-sheet.html
Normal file
11
templates/item-empoignade-sheet.html
Normal file
@ -0,0 +1,11 @@
|
||||
<form class="{{cssClass}}" autocomplete="off">
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/header-item.html"}}
|
||||
{{!-- Sheet Body --}}
|
||||
<section class="sheet-body">
|
||||
<div class="form-group">
|
||||
<label for="xp">Points d'Empoignade </label>
|
||||
<input class="attribute-value" type="text" name="system.pointsemp" value="{{system.pointsemp}}" data-dtype="Number"/>
|
||||
</div>
|
||||
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-item-description.html"}}
|
||||
</section>
|
||||
</form>
|
@ -5,7 +5,7 @@
|
||||
<h2>{{name}} se repose</h2>
|
||||
</span></div>
|
||||
<div class="flexrow"><span>
|
||||
<input class type="radio" name="repos" id="sieste" value="sieste">
|
||||
<input type="radio" name="repos" id="sieste" value="sieste" {{#unless system.sommeil.nouveaujour}}checked{{/unless}}>
|
||||
<label for="sieste">Sieste de
|
||||
<input type="number" name="sieste.heures" value="1" data-dtype="Number" />
|
||||
heures</label>
|
||||
|
Loading…
Reference in New Issue
Block a user