Compare commits

..

56 Commits

Author SHA1 Message Date
24518642a7 Merge pull request '12.0.37 - Les enchantements d'Astrobazzarh' (#742) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#742
2025-01-21 09:37:15 +01:00
d917f80e88 Enchantement des gemmes et potions 2025-01-21 03:11:47 +01:00
b29027c61a Cleanup 2025-01-21 03:08:11 +01:00
6083dd41fa Amélioration du split
Bouton split géré avec itemActions

Ajout d'un bouton pour augmenter les piles (pour le MJ)
2025-01-21 03:08:11 +01:00
8969d5e0ed Merge pull request '12.0.36 - L'alchimie d'Astrobazzarh' (#740) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#740
2025-01-19 09:50:20 +01:00
7d135a214f Version 12.0.36 2025-01-19 01:21:12 +01:00
9d66a479c4 Heures des Queues lors de la nuit
L'heure des queues ajoutées à fin chateau dormant sont maintenant
décalées d'une minute dans le passé pour s'appliquer correctement
12 heures draconiques à partir de vaisseau (et non pas
2025-01-19 01:17:57 +01:00
800b4a2f32 Remove unused import 2025-01-19 00:49:58 +01:00
9fc21e6d97 Gestion des actions sur les items
- dans la liste d'équipement, liste d'actions fournie par code
- support d'actions multiples
- listener générique

Utilisation du mécanisme pour tous les items, standardisation de
l'apparence des boutons d'items
2025-01-19 00:38:24 +01:00
ce7f5381ca Fix update endurance/vie
Dans certains cas, les valeurs de vie/endurance n'étaient pas mises
à jour
2025-01-18 23:13:40 +01:00
48acdaaca6 Fix bouton tchat jet encaissement 2025-01-18 23:13:40 +01:00
425c6af672 Correction ouverture feuille de conteneur 2025-01-18 14:49:44 +01:00
4281f76dfb Simplification des actionPrincipales
Préparation pour supporter une liste d'actions sur les items
2025-01-18 14:49:44 +01:00
8ebd6ec771 Normalisation des listes d'items
Utiliser le type d'Item + 's'
2025-01-18 14:49:44 +01:00
14391daa0b Simplification monnaies
Note: bug étrange causé par la 12.0.34, mais le code était bon
2025-01-18 14:49:43 +01:00
ac29e1410e Correction de remedes enchantables
Les remedes ne sont pas enchantable, changement en Alchimie
2025-01-18 14:49:43 +01:00
442ffe9b3a Ajout de jets aux descriptions 2025-01-18 14:49:43 +01:00
d922e4fdd5 Move type constants
Prevent problems of circular dependencies when sepating actions from
items & sheets
2025-01-18 14:49:42 +01:00
a8dc07b4db Enchanter des Potions 2025-01-18 14:49:42 +01:00
40a47ee8a2 Ajout du symbole d20, pour les roll de formule 2025-01-14 22:24:20 +01:00
c5633a9fc5 Fix commande /tmra 2025-01-14 22:23:35 +01:00
7e8da49912 Fix Hook init usage in v12, upgrade version 2025-01-14 00:40:42 +01:00
b524716ede Fix Hook init usage in v12 2025-01-14 00:39:28 +01:00
72a9752820 Fix Hook init usage in v12 2025-01-14 00:23:04 +01:00
8e578c6566 Merge pull request 'Fix: lien jets de dés' (#739) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#739
2025-01-13 23:44:45 +01:00
5ac9c682d9 Support difficulté numérique 2025-01-13 22:34:34 +01:00
6de19eb357 Convertir tous les jets de dés dans le tchat 2025-01-13 22:27:13 +01:00
90d096a6df Utilisation des class au lieu d'id
Un seul fichier oublié sur un changement précédent (pas de vrai
sauvegarde dans VSCode...)
2025-01-13 22:03:18 +01:00
c733644f3a Jets de compétence avec carac imposée
Fonctionne maintenant correctement

Pour les créatures/entitées: on fait au mieux, mais ça ne correspond
pas forcément bien, car les carac/compétences ne correspondent
pas toujours
2025-01-13 21:44:02 +01:00
de9d3bbb48 Difficultés aléatoires dans les maladresses 2025-01-13 20:10:11 +01:00
efdffd171c Support de difficultés aléatoires
Par exemple: `@roll[vue/vigil/-1d6]`
pour  "VUE / Vigilance à -1d6"
2025-01-13 19:46:55 +01:00
8406c8434a Fix: lien jets de dés
- les boutons pour envoyer dans le tchat sont affichés
- les jets de carac utilisent bien les compétences et difficulté
2025-01-13 19:25:55 +01:00
a4b474970c Merge pull request 'Fin des liens "jet de dés"' (#738) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#738
2025-01-12 23:02:19 +01:00
135e5e46a0 Support des maladresses 2025-01-12 22:56:11 +01:00
969bc3b573 Liens de jets de dés dans le tchat 2025-01-12 22:56:11 +01:00
a9eb101c9d Merge pull request 'Liens jets de dés' (#737) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#737
2025-01-12 16:15:33 +01:00
d53da1f011 Jets d'alchimie dans descriptions/journaux
On ne se limite plus aux manipulations alchimiques, et les jets
apparaissent comme tous les autres types de jets
2025-01-12 02:38:31 +01:00
ab0f7e563f Ajout de jets de dés dans les compendiums 2025-01-12 00:39:17 +01:00
57c41a0218 Utiliser perception/reve pour les créatures
Ces caractéristiques remplacent toutes les autres
2025-01-11 19:45:58 +01:00
1b75decb18 Utiliser les fenêtres de jets pour les jets 2025-01-11 19:29:29 +01:00
551438f514 Correction sur les liens de jets de dés
- correction jet de rêve actuel
- utilisation du personnage du joueur
2025-01-11 19:04:38 +01:00
792558ac84 Support @roll[2d6] 2025-01-11 17:32:43 +01:00
06aff9a3c0 Liens jets de dés dans les journaux 2025-01-11 02:21:11 +01:00
7e736a00d7 Gestion des blocs secrets dans les descriptions 2025-01-11 02:19:56 +01:00
b87f406093 Ajouter de "jet de dés" dans les descriptions,
On peut maintenant ajouter des liens dans les descriptions
(acteurs, items) et autres champs similaires.
2025-01-11 00:44:59 +01:00
785bd4b9ce Merge pull request '12.0.33 - la vieillesse d'Astrobazzarh' (#736) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#736
2025-01-10 20:40:36 +01:00
daca86b1df Fix: experience sur particulière 2025-01-10 19:37:34 +01:00
aa52e26e1a Fix: pas de message si carac exacte pas trouvée
La recherche se fait en deux temps, on avait un message d'erreur pour
odorat-gout
2025-01-09 00:16:52 +01:00
f956da1fc0 Merge pull request '12.0.32 - les rêveries d'Astrobazzarh' (#735) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#735
2024-12-29 22:01:08 +01:00
10971e9e7b Fix typo 2024-12-29 17:47:10 +01:00
a3e6b95ef3 Simplification import 2024-12-29 17:43:54 +01:00
d0a5a3617c Pas de drag depuis champs dérivés/biographie 2024-12-29 17:39:03 +01:00
755df936fb Cleanup valeurs numériques 2024-12-29 16:50:09 +01:00
2fa0ce5f15 Support de races
L'item "race" permet de paramétrer des ajustements de caracs,
des min/max de taille, et une limite de force.

Ajouter une race à un acteur enlève la/les races précédentes et ajoute
les modificateurs de caracs de la nouvelle race.

Enlever une race enlève les modificateurs de caracs de la race
aux caractéristiques
2024-12-29 16:50:08 +01:00
b4eed49e9a Correction compendiums Voies Draconic
Le nom des voies ne doit pas contenir "Voie de "
2024-12-28 22:36:09 +01:00
af4404aab1 Merge pull request 'Fix: automatisations de combat' (#734) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#734
2024-12-20 10:52:54 +01:00
475 changed files with 7868 additions and 4369 deletions

View File

@ -1,4 +1,48 @@
# 12.0
## 12.0.37 - Les enchantements d'Astrobazzarh
- les potions ont un état, seules les potions liquides sont enchantables
- les lancements de sorts du jour sont conservés jusqu'à chateau dormant
- lorsqu'un joueur souhaite enchanter une potion, les sorts d'enchantements/purification/permanence doivent avoir été lancés auparavant
- on peut enchanter des gemmes exactement comme des potions
## 12.0.36 - L'alchimie d'Astrobazzarh
- Nouveautés
- ajout d'un bouton pour enchanter les potions
- standardisation des boutons d'actions sur les items
- utilisations d'icones pour les actions de l'inventaire
- Corrections:
- la commande /tmra sans paramètres fonctionne
- les jets d'encaissement depuis le tchat fonctionnent
- affichage de la vie/endurance en cas de blessures et remise à neuf
- les queues durant 12 heures ajoutées début Vaisseau ne durent plus 24 heures
- Compendiums
- Corrections des remedes enchantables
- Corrections de descriptions pour proposer les jet de dés
## 12.0.35 - La Solution d'Astrobazzarh
- Fix problème d'initialisation des feuilles d'items
## 12.0.34 - la tête d'Astrobazzarh
- support de liens "jets de dés"
- on peut ajouter des liens "jet de dés" dans les journaux, descriptions, notes, maladresses, ...
- avec la syntaxe `@roll[...]` on peut ajouter le lien vers:
- un jet de caractéristique/compétence `@roll[carac/competence/difficulte]` / `@roll[carac/difficulte]` / `@roll[carac/competence]`
- une formule foundry `@roll[2d6]` pour lancer 2d6
- une manipulation alchimique `@roll[couleur vert-bleu]`
- les liens "jet avec caractéristiques" s'appliquent:
- à tous les tokens sélectionnés
- sinon, à l'acteur propriétaire (dans le cas d'un Item) ou à l'acteur courant
- sinon, au personnage du joueur
- on peut poster les liens dans le tchat pour proposer un jet aux joueurs
- gestion des blocs secrets dans les descriptions
## 12.0.33 - la vieillesse d'Astrobazzarh
- retour de l'expérience pour les joueurs
- suppression du message "Pas de caractéristique" sur les jets d'odorat-goût
## 12.0.32 - les rêveries d'Astrobazzarh
- Ajout des Items Race pour gérer les ajustements liés aux races
## 12.0.31 - le mausolée d'Astrobazzarh
- Correction: les automatisation de combat jouer-MJ fonctionnentde nouveau

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -40,6 +40,7 @@
"possession": "Possession",
"potion": "Potion",
"queue": "Queue de Dragon",
"race": "Race",
"recettealchimique": "Recette alchimique",
"recettecuisine": "Recette de cuisine",
"rencontre": "Rencontre TMR",

View File

@ -12,12 +12,14 @@ import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js";
import { RdDBaseActorReveSheet } from "./actor/base-actor-reve-sheet.js";
import { ITEM_TYPES } from "./constants.js";
import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
import { RdDBaseActorSangSheet } from "./actor/base-actor-sang-sheet.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
/* -------------------------------------------- */
/**
@ -43,8 +45,8 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
cssClass: this.isEditable ? "editable" : "locked",
limited: this.actor.limited,
owner: this.actor.isOwner,
biographie: await TextEditor.enrichHTML(this.actor.system.biographie, { async: true }),
notes: await TextEditor.enrichHTML(this.actor.system.notes, { async: true }),
biographie: await RdDTextEditor.enrichHTML(this.actor.system.biographie, this.actor),
notes: await RdDTextEditor.enrichHTML(this.actor.system.notes, this.actor),
});
foundry.utils.mergeObject(formData.calc, {
surenc: this.actor.computeMalusSurEncombrement(),
@ -97,6 +99,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
cacheTMR: this.actor.isTMRCache()
}
formData.race = actor.itemTypes[ITEM_TYPES.race].find(it => true)
formData.subacteurs = {
vehicules: this.actor.listeVehicules(),
montures: this.actor.listeMontures(),
@ -181,22 +184,19 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
await blessure?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } })
});
// Equip Inventory Item
this.html.find('.item-equip').click(async event => this.actor.equiperObjet(RdDSheetUtility.getItemId(event)))
this.html.find('.roll-chance-actuelle').click(async event => this.actor.rollCarac('chance-actuelle'))
this.html.find('.button-appel-chance').click(async event => this.actor.rollAppelChance())
this.html.find('[name="jet-astrologie"]').click(async event => this.actor.astrologieNombresAstraux())
this.html.find('.tache-label a').click(async event => this.actor.rollTache(RdDSheetUtility.getItemId(event)))
this.html.find('.action-tache').click(async event => this.actor.rollTache(RdDSheetUtility.getItemId(event)))
this.html.find('.meditation-label a').click(async event => this.actor.rollMeditation(RdDSheetUtility.getItemId(event)))
this.html.find('.chant-label a').click(async event => this.actor.rollChant(RdDSheetUtility.getItemId(event)))
this.html.find('.danse-label a').click(async event => this.actor.rollDanse(RdDSheetUtility.getItemId(event)))
this.html.find('.musique-label a').click(async event => this.actor.rollMusique(RdDSheetUtility.getItemId(event)))
this.html.find('.oeuvre-label a').click(async event => this.actor.rollOeuvre(RdDSheetUtility.getItemId(event)))
this.html.find('.jeu-label a').click(async event => this.actor.rollJeu(RdDSheetUtility.getItemId(event)))
this.html.find('.recettecuisine-label a').click(async event => this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event)))
this.html.find('.action-chant').click(async event => this.actor.rollChant(RdDSheetUtility.getItemId(event)))
this.html.find('.action-danse').click(async event => this.actor.rollDanse(RdDSheetUtility.getItemId(event)))
this.html.find('.action-musique').click(async event => this.actor.rollMusique(RdDSheetUtility.getItemId(event)))
this.html.find('.action-oeuvre').click(async event => this.actor.rollOeuvre(RdDSheetUtility.getItemId(event)))
this.html.find('.action-jeu').click(async event => this.actor.rollJeu(RdDSheetUtility.getItemId(event)))
this.html.find('.action-recettecuisine').click(async event => this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event)))
this.html.find('.description-aleatoire').click(async event => new AppPersonnageAleatoire(this.actor).render(true))
if (game.user.isGM) {
@ -214,13 +214,12 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
// Boutons spéciaux MJs
this.html.find('.forcer-tmr-aleatoire').click(async event => this.actor.reinsertionAleatoire("Action MJ"))
this.html.find('.don-de-haut-reve').click(async event => this.actor.addDonDeHautReve())
this.html.find('.sortreserve-add').click(async event => this.actor.addSortReserve(RdDSheetUtility.getItemId(event)))
this.html.find('.afficher-tmr').click(async event => this.actor.changeTMRVisible())
}
// Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', true))
this.html.find('.empoignade-label a').click(async event => RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor)))
this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', {resistance:true}))
this.html.find('.action-empoignade').click(async event => RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor)))
this.html.find('.roll-arme').click(async event => this.actor.rollArme(foundry.utils.duplicate(this._getEventArmeCombat(event)), 'competence'))
@ -324,7 +323,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
/* -------------------------------------------- */
async selectTypeOeuvreToCreate() {
let types = RdDItem.getTypesOeuvres();
let content = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
let content = `<span class="generic-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
for (let typeName of types) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}

View File

@ -17,7 +17,7 @@ import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { Draconique } from "./tmr/draconique.js";
import { RdDCarac } from "./rdd-carac.js";
import { LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js";
import { DialogConsommer } from "./dialog-item-consommer.js";
import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
@ -32,7 +32,7 @@ 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 { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
import { RdDBaseActorSang } from "./actor/base-actor-sang.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { DialogChoixXpCarac } from "./dialog-choix-xp-carac.js";
@ -41,6 +41,7 @@ import { RdDCombatManager } from "./rdd-combat.js";
import { RdDItemTete } from "./item/tete.js";
import { DialogSelect } from "./dialog-select.js";
import { PAS_DE_DRACONIC, POSSESSION_SANS_DRACONIC } from "./item/base-items.js";
import { RdDItemRace } from "./item/race.js";
export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
@ -58,7 +59,7 @@ export class RdDActor extends RdDBaseActorSang {
*/
prepareActorData() {
this.system.carac.force.value = Math.min(this.system.carac.force.value, parseInt(this.system.carac.taille.value) + 4);
RdDItemRace.applyRacialLimits(this)
this.system.carac.melee.value = Math.floor((this.getForce() + parseInt(this.system.carac.agilite.value)) / 2);
this.system.carac.tir.value = Math.floor((parseInt(this.system.carac.vue.value) + parseInt(this.system.carac.dexterite.value)) / 2);
@ -165,29 +166,30 @@ export class RdDActor extends RdDBaseActorSang {
}
/* -------------------------------------------- */
async $perteRevePotionsEnchantees() {
let potions = this.itemTypes[ITEM_TYPES.potion]
.filter(it => Grammar.includesLowerCaseNoAccent(it.system.categorie, 'enchanté') && !it.system.prpermanent)
async $perteReveEnchantementsChateauDormants() {
const toUpdate = this.items.filter(it => [ITEM_TYPES.potion, ITEM_TYPES.gemme].includes(it.type))
.map(it => it.perteReveChateauDormant())
.filter(it => it != undefined)
const potionUpdates = await Promise.all(potions.map(async it => {
const nouveauReve = Math.max(it.system.pr - 1, 0)
if (toUpdate.length > 0) {
console.log('perte de rêve des enchantements', toUpdate)
const messageUpdates = await Promise.all(
toUpdate.map(async it => await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-pertereve-enchantement-chateaudormant.hbs`, it)))
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html`, {
pr: nouveauReve,
alias: this.getAlias(),
potionName: it.name,
potionImg: it.img
})
content: messageUpdates.reduce(Misc.joining('<br>'))
})
return {
_id: it._id,
'system.pr': nouveauReve,
'system.quantite': nouveauReve > 0 ? it.system.quantite : 0
}
}))
await this.updateEmbeddedDocuments('Item', toUpdate.map(it => it.update));
}
}
await this.updateEmbeddedDocuments('Item', potionUpdates);
async $suppressionLancementsSort() {
const updates = this.itemTypes[ITEM_TYPES.sort]
.filter(it => it.system.lancements.length > 0)
.map(it => { return { _id: it.id, 'system.lancements': [] } })
if (updates.length > 0) {
await this.updateEmbeddedDocuments('Item', updates)
}
}
/** --------------------------------------------
@ -244,7 +246,7 @@ export class RdDActor extends RdDBaseActorSang {
}
async _recuperationSante(message) {
const maladiesPoisons = this.getMaladiePoisons();
const maladiesPoisons = this.getMaladiesPoisons();
const isMaladeEmpoisonne = maladiesPoisons.length > 0;
this._messageRecuperationMaladiePoisons(maladiesPoisons, message);
@ -252,7 +254,7 @@ export class RdDActor extends RdDBaseActorSang {
await this._recupererVie(message, isMaladeEmpoisonne);
}
getMaladiePoisons() {
getMaladiesPoisons() {
return this.items.filter(item => item.type == 'maladie' || (item.type == 'poison' && item.system.active));
}
@ -282,13 +284,14 @@ export class RdDActor extends RdDBaseActorSang {
};
await this._recuperationSante(message)
await this._recupereMoralChateauDormant(message);
await this._recupereChance();
await this.transformerStress();
await this.retourSeuilDeReve(message);
await this.setBonusPotionSoin(0);
await this.retourSust(message);
await this.$perteRevePotionsEnchantees();
await this._recupereMoralChateauDormant(message)
await this._recupereChance()
await this.transformerStress()
await this.retourSeuilDeReve(message)
await this.setBonusPotionSoin(0)
await this.retourSust(message)
await this.$perteReveEnchantementsChateauDormants()
await this.$suppressionLancementsSort()
await RdDCoeur.applyCoeurChateauDormant(this, message)
if (message.content != "") {
message.content = `A la fin Chateau Dormant, ${message.content}<br>Un nouveau jour se lève`;
@ -412,19 +415,18 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async remiseANeuf() {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: 'Remise à neuf de ' + this.name
});
await this.supprimerBlessures(it => true);
await this.removeEffects(e => e.id != STATUSES.StatusDemiReve);
const updates = {
await this.update({
'system.sante.endurance.value': this.system.sante.endurance.max,
'system.sante.vie.value': this.system.sante.vie.max,
'system.sante.fatigue.value': 0,
'system.compteurs.ethylisme': { value: 1, nb_doses: 0, jet_moral: false }
};
await this.update(updates);
})
await this.removeEffects(e => e.id != STATUSES.StatusDemiReve);
await this.supprimerBlessures(it => true);
await ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: 'Remise à neuf de ' + this.name
});
}
/* -------------------------------------------- */
@ -528,7 +530,7 @@ export class RdDActor extends RdDBaseActorSang {
jet_moral: false,
value: value
}
});
})
}
/* -------------------------------------------- */
@ -660,18 +662,16 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async updateCarac(caracName, to) {
if (caracName == "force") {
if (Number(to) > this.getTaille() + 4) {
ui.notifications.warn("Votre FORCE doit être au maximum de TAILLE+4");
return;
}
to = Number(to)
if (!RdDItemRace.checkRacialMax(this, caracName, to)) {
return
}
if (caracName == "reve") {
if (caracName == LIST_CARAC_PERSONNAGE.reve.code) {
if (to > Misc.toInt(this.system.reve.seuil.value)) {
this.setPointsDeSeuil(to);
}
}
if (caracName == "chance") {
if (caracName == LIST_CARAC_PERSONNAGE.chance.code) {
if (to > Misc.toInt(this.system.compteurs.chance.value)) {
this.setPointsDeChance(to);
}
@ -994,13 +994,10 @@ export class RdDActor extends RdDBaseActorSang {
}
}
async addSortReserve(itemId) {
if (itemId) {
const item = this.items.get(itemId)
if (item.type == ITEM_TYPES.sort && !item.system.isrituel) {
this.$createSortReserve(item)
return
}
async addSortReserve(item) {
if (item?.type == ITEM_TYPES.sort && !item.system.isrituel) {
this.$createSortReserve(item)
return
}
const selectSortReserve = {
title: "Créer un sort en réserve",
@ -1204,42 +1201,6 @@ export class RdDActor extends RdDBaseActorSang {
new RdDRollDialogEthylisme(html, rollData, this, r => this.saouler(r.forceAlcool)).render(true);
}
async actionPrincipale(item, onActionItem = async () => { }) {
let result = await super.actionPrincipale(item, onActionItem)
if (result) { return result }
result = await this.actionNourritureboisson(item, onActionItem)
if (result) { return result }
switch (item.type) {
case ITEM_TYPES.potion: return await this.consommerPotion(item, onActionItem);
case ITEM_TYPES.livre: return await this.actionLire(item);
case ITEM_TYPES.conteneur: return await item.sheet.render(true);
case ITEM_TYPES.herbe: return await this.actionHerbe(item, onActionItem);
case ITEM_TYPES.queue: case ITEM_TYPES.ombre: return await this.actionRefoulement(item);
}
return undefined
}
async actionNourritureboisson(item, onActionItem) {
switch (item.getUtilisationCuisine()) {
case 'brut': {
const utilisation = new Dialog({
title: "Nourriture brute",
content: `Que faire de votre ${item.name}`,
buttons: {
'cuisiner': { icon: '<i class="fa-solid fa-utensils"></i>', label: 'Cuisiner', callback: async () => await this.preparerNourriture(item) },
'manger': { icon: '<i class="fa-solid fa-drumstick-bite"></i>', label: 'Manger cru', callback: async () => await this.mangerNourriture(item, onActionItem) }
}
});
return utilisation.render(true);
}
case 'pret':
return await this.mangerNourriture(item, onActionItem);
}
return undefined;
}
async mangerNourriture(item, onActionItem) {
return (await DialogConsommer.create(this, item, onActionItem)).render(true);
}
@ -1251,9 +1212,9 @@ export class RdDActor extends RdDBaseActorSang {
}
}
async actionHerbe(item, onActionItem = async () => { }) {
async fabriquerDecoctionHerbe(item) {
if (item.isHerbeAPotion()) {
return DialogFabriquerPotion.create(this, item, onActionItem);
return DialogFabriquerPotion.create(this, item);
}
return;
}
@ -1388,7 +1349,7 @@ export class RdDActor extends RdDBaseActorSang {
}
await RdDResolutionTable.rollData(ethylismeData.jetVie);
this._gererExperience(ethylismeData.jetVie);
this.gererExperience(ethylismeData.jetVie);
RollDataAjustements.calcul(ethylismeData.jetVie, this);
if (ethylismeData.jetVie.rolled.isSuccess) {
ethylisme.nb_doses++;
@ -1420,7 +1381,7 @@ export class RdDActor extends RdDBaseActorSang {
finalLevel: Number(ethylisme.value) + Number(this.system.compteurs.moral.value)
}
await RdDResolutionTable.rollData(ethylismeData.jetVolonte);
this._gererExperience(ethylismeData.jetVolonte);
this.gererExperience(ethylismeData.jetVolonte);
RollDataAjustements.calcul(ethylismeData.jetVolonte, this);
}
}
@ -1531,13 +1492,12 @@ export class RdDActor extends RdDBaseActorSang {
};
}
/* -------------------------------------------- */
isCaracMax(code) {
if (code == 'force' && parseInt(this.system.carac.force.value) >= parseInt(this.system.carac.taille.value) + 4) {
return true;
}
return false
return RdDItemRace.isRacialMax(this, code)
}
async checkCaracXP(caracName, display = true) {
let carac = this.findCaracByName(caracName);
if (carac && carac.xp > 0) {
@ -1598,7 +1558,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') {
if (!Misc.isFirstConnectedGM()) {
if (!Misc.hasConnectedGM()) {
return
}
hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
@ -1664,10 +1624,12 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollUnSort(coord) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
if (RdDEmpoignade.checkEmpoignadeEnCours(this)) {
return
}
if (EffetsDraconiques.isSortImpossible(this)) {
ui.notifications.error("Une queue ou un souffle vous empèche de lancer de sort!");
return;
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 = foundry.utils.duplicate(this.$filterSortList(this.itemTypes['sort'], coord));
@ -1772,6 +1734,17 @@ export class RdDActor extends RdDBaseActorSang {
if (rollData.isSortReserve) {
await this.sortMisEnReserve(selectedSort, rollData.competence, rollData.tmr.coord, Number(selectedSort.system.ptreve_reel));
}
else {
console.log('lancement de sort', rollData.selectedSort)
const precedents = rollData.selectedSort.system.lancements ?? []
const lancements = [...precedents, {
timestamp: game.system.rdd.calendrier.getTimestamp(),
reve: rollData.selectedSort.system.ptreve_reel
}]
await this.updateEmbeddedDocuments('Item',
[{ _id: rollData.selectedSort._id, 'system.lancements': lancements }]
)
}
}
else {
rollData.depenseReve = 0;
@ -1799,38 +1772,8 @@ export class RdDActor extends RdDBaseActorSang {
}
}
/**
* Méthode pour faire un jet prédéterminer sans ouvrir la fenêtre de dialogue
* @param {*} caracName
* @param {*} compName
* @param {*} diff
* @param {*} options
* @returns
*/
async doRollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
const carac = this.getCaracByName(caracName);
if (!carac) {
ui.notifications.warn(`${this.name} n'a pas de caractéristique correspondant à ${caracName}`)
return;
}
const competence = this.getCompetence(compName);
let rollData = {
alias: this.getAlias(),
caracValue: Number(carac.value),
selectedCarac: carac,
competence: competence,
diffLibre: diff,
show: { title: options?.title ?? '' }
};
RollDataAjustements.calcul(rollData, this);
await RdDResolutionTable.rollData(rollData);
this._gererExperience(rollData);
await RdDResolutionTable.displayRollData(rollData, this)
return rollData.rolled;
}
/* -------------------------------------------- */
_gererExperience(rollData) {
gererExperience(rollData) {
const callback = this.createCallbackExperience();
if (callback.condition(rollData)) {
callback.action(rollData);
@ -1880,26 +1823,6 @@ export class RdDActor extends RdDBaseActorSang {
return undefined;
}
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(compName);
await this.openRollDialog({
name: 'jet-competence',
label: 'Jet ' + Grammar.apostrophe('de', competence.name),
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
rollData: {
alias: this.getAlias(),
carac: this.system.carac,
selectedCarac: this.getCaracByName(caracName),
selectedCaracName: caracName,
diffLibre: diff,
competence: competence,
show: { title: options?.title ?? '' }
},
callbackAction: r => this.$onRollCompetence(r, options)
});
}
/* -------------------------------------------- */
async rollTache(id, options = {}) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
@ -1941,8 +1864,8 @@ export class RdDActor extends RdDBaseActorSang {
}
rollData.tache.system.tentatives = rollData.tache.system.nb_jet_succes + rollData.tache.system.nb_jet_echec;
this.updateEmbeddedDocuments('Item', [rollData.tache]);
this.santeIncDec("fatigue", rollData.tache.system.fatigue);
await this.updateEmbeddedDocuments('Item', [rollData.tache]);
await this.santeIncDec("fatigue", rollData.tache.system.fatigue);
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-tache.html');
if (options?.onRollAutomate) {
@ -2620,8 +2543,7 @@ export class RdDActor extends RdDBaseActorSang {
}
/* -------------------------------------------- */
async equiperObjet(itemID) {
let item = this.getEmbeddedDocument('Item', itemID);
async equiperObjet(item) {
if (item?.isEquipable()) {
const isEquipe = !item.system.equipe;
await item.update({ "system.equipe": isEquipe });
@ -2640,7 +2562,7 @@ export class RdDActor extends RdDBaseActorSang {
for (const armure of armures) {
protection += await RdDDice.rollTotal(armure.system.protection.toString());
if (dmg > 0 && attackerRoll.dmg.encaisserSpecial != "noarmure") {
armure.deteriorerArmure(dmg);
await armure.deteriorerArmure(dmg)
dmg = 0;
}
}
@ -2946,12 +2868,13 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async fabriquerPotion(herbeData) {
let newPotion = {
const newPotion = {
name: `Potion de ${herbeData.system.categorie} (${herbeData.name})`, type: 'potion',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/fiole_verre.webp",
system: {
quantite: 1, cout: 0, encombrement: 0.1,
categorie: herbeData.system.categorie,
etat: 'Liquide',
herbe: herbeData.name,
rarete: herbeData.system.rarete,
herbebrins: herbeData.nbBrins,
@ -2959,29 +2882,23 @@ export class RdDActor extends RdDBaseActorSang {
description: ""
}
}
await this.createEmbeddedDocuments('Item', [newPotion], { renderSheet: true });
let newQuantite = herbeData.system.quantite - herbeData.nbBrins;
let messageData = {
alias: this.getAlias(),
nbBrinsReste: newQuantite,
potion: newPotion,
herbe: herbeData
}
this.diminuerQuantiteObjet(herbeData._id, herbeData.nbBrins);
await this.createEmbeddedDocuments('Item', [newPotion])
await this.diminuerQuantiteObjet(herbeData._id, herbeData.nbBrins)
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html`, messageData)
});
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html`, {
alias: this.getAlias(),
nbBrinsReste: herbeData.system.quantite - herbeData.nbBrins,
potion: newPotion,
herbe: herbeData
})
})
}
/* -------------------------------------------- */
async diminuerQuantiteObjet(id, nb, options = { supprimerSiZero: false }) {
const item = this.getItem(id);
if (item) {
await item.diminuerQuantite(nb, options);
}
await this.getItem(id)?.diminuerQuantite(nb, options);
}
/* -------------------------------------------- */
@ -3033,12 +2950,14 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async onCreateItem(item, options, id) {
switch (item.type) {
case 'tete':
case 'queue':
case 'ombre':
case 'souffle':
await this.onCreateOwnedDraconique(item, options, id);
break;
case ITEM_TYPES.tete:
case ITEM_TYPES.queue:
case ITEM_TYPES.ombre:
case ITEM_TYPES.souffle:
await this.onCreateOwnedDraconique(item, options, id)
break
case ITEM_TYPES.race:
await this.onCreateOwnedRace(item, options, id)
}
await item.onCreateItemTemporel(this);
await item.onCreateDecoupeComestible(this);
@ -3046,16 +2965,19 @@ export class RdDActor extends RdDBaseActorSang {
async onDeleteItem(item, options, id) {
switch (item.type) {
case 'tete':
case 'queue':
case 'ombre':
case 'souffle':
case ITEM_TYPES.tete:
case ITEM_TYPES.queue:
case ITEM_TYPES.ombre:
case ITEM_TYPES.souffle:
await this.onDeleteOwnedDraconique(item, options, id)
break
case 'casetmr':
case ITEM_TYPES.race:
await this.onDeleteOwnedRace(item, options, id)
break
case ITEM_TYPES.casetmr:
await this.onDeleteOwnedCaseTmr(item, options, id)
break
case 'empoignade':
case ITEM_TYPES.empoignade:
await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item)
break
}
@ -3084,6 +3006,35 @@ export class RdDActor extends RdDBaseActorSang {
}
}
async onCreateOwnedRace(item, options, id) {
if (Misc.isFirstConnectedGM()) {
const raceIds = this.itemTypes[ITEM_TYPES.race].map(it => it.id).filter(id => id != item.id)
if (raceIds.length > 0) {
await this.deleteEmbeddedDocuments('Item', raceIds)
}
await this._applyRaceCaracUpdates(item, 1)
}
}
async onDeleteOwnedRace(item, options, id) {
if (Misc.isFirstConnectedGM()) {
await this._applyRaceCaracUpdates(item, -1)
}
}
async _applyRaceCaracUpdates(item, sign) {
const updates = {};
RdDCarac.caracs(it => true).forEach(c => {
const toAdd = Number(foundry.utils.getProperty(item, c.path)) * sign
if (toAdd != 0) {
updates[c.path] = Number(foundry.utils.getProperty(this, c.path)) + toAdd
}
})
if (Object.keys(updates).length > 0) {
await this.update(updates)
}
}
/* -------------------------------------------- */
async onDeleteOwnedCaseTmr(item, options, id) {
if (Misc.isFirstConnectedGM()) {

View File

@ -1,5 +1,6 @@
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js";
import { Grammar } from "../grammar.js";
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDBaseActorSheet } from "./base-actor-sheet.js";
@ -27,7 +28,7 @@ export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
this.html.find('.button-encaissement').click(async event => this.actor.encaisser())
this.html.find('.roll-carac').click(async event => {
this.actor.rollCarac(Grammar.toLowerCaseNoAccent(event.currentTarget.attributes['data-carac-name'].value))});
this.actor.rollCarac(Grammar.toLowerCaseNoAccent(event.currentTarget.attributes['data-carac-name'].value))})
this.html.find('.roll-competence').click(async event => this.actor.rollCompetence(RdDSheetUtility.getItemId(event)));
this.html.find('.endurance-plus').click(async event => this.actor.santeIncDec("endurance", 1));
this.html.find('.endurance-moins').click(async event => this.actor.santeIncDec("endurance", -1));
@ -47,6 +48,9 @@ export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
}
}], { renderSheet: true })
)
this.html.find('.roll-text').click(async event => await RdDTextEditor.rollText(event, this.actor))
this.html.find('.chat-roll-text').click(async event => await RdDTextEditor.chatRollText(event))
if (this.options.vueDetaillee) {
// On carac change

View File

@ -7,7 +7,7 @@ import { RdDRoll } from "../rdd-roll.js";
import { RdDUtility } from "../rdd-utility.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { RdDBaseActor } from "./base-actor.js";
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { RdDItemCompetence } from "../item-competence.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDItemArme } from "../item-arme.js";
@ -22,6 +22,7 @@ import { RdDCombat } from "../rdd-combat.js";
import { RdDEmpoignade } from "../rdd-empoignade.js";
import { RdDPossession } from "../rdd-possession.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, POSSESSION_SANS_DRACONIC } from "../item/base-items.js";
import { RollDataAjustements } from "../rolldata-ajustements.js";
/**
* Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
@ -293,6 +294,57 @@ export class RdDBaseActorReve extends RdDBaseActor {
createCallbackAppelAuMoral() { return this.createEmptyCallback(); }
async _onCloseRollDialog(html) { }
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(compName);
await this.openRollDialog({
name: 'jet-competence',
label: competence? 'Jet ' + Grammar.apostrophe('de', competence.name) : `Jet sans compétence (${compName})`,
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
rollData: {
alias: this.getAlias(),
carac: this.system.carac,
selectedCarac: this.getCaracByName(caracName),
selectedCaracName: caracName,
diffLibre: diff,
competence: competence,
show: { title: options?.title ?? '' }
},
callbackAction: r => this.$onRollCompetence(r, options)
});
}
/**
* Méthode pour faire un jet prédéterminer sans ouvrir la fenêtre de dialogue
* @param {*} caracName code ou label de la caractéristique. On peut utiliser 'intel' pour Intellect.
* @param {*} compName nom de compétence ou nom abrégé.
* @param {*} diff difficulté (0 si undefined)
* @param {*} options
* @returns le jet effectué
*/
async doRollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
const carac = this.getCaracByName(caracName);
if (!carac) {
ui.notifications.warn(`${this.name} n'a pas de caractéristique correspondant à ${caracName}`)
return
}
const competence = this.getCompetence(compName);
let rollData = {
alias: this.getAlias(),
caracValue: Number(carac.value),
selectedCarac: carac,
competence: competence,
diffLibre: diff ?? 0,
show: { title: options?.title ?? '' }
}
RollDataAjustements.calcul(rollData, this);
await RdDResolutionTable.rollData(rollData);
this.gererExperience(rollData);
await RdDResolutionTable.displayRollData(rollData, this)
return rollData.rolled;
}
gererExperience(rollData) { }
/* -------------------------------------------- */
async roll() {
RdDEmpoignade.checkEmpoignadeEnCours(this)
@ -311,14 +363,14 @@ export class RdDBaseActorReve extends RdDBaseActor {
competences: this.itemTypes['competence']
},
callbackAction: r => this.$onRollCaracResult(r)
});
})
}
/* -------------------------------------------- */
async rollCarac(caracName, jetResistance = undefined) {
async rollCarac(caracName, options = {}) {
if (Grammar.equalsInsensitive(caracName, 'taille')) {
return
}
foundry.utils.mergeObject(options, { resistance: false, diff: 0 }, { overwrite: false })
RdDEmpoignade.checkEmpoignadeEnCours(this)
let selectedCarac = this.getCaracByName(caracName)
console.log("selectedCarac", selectedCarac)
@ -329,7 +381,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
rollData: {
selectedCarac: selectedCarac,
competences: this.itemTypes['competence'],
jetResistance: jetResistance ? caracName : undefined
diffLibre: options.diff ?? 0,
jetResistance: options.resistance ? caracName : undefined
},
callbackAction: r => this.$onRollCaracResult(r)
});

View File

@ -1,7 +1,7 @@
import { RdDUtility } from "../rdd-utility.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { STATUSES } from "../settings/status-effects.js";
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { RdDBaseActorReve } from "./base-actor-reve.js";
import { RdDDice } from "../rdd-dice.js";
import { RdDItemBlessure } from "../item/blessure.js";

View File

@ -3,8 +3,11 @@ 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, ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js";
import { ItemAction } from "../item/item-actions.js";
/* -------------------------------------------- */
/**
@ -25,7 +28,7 @@ export class RdDBaseActorSheet extends ActorSheet {
/* -------------------------------------------- */
async getData() {
Monnaie.validerMonnaies(this.actor.itemTypes['monnaie']);
Monnaie.validerMonnaies(this.actor)
this.actor.computeEtatGeneral();
let formData = {
@ -35,13 +38,13 @@ export class RdDBaseActorSheet extends ActorSheet {
img: this.actor.img,
name: this.actor.name,
system: this.actor.system,
description: await TextEditor.enrichHTML(this.actor.system.description, { async: true }),
notesmj: await TextEditor.enrichHTML(this.actor.system.notesmj, { async: true }),
description: await RdDTextEditor.enrichHTML(this.actor.system.description, this.actor),
notesmj: await RdDTextEditor.enrichHTML(this.actor.system.notesmj, this.actor),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.actor, this.isEditable),
effects: this.actor.effects
}
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
RdDUtility.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
formData.calc = {
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
@ -53,6 +56,7 @@ export class RdDBaseActorSheet extends ActorSheet {
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature)
.forEach(it => it.isdommages = RdDItemCompetenceCreature.isDommages(it))
return formData;
}
@ -76,95 +80,36 @@ export class RdDBaseActorSheet extends ActorSheet {
}
}
/* -------------------------------------------- */
static filterItemsPerTypeForSheet(formData, itemTypes) {
formData.blessures = Misc.arrayOrEmpty(itemTypes['blessure']);
formData.recettescuisine = Misc.arrayOrEmpty(itemTypes['recettecuisine']);
formData.recettesAlchimiques = Misc.arrayOrEmpty(itemTypes['recettealchimique']);
formData.maladies = Misc.arrayOrEmpty(itemTypes['maladie']);
formData.poisons = Misc.arrayOrEmpty(itemTypes['poison']);
formData.possessions = Misc.arrayOrEmpty(itemTypes['possession']);
formData.maladiesPoisons = formData.maladies.concat(formData.poisons);
formData.competences = (itemTypes['competence'] ?? []).concat(itemTypes['competencecreature'] ?? []);
formData.sortsReserve = Misc.arrayOrEmpty(itemTypes['sortreserve']);
formData.sorts = Misc.arrayOrEmpty(itemTypes['sort']);
formData.rencontres = Misc.arrayOrEmpty(itemTypes['rencontre']);
formData.casestmr = Misc.arrayOrEmpty(itemTypes['casetmr']);
formData.signesdraconiques = Misc.arrayOrEmpty(itemTypes['signedraconique']);
formData.queues = Misc.arrayOrEmpty(itemTypes['queue']);
formData.souffles = Misc.arrayOrEmpty(itemTypes['souffle']);
formData.ombres = Misc.arrayOrEmpty(itemTypes['ombre']);
formData.tetes = Misc.arrayOrEmpty(itemTypes['tete']);
formData.taches = Misc.arrayOrEmpty(itemTypes['tache']);
formData.meditations = Misc.arrayOrEmpty(itemTypes['meditation']);
formData.chants = Misc.arrayOrEmpty(itemTypes['chant']);
formData.danses = Misc.arrayOrEmpty(itemTypes['danse']);
formData.musiques = Misc.arrayOrEmpty(itemTypes['musique']);
formData.oeuvres = Misc.arrayOrEmpty(itemTypes['oeuvre']);
formData.jeux = Misc.arrayOrEmpty(itemTypes['jeu']);
formData.services = Misc.arrayOrEmpty(itemTypes['service']);
formData.conteneurs = Misc.arrayOrEmpty(itemTypes['conteneur']);
formData.materiel = Misc.arrayOrEmpty(itemTypes['objet']);
formData.armes = Misc.arrayOrEmpty(itemTypes['arme']);
formData.armures = Misc.arrayOrEmpty(itemTypes['armure']);
formData.munitions = Misc.arrayOrEmpty(itemTypes['munition']);
formData.livres = Misc.arrayOrEmpty(itemTypes['livre']);
formData.potions = Misc.arrayOrEmpty(itemTypes['potion']);
formData.plantes = Misc.arrayOrEmpty(itemTypes['plante']);
formData.ingredients = Misc.arrayOrEmpty(itemTypes['ingredient']);
formData.faunes = Misc.arrayOrEmpty(itemTypes['faune']);
formData.herbes = Misc.arrayOrEmpty(itemTypes['herbe']);
formData.nourritureboissons = Misc.arrayOrEmpty(itemTypes['nourritureboisson']);
formData.gemmes = Misc.arrayOrEmpty(itemTypes['gemme']);
formData.monnaies = Misc.arrayOrEmpty(itemTypes['monnaie']).sort(Monnaie.triValeurEntiere());
formData.objets = Misc.arrayOrEmpty(itemTypes['objet'])
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('.actionItem').click(event => ItemAction.onActionItem(event, this.actor, this.options))
this.html.find('.item-edit').click(async event => this.itemActionEdit(event))
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(this.getItemId(event));
this.render(true);
});
RdDUtility.toggleAfficheContenu(this.getItemId(event))
this.render(true)
})
this.html.find('.actor-montrer').click(async event => this.actor.postActorToChat());
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('.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);
.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;
this.html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.actionPrincipale(this.actor, async () => this.render())
});
this.html.find('.item-split').click(async event => {
const item = this.getItem(event);
RdDSheetUtility.splitItem(item, this.actor);
});
this.html.find('.item-equip-armure').click(async event => this.actor.equiperObjet(this.getItem(event)))
this.html.find('.item-delete').click(async event => RdDUtility.confirmActorItemDelete(this.getItem(event), this.actor));
this.html.find('.item-quantite-plus').click(async event => this.actor.itemQuantiteIncDec(this.getItemId(event), 1));
this.html.find('.item-quantite-moins').click(async event => this.actor.itemQuantiteIncDec(this.getItemId(event), -1));
this.html.find('.item-delete').click(async event => RdDUtility.confirmActorItemDelete(this, this.getItem(event)));
this.html.find('.item-vendre').click(async event => this.vendre(this.getItem(event)));
this.html.find('.creer-un-objet').click(async event => {
this.selectObjetTypeToCreate();
@ -179,6 +124,11 @@ export class RdDBaseActorSheet extends ActorSheet {
});
}
itemActionEdit(event) {
const item = this.getItem(event);
return item?.sheet.render(true);
}
_rechercherKeyup(event) {
const currentTarget = event.currentTarget;
const nouvelleRecherche = this._optionRecherche(currentTarget);
@ -245,7 +195,7 @@ export class RdDBaseActorSheet extends ActorSheet {
/* -------------------------------------------- */
async selectObjetTypeToCreate() {
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">`;
let content = `<span class="generic-label">Selectionnez le type d'équipement</span><select class="item-type">`;
for (let typeName of types) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}

View File

@ -3,7 +3,7 @@ import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Monnaie } from "../item-monnaie.js";
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js";
import { RdDConfirm } from "../rdd-confirm.js";
@ -21,7 +21,7 @@ export class RdDBaseActor extends Actor {
static $findCaracByName(carac, name) {
const caracList = Object.entries(carac);
let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique' });
let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique', onMessage: m => { } });
if (!entry || entry.length == 0) {
entry = Misc.findFirstLike(name, caracList, { mapper: it => it[1].label, description: 'caractéristique' });
}
@ -81,7 +81,6 @@ export class RdDBaseActor extends Actor {
}
}
static getRealActor(actorId, tokenId) {
if (tokenId) {
let token = canvas.tokens.get(tokenId)
@ -161,8 +160,11 @@ export class RdDBaseActor extends Actor {
return RdDBaseActor.$findCaracByName(carac, name);
}
mapCarac(caracCode) { return caracCode }
getCaracByName(name) {
switch (Grammar.toLowerCaseNoAccent(name)) {
name = this.mapCarac(Grammar.toLowerCaseNoAccent(name))
switch (name) {
case 'reve-actuel': case 'reve actuel':
return this.getCaracReveActuel();
case 'chance-actuelle': case 'chance-actuelle':
@ -735,25 +737,20 @@ export class RdDBaseActor extends Actor {
actionImpossible(action) {
ui.notifications.info(`${this.getAlias()} ne peut pas faire cette action: ${action}`)
}
async jetEthylisme() { this.actionImpossible("jet d'éthylisme") }
async rollAppelChance() { this.actionImpossible("appel à la chance") }
async jetDeMoral() { this.actionImpossible("jet de moral") }
async actionPrincipale(item, onActionItem = async () => { }) {
switch (item.type) {
case ITEM_TYPES.conteneur: return await item.sheet.render(true);
}
return undefined
}
async resetItemUse() { }
async incDecItemUse(itemId, inc = 1) { }
getItemUse(itemId) { return 0; }
async finDeRound(options = { terminer: false }) { }
isActorCombat() { return false }
getCaracInit(competence) { return 0 }
listActionsCombat() { return [] }
listActionsPossessions() {
return this.itemTypes[ITEM_TYPES.possession]

View File

@ -45,6 +45,7 @@ export class RdDCommerceSheet extends RdDBaseActorSheet {
super.activateListeners(html);
this.html.find('a.item-acheter').click(async event => await this.vente(this.getItem(event)));
this.html.find('.service-acheter').click(async event => await this.vente(this.getItem(event)));
if (!this.options.editable) return;

View File

@ -1,4 +1,6 @@
import { ITEM_TYPES } from "../item.js";
import { Grammar } from "../grammar.js";
import { ITEM_TYPES } from "../constants.js";
import { LIST_CARAC_AUTRES } from "../rdd-carac.js";
import { RdDBaseActorSang } from "./base-actor-sang.js";
export class RdDCreature extends RdDBaseActorSang {
@ -32,4 +34,16 @@ export class RdDCreature extends RdDBaseActorSang {
}
}
mapCarac(caracCode) {
switch (caracCode) {
case 'vue': case 'ouie': case 'odoratgout': case 'empathie': case 'perception':
return 'perception'
case 'agilite':
return 'force'
case 'force': case 'constitution': case 'taille': case 'reve': case 'volonte':
return caracCode
}
return undefined
}
}

View File

@ -1,5 +1,5 @@
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { Misc } from "../misc.js";
import { RdDCarac } from "../rdd-carac.js";
import { RdDEncaisser } from "../rdd-roll-encaisser.js";
@ -117,4 +117,14 @@ export class RdDEntite extends RdDBaseActorReve {
super.setEntiteReveAccordee(actor)
}
}
mapCarac(caracCode) {
switch (caracCode) {
case 'taille':
case 'reve':
return caracCode
}
return 'reve'
}
}

View File

@ -6,8 +6,8 @@ import { ExportScriptarium } from "./export-scriptarium.js";
import { CATEGORIES_COMPETENCES, CATEGORIES_DRACONIC, Mapping } from "./mapping.js";
export class RdDActorExportSheet extends RdDActorSheet {
static async init() {
await loadTemplates([
static init() {
loadTemplates([
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/arme.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessures.hbs",
@ -23,6 +23,7 @@ export class RdDActorExportSheet extends RdDActorSheet {
])
Actors.registerSheet(SYSTEM_RDD, RdDActorExportSheet, { types: ["personnage"], makeDefault: false, label: "Feuille simplifiée" })
}
static get defaultOptions() {
return foundry.utils.mergeObject(RdDActorSheet.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/actor-encart-sheet.hbs",

View File

@ -1,4 +1,4 @@
import { ACTOR_TYPES } from "../../item.js"
import { ACTOR_TYPES } from "../../constants.js"
import { Misc } from "../../misc.js"
import { EXPORT_CSV_SCRIPTARIUM, OptionsAvancees } from "../../settings/options-avancees.js"
import { Mapping } from "./mapping.js"

View File

@ -2,7 +2,7 @@ import { Grammar } from "../../grammar.js"
import { RdDItemArme } from "../../item-arme.js"
import { RdDItemCompetence } from "../../item-competence.js"
import { RdDItemSort } from "../../item-sort.js"
import { ITEM_TYPES } from "../../item.js"
import { ITEM_TYPES } from "../../constants.js"
import { Misc } from "../../misc.js"
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
import { RdDBonus } from "../../rdd-bonus.js"

View File

@ -6,7 +6,7 @@ import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js";
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
import { RdDItemTete } from "../item/tete.js";
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
const WHITESPACES = "\\s+"
const NUMERIC = "[\\+\\-]?\\d+"
@ -34,7 +34,6 @@ const XREGEXP_WEAPON_MANIEMENT = "(?<maniement>(" + Misc.join(Object.keys(MANIEM
const XREGEXP_SORT_VOIE = "(?<voies>[OHNT](\\/[OHNT])*)"
const XREGEXP_SORT_NAME = "(?<name>[^\\(]+)"
// const XREGEXP_SORT_CASE = "(?<coord>([A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+|[A-M]\\d{1,2})+)"
const XREGEXP_SORT_CASE = "(?<coord>([A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+|[A-M]\\d{1,2}))"
const XREGEXP_SORT = "(" + XREGEXP_SORT_VOIE
@ -165,26 +164,18 @@ export class RdDStatBlockParser {
switch (type) {
case "creature":
RdDStatBlockParser.parseCreature(statString, actorData)
await RdDStatBlockParser.parseCompetences(statString, actorData, items)
break
case "entite":
RdDStatBlockParser.parseEntite(statString, actorData)
await RdDStatBlockParser.parseCompetences(statString, actorData, items)
break
}
if (type == "personnage") {
// Now process armors
await RdDStatBlockParser.parseArmors(statString, actorData, items);
}
// Get skills from compendium
await RdDStatBlockParser.parseCompetences(statString, actorData, items);
if (type == "personnage") {
// Now process weapons
await RdDStatBlockParser.parseWeapons(statString, items);
await RdDStatBlockParser.parseHautReve(statString, actorData, items);
RdDStatBlockParser.parsePersonnage(statString, actorData);
case "personnage":
await RdDStatBlockParser.parseArmors(statString, actorData, items);
await RdDStatBlockParser.parseCompetences(statString, actorData, items);
await RdDStatBlockParser.parseWeapons(statString, items);
await RdDStatBlockParser.parseHautReve(statString, actorData, items);
RdDStatBlockParser.parsePersonnage(statString, actorData);
}
const name = RdDStatBlockParser.extractName(type, statString);

View File

@ -0,0 +1,78 @@
import "./xregexp-all.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { ACTOR_TYPES } from "../constants.js";
import { TextRollAlchimie } from "./textroll/text-roll-alchimie.js";
import { TextRollCaracCompetence } from "./textroll/text-roll-carac-competence.js";
import { TextRollFormula } from "./textroll/text-roll-formula.js";
import { TextRollManager } from "./textroll/text-roll-formatter.js";
const TEXT_ROLL_MANAGERS = [
new TextRollAlchimie(),
new TextRollCaracCompetence(),
new TextRollFormula()]
export class RdDTextEditor {
static registerChatCallbacks(html) {
html.on("click", '.roll-text', async event => await RdDTextEditor.rollText(event))
}
static async enrichHTML(text, object, options = {showlink:true}) {
const context = {
text,
object,
options,
competences: await SystemCompendiums.getCompetences(ACTOR_TYPES.personnage),
}
for (let manager of TEXT_ROLL_MANAGERS) {
context.code = manager.code
context.template = manager.template
context.text = await manager.onReplaceRoll(context);
}
return await TextEditor.enrichHTML(context.text, {
relativeTo: object,
secrets: object?.isOwner,
async: true
})
}
static async _applyReplaceAll(manager, context) {
context.code = manager.code
context.template = manager.template
context.text = await manager.onReplaceRoll(context);
return context.text
}
static getEventElement(event) {
return $(event.currentTarget)?.parents(".roll-text-link");
}
static async rollText(event, actor) {
const code = TextRollManager.getNode(event)?.data('code')
const manager = TEXT_ROLL_MANAGERS.find(it => it.code == code)
if (manager) {
await manager.onRollText(event, actor)
}
}
static async chatRollText(event) {
const node = TextRollManager.getNode(event);
if (node) {
const code = node.data('code')
const param = node.data('json')
const manager = TEXT_ROLL_MANAGERS.find(it => it.code == code)
const text = await TextRollManager.createRollText(
{
code,
template: manager.template,
options: { showLink: false }
},
param)
ChatMessage.create({
content: text
})
}
}
}

View File

@ -0,0 +1,79 @@
import "../xregexp-all.js";
import { ACTOR_TYPES, ITEM_TYPES } from "../../constants.js";
import { RdDCarac } from "../../rdd-carac.js";
import { RdDUtility } from "../../rdd-utility.js";
import { RdDAlchimie } from "../../rdd-alchimie.js";
import { TextRollManager } from "./text-roll-formatter.js";
const REGEX_ALCHIMIE_TERMES = "(?<termes>(\\w|-)+)"
const REGEX_ALCHIMIE_MANIP = "(?<manip>(couleur|consistance))"
const XREGEXP_ROLL_ALCHIMIE = XRegExp("@roll\\[" + REGEX_ALCHIMIE_MANIP + "\\s+" + REGEX_ALCHIMIE_TERMES + "\\]", 'giu')
const XREGEXP_ROLL_ALCHIMIE_MANIP = XRegExp("@" + REGEX_ALCHIMIE_MANIP + "\\{" + REGEX_ALCHIMIE_TERMES + "\\}", 'giu')
/**
* classe pour gérer les jets d'alchimie
*/
export class TextRollAlchimie {
get code() { return 'alchimie' }
get template() { return `systems/foundryvtt-reve-de-dragon/templates/apps/textroll/link-text-roll-alchimie.hbs` }
async onReplaceRoll(context) {
const handler = new AlchimieTextBuilder(context)
return await handler.replaceAll()
}
async onRollText(event, actor) {
actor = this.getSelectedActor(actor)
if (actor) {
const node = TextRollManager.getNode(event)
const recetteId = node.data('recetteid')
const manip = node.data('manip')
const termes = node.data('termes')
if (recetteId) {
await actor.effectuerTacheAlchimie(recetteId, manip, termes)
}
else {
const carac = RdDCarac.caracDetails(RdDAlchimie.getCaracTache(manip))
const diff = RdDAlchimie.getDifficulte(termes)
await actor.rollCaracCompetence(carac.code, 'Alchimie', diff)
}
}
}
getSelectedActor(actor) {
actor = actor ?? RdDUtility.getSelectedActor()
if (actor && actor.type == ACTOR_TYPES.personnage) {
return actor
}
return undefined
}
}
class AlchimieTextBuilder {
constructor(context) {
this.context = context
}
async replaceAll() {
await XRegExp.forEach(this.context.text, XREGEXP_ROLL_ALCHIMIE, async (rollMatch, i) => await this.replaceMatch(rollMatch, i))
await XRegExp.forEach(this.context.text, XREGEXP_ROLL_ALCHIMIE_MANIP, async (rollMatch, i) => await this.replaceMatch(rollMatch, i))
return this.context.text
}
async replaceMatch(rollMatch, i) {
if (rollMatch.termes && rollMatch.manip) {
const manip = rollMatch.manip
const termes = rollMatch.termes
const carac = RdDCarac.caracDetails(RdDAlchimie.getCaracTache(manip))
const diff = RdDAlchimie.getDifficulte(termes)
const recette = (this.context.object instanceof Item && this.context.object.type == ITEM_TYPES.recettealchimique) ? this.context.object : undefined
const replacement = await TextRollManager.createRollText(this.context,
{
code: this.context.code,
manip, termes, carac, diff, recetteid: recette?.id,
})
this.context.text = this.context.text.replace(rollMatch[0], replacement);
}
}
}

View File

@ -0,0 +1,98 @@
import "../xregexp-all.js";
import { RdDCarac } from "../../rdd-carac.js";
import { RdDItemCompetence } from "../../item-competence.js";
import { RdDUtility } from "../../rdd-utility.js";
import { TextRollManager } from "./text-roll-formatter.js";
const REGECP_CARAC = "(?<carac>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+)"
const REGEXP_COMP = "(\\/(?<competence>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+))?"
const REGEXP_DIFF = "(/(?<diff>[\\+\\-]?\\d+(d\\d+)?))?"
const REGEXP_ROLL_CARAC_COMP = REGECP_CARAC + REGEXP_COMP + REGEXP_DIFF
const XREGEXP_ROLL_CARAC_COMP = XRegExp("@roll\\[" + REGEXP_ROLL_CARAC_COMP + "\\]", 'giu')
/**
* classe pour gérer les jets de caractéristique/compétence depuis
* les journaux/descriptions
*/
export class TextRollCaracCompetence {
get code() { return 'carac' }
get template() { return `systems/foundryvtt-reve-de-dragon/templates/apps/textroll/link-text-roll-carac-competence.hbs` }
async onReplaceRoll(context) {
const handler = new CaracCompetenceTextBuilder(context)
return await handler.replaceAll()
}
async onRollText(event, actor) {
const node = TextRollManager.getNode(event)
const caracCode = node.data('carac-code')
if (caracCode) {
const competence = node.data('competence')
const diff = await this.calculDiff(node)
const actors = this.getSelectedActors(actor)
actors.forEach(async it => await this.doRoll(it, caracCode, competence, diff))
}
}
async calculDiff(node) {
const diff = node.data('diff') ?? 0
if (!Number.isInteger(diff)) {
const roll = new Roll(diff)
await roll.evaluate()
await roll.toMessage({ flavor: `La difficulté de ${diff} a donné ${roll.total}` })
return roll.total
}
return diff
}
async doRoll(actor, caracCode, competence, diff) {
caracCode = actor.mapCarac(caracCode)
if (caracCode) {
if (competence) {
await actor.rollCaracCompetence(caracCode, competence, diff)
}
else {
await actor.rollCarac(caracCode, { diff })
}
}
}
getSelectedActors(actor) {
const selected = canvas.tokens.controlled.map(it => it.actor).filter(it => it)
if (selected.length > 0) {
return selected
}
actor = actor ?? RdDUtility.getSelectedActor()
if (actor) {
return [actor]
}
return []
}
}
class CaracCompetenceTextBuilder {
constructor(context) {
this.context = context
}
async replaceAll() {
await XRegExp.forEach(this.context.text, XREGEXP_ROLL_CARAC_COMP, async (rollMatch, i) => await this.replaceMatch(rollMatch, i))
return this.context.text
}
async replaceMatch(rollMatch, i) {
const carac = RdDCarac.caracDetails(rollMatch.carac)
if (carac) {
const competence = rollMatch.competence ? RdDItemCompetence.findCompetence(this.context.competences, rollMatch.competence) : undefined
const replacement = await TextRollManager.createRollText(this.context,
{
code: this.context.code,
carac: carac,
competence: competence?.name,
diff: rollMatch.diff,
})
this.context.text = this.context.text.replace(rollMatch[0], replacement)
}
}
}

View File

@ -0,0 +1,13 @@
export class TextRollManager {
static async createRollText(context, param) {
return await renderTemplate(context.template, {
param, options: context.options
})
}
static getNode(event) {
return $(event.currentTarget)?.parents(".roll-text-link");
}
}

View File

@ -0,0 +1,51 @@
import "../xregexp-all.js";
import { TextRollManager } from "./text-roll-formatter.js";
const REGEXP_ROLL_FORMULA = "(?<formula>[^\\[\\]]+)"
const XREGEXP_ROLL_FORMULA = XRegExp("@roll\\[" + REGEXP_ROLL_FORMULA + "\\]", 'giu')
/**
* classe pour gérer les jets de dés (formules Foundry)
*/
export class TextRollFormula {
get code() { return 'formula' }
get template() { return `systems/foundryvtt-reve-de-dragon/templates/apps/textroll/link-text-roll-formula.hbs` }
async onReplaceRoll(context) {
const handler = new FormulaTextBuilder(context)
return await handler.replaceAll()
}
async onRollText(event, actor) {
const node = TextRollManager.getNode(event)
const rollFormula = node.data('formula')
if (rollFormula) {
const roll = new Roll(rollFormula)
await roll.evaluate()
await roll.toMessage()
}
}
}
class FormulaTextBuilder {
constructor(context) {
this.context = context
}
async replaceAll() {
await XRegExp.forEach(this.context.text, XREGEXP_ROLL_FORMULA,
async (rollMatch, i) => await this.replaceMatch(rollMatch, i))
return this.context.text
}
async replaceMatch(rollMatch, i) {
if (rollMatch.formula) {
const replacement = await TextRollManager.createRollText(this.context,
{
code: this.context.code,
formula: rollMatch.formula,
})
this.context.text = this.context.text.replace(rollMatch[0], replacement)
}
}
}

View File

@ -2002,7 +2002,7 @@ XRegExp.exec = function (str, regex, pos, sticky) {
*/
XRegExp.forEach = function (str, regex, callback) {
XRegExp.forEach = async function (str, regex, callback) {
var pos = 0;
var i = -1;
var match;
@ -2014,7 +2014,7 @@ XRegExp.forEach = function (str, regex, callback) {
// at least. Actually, because of the way `XRegExp.exec` caches globalized versions of
// regexes, mutating the regex will not have any effect on the iteration or matched strings,
// which is a nice side effect that brings extra safety.
callback(match, ++i, str, regex);
await callback(match, ++i, str, regex);
pos = match.index + (match[0].length || 1);
}
};

View File

@ -1,6 +1,7 @@
import { Misc } from "./misc.js";
import { SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
/**
@ -198,6 +199,7 @@ export class ChatUtility {
static async onCreateChatMessage(chatMessage, options, id) {
if (chatMessage.isAuthor) {
await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp());
await chatMessage.update({ content: await RdDTextEditor.enrichHTML(chatMessage.content, undefined, {showLink:false}) })
}
}
}

View File

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

View File

@ -1,13 +1,13 @@
export const SYSTEM_RDD = 'foundryvtt-reve-de-dragon';
export const SYSTEM_SOCKET_ID = 'system.foundryvtt-reve-de-dragon';
export const LOG_HEAD = 'RdD | ';
export const SYSTEM_RDD = 'foundryvtt-reve-de-dragon'
export const SYSTEM_SOCKET_ID = 'system.foundryvtt-reve-de-dragon'
export const LOG_HEAD = 'RdD | '
export const HIDE_DICE = 'hide';
export const SHOW_DICE = 'show';
export const HIDE_DICE = 'hide'
export const SHOW_DICE = 'show'
export const ENTITE_INCARNE = 'incarne';
export const ENTITE_NONINCARNE = 'nonincarne';
export const ENTITE_BLURETTE = 'blurette';
export const ENTITE_INCARNE = 'incarne'
export const ENTITE_NONINCARNE = 'nonincarne'
export const ENTITE_BLURETTE = 'blurette'
export const RDD_CONFIG = {
niveauEthylisme : [
@ -50,4 +50,59 @@ export const RDD_CONFIG = {
{value: "Rare", label: "Rare"},
{value: "Rarissime", label: "Rarissime"}
]
}
}
export const ACTOR_TYPES = {
personnage: 'personnage',
creature: 'creature',
entite: 'entite',
commerce: 'commerce',
vehicule: 'vehicule'
}
export const ITEM_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',
race: 'race',
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',
}

View File

@ -1,88 +1,79 @@
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js";
export class DialogFabriquerPotion extends Dialog {
/* -------------------------------------------- */
static async create(actor, item, onActionItem) {
const min = DialogFabriquerPotion.nombreBrinsMinimum(item);
if (item.system.quantite < min) {
ui.notifications.warn(`Vous avez ${item.system.quantite} brins de ${item.name}, il en faut au moins ${min} pour faire une potion!`);
return;
static async create(actor, item) {
const brinsMinimum = DialogFabriquerPotion.nombreBrinsMinimum(item)
if (item.system.quantite < brinsMinimum) {
ui.notifications.warn(`Vous avez ${item.system.quantite} brins de ${item.name}, il en faut au moins ${brinsMinimum} pour faire une potion!`)
return
}
let potionData = DialogFabriquerPotion.prepareData(actor, item);
const potionData = DialogFabriquerPotion.prepareData(item, brinsMinimum)
const options = { classes: ["dialogfabriquerpotion"], width: 600, height: 160, 'z-index': 99999 }
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-fabriquer-potion-base.html', potionData)
const html = await renderTemplate( 'systems/foundryvtt-reve-de-dragon/templates/dialog-fabriquer-potion-base.html', potionData);
let options = { classes: ["dialogfabriquerpotion"], width: 600, height: 160, 'z-index': 99999 };
new DialogFabriquerPotion(actor, potionData, onActionItem, html, options).render(true);
new DialogFabriquerPotion(actor, potionData, html, options).render(true)
}
/* -------------------------------------------- */
static prepareData(actor, item) {
let potionData = foundry.utils.duplicate(item)
potionData.nbBrinsSelect = RdDUtility.buildListOptions(
DialogFabriquerPotion.nombreBrinsMinimum(item),
DialogFabriquerPotion.nombreBrinsOptimal(item));
potionData.nbBrins = Math.min(potionData.system.quantite, DialogFabriquerPotion.nombreBrinsOptimal(potionData));
potionData.herbebonus = item.system.niveau;
potionData.buttonName = "Fabriquer";
return potionData;
static prepareData(item, brinsMinimum) {
const brinsOptimal = DialogFabriquerPotion.nombreBrinsOptimal(item)
return foundry.utils.mergeObject(foundry.utils.duplicate(item), {
nbBrinsSelect: RdDUtility.buildListOptions(brinsMinimum, brinsOptimal),
nbBrins: Math.min(item.system.quantite, brinsOptimal),
herbebonus: item.system.niveau
})
}
/* -------------------------------------------- */
constructor(actor, potionData, onActionItem, html, options) {
constructor(actor, potionData, html, options) {
const conf = {
title: `Fabriquer une potion de ${potionData.system.categorie}`,
content: html,
default: 'fabriquer',
buttons: {
'fabriquer': {
label: potionData.buttonName, callback: it => this.onFabriquer()
}
'fabriquer': { label: "Fabriquer", callback: it => this.onFabriquer() }
}
};
}
super(conf, options)
super(conf, options);
this.actor = actor;
this.potionData = potionData;
this.onActionItem = onActionItem;
this.actor = actor
this.potionData = potionData
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
super.activateListeners(html)
this.html = html
this.html.find("[name='nbBrins']").change(event => {
this.potionData.nbBrins = Misc.toInt(event.currentTarget.value);
const brinsManquants = Math.max(0, DialogFabriquerPotion.nombreBrinsOptimal(this.potionData) - this.potionData.nbBrins);
this.potionData.nbBrins = Misc.toInt(event.currentTarget.value)
const brinsManquants = Math.max(0, DialogFabriquerPotion.nombreBrinsOptimal(this.potionData) - this.potionData.nbBrins)
this.potionData.herbebonus = Math.max(0, this.potionData.system.niveau - brinsManquants)
});
})
}
/* -------------------------------------------- */
async onFabriquer() {
await this.html.find("[name='nbBrins']").change();
await this.actor.fabriquerPotion(this.potionData);
this.close();
await this.onActionItem()
await this.html.find("[name='nbBrins']").change()
await this.actor.fabriquerPotion(this.potionData)
this.close()
}
static nombreBrinsMinimum(herbeData) {
switch (herbeData.system.categorie ?? '') {
case "Soin": return 1 + Math.max(0, 12 - 2 * herbeData.system.niveau);
case "Repos": return 1 + Math.max(0, 7 - 2 * herbeData.system.niveau);
case "Soin": return 1 + Math.max(0, 12 - 2 * herbeData.system.niveau)
case "Repos": return 1 + Math.max(0, 7 - 2 * herbeData.system.niveau)
}
return 1;
return 1
}
static nombreBrinsOptimal(herbeData) {
switch (herbeData.system.categorie ?? '') {
case "Soin": return 12 - herbeData.system.niveau;
case "Repos": return 7 - herbeData.system.niveau;
case "Soin": return 12 - herbeData.system.niveau
case "Repos": return 7 - herbeData.system.niveau
}
return 1;
return 1
}
}

View File

@ -0,0 +1,177 @@
import { ITEM_TYPES } from "../constants.js"
import { RdDItemSort } from "../item-sort.js"
import { Misc } from "../misc.js"
export const ACTION_ITEM_ENCHANTER = {
code: 'item-enchanter', label: 'Enchanter', icon: it => 'fa-solid fa-sparkles',
filter: it => game.user.isGM || DialogEnchanter.isEnchantable(it),
optionsFilter: options => options.editable,
action: (item, actor) => DialogEnchanter.enchanter(item)
}
export class DialogEnchanter extends Dialog {
static isEnchantable(item) {
if (!item.isEnchantementPossible) {
return false
}
if (game.user.isGM) {
return true
}
if (item.system.prpermanent) {
return false
}
if (!item.parent?.isHautRevant()) {
return false
}
return RdDItemSort.lancements(RdDItemSort.findEnchantement(item.parent)) > 0
|| RdDItemSort.lancements(RdDItemSort.findPurification(item.parent)) > 0
|| RdDItemSort.lancements(RdDItemSort.findPermanence(item.parent)) > 0
}
static dateEnchantement() {
return game.system.rdd.calendrier.getTimestamp().debutJournee().indexDate
}
static async enchanter(item) {
const actor = item.parent
const sorts = {
enchantement: RdDItemSort.findEnchantement(actor),
purification: RdDItemSort.findPurification(actor),
permanence: RdDItemSort.findPermanence(actor)
}
const nouveauxpr = (sorts.enchantement?.system.lancements ?? []).map(it => it.reve)
const purification = (sorts.purification?.system.lancements ?? []).find(it => true)
const permanence = (sorts.permanence?.system.lancements ?? []).find(it => true)
const enchanter = {
type: item.type == ITEM_TYPES.potion ? 'potion' : Misc.typeName('Item', item.type),
actor: actor,
item: item,
reve: item.system.pr,
sorts: sorts,
nouveauxpr: nouveauxpr,
purification: purification != undefined,
permanence: permanence != undefined,
options: { isGM: game.user.isGM }
}
if (!item.isEnchantementPossible) {
ui.notifications.info("Seuls les liquides et les gemmes sont enchantables")
return
}
if (item.system.prpermanent) {
ui.notifications.info(`La ${enchanter.type} est permanente`)
return
}
if (!game.user.isGM) {
if (!(actor?.isPersonnage() || actor?.isHautRevant())) {
ui.notifications.info(`Seul un haut rêvant peut enchanter une ${enchanter.type}`)
return
}
if (item.system.quantite != 1) {
ui.notifications.info(`Impossible d'enchanter ${item.system.quantite} ${enchanter.type}s ${item.system.quantite > 1 ? 'en une seule fois' : ''}`)
return
}
if (!(nouveauxpr.length > 0 || purification || permanence)) {
ui.notifications.info("Le haut-rêvant n'a lancé de rituel d'enchantement")
return
}
if (item.system.magique && item.system.purifie && !purification && !permanence) {
ui.notifications.info(`La ${enchanter.type} est déjà enchantée et doit être purifiée`)
return
}
}
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/enchantement/dialog-enchanter.hbs`, enchanter)
const dialog = new DialogEnchanter(enchanter, html)
dialog.render(true)
}
constructor(enchanter, html) {
let options = { classes: ["dialog-enchanter"], width: 400, height: 'fit-content', 'z-index': 99999 }
let conf = {
title: `Enchanter une ${enchanter.type}`,
content: html,
default: "enchanter",
buttons: {
"enchanter": { label: "Enchanter", callback: it => this.onEnchanter() }
}
};
super(conf, options)
this.html = html
this.enchanter = enchanter
this.item = enchanter.item
}
activateListeners(html) {
super.activateListeners(html)
this.html = html
this.html.find("input.enchantement").change(event => this.$onSelectEnchantement(event))
this.html.find("input.reve").change(event => this.$onForceReve(event))
}
$onSelectEnchantement(event) {
const addReve = $(event.currentTarget).data('reve')
this.enchanter.idx = $(event.currentTarget).data('idx')
this.enchanter.reve = this.item.system.pr + Number(addReve)
this.html.find("input.reve").val(this.enchanter.reve)
for (let idx = 0; idx < this.enchanter.nouveauxpr.length; idx++) {
if (idx != this.enchanter.idx) {
this.html.find(`.enchantement[data-idx='${idx}']`).prop("checked", false)
}
}
}
$onForceReve(event) {
const newReve = Number(event.currentTarget.value)
if (this.enchanter.reve != newReve) {
this.enchanter.idx = undefined
}
this.enchanter.reve = newReve
}
async onEnchanter() {
foundry.utils.mergeObject(this.enchanter,
{
rendrepurifie: this.html.find("input.rendrepurifie").prop("checked"),
rendrepermanent: this.html.find("input.rendrepermanent").prop("checked"),
addreve: this.enchanter.reve != this.item.system.pr
},
{ inplace: true })
const item = this.enchanter.item
const actor = this.enchanter.actor
if (this.enchanter.reve == 0) {
await item.update({
'system.pr': 0,
'system.magique': false,
'system.purifie': false,
'system.prpermanent': false,
'system.prdate': 0
})
}
else {
const isPurifiee = this.enchanter.addreve
? (this.enchanter.rendrepurifie && (item.item.system.pr == 0 ? true : item.system.purifie))
: (this.enchanter.rendrepurifie || item.system.purifie)
await item.update({
'system.pr': this.enchanter.reve,
'system.magique': true,
'system.purifie': isPurifiee,
'system.prpermanent': item.system.prpermanent || this.enchanter.rendrepermanent,
'system.prdate': DialogEnchanter.dateEnchantement()
})
if (actor) {
if (this.enchanter.rendrepurifie) {
await RdDItemSort.changeLancementsSort(this.enchanter.sorts.purification, it => it.slice(1));
}
if (this.enchanter.rendrepermanent) {
await RdDItemSort.changeLancementsSort(this.enchanter.sorts.permanence, it => it.slice(1));
}
if (this.enchanter.addreve && this.enchanter.idx != undefined) {
await RdDItemSort.changeLancementsSort(RdDItemSort.findEnchantement(actor), it => it.toSpliced(this.enchanter.idx, 1))
}
}
}
}
}

View File

@ -1,6 +1,6 @@
import { Grammar } from "./grammar.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
import { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
import { BASE_CORPS_A_CORPS } from "./item/base-items.js";
import { RdDCombatManager } from "./rdd-combat.js";

View File

@ -1,5 +1,5 @@
import { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
import { RdDCombatManager } from "./rdd-combat.js";
export const CATEGORIES_COMPETENCES_CREATURES = {

View File

@ -1,5 +1,5 @@
import { Misc } from "./misc.js";
import { LOG_HEAD } from "./constants.js";
import { ITEM_TYPES, LOG_HEAD } from "./constants.js";
const MONNAIE_ETAIN = {
name: "Denier (étain)", type: 'monnaie',
@ -70,18 +70,17 @@ export class Monnaie {
}
static getFortune(monnaies) {
return (monnaies??[])
return (monnaies ?? [])
.map(m => Number(m.system.cout) * Number(m.system.quantite))
.reduce(Misc.sum(), 0);
}
static async optimiserFortune(actor, fortune) {
Monnaie.validerMonnaies(actor)
let resteEnDeniers = Math.round(fortune * 100);
let monnaies = actor.itemTypes['monnaie'];
let updates = [];
Monnaie.validerMonnaies(monnaies, actor);
const updates = []
let parValeur = Misc.classifyFirst(monnaies, it => VALEUR_DENIERS(it.system.cout));
const parValeur = Misc.classifyFirst(actor.itemTypes[ITEM_TYPES.monnaie], it => VALEUR_DENIERS(it.system.cout));
for (let valeurDeniers of [1000, 100, 10, 1]) {
const itemPiece = parValeur[valeurDeniers];
if (itemPiece) {
@ -102,8 +101,11 @@ export class Monnaie {
}
}
static validerMonnaies(monnaies, actor = undefined) {
monnaies.filter(it => VALEUR_DENIERS(it.system.cout) == 0)
static validerMonnaies(actor) {
if (!actor) {
return
}
actor.itemTypes[ITEM_TYPES.monnaie]?.filter(it => VALEUR_DENIERS(it.system.cout) == 0)
.map(it => `La monnaie ${it.name} de l'acteur ${actor?.name ?? 'sélectionné'} a une valeur de 0!`)
.forEach(message => {
ui.notifications.warn(message);

View File

@ -1,9 +1,7 @@
import { ACTOR_TYPES, ITEM_TYPES } from "./constants.js";
import { RdDItemSort } from "./item-sort.js";
import { RdDUtility } from "./rdd-utility.js";
import { RdDAlchimie } from "./rdd-alchimie.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDGemme } from "./rdd-gemme.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { SYSTEM_RDD } from "./constants.js";
@ -12,8 +10,11 @@ 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 { ITEM_TYPES, RdDItem } from "./item.js";
import { RdDItem } from "./item.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
import { ItemAction } from "./item/item-actions.js";
import { RdDItemGemme } from "./item/gemme.js";
/**
* Extend the basic ItemSheet for RdD specific items
@ -26,8 +27,8 @@ export class RdDItemSheet extends ItemSheet {
static defaultTemplate(type) {
return type ?
`systems/foundryvtt-reve-de-dragon/templates/item-${type}-sheet.html` :
"systems/foundryvtt-reve-de-dragon/templates/item-sheet.html";
`systems/foundryvtt-reve-de-dragon/templates/item/${type}-sheet.hbs` :
"systems/foundryvtt-reve-de-dragon/templates/item/item-sheet.hbs";
}
static register(sheetClass) {
@ -97,11 +98,11 @@ export class RdDItemSheet extends ItemSheet {
name: this.item.name,
system: this.item.system,
actorId: this.actor?.id,
description: await TextEditor.enrichHTML(this.item.system.description, { async: true }),
descriptionmj: await TextEditor.enrichHTML(this.item.system.descriptionmj, { async: true }),
description: await RdDTextEditor.enrichHTML(this.item.system.description, this.item),
descriptionmj: await RdDTextEditor.enrichHTML(this.item.system.descriptionmj, this.item),
isComestible: this.item.getUtilisationCuisine(),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable),
competences: await SystemCompendiums.getCompetences('personnage'),
competences: await SystemCompendiums.getCompetences(ACTOR_TYPES.personnage),
categories: RdDItem.getCategories(this.item.type),
}
@ -120,26 +121,19 @@ export class RdDItemSheet extends ItemSheet {
formData.competences = formData.competences.filter(it => it.isCompetenceArme())
}
if (this.item.type == ITEM_TYPES.recettecuisine) {
formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, { async: true })
formData.ingredients = await RdDTextEditor.enrichHTML(this.item.system.ingredients, this.item)
}
if (this.item.type == ITEM_TYPES.extraitpoetique) {
formData.extrait = await TextEditor.enrichHTML(this.object.system.extrait, { async: true })
formData.texte = await TextEditor.enrichHTML(this.object.system.texte, { async: true })
formData.extrait = await RdDTextEditor.enrichHTML(this.item.system.extrait, this.item)
formData.texte = await RdDTextEditor.enrichHTML(this.item.system.texte, this.item)
}
if (this.item.type == ITEM_TYPES.recettealchimique) {
RdDAlchimie.processManipulation(this.item, this.actor?.id);
formData.manipulation_update = await TextEditor.enrichHTML(this.object.system.manipulation_update, { async: true })
formData.utilisation = await TextEditor.enrichHTML(this.object.system.utilisation, { async: true })
formData.enchantement = await TextEditor.enrichHTML(this.object.system.enchantement, { async: true })
formData.sureffet = await TextEditor.enrichHTML(this.object.system.sureffet, { async: true })
}
if (this.item.type == ITEM_TYPES.gemme) {
formData.gemmeTypeList = RdDGemme.getGemmeTypeOptionList();
RdDGemme.calculDataDerivees(this.item)
}
if (this.item.type == ITEM_TYPES.potion) {
RdDHerbes.calculFormData(formData, this.item)
formData.manipulation = await RdDTextEditor.enrichHTML(this.item.system.manipulation, this.item)
formData.utilisation = await RdDTextEditor.enrichHTML(this.item.system.utilisation, this.item)
formData.enchantement = await RdDTextEditor.enrichHTML(this.item.system.enchantement, this.item)
formData.sureffet = await RdDTextEditor.enrichHTML(this.item.system.sureffet, this.item)
}
if (this.item.type == ITEM_TYPES.herbe) {
if (formData.options.isOwned && ['Soin', 'Repos'].includes(formData.system.categorie)) {
formData.isIngredientPotionBase = true;
@ -166,7 +160,7 @@ export class RdDItemSheet extends ItemSheet {
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionnelles.isUsing('afficher-prix-joueurs')
|| game.user.isGM
|| !this.item.isOwned);
HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique());
HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
@ -194,47 +188,27 @@ export class RdDItemSheet extends ItemSheet {
this.supprimerBonusCase(event.currentTarget.attributes['data-deleteCoord'].value)
})
this.html.find('.date-enchantement').change((event) => {
const jour = Number(this.html.find('input.date-enchantement[name="enchantement.jour"]').val());
const mois = RdDTimestamp.definition(this.html.find('select.date-enchantement[name="enchantement.mois"]').val());
const indexDate = game.system.rdd.calendrier.getIndexFromDate(jour, mois.heure);
this.item.update({ 'system.prdate': indexDate });
console.warn(`Date d'enchantement modifiée ${jour}/${mois.heure}: ${indexDate}`)
});
this.html.find('.creer-tache-livre').click((event) => this._getEventActor(event).creerTacheDepuisLivre(this.item));
this.html.find('.consommer-potion').click((event) => this._getEventActor(event).consommerPotion(this.item, this.getActionRenderItem()));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).actionHerbe(this.item));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).fabriquerDecoctionHerbe(this.item))
this.html.find('input[name="system.cacher_points_de_tache"]').change(async event => await this.item.update({ 'system.cacher_points_de_tache': event.currentTarget.checked }));
this.html.find('.alchimie-tache a').click((event) => {
let actor = this._getEventActor(event);
if (actor) {
let recetteId = event.currentTarget.attributes['data-recette-id'].value;
let tacheName = event.currentTarget.attributes['data-alchimie-tache'].value;
let tacheData = event.currentTarget.attributes['data-alchimie-data'].value;
actor.effectuerTacheAlchimie(recetteId, tacheName, tacheData);
} else {
ui.notifications.info("Impossible trouver un acteur pour réaliser cette tache Alchimique.");
}
});
this.html.find('.roll-text').click(async event => await RdDTextEditor.rollText(event, this.actor))
this.html.find('.chat-roll-text').click(async event => await RdDTextEditor.chatRollText(event))
if (this.actor) {
this.html.find('.item-split').click(async event => RdDSheetUtility.splitItem(RdDSheetUtility.getItem(event, this.actor), this.actor, this.getActionRenderItem()));
this.html.find('.item-edit').click(async event => RdDSheetUtility.getItem(event, this.actor)?.sheet.render(true));
this.html.find('.item-delete').click(async event => RdDUtility.confirmActorItemDelete(this, RdDSheetUtility.getItem(event, this.actor)));
this.html.find('.item-vendre').click(async event => RdDSheetUtility.getItem(event, this.actor)?.proposerVente());
this.html.find('.item-montrer').click(async event => RdDSheetUtility.getItem(event, this.actor)?.postItemToChat());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor, this.getActionRenderItem()));
this.html.find('.actionItem').click(event => ItemAction.onActionItem(event, this.actor, this.options))
// TODO: utiliser un itemAction?
this.html.find('.item-potion-consommer').click(event => this.itemActionConsommer(event))
this.html.find('.item-quantite-plus').click(async event => {
await this.actor.itemQuantiteIncDec(RdDSheetUtility.getItemId(event), 1)
this.render();
});
this.render()
})
this.html.find('.item-quantite-moins').click(async event => {
await this.actor.itemQuantiteIncDec(RdDSheetUtility.getItemId(event), -1)
this.render();
});
this.render()
})
}
const updateItemTimestamp = (path, timestamp) => this.item.update({ [path]: foundry.utils.duplicate(timestamp) })
@ -243,13 +217,24 @@ export class RdDItemSheet extends ItemSheet {
RdDTimestamp.handleTimestampEditor(this.html, 'system.temporel.fin', updateItemTimestamp);
}
getActionRenderItem() {
return async () => {
let item = this.item;
while (item) {
await item.sheet?.render()
item = this.actor.getContenant(item)
}
itemActionDelete(event) {
const item = RdDSheetUtility.getItem(event, this.actor)
return RdDUtility.confirmActorItemDelete(item, this.actor)
}
async itemActionConsommer(event) {
const item = RdDSheetUtility.getItem(event, this.actor)
if (item) {
await actor.consommerPotion(item)
await RdDSheetUtility.renderItemBranch(this.actor, item)
}
}
async itemActionSplit(event) {
const item = RdDSheetUtility.getItem(event, this.actor)
if (item) {
await RdDSheetUtility.splitItem(item, this.actor)
await RdDSheetUtility.renderItemBranch(this.actor, item)
}
}
@ -272,7 +257,7 @@ export class RdDItemSheet extends ItemSheet {
}
}
async supprimerBonusCase(deleteCoord){
async supprimerBonusCase(deleteCoord) {
if (this.item.type == ITEM_TYPES.sort) {
const oldList = RdDItemSort.getBonusCaseList(this.item)
const newList = oldList.filter(it => it.case != deleteCoord);

View File

@ -1,6 +1,6 @@
import { Grammar } from "./grammar.js";
import { RdDItemCompetence } from "./item-competence.js";
import { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
import { Misc } from "./misc.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js";
@ -22,7 +22,21 @@ export class RdDItemSort extends Item {
}
static addSpaceToNonNumeric(value) {
return Number.isNumeric(value) || ['-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes( String(value).charAt[0]) ? value : ' ' + RdDItemSort.toVar(value)
return Number.isNumeric(value) || ['-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(String(value).charAt[0]) ? value : ' ' + RdDItemSort.toVar(value)
}
static lancements(sort) { return sort?.system.lancements.length ?? 0 }
static findEnchantement(actor) { return RdDItemSort.findSort(actor, 'Enchantement', ['Narcos', 'N']) }
static findPermanence(actor) { return RdDItemSort.findSort(actor, 'Permanence', ['Narcos', 'N']) }
static findPurification(actor) { return RdDItemSort.findSort(actor, 'Purification', ['Narcos', 'N']) }
static findSort(actor, name, draconics) {
return actor.itemTypes[ITEM_TYPES.sort].find(it => Grammar.includesLowerCaseNoAccent(it.name, name)
&& (draconics == undefined || draconics.includes(it.system.draconic)))
}
static async changeLancementsSort(sort, changement) {
await sort?.update({ 'system.lancements': changement(sort.system.lancements) });
}
static toVar(value) {
@ -151,5 +165,4 @@ export class RdDItemSort extends Item {
.map(it => it.split(':'))
.map(it => { return { case: it[0], bonus: it[1] } });
}
}

View File

@ -1,7 +1,7 @@
import { ITEM_TYPES } from "./constants.js";
import { DialogItemVente } from "./achat-vente/dialog-item-vente.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDUtility } from "./rdd-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
@ -9,60 +9,7 @@ import { RdDRaretes } from "./item/raretes.js";
import { CATEGORIES_COMPETENCES } from "./item-competence.js";
import { CATEGORIES_COMPETENCES_CREATURES } from "./item-competencecreature.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE } from "./item/base-items.js";
export const ACTOR_TYPES = {
personnage: 'personnage',
creature: 'creature',
entite: 'entite',
commerce: 'commerce',
vehicule: 'vehicule'
}
export const ITEM_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',
}
import { ITEM_ACTIONS, DEFAULT_ACTIONS, COMMON_ACTIONS } from "./item/item-actions.js";
const typesInventaireMateriel = [
ITEM_TYPES.arme,
@ -94,51 +41,47 @@ const typesObjetsTemporels = [ITEM_TYPES.blessure, ITEM_TYPES.poison, ITEM_TYPES
const typesObjetsEquipable = [ITEM_TYPES.arme, ITEM_TYPES.armure, ITEM_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
densité 3.5 (~2.3 à 4, parfois plus) -- https://www.juwelo.fr/guide-des-pierres/faits-et-chiffres/
*/
export const defaultItemImg = {
competence: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
competencecreature: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
arme: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/epee_gnome.webp",
armure: "systems/foundryvtt-reve-de-dragon/icons/armes_armures/armure_plaques.webp",
conteneur: "systems/foundryvtt-reve-de-dragon/icons/objets/sac_a_dos.webp",
sort: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp",
herbe: "systems/foundryvtt-reve-de-dragon/icons/botanique/Endorlotte.webp",
faune: "systems/foundryvtt-reve-de-dragon/icons/faune/rongeur.webp",
ingredient: "systems/foundryvtt-reve-de-dragon/icons/objets/sable_poudre.webp",
livre: "systems/foundryvtt-reve-de-dragon/icons/objets/livre.webp",
potion: "systems/foundryvtt-reve-de-dragon/icons/objets/liqueur_de_bagdol.webp",
rencontre: "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp",
queue: "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp",
ombre: "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp",
souffle: "systems/foundryvtt-reve-de-dragon/icons/souffle_dragon.webp",
tete: "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp",
meditation: "systems/foundryvtt-reve-de-dragon/icons/meditations_ecrits/meditation_alchimie.webp",
recettealchimique: "systems/foundryvtt-reve-de-dragon/icons/competence_alchimie.webp",
chant: "systems/foundryvtt-reve-de-dragon/icons/arts/chant_0.webp",
competence: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
competencecreature: "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp",
conteneur: "systems/foundryvtt-reve-de-dragon/icons/objets/sac_a_dos.webp",
danse: "systems/foundryvtt-reve-de-dragon/icons/arts/danse_0.webp",
empoignade: "systems/foundryvtt-reve-de-dragon/icons/empoignade.webp",
extraitpoetique: "systems/foundryvtt-reve-de-dragon/icons/competence_ecriture.webp",
faune: "systems/foundryvtt-reve-de-dragon/icons/faune/rongeur.webp",
gemme: "systems/foundryvtt-reve-de-dragon/icons/gemmes/almaze.webp",
herbe: "systems/foundryvtt-reve-de-dragon/icons/botanique/Endorlotte.webp",
ingredient: "systems/foundryvtt-reve-de-dragon/icons/objets/sable_poudre.webp",
jeu: "systems/foundryvtt-reve-de-dragon/icons/arts/jeux_petasse.webp",
recettecuisine: "systems/foundryvtt-reve-de-dragon/icons/arts/recette_cuisine_1.webp",
musique: "systems/foundryvtt-reve-de-dragon/icons/arts/chant_0.webp",
livre: "systems/foundryvtt-reve-de-dragon/icons/objets/livre.webp",
maladie: "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/maladie.webp",
poison: "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/venin.webp",
oeuvre: "systems/foundryvtt-reve-de-dragon/icons/competence_comedie.webp",
meditation: "systems/foundryvtt-reve-de-dragon/icons/meditations_ecrits/meditation_alchimie.webp",
musique: "systems/foundryvtt-reve-de-dragon/icons/arts/chant_0.webp",
nourritureboisson: "systems/foundryvtt-reve-de-dragon/icons/objets/provision_crue.webp",
oeuvre: "systems/foundryvtt-reve-de-dragon/icons/competence_comedie.webp",
ombre: "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp",
poison: "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/venin.webp",
possession: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
potion: "systems/foundryvtt-reve-de-dragon/icons/objets/liqueur_de_bagdol.webp",
queue: "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp",
recettealchimique: "systems/foundryvtt-reve-de-dragon/icons/competence_alchimie.webp",
recettecuisine: "systems/foundryvtt-reve-de-dragon/icons/arts/recette_cuisine_1.webp",
rencontre: "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp",
service: "systems/foundryvtt-reve-de-dragon/icons/services/lit.webp",
signedraconique: "systems/foundryvtt-reve-de-dragon/icons/tmr/signe_draconique.webp",
gemme: "systems/foundryvtt-reve-de-dragon/icons/gemmes/almaze.webp",
possession: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp",
sort: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp",
sortreserve: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp",
extraitpoetique: "systems/foundryvtt-reve-de-dragon/icons/competence_ecriture.webp",
souffle: "systems/foundryvtt-reve-de-dragon/icons/souffle_dragon.webp",
tarot: "systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp",
empoignade: "systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp"
tete: "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp",
}
/* -------------------------------------------- */
export class RdDItem extends Item {
static get defaultIcon() {
return undefined;
}
@ -237,7 +180,6 @@ export class RdDItem extends Item {
isCompetenceCreature() { return this.type == ITEM_TYPES.competencecreature }
isConteneur() { return this.type == ITEM_TYPES.conteneur; }
isMonnaie() { return this.type == ITEM_TYPES.monnaie; }
isPotion() { return this.type == ITEM_TYPES.potion; }
isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; }
isService() { return this.type == ITEM_TYPES.service; }
@ -276,6 +218,12 @@ export class RdDItem extends Item {
return this.getEnvironnements(milieux).length > 0
}
get nameDisplay() {
return this.isMagique ? (this.name + ' <i class="fa-solid fa-sparkles"></i>') : this.name
}
get isEnchantementPossible() { return false }
getEnvironnements(milieux = undefined) {
const environnements = this.isInventaire() ? this.system.environnement : undefined;
if (milieux == undefined || !environnements) {
@ -355,13 +303,6 @@ export class RdDItem extends Item {
getUtilisation() {
switch (this.type) {
case ITEM_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 ITEM_TYPES.nourritureboisson: return 'cuisine';
case ITEM_TYPES.herbe: case ITEM_TYPES.faune: case ITEM_TYPES.ingredient: case ITEM_TYPES.plante:
switch (this.system.categorie) {
@ -391,7 +332,7 @@ export class RdDItem extends Item {
return this.type == ITEM_TYPES.objet && Grammar.includesLowerCaseNoAccent(this.name, 'cristal alchimique') && this.system.quantite > 0;
}
isMagique() {
get isMagique() {
return this.system.magique
}
@ -402,6 +343,7 @@ export class RdDItem extends Item {
isNomLike(texte) {
return Grammar.includesLowerCaseNoAccent(this.name, texte)
}
isNomTypeLike(texte) {
return this.isNomLike(texte) || Grammar.includesLowerCaseNoAccent(Misc.typeName(this.type, 'Item'), texte)
}
@ -420,8 +362,6 @@ export class RdDItem extends Item {
return 0;
case ITEM_TYPES.herbe:
return this.getEncHerbe();
case ITEM_TYPES.gemme:
return encPepin * this.system.taille;
}
return Math.max(this.system.encombrement ?? 0, 0);
}
@ -470,68 +410,47 @@ export class RdDItem extends Item {
// appliquer le pourcentage
return this.parent.calculerPrix(this);
}
return this.system.cout;
return this.system.cout
}
prepareDerivedData() {
super.prepareDerivedData();
if (this.isInventaire()) {
this.system.encTotal = this.getEncTotal();
if (this.isPotion()) {
this.prepareDataPotion()
}
this.system.actionPrincipale = this.getActionPrincipale({ warnIfNot: false });
this.system.encTotal = this.getEncTotal()
}
this.equipable = this.isEquipable();
this.equipable = this.isEquipable()
}
prepareDataPotion() {
const categorie = Grammar.toLowerCaseNoAccent(this.system.categorie);
this.system.magique = categorie.includes('enchante');
if (this.system.magique) {
if (categorie.includes('soin') || categorie.includes('repos')) {
// TODO: utiliser calculPointsRepos / calculPointsGuerison
this.system.puissance = RdDHerbes.calculPuissancePotion(this);
}
}
itemActions() {
return COMMON_ACTIONS.concat(this.itemSpecificActions()).concat(DEFAULT_ACTIONS)
}
getActionPrincipale(options = { warnIfNot: true }) {
itemSpecificActions() {
const actions = ITEM_ACTIONS[this.type] ?? []
return actions
}
isActionAllowed(code) {
switch (this.type) {
case ITEM_TYPES.conteneur: return 'Ouvrir';
}
if (this.actor?.isPersonnage()) {
const warn = options.warnIfNot;
if (this.getUtilisationCuisine() == 'brut') {
return 'Cuisiner';
}
switch (this.type) {
case ITEM_TYPES.nourritureboisson: return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
case ITEM_TYPES.potion: return this._actionOrWarnQuantiteZero('Consommer', warn);
case ITEM_TYPES.livre: return this._actionOrWarnQuantiteZero('Lire', warn);
case ITEM_TYPES.herbe: return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
case ITEM_TYPES.queue: case ITEM_TYPES.ombre: return this.system.refoulement > 0 ? 'Refouler' : undefined;
}
}
return undefined;
}
/* -------------------------------------------- */
async actionPrincipale(actor, onActionItem = async () => { }) {
if (!this.getActionPrincipale()) { return }
await actor?.actionPrincipale(this, onActionItem);
}
_actionOrWarnQuantiteZero(actionName, warn) {
if ((this.system.quantite ?? 0) <= 0) {
if (warn) {
ui.notifications.warn(`Vous n'avez plus de ${this.name}.`);
}
return undefined;
}
else {
return actionName;
case ITEM_TYPES.possession:
case ITEM_TYPES.empoignade:
case ITEM_TYPES.rencontre:
case ITEM_TYPES.signedraconique:
switch (code) {
case 'item-edit':
case 'item-delete':
return game.user.isGM
}
case ITEM_TYPES.maladie:
case ITEM_TYPES.poison:
return game.user.isGM
case ITEM_TYPES.casetmr:
switch (code) {
case 'item-delete':
return game.user.isGM
}
}
return true
}
async diminuerQuantite(nombre, options = { diminuerQuantite: true, supprimerSiZero: false }) {
@ -804,14 +723,6 @@ export class RdDItem extends Item {
]
}
/* -------------------------------------------- */
_potionChatData() {
return [
`<b>Rareté</b>: ${this.system.rarete}`,
`<b>Catégorie</b>: ${this.system.categorie}`,
...this._inventaireTemplateChatData()
]
}
/* -------------------------------------------- */
_queueChatData() {
function label(categorie) {
switch (categorie) {

View File

@ -1,14 +1,17 @@
import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
export class RdDItemArmure extends RdDItem {
static get ITEM_TYPE() { return ITEM_TYPES.armure }
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/armes_armures/armure_plaques.webp";
}
deteriorerArmure(dmg) {
async deteriorerArmure(dmg) {
if (!ReglesOptionnelles.isUsing('deteriorationArmure') || this.system.protection == '0') {
return;
}
@ -20,12 +23,10 @@ export class RdDItemArmure extends RdDItem {
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
}
});
await this.update({
'system.deterioration': deterioration,
'system.protection': protection
})
}
calculProtectionDeterioree() {

View File

@ -30,7 +30,7 @@ export class RdDItemBlessure extends RdDItem {
prepareDerivedData() {
super.prepareDerivedData();
this.system.label = this.getLabelGravite()
this.system.label = RdDItemBlessure.getLabelGravite(this.system.gravite)
}
static prepareTacheSoin(gravite) {
@ -43,29 +43,32 @@ export class RdDItemBlessure extends RdDItem {
}
static async applyFullBlessure(actor, gravite) {
const definition = RdDItemBlessure.getDefinition(gravite)
let lostEndurance = 0
let lostVie = 0
if (definition.endurance) {
lostEndurance = new Roll(definition.endurance)
await lostEndurance.roll();
actor.santeIncDec("endurance", -Number(lostEndurance.total));
}
const definition = foundry.utils.duplicate(RdDItemBlessure.getDefinition(gravite))
if (definition.vie) {
lostVie = definition.vie
actor.santeIncDec("vie", definition.vie)
await actor.santeIncDec("vie", definition.vie)
}
const lostEndurance = await RdDItemBlessure.rollLostEndurance(definition.endurance)
if (lostEndurance) {
await actor.santeIncDec("endurance", -Number(lostEndurance));
}
await this.createBlessure(actor, gravite)
ChatMessage.create({
content: `Blessure ${definition.label} appliquée à ${actor.name}`+
`<br>Perte d'endurance : ${lostEndurance}`+
`<br>Perte de Vie : ${lostVie}`,
//TODO: hbs
content: `Blessure ${definition.label} appliquée à ${actor.name}<br>Perte d'endurance : ${lostEndurance} (${definition.endurance})<br>Perte de Vie : ${definition.vie}`,
whisper: ChatUtility.getOwners(actor)
});
actor.sheet?.render()
}
static async rollLostEndurance(formula) {
if (formula) {
const roll = new Roll(formula)
await roll.evaluate()
return roll.total
}
return 0
}
static async createBlessure(actor, gravite, localisation = '', attackerToken) {
@ -125,10 +128,10 @@ export class RdDItemBlessure extends RdDItem {
}
if (this.system.gravite > 0) {
const update = { system: { premierssoins: { bonus: 0 }, soinscomplets: { bonus: 0 } } }
const gravite = this.system.gravite;
const graviteMoindre = gravite - 2;
const gravite = this.system.gravite
const graviteMoindre = gravite - 2
const moindres = blessures.filter(it => it.system.gravite == graviteMoindre, 'blessures').length
const label = this.getLabelGravite();
const label = RdDItemBlessure.getLabelGravite(this.system.gravite)
let rolled = await actor.jetRecuperationConstitution(this.system.soinscomplets.bonus, message);
@ -158,11 +161,11 @@ export class RdDItemBlessure extends RdDItem {
}
peutRetrograder(graviteMoindre, moindres) {
return moindres < RdDItemBlessure.getDefinition(graviteMoindre).max
return moindres < RdDItemBlessure.maxBlessures(graviteMoindre)
}
async calculerFinPeriodeTemporel(debut) {
return await debut.nouveauJour().addJours(this.system.gravite);
return debut.nouveauJour().addJours(this.system.gravite);
}
async onFinPeriode(oldTimestamp, newTimestamp) {
@ -182,16 +185,16 @@ export class RdDItemBlessure extends RdDItem {
return `systems/foundryvtt-reve-de-dragon/icons/sante/${soins ? 'blessure-soins' : img}.webp`
}
getLabelGravite() {
return RdDItemBlessure.getDefinition(this.system.gravite).label
static getLabelGravite(gravite) {
return definitionsBlessures.find(it => it.gravite >= gravite).label
}
static getDefinition(gravite) {
return definitionsBlessures.sort(Misc.ascending(it => it.gravite))
.find(it => it.gravite >= gravite);
return definitionsBlessures.find(it => it.gravite >= gravite)
}
static maxBlessures(gravite) {
return RdDItemBlessure.getDefinition(gravite).max
return definitionsBlessures.find(it => it.gravite >= gravite).max
}
isContusion() {
@ -216,7 +219,7 @@ export class RdDItemBlessure extends RdDItem {
`<b>Heure et Date</b>: ${new RdDTimestamp(this.system.temporel.debut).formatDateHeure()}`,
RdDItem.propertyIfDefined('Blessé', this.parent?.name, this.parent),
`<b>Localisation</b>: ${this.system.localisation}`,
`<b>Gravité</b>: ${RdDItemBlessure.getDefinition(this.system.gravite).label}`,
`<b>Gravité</b>: ${this.system.label}`,
`<b>Difficulté des soins</b>: ${this.system.difficulte}`,
(this.system.soinscomplets.done ?
`<b>Bonus soins complets</b>: ${this.system.soinscomplets.bonus}` :

112
module/item/gemme.js Normal file
View File

@ -0,0 +1,112 @@
import { RdDItem } from "../item.js";
import { ACTION_ITEM_ENCHANTER } from "../enchantement/dialog-enchanter.js";
const tableGemmes = {
"almaze": { label: "Almaze", couleur: "Blanc" },
"aquafane": { label: "Aquafane", couleur: "Vert Profond" },
"asterite": { label: "Astérite", couleur: "Bleu, Violet ou Blanc" },
"cyanolithe": { label: "Cyanolithe", couleur: "Bleu Intense" },
"larmededragon": { label: "Larme de Dragon", couleur: "Rouge Intense" },
"muska": { label: "Muska", couleur: "Violet Profond" },
"nebuleuse": { label: "Nébuleuse", couleur: "Brouillard Intense" },
"nebuleuse": { label: "Nébuleuse", couleur: "Brouillard Intense, Rose, Vert ou Bleu Pâle" },
"oeildetigre": { label: "Oeil de Tigre", couleur: "Jaune" },
"scarlatine": { label: "Scarlatine", couleur: "Rouge Clair ou Orangé" },
"seliphane": { label: "Séliphane", couleur: "Vert Lumineux" },
"tournelune": { label: "Tournelune", couleur: "Violet ou Bleu" },
"zebraide": { label: "Zebraïde", couleur: "Bandes Bicolores, toutes couleurs" }
}
/**
* un pépin de gemme = 1/10 cm3 = 1/1000 l = 3.5/1000 kg = 7/2000 kg = 7/1000 enc
* densité 3.5 (~2.3 à 4, parfois plus) -- https://www.juwelo.fr/guide-des-pierres/faits-et-chiffres/
*/
const encPepin = 0.0007;
/**
* Item pour gérer les gemmes
*/
export class RdDItemGemme extends RdDItem {
static getGemmeTypeOptionList() {
// TODO: look how to map object key-value pairs
let options = ""
for (let gemmeKey in tableGemmes) {
options += `<option value="${gemmeKey}">${tableGemmes[gemmeKey].label}</option>`
}
return options;
}
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/gemmes/almaze.webp"
}
get isEnchantementPossible() {
return this.enchantabilite > 0
}
get inertie() { return 7 - Number(this.system.purete) }
get enchantabilite() { return this.system.taille - this.inertie }
getEnc() {
return encPepin * this.system.taille;
}
itemSpecificActions() {
return [ACTION_ITEM_ENCHANTER]
}
prepareDerivedData() {
super.prepareDerivedData()
this.system.cout = (this.system.taille * this.system.purete) + this.system.qualite
this.system.inertie = this.inertie
this.system.enchantabilite = this.enchantabilite
}
getUtilisation() {
switch (this.system.categorie) {
case 'Alchimie': case 'Autre': case 'AlchimieAutre':
// TODO: distinguer les remèdes alchimiques enchantables/non
return 'alchimie'
case 'Cuisine':
return 'cuisine'
case 'Remede': case 'Repos': case 'Soin':
return 'soins'
}
return ''
}
getProprietes() {
const proprietes = [
`<b>Taille</b>: ${this.system.taille}`,
`<b>Pureté</b>: ${this.system.purete}`,
`<b>Inertie</b>: ${this.system.inertie}`,
`<b>Enchantabilité</b>: ${this.system.enchantabilite}`
]
const proprietesMagiques = this.system.magique ? [
`<b>Enchantée</b> <i class="fa-solid fa-sparkles"></i> ${this.system.purifie ? ', purifiée' : ''} ${this.system.prpermanent ? 'permanente' : ''} `,
`<b>Points de rêve</b>: ${this.system.pr}`,
`<b>Puissance</b>: ${this.system.puissance}`,
] : []
return proprietes
.concat(proprietesMagiques)
.concat(this._inventaireTemplateChatData())
.filter(it => it != undefined)
}
perteReveChateauDormant() {
if (this.system.magique && !this.system.prpermanent && this.system.pr > 0) {
const nouveauReve = Math.max(this.system.pr - 1, 0)
return {
alias: this.parent.getAlias(),
item: this,
update: {
_id: this.id,
'system.pr': nouveauReve,
'system.magique': nouveauReve > 0
}
}
}
return undefined
}
}

146
module/item/item-actions.js Normal file
View File

@ -0,0 +1,146 @@
import { Misc } from "../misc.js"
import { RdDSheetUtility } from "../rdd-sheet-utility.js"
import { RdDUtility } from "../rdd-utility.js"
/**
* TODO:
* options.editable ?
*
*/
const _SPACEHOLDER = { placeholder: true }
const _VENDRE = {
code: 'item-vendre', label: 'Vendre ou donner', icon: it => 'fa-solid fa-comments-dollar',
filter: it => Misc.toInt(it.system.quantite) > 0,
optionsFilter: options => options.editable,
action: (item, actor) => item.proposerVente()
}
const _ACHAT_SERVICE = {
code: 'item-service-acheter', label: 'Acheter', icon: it => 'fa-regular fa-coins',
//filter: it => Misc.toInt(it.system.quantite) > 0,
//optionsFilter: options => options.editable,
//action: (item, actor) => item.proposerVente()
}
const _MONTRER = {
code: 'item-montrer', label: 'Montrer', icon: it => 'fa-solid fa-comment',
action: (item, actor) => item.postItemToChat()
}
const _SPLIT = {
code: 'item-split', label: 'Séparer le goupe', icon: it => 'fa-solid fa-unlink',
filter: it => Misc.toInt(it.system.quantite) > 1,
action: (item, actor) => RdDSheetUtility.splitItem(item, actor)
}
const _EDIT = {
code: 'item-edit', label: 'Editer', icon: it => 'fa-solid fa-edit',
action: (item, actor) => item.sheet.render(true)
}
const _DELETE = {
code: 'item-delete', label: 'Supprimer', icon: it => 'fa-solid fa-trash',
optionsFilter: options => options.editable && options.isOwner,
action: (item, actor) => RdDUtility.confirmActorItemDelete(item, actor)
}
const _EQUIPER = {
code: 'item-equip', label: 'Equiper', icon: it => it.system.equipe ? 'fa-solid fa-hand-rock' : 'fa-regular fa-hand-paper',
filter: it => !it.estContenu && it.isEquipable(),
action: (item, actor) => actor.equiperObjet(item)
}
const _CUISINER = {
code: 'item-cuisiner', label: 'Cuisiner', icon: it => 'fa-solid fa-utensils',
filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0,
optionsFilter: options => options.editable,
action: (item, actor) => actor.preparerNourriture(item)
}
const _MANGER_CRU = {
code: 'item-manger-cru', label: 'Manger cru', icon: it => 'fa-solid fa-drumstick-bite',
filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0,
optionsFilter: options => options.editable,
action: (item, actor) => actor.mangerNourriture(item)
}
const _MANGER = {
code: 'item-manger', label: 'Manger', icon: it => 'fa-solid fa-utensils',
filter: it => !(it.system.boisson),
optionsFilter: options => options.editable,
action: (item, actor) => actor.mangerNourriture(item)
}
const _BOIRE = {
code: 'item-boire', label: 'Boire', icon: it => 'fa-solid fa-glass-water',
filter: it => it.system.boisson,
optionsFilter: options => options.editable,
action: (item, actor) => actor.mangerNourriture(item)
}
const _DECOCTION = {
code: 'item-decoction', label: 'Décoction', icon: it => 'fa-solid fa-flask-vial',
optionsFilter: options => options.editable,
action: (item, actor) => actor.fabriquerDecoctionHerbe(item)
}
const _OUVRIR = {
code: 'item-edit', label: 'Ouvrir', icon: it => 'fa-solid fa-eye',
action: (item, actor) => item.sheet.render(true)
}
const _LIRE = {
code: 'item-lire', label: 'Lire', icon: it => 'fa-solid fa-book-open',
optionsFilter: options => options.editable,
action: (item, actor) => actor.actionLire(item)
}
const _REFOULER = {
code: 'item-refouler', label: 'Refouler', icon: it => 'fa-solid fa-burst',
filter: it => it.system.refoulement > 0,
optionsFilter: options => options.editable,
action: (item, actor) => actor.actionRefoulement(item)
}
const _SORT_RESERVE = {
code: 'item-sortreserve-add', label: 'Ajouter en réserve', icon: it => 'fa-solid fa-sparkles',
filter: it => game.user.isGM && !it.system.isrituel,
action: (item, actor) => actor.addSortReserve(item)
}
export const COMMON_ACTIONS = [_EQUIPER]
export const DEFAULT_ACTIONS = [_SPACEHOLDER, _SPLIT, _VENDRE, _MONTRER, _EDIT, _DELETE]
export const ITEM_ACTIONS = {
faune: [_CUISINER, _MANGER_CRU],
ingredient: [_CUISINER, _MANGER_CRU],
conteneur: [_OUVRIR],
herbe: [_DECOCTION, _CUISINER, _MANGER_CRU],
livre: [_LIRE],
nourritureboisson: [_MANGER, _BOIRE],
ombre: [_REFOULER],
plante: [_CUISINER, _MANGER_CRU],
queue: [_REFOULER],
sort: [_SORT_RESERVE],
service: [_ACHAT_SERVICE]
}
export class ItemAction {
static applies(action, item, options) {
return action && item
&& item.isActionAllowed(action.code)
&& (!action.filter || action.filter(item))
&& (!action.optionsFilter || action.optionsFilter(options))
}
static icon(action, item) {
if (action && action.icon) {
return action.icon(item)
}
return undefined
}
static onActionItem(event, actor, options) {
const item = RdDSheetUtility.getItem(event, actor)
const code = $(event.currentTarget).data('code')
const action = item?.itemActions().find(it => it.code == code)
if (action && (!action.optionsFilter || action.optionsFilter(options))) {
action.action(item, actor)
}
}
}

View File

@ -10,7 +10,7 @@ export class RdDItemMaladie extends RdDItem {
}
async calculerFinPeriodeTemporel(debut) {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite);
return debut.addPeriode(this.system.periode.nombre, this.system.periode.unite);
}
async onFinPeriode(oldTimestamp, newTimestamp) {

View File

@ -1,11 +1,8 @@
import { RdDItem } from "../item.js";
import { RdDItemQueue } from "./queue.js";
export class RdDItemOmbre extends RdDItemQueue {
export class RdDItemOmbre extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

View File

@ -8,7 +8,7 @@ export class RdDItemPoison extends RdDItem {
}
async calculerFinPeriodeTemporel(debut) {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite) ;
return debut.addPeriode(this.system.periode.nombre, this.system.periode.unite) ;
}
async onFinPeriode(oldTimestamp, newTimestamp) {

105
module/item/potion.js Normal file
View File

@ -0,0 +1,105 @@
import { Grammar } from "../grammar.js";
import { RdDItem } from "../item.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { ACTION_ITEM_ENCHANTER } from "../enchantement/dialog-enchanter.js";
// --- Actions sur les "potions"
const _CONSOMMER_POTION = {
code: 'item-potion-consommer', label: 'Consommer', icon: it => 'fa-solid fa-vial',
optionsFilter: options => options.editable,
action: (item, actor) => actor.consommerPotion(item)
}
/**
* Item pour gérer les potions
*/
export class RdDItemPotion extends RdDItem {
static async herbesSoins() {
return await RdDItemPotion.$listHerbes(it => Grammar.equalsInsensitive(it.system.categorie, 'Soin') && it.system.niveau > 0)
}
static async herbesRepos() {
return await RdDItemPotion.$listHerbes(it => Grammar.equalsInsensitive(it.system.categorie, 'Repos') && it.system.niveau > 0)
}
static async $listHerbes(filter) {
const herbes = await SystemCompendiums.getWorldOrCompendiumItems('herbe', 'faune-flore-mineraux');
return herbes.filter(filter)
}
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/objets/liqueur_de_bagdol.webp"
}
get isEnchantementPossible() {
return this.system.etat == 'Liquide'
}
itemSpecificActions() {
return [_CONSOMMER_POTION, ACTION_ITEM_ENCHANTER]
}
prepareDerivedData() {
super.prepareDerivedData()
this.system.puissance = this.system.magique ? this.calculPuissance() : 0
}
getUtilisation() {
switch (this.system.categorie) {
case 'Alchimie': case 'Autre': case 'AlchimieAutre':
// TODO: distinguer les remèdes alchimiques enchantables/non
return 'alchimie'
case 'Cuisine':
return 'cuisine'
case 'Remede': case 'Repos': case 'Soin':
return 'soins'
}
return ''
}
getProprietes() {
const proprietes = [
`<b>Rareté</b>: ${this.system.rarete} `,
`<b>Catégorie</b>: ${this.system.categorie}`,
`<b>Etat</b>: ${this.system.etat}`
]
const proprietesMagiques = this.system.magique ? [
`<b>Enchantée</b> <i class="fa-solid fa-sparkles"></i> ${this.system.purifie ? ', purifiée' : ''} ${this.system.prpermanent ? 'permanente' : ''} `,
`<b>Points de rêve</b>: ${this.system.pr}`,
] : []
return proprietes
.concat(proprietesMagiques)
.concat(this._inventaireTemplateChatData())
.filter(it => it != undefined)
}
perteReveChateauDormant() {
if (this.system.magique && !this.system.prpermanent && this.system.pr > 0) {
const nouveaupr = Math.max(this.system.pr - 1, 0)
return {
alias: this.parent.getAlias(),
item: this,
nouveaupr: nouveaupr,
update: {
_id: this.id,
'system.pr': nouveaupr,
'system.magique': nouveaupr > 0
}
}
}
return undefined
}
calculPuissance() { return this.system.herbebonus * this.system.pr }
static buildHerbesList(listeHerbes, max) {
let list = {}
for (let herbe of listeHerbes) {
let brins = max - herbe.system.niveau;
list[herbe.name] = `${herbe.name} (Bonus: ${herbe.system.niveau}, Brins: ${brins})`;
}
list['Autre'] = 'Autre (Bonus: variable, Brins: variable)'
return list;
}
}

View File

@ -6,8 +6,9 @@ export class RdDItemQueue extends RdDItem {
return "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
async calculerFinPeriodeTemporel(timestamp) {
// décaller le début pour calcul correct si la queue dure 12h
const debut = timestamp.addMinutes(timestamp.indexMinute == 0 ? -1 : 0);
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

101
module/item/race.js Normal file
View File

@ -0,0 +1,101 @@
import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { LIST_CARAC_PERSONNAGE, RdDCarac } from "../rdd-carac.js";
export class RdDItemRace extends RdDItem {
static get ITEM_TYPE() { return ITEM_TYPES.race }
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/humanoides/humain.webp";
}
static checkRacialMax(actor, code, value) {
const race = RdDItemRace.getRace(actor)
if (code == LIST_CARAC_PERSONNAGE.force.code) {
if (!race.isForceValid(actor, value)) {
ui.notifications.warn(race.system.carac.force.limitmessage)
return false
}
}
const carac = RdDCarac.carac(code)
if (race.isMax(actor, code, value - 1)) {
ui.notifications.warn(`${value} est supérieure au maximum de ${carac.label}`)
return false
}
return true
}
static applyRacialLimits(actor) {
const race = RdDItemRace.getRace(actor)
actor.system.carac.taille.value = race.getValidTaille(actor.getTaille())
actor.system.carac.force.value = Math.min(
actor.getForce(),
race.getForceMax(actor))
}
static isRacialMax(actor, code, value = undefined) {
return RdDItemRace.getRace(actor).isMax(actor, code, value)
}
static getRace(actor) {
return actor.itemTypes[ITEM_TYPES.race].find(it => true) ?? RdDItemRace.getFallbackRace()
}
static getFallbackRace() {
if (RdDItemRace.fallback == undefined) {
RdDItemRace.fallback = new RdDItemRace({ name: 'Humain', type: RdDItemRace.ITEM_TYPE })
}
return RdDItemRace.fallback
}
isMax(actor, code, value = undefined) {
const path = RdDCarac.carac(code)?.path
if (value == undefined) {
value = path ? foundry.utils.getProperty(actor, path) : 0
}
if (code == LIST_CARAC_PERSONNAGE.force.code) {
return value >= this.getForceMax(actor)
}
const max = foundry.utils.getProperty(this, path) ?? -1
return (max > 0 && value >= max)
}
getValidTaille(taille) {
const min = Math.max(this.system.carac.taille.min, 0)
if (min > taille) {
ui.notifications.warn("La Taille est inférieur au minimum racial")
return min
}
const raceMax = this.system.carac.taille.max;
const max = raceMax < 0 ? taille + 1 : raceMax
if (max < taille) {
ui.notifications.warn("La Taille est supérieure au maximum racial")
return max
}
return taille
}
isForceValid(actor, value) {
return value <= this.getForceMax(actor)
}
getForceMax(actor) {
const terms = this.system.carac.force.limit.replaceAll(' ', '').split('+')
return terms.map(
it => {
const term = Number.parseInt(it)
if (Number.isInteger(term)) {
return term
}
const path = RdDCarac.carac(it)?.path
if (path) {
return foundry.utils.getProperty(actor, path)
}
return 0
}
).reduce(Misc.sum());
}
}

View File

@ -74,7 +74,7 @@ export class RdDRencontre extends RdDItem {
}
async calculerFinPeriodeTemporel(debut) {
return await debut.nouvelleHeure().addHeures(12);
return debut.nouvelleHeure().addHeures(12);
}
}

View File

@ -1,11 +1,12 @@
import { RdDBaseActorSheet } from "../actor/base-actor-sheet.js";
import { ITEM_TYPES } from "../constants.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDConteneurItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "conteneur" };
static get ITEM_TYPE() { return ITEM_TYPES.conteneur };
async getData() {
const formData = await super.getData();
@ -28,9 +29,10 @@ export class RdDConteneurItemSheet extends RdDItemInventaireSheet {
/* -------------------------------------------- */
prepareConteneurData(formData) {
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
RdDUtility.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
const subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
formData.subItems = subItems
}
async _onDragStart(event) {

View File

@ -0,0 +1,38 @@
import { ITEM_TYPES } from "../constants.js";
import { DialogEnchanter } from "../enchantement/dialog-enchanter.js";
import { RdDTimestamp } from "../time/rdd-timestamp.js";
import { RdDItemGemme } from "./gemme.js";
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDGemmeItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return ITEM_TYPES.gemme };
async getData() {
const formData = foundry.utils.mergeObject(await super.getData(),
{
inertie: this.item.inertie,
enchantabilite: this.item.enchantabilite,
isEnchantementPossible: this.item.isEnchantementPossible,
gemmeTypeList: RdDItemGemme.getGemmeTypeOptionList(),
dateActuelle: game.system.rdd.calendrier.dateCourante(),
enchantement: RdDTimestamp.splitIndexDate(this.item.system.prdate)
})
return formData
}
activateListeners(html) {
super.activateListeners(html);
this.html.find('.item-enchanter').click((event) => DialogEnchanter.enchanter(this.item))
this.html.find('.date-enchantement').change((event) => {
const jour = Number(this.html.find('input.date-enchantement[name="enchantement.jour"]').val())
const mois = RdDTimestamp.definition(this.html.find('select.date-enchantement[name="enchantement.mois"]').val())
const indexDate = game.system.rdd.calendrier.getIndexFromDate(jour, mois.heure)
this.item.update({ 'system.prdate': indexDate })
console.warn(`Date d'enchantement modifiée ${jour}/${mois.heure}: ${indexDate}`)
});
}
}

View File

@ -0,0 +1,57 @@
import { ITEM_TYPES } from "../constants.js";
import { DialogEnchanter } from "../enchantement/dialog-enchanter.js";
import { RdDTimestamp } from "../time/rdd-timestamp.js";
import { RdDItemPotion } from "./potion.js";
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDPotionItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return ITEM_TYPES.potion };
static $calculBonusHerbe(formData, herbesList, max) {
if (Number(formData.system.herbebrins)) {
let herbe = herbesList.find(h => h.name.toLowerCase() == formData.system.herbe.toLowerCase());
if (herbe) {
const brinsRequis = max - herbe.system.niveau;
const brinsManquants = Math.max(brinsRequis - formData.system.herbebrins, 0);
formData.system.herbebonus = Math.max(herbe.system.niveau - brinsManquants, 0);
}
}
}
async getData() {
const formData = await super.getData()
formData.isEnchantementPossible = this.item.isEnchantementPossible
formData.isSoins = this.item.categorie == 'Soin'
formData.isRepos = this.item.categorie == 'Repos'
if (formData.isSoins) {
const herbesSoins = await RdDItemPotion.herbesSoins()
RdDPotionItemSheet.$calculBonusHerbe(formData, herbesSoins, 12);
formData.herbesSoins = RdDItemPotion.buildHerbesList(herbesSoins, 12)
}
if (formData.isRepos) {
const herbesRepos = await RdDItemPotion.herbesRepos()
RdDPotionItemSheet.$calculBonusHerbe(formData, herbesRepos, 7);
formData.herbesRepos = RdDItemPotion.buildHerbesList(herbesRepos, 7)
}
formData.dateActuelle = game.system.rdd.calendrier.dateCourante()
formData.enchantement = RdDTimestamp.splitIndexDate(this.item.system.prdate)
return formData
}
activateListeners(html) {
super.activateListeners(html);
this.html.find('.item-enchanter').click((event) => DialogEnchanter.enchanter(this.item))
this.html.find('.date-enchantement').change((event) => {
const jour = Number(this.html.find('input.date-enchantement[name="enchantement.jour"]').val())
const mois = RdDTimestamp.definition(this.html.find('select.date-enchantement[name="enchantement.mois"]').val())
const indexDate = game.system.rdd.calendrier.getIndexFromDate(jour, mois.heure)
this.item.update({ 'system.prdate': indexDate })
console.warn(`Date d'enchantement modifiée ${jour}/${mois.heure}: ${indexDate}`)
});
}
}

View File

@ -106,7 +106,7 @@ export class RdDItemSigneDraconique extends RdDItem {
}
static async randomSigneDescription() {
return await RdDRollTables.drawTextFromRollTable("Signes draconiques", false);
return await RdDRollTables.drawTextFromRollTable("Signes draconiques", {toChat:false});
}
}

View File

@ -1,5 +1,6 @@
import { ITEM_TYPES } from "../constants.js";
import { RdDItem } from "../item.js";
import { Grammar } from "../grammar.js"
import { ITEM_TYPES, RdDItem } from "../item.js"
import { SystemCompendiums } from "../settings/system-compendiums.js"
const DON_HAUT_REVE = "Don de Haut-Rêve"

View File

@ -0,0 +1,29 @@
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js";
import { SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
export class RdDJournalSheet extends JournalTextPageSheet {
static register() {
DocumentSheetConfig.unregisterSheet(JournalEntryPage, "core", JournalTextPageSheet)
DocumentSheetConfig.registerSheet(JournalEntryPage,
SYSTEM_RDD,
RdDJournalSheet, {
types: ["text"],
makeDefault: true,
});
}
async getData(options) {
const journalData = await super.getData(options);
journalData.editor.content = await RdDTextEditor.enrichHTML(journalData.document.text.content, this.object)
return journalData
}
activateListeners(html) {
super.activateListeners(html);
html.find('.roll-text').click(async event => await RdDTextEditor.rollText(event, this.actor))
html.find('.chat-roll-text').click(async event => await RdDTextEditor.chatRollText(event))
}
}

View File

@ -2,10 +2,12 @@ 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, ITEM_TYPES } from "./item.js";
import { ITEM_TYPES, ACTOR_TYPES } from "./constants.js";
import { RdDItem } from "./item.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
import { VOIES_DRACONIC } from "./item-sort.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
class Migration {
get code() { return "sample"; }
@ -566,6 +568,63 @@ class _12_0_26_MigrationVoieSorts extends Migration {
}
}
class _12_0_32_MigrationRaces extends Migration {
get code() { return "migration-races" }
get version() { return "12.0.32" }
async migrate() {
const races = await SystemCompendiums.getItems("races", ITEM_TYPES.race)
await game.actors.filter(it => it.type == ACTOR_TYPES.personnage).forEach(async actor => {
if (actor.itemTypes[ITEM_TYPES.race].length == 0) {
const raceName = actor.system.race ?? 'Humain'
const race = races.find(it => Grammar.equalsInsensitive(raceName, it.name))
if (race) {
console.log(this.code, `Adding race ${race.name} to actor ${actor.name}`)
actor.createEmbeddedDocuments('Item', [race])
console.log(this.code, `Neutralizing race ${race.name} adjustments for actor ${actor.name}`)
actor._applyRaceCaracUpdates(race, -1)
}
}
})
}
}
class _12_0_37_MigrationAlchimieEtat extends Migration {
get code() { return "migration-alchimie-etat" }
get version() { return "12.0.37" }
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => [ITEM_TYPES.potion].includes(it.type))
.map(it => this.migratePotion(it))
)
}
mappingCategorie(categorie) {
switch (categorie) {
case 'AlchimieEnchante': return 'Alchimie'
case 'ReposEnchante': return 'Repos'
case 'SoinEnchante': return 'Soin'
case 'AutreEnchante': return 'Autre'
}
return categorie
}
mappingEtat(categorie) {
return ['Alchimie', 'Repos', 'Soin', 'Autre'].includes(categorie) ? 'Liquide' : 'Autre'
}
async migratePotion(potion) {
const newCategorie = this.mappingCategorie(potion.system.categorie)
return {
_id: potion.id,
'system.etat': this.mappingEtat(potion.system.categorie),
'system.magique': potion.system.pr > 0,
'system.categorie': newCategorie
}
}
}
export class Migrations {
static getMigrations() {
return [
@ -585,7 +644,9 @@ export class Migrations {
new _10_7_19_CategorieCompetenceCreature(),
new _10_7_19_PossessionsEntiteVictime(),
new _11_2_20_MigrationAstrologie(),
new _12_0_26_MigrationVoieSorts()
new _12_0_26_MigrationVoieSorts(),
new _12_0_32_MigrationRaces(),
new _12_0_37_MigrationAlchimieEtat(),
];
}

View File

@ -1,5 +1,12 @@
import { Grammar } from "./grammar.js";
const DEFAULT_FIND_OPTIONS = {
mapper: it => it.name,
preFilter: it => true,
description: 'valeur',
onMessage: m => ui.notifications.info(m)
}
/**
* This class is intended as a placeholder for utility methods unrelated
* to actual classes of the game system or of FoundryVTT
@ -209,6 +216,10 @@ export class Misc {
static isFirstConnectedGM() {
return game.user == Misc.firstConnectedGM();
}
static hasConnectedGM() {
return Misc.firstConnectedGM();
}
static firstConnectedGMId() {
return Misc.firstConnectedGM()?.id;
@ -226,13 +237,7 @@ export class Misc {
/* -------------------------------------------- */
static findFirstLike(value, elements, options = {}) {
options = foundry.utils.mergeObject({
mapper: it => it.name,
preFilter: it => true,
description: 'valeur',
onMessage: m => ui.notifications.info(m)
}, options, { overwrite: true, inplace: false });
options = foundry.utils.mergeObject(DEFAULT_FIND_OPTIONS, options, { overwrite: true, inplace: false });
const subset = this.findAllLike(value, elements, options);
if (subset.length == 0) {
console.log(`Aucune ${options.description} pour ${value}`);
@ -251,13 +256,7 @@ export class Misc {
}
static findAllLike(value, elements, options = {}) {
options = foundry.utils.mergeObject({
mapper: it => it.name,
preFilter: it => true,
description: 'valeur',
onMessage: m => ui.notifications.info(m)
}, options);
options = foundry.utils.mergeObject(DEFAULT_FIND_OPTIONS, options, { overwrite: true, inplace: false });
if (!value) {
options.onMessage(`Pas de ${options.description} correspondant à une valeur vide`);
return [];

View File

@ -1,48 +1,8 @@
/* -------------------------------------------- */
import { Misc } from "./misc.js";
const matchOperations = new RegExp(/@(\w*){([\w\-]+)}/ig);
const matchOperationTerms = new RegExp(/@(\w*){([\w\-]+)}/i);
/* -------------------------------------------- */
export class RdDAlchimie {
/* -------------------------------------------- */
static processManipulation(recette, actorId = undefined) {
let manip = recette.system.manipulation;
let matchArray = manip.match(matchOperations);
if (matchArray) {
for (let matchStr of matchArray) {
let result = matchStr.match(matchOperationTerms);
if (result[1] && result[2]) {
let commande = Misc.upperFirst(result[1]);
let replacement = this[`_alchimie${commande}`](recette, result[2], actorId);
manip = manip.replace(result[0], replacement);
}
}
}
recette.system.manipulation_update = manip;
}
/* -------------------------------------------- */
static _alchimieCouleur(recette, couleurs, actorId) {
return RdDAlchimie._alchimieLink(recette, couleurs, actorId, 'couleur', 'Température');
}
/* -------------------------------------------- */
static _alchimieConsistance(recette, consistances, actorId) {
return RdDAlchimie._alchimieLink(recette, consistances, actorId, 'consistance', 'Consistance');
}
static _alchimieLink(recette, termes, actorId, tacheAlchimie, labelTache) {
const difficulte = RdDAlchimie.getDifficulte(termes);
const link = actorId ? ` <a data-recette-id="${recette._id}" data-actor-id="${actorId}" data-alchimie-tache="${tacheAlchimie}" data-alchimie-data="${termes}">` : '';
const endLink = actorId ? '</a>' : '';
return `<span class="alchimie-tache">${link}${labelTache} ${termes} (${difficulte})${endLink}</span>`;
}
/* -------------------------------------------- */
static getDifficulte(aspects) {
let elements = aspects.split('-');
static getDifficulte(termes) {
let elements = termes.split('-');
let composantes = elements.length;
let distincts = Object.keys(Misc.classifyFirst(elements, it => it)).length;
if (distincts == 1) {
@ -58,5 +18,4 @@ export class RdDAlchimie {
}
return 'intellect';
}
}

View File

@ -37,8 +37,64 @@ const TABLE_CARACTERISTIQUES_DERIVEES = {
32: { xp: 180, niveau: 11, poids: "1501-2000", poidsMin: 1501, poidsMax: 2000, plusdom: +11, sconst: 10, sust: 17 }
};
export const LIST_CARAC_PERSONNAGE = {
'taille': { code: 'taille', label: 'Taille', isCarac: true, path: 'system.carac.taille.value' },
'apparence': { code: 'apparence', label: 'Apparence', isCarac: true, path: 'system.carac.apparence.value' },
'constitution': { code: 'constitution', label: 'Constitution', isCarac: true, path: 'system.carac.constitution.value' },
'force': { code: 'force', label: 'Force', isCarac: true, path: 'system.carac.force.value' },
'agilite': { code: 'agilite', label: 'Agilité', isCarac: true, path: 'system.carac.agilite.value' },
'dexterite': { code: 'dexterite', label: 'Dextérité', isCarac: true, path: 'system.carac.dexterite.value' },
'vue': { code: 'vue', label: 'Vue', isCarac: true, path: 'system.carac.vue.value' },
'ouie': { code: 'ouie', label: 'Ouïe', isCarac: true, path: 'system.carac.ouie.value' },
'odoratgout': { code: 'odoratgout', label: 'Odorat-Goût', isCarac: true, path: 'system.carac.odoratgout.value' },
'volonte': { code: 'volonte', label: 'Volonté', isCarac: true, path: 'system.carac.volonte.value' },
'intellect': { code: 'intellect', label: 'Intellect', isCarac: true, path: 'system.carac.intellect.value' },
'empathie': { code: 'empathie', label: 'Empathie', isCarac: true, path: 'system.carac.empathie.value' },
'reve': { code: 'reve', label: 'Rêve', isCarac: true, path: 'system.carac.reve.value' },
'chance': { code: 'chance', label: 'Chance', isCarac: true, path: 'system.carac.chance.value' },
'protection': { code: 'protection', label: 'Protection naturelle', isCarac: false, path: 'system.attributs.protection.value' },
'beaute': { code: 'beaute', label: 'Beauté', isCarac: false, path: 'system.background.beaute.value' }
}
export const LIST_CARAC_AUTRES = {
'perception': { code: 'perception', label: 'Perception', path: 'system.carac.perception.value' },
}
const LIST_CARAC_DERIVEE = {
'melee': { code: "melee", label: 'Mêlée', path: 'system.carac.melee.value' },
'tir': { code: "tir", label: 'Tir', path: 'system.carac.tir.value' },
'lancer': { code: "lancer", label: 'Lancer', path: 'system.carac.lancer.value' },
'derobee': { code: "derobee", label: 'Dérobée', path: 'system.carac.derobee.value' },
'chance-actuelle': { code: "chance-actuelle", label: 'Chance actuelle', path: 'system.carac.lancer.value' },
'reve-actuel': { code: "reve-actuel", label: 'Rêve actuel', path: 'system.reve.reve.value' },
}
const LIST_CARAC_ROLL = Object.values(LIST_CARAC_PERSONNAGE).filter(it => it.isCarac && it.code != 'taille')
.concat(Object.values(LIST_CARAC_AUTRES))
.concat(Object.values(LIST_CARAC_DERIVEE))
export class RdDCarac {
static caracDetails(name) {
let entry = Misc.findFirstLike(name, LIST_CARAC_ROLL, { mapper: it => it.code, description: 'caractéristique', onMessage: m => { } })
if (entry) {
return entry
}
return Misc.findFirstLike(name, LIST_CARAC_ROLL, { mapper: it => it.label, description: 'caractéristique' })
}
static carac(code) {
return LIST_CARAC_PERSONNAGE[code]
}
static label(code) {
return RdDCarac.carac(code)?.label ?? '---'
}
static caracs(filter = it => it.isCarac) {
return Object.values(LIST_CARAC_PERSONNAGE).filter(filter)
}
static isAgiliteOuDerobee(selectedCarac) {
return selectedCarac?.label.match(/(Agilité|Dérobée)/);
}

View File

@ -472,15 +472,15 @@ export class RdDCombat {
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of [
'#parer-button',
'#esquiver-button',
'#particuliere-attaque',
'#encaisser-button',
'#appel-chance-defense',
'#appel-destinee-defense',
'#appel-chance-attaque',
'#appel-destinee-attaque',
'#echec-total-attaque',
'.parer-button',
'.esquiver-button',
'.particuliere-attaque',
'.encaisser-button',
'.appel-chance-defense',
'.appel-destinee-defense',
'.appel-chance-attaque',
'.appel-destinee-attaque',
'.echec-total-attaque',
]) {
html.on("click", button, event => {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
@ -539,22 +539,22 @@ export class RdDCombat {
const compId = event.currentTarget.attributes['data-compid']?.value;
switch (button) {
case '#particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '#parer-button': return this.parade(attackerRoll, armeParadeId);
case '#esquiver-button': return this.esquive(attackerRoll, compId, competence);
case '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll);
case '#echec-total-attaque': return this._onEchecTotal(attackerRoll);
case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '.parer-button': return this.parade(attackerRoll, armeParadeId);
case '.esquiver-button': return this.esquive(attackerRoll, compId, competence);
case '.encaisser-button': return this.encaisser(attackerRoll, defenderRoll);
case '.echec-total-attaque': return this._onEchecTotal(attackerRoll);
case '#appel-chance-attaque': return this.attacker.rollAppelChance(
case '.appel-chance-attaque': return this.attacker.rollAppelChance(
() => this.attaqueChanceuse(attackerRoll),
() => this._onEchecTotal(attackerRoll));
case '#appel-chance-defense': return this.defender.rollAppelChance(
case '.appel-chance-defense': return this.defender.rollAppelChance(
() => this.defenseChanceuse(attackerRoll, defenderRoll),
() => this.afficherOptionsDefense(attackerRoll, defenderRoll, { defenseChance: true }));
case '#appel-destinee-attaque': return this.attacker.appelDestinee(
case '.appel-destinee-attaque': return this.attacker.appelDestinee(
() => this.attaqueSignificative(attackerRoll),
() => { });
case '#appel-destinee-defense': return this.defender.appelDestinee(
case '.appel-destinee-defense': return this.defender.appelDestinee(
() => this.defenseDestinee(defenderRoll),
() => { });
}
@ -967,7 +967,6 @@ export class RdDCombat {
async _onAttaqueEchec(rollData) {
console.log("RdDCombat.onAttaqueEchec >>>", rollData);
await RdDResolutionTable.displayRollData(rollData, this.attacker, 'chat-resultat-attaque.html');
}
/* -------------------------------------------- */

View File

@ -361,13 +361,14 @@ export class RdDCommands {
async getTMRAleatoire(msg, params) {
if (params.length < 2) {
let type = params[0]
const solvedTerrain = TMRUtility.findTMRLike(type)?.type
if (solvedTerrain){
const tmr = await TMRUtility.getTMRAleatoire(type ? (it => it.type == solvedTerrain) : (it => true))
const solvedTerrain = type ? TMRUtility.findTMRLike(type)?.type : undefined
const filter = solvedTerrain ? (it => it.type == solvedTerrain) : (it => true)
if (type == undefined || solvedTerrain != undefined) {
const tmr = await TMRUtility.getTMRAleatoire(filter)
return RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`)
}
}
return false;
return false
}
async findTMR(msg, params) {

View File

@ -4,8 +4,7 @@ import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ChatUtility } from "./chat-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
/* -------------------------------------------- */
@ -429,7 +428,6 @@ export class RdDEmpoignade {
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: foundry.utils.randomID(16), compteempoigne: 0, empoigneurid: attacker.id, empoigneid: defender.id, ptsemp: 0, empoigneurname: attacker.name, empoignename: defender.name }
},
{

View File

@ -1,36 +0,0 @@
const tableGemmes = {
"almaze": { label: "Almaze", couleur: "Blanc"},
"aquafane": { label: "Aquafane", couleur: "Vert Profond"},
"asterite": { label: "Astérite", couleur: "Bleu, Violet ou Blanc"},
"cyanolithe": { label: "Cyanolithe", couleur: "Bleu Intense"},
"larmededragon": { label: "Larme de Dragon", couleur: "Rouge Intense"},
"muska": { label: "Muska", couleur: "Violet Profond"},
"nebuleuse": { label: "Nébuleuse", couleur: "Brouillard Intense"},
"nebuleuse": { label: "Nébuleuse", couleur: "Brouillard Intense, Rose, Vert ou Bleu Pâle"},
"oeildetigre": { label: "Oeil de Tigre", couleur: "Jaune"},
"scarlatine": { label: "Scarlatine", couleur: "Rouge Clair ou Orangé"},
"seliphane": { label: "Séliphane", couleur: "Vert Lumineux"},
"tournelune": { label: "Tournelune", couleur: "Violet ou Bleu"},
"zebraide": { label: "Zebraïde", couleur: "Bandes Bicolores, toutes couleurs"}
}
export class RdDGemme extends Item {
static getGemmeTypeOptionList() {
// TODO: look how to map object key-value pairs
let options = ""
for (let gemmeKey in tableGemmes) {
options += `<option value="${gemmeKey}">${tableGemmes[gemmeKey].label}</option>`
}
return options;
}
static calculDataDerivees(gemme) {
gemme.system.cout = (gemme.system.taille * gemme.system.purete) + gemme.system.qualite;
gemme.system.inertie = 7 - gemme.system.purete;
gemme.system.enchantabilite = gemme.system.taille - gemme.system.inertie;
}
}

View File

@ -1,73 +0,0 @@
import { Grammar } from "./grammar.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
/* -------------------------------------------- */
export class RdDHerbes extends Item {
/* -------------------------------------------- */
static async onReady() {
this.herbesSoins = await RdDHerbes.listCategorieHerbes('Soin');
this.herbesRepos = await RdDHerbes.listCategorieHerbes('Repos');
}
static async listCategorieHerbes(categorie) {
const herbes = await SystemCompendiums.getWorldOrCompendiumItems('herbe', 'faune-flore-mineraux');
return herbes.filter(it => Grammar.equalsInsensitive(it.system.categorie, categorie));
}
/* -------------------------------------------- */
static buildHerbesList(listeHerbes, max) {
let list = {}
for (let herbe of listeHerbes) {
let brins = max - herbe.system.niveau;
list[herbe.name] = `${herbe.name} (Bonus: ${herbe.system.niveau}, Brins: ${brins})`;
}
list['Autre'] = 'Autre (Bonus: variable, Brins: variable)'
return list;
}
/* -------------------------------------------- */
static calculFormData(formData, item) {
formData.isSoins = item.system.categorie.includes('Soin');
formData.isRepos = item.system.categorie.includes('Repos');
if (formData.isSoins) {
RdDHerbes.calculBonusHerbe(formData, this.herbesSoins, 12);
}
if (formData.isRepos) {
RdDHerbes.calculBonusHerbe(formData, this.herbesRepos, 7);
}
formData.herbesSoins = RdDHerbes.buildHerbesList(this.herbesSoins, 12);
formData.herbesRepos = RdDHerbes.buildHerbesList(this.herbesRepos, 7);
formData.dateActuelle = game.system.rdd.calendrier.dateCourante();
formData.enchantement = RdDTimestamp.splitIndexDate(item.system.prdate);
}
/* -------------------------------------------- */
static calculPuissancePotion(potion) {
return potion.system.herbebonus * potion.system.pr;
}
/* -------------------------------------------- */
static calculPointsRepos(potion) {
return potion.system.herbebonus * potion.system.pr;
}
/* -------------------------------------------- */
static calculPointsGuerison(potion) {
return potion.system.herbebonus * potion.system.pr;
}
/* -------------------------------------------- */
static calculBonusHerbe(formData, herbesList, max) {
if (Number(formData.system.herbebrins)) {
let herbe = herbesList.find(item => item.name.toLowerCase() == formData.system.herbe.toLowerCase());
if (herbe) {
const brinsRequis = max - herbe.system.niveau;
const brinsManquants = Math.max(brinsRequis - formData.system.herbebrins, 0);
formData.system.herbebonus = Math.max(herbe.system.niveau - brinsManquants, 0);
}
}
}
}

View File

@ -1,6 +1,6 @@
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
export class RdDHotbar {

View File

@ -18,7 +18,6 @@ import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js"
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js"
import { RdDHerbes } from "./rdd-herbes.js"
import { RdDDice } from "./rdd-dice.js"
import { RdDPossession } from "./rdd-possession.js"
import { Misc } from "./misc.js"
@ -28,9 +27,11 @@ import { Environnement } from "./environnement.js"
import { RdDActor } from "./actor.js"
import { RdDBaseActor } from "./actor/base-actor.js"
import { RdDCreature } from "./actor/creature.js"
import { RdDCommerce } from "./actor/commerce.js"
import { RdDEntite } from "./actor/entite.js"
import { RdDVehicule } from "./actor/vehicule.js"
import { RdDActorSheet } from "./actor-sheet.js"
import { RdDCommerceSheet } from "./actor/commerce-sheet.js"
import { RdDCreatureSheet } from "./actor/creature-sheet.js"
@ -38,6 +39,7 @@ import { RdDActorEntiteSheet } from "./actor/entite-sheet.js"
import { RdDActorVehiculeSheet } from "./actor/vehicule-sheet.js"
import { RdDItem } from "./item.js"
import { RdDItemArmure } from "./item/armure.js"
import { RdDItemBlessure } from "./item/blessure.js"
import { RdDItemService } from "./item/service.js"
import { RdDItemMaladie } from "./item/maladie.js"
@ -45,7 +47,11 @@ import { RdDItemPoison } from "./item/poison.js"
import { RdDItemSigneDraconique } from "./item/signedraconique.js"
import { RdDItemQueue } from "./item/queue.js"
import { RdDItemOmbre } from "./item/ombre.js"
import { RdDItemSort } from "./item-sort.js"
import { RdDItemTete } from "./item/tete.js"
import { RdDItemRace } from "./item/race.js"
import { RdDItemSouffle } from "./item/souffle.js"
import { RdDRencontre } from "./item/rencontre.js"
import { RdDItemSheet } from "./item-sheet.js"
@ -57,20 +63,22 @@ import { RdDPlanteItemSheet } from "./item/sheet-plante.js"
import { RdDIngredientItemSheet } from "./item/sheet-ingredient.js"
import { RdDFauneItemSheet } from "./item/sheet-faune.js"
import { RdDConteneurItemSheet } from "./item/sheet-conteneur.js"
import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js"
import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js"
import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js"
import { AppAstrologie } from "./sommeil/app-astrologie.js"
import { RdDItemArmure } from "./item/armure.js"
import { AutoAdjustDarkness } from "./time/auto-adjust-darkness.js"
import { RdDCreature } from "./actor/creature.js"
import { RdDTMRDialog } from "./rdd-tmr-dialog.js"
import { OptionsAvancees } from "./settings/options-avancees.js"
import { ExportScriptarium } from "./actor/export-scriptarium/export-scriptarium.js"
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js"
import { RdDActorExportSheet } from "./actor/export-scriptarium/actor-encart-sheet.js"
import { RdDStatBlockParser } from "./apps/rdd-import-stats.js"
import { RdDItemSort } from "./item-sort.js"
import { RdDItemTete } from "./item/tete.js"
import { RdDJournalSheet } from "./journal/journal-sheet.js"
import { RdDPotionItemSheet } from "./item/sheet-potion.js"
import { RdDItemPotion } from "./item/potion.js"
import { RdDItemGemme } from "./item/gemme.js"
import { RdDGemmeItemSheet } from "./item/sheet-gemme.js"
/**
* RdD system
@ -81,9 +89,9 @@ export class SystemReveDeDragon {
static start() {
const system = new SystemReveDeDragon()
Hooks.once('init', async () => await system.onInit())
Hooks.once('init', () => system.onInit())
Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d))
Hooks.once('ready', async () => await system.onReady())
Hooks.once('ready', () => system.onReady())
}
constructor() {
@ -94,15 +102,18 @@ export class SystemReveDeDragon {
this.itemClasses = {
armure: RdDItemArmure,
blessure: RdDItemBlessure,
gemme: RdDItemGemme,
maladie: RdDItemMaladie,
ombre: RdDItemOmbre,
poison: RdDItemPoison,
potion: RdDItemPotion,
queue: RdDItemQueue,
tete: RdDItemTete,
race: RdDItemRace,
rencontre: RdDRencontre,
service: RdDItemService,
signedraconique: RdDItemSigneDraconique,
souffle: RdDItemSouffle,
tete: RdDItemTete,
}
this.actorClasses = {
commerce: RdDCommerce,
@ -116,7 +127,7 @@ export class SystemReveDeDragon {
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* -------------------------------------------- */
async onInit() {
onInit() {
game.system.rdd = this
this.AppAstrologie = AppAstrologie
@ -180,33 +191,39 @@ export class SystemReveDeDragon {
Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true })
Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true })
Items.unregisterSheet("core", ItemSheet)
await RdDActorExportSheet.init()
RdDItemSheet.register(RdDSigneDraconiqueItemSheet)
RdDItemSheet.register(RdDRencontreItemSheet)
RdDItemSheet.register(RdDConteneurItemSheet)
RdDItemSheet.register(RdDHerbeItemSheet)
RdDItemSheet.register(RdDFauneItemSheet)
RdDItemSheet.register(RdDPlanteItemSheet)
RdDItemSheet.register(RdDIngredientItemSheet)
RdDItemSheet.register(RdDServiceItemSheet)
RdDItemSheet.register(RdDBlessureItemSheet)
RdDActorExportSheet.init()
Items.registerSheet(SYSTEM_RDD, RdDItemInventaireSheet, {
types: [
"objet", "arme", "armure", "livre", "potion", "munition",
"monnaie", "nourritureboisson", "gemme",
], makeDefault: true
"objet", "arme", "armure", "livre", "munition",
"monnaie", "nourritureboisson",
],
makeDefault: true
})
Items.registerSheet(SYSTEM_RDD, RdDItemSheet, {
types: [
"competence", "competencecreature",
"recettealchimique", "musique", "chant", "danse", "jeu", "recettecuisine", "oeuvre",
"meditation", "queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve",
"recettealchimique", "musique", "chant", "danse", "jeu", "race",
"recettecuisine", "oeuvre", "meditation",
"queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve",
"nombreastral", "tache", "maladie", "poison", "possession",
"tarot", "extraitpoetique", "empoignade"
], makeDefault: true
],
makeDefault: true
})
RdDItemSheet.register(RdDBlessureItemSheet)
RdDItemSheet.register(RdDConteneurItemSheet)
RdDItemSheet.register(RdDFauneItemSheet)
RdDItemSheet.register(RdDGemmeItemSheet)
RdDItemSheet.register(RdDHerbeItemSheet)
RdDItemSheet.register(RdDIngredientItemSheet)
RdDItemSheet.register(RdDPlanteItemSheet)
RdDItemSheet.register(RdDPotionItemSheet)
RdDItemSheet.register(RdDRencontreItemSheet)
RdDItemSheet.register(RdDServiceItemSheet)
RdDItemSheet.register(RdDSigneDraconiqueItemSheet)
RdDJournalSheet.register()
// préparation des différents modules
console.log(`Initializing Reve de Dragon Hooks and handlers`)
@ -307,7 +324,6 @@ export class SystemReveDeDragon {
}
StatusEffects.onReady()
RdDHerbes.onReady()
RdDDice.onReady()
RdDStatBlockParser.parseStatBlock()

View File

@ -3,7 +3,7 @@ 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 { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
/* -------------------------------------------- */
/* On part du principe qu'une entité démarre tjs
@ -174,7 +174,7 @@ export class RdDPossession {
await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
// conjuration
victime.deleteEmbeddedDocuments("Item", [rollData.possession._id])
await victime.deleteEmbeddedDocuments("Item", [rollData.possession._id])
}
}

View File

@ -8,6 +8,7 @@ import { RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { Grammar } from "./grammar.js";
import { ACTOR_TYPES } from "./constants.js";
/**
* Extend the base Dialog entity to select roll parameters
@ -62,7 +63,7 @@ export class RdDRoll extends Dialog {
forceDiceResult: -1
}
// Mini patch :Ajout du rêve actuel
if (actor.system.type == "personnage") {
if (actor.type == ACTOR_TYPES.personnage) {
defaultRollData.carac["reve-actuel"] = actor.system.reve.reve
}
@ -131,11 +132,16 @@ export class RdDRoll extends Dialog {
console.log('RdDRoll.activateListeners', this.rollData);
// Update html, according to rollData
if (this.rollData.competence) {
const defaut_carac = this.rollData.competence.system.defaut_carac
if (!this.rollData.selectedCarac && this.rollData.competence) {
// Set the default carac from the competence item
this.rollData.selectedCarac = this.rollData.carac[defaut_carac];
this.html.find("[name='carac']").val(defaut_carac);
this.rollData.selectedCarac = this.rollData.carac[this.actor.mapCarac(this.rollData.competence.system.defaut_carac)]
}
if (this.rollData.selectedCarac) {
this.html.find("[name='carac']").val(
this.actor?.type == ACTOR_TYPES.personnage
? RdDCarac.caracDetails(this.rollData.selectedCarac.label).code
: this.rollData.selectedCarac.label
)
}
if (this.rollData.selectedSort) {
this.setSelectedSort(this.rollData.selectedSort);
@ -173,7 +179,7 @@ export class RdDRoll extends Dialog {
this.updateRollResult(html);
this.html.find("[name='diffLibre']").val(this.rollData.diffLibre);
});
this.html.find('.roll-carac-competence').change((event) => {
this.html.find('.roll-text').change((event) => {
const competence = event.currentTarget.value
this.rollData.competence = this.rollData.competences.find(it => Grammar.equalsInsensitive(it.name, competence))
this.updateRollResult(html);

View File

@ -28,8 +28,8 @@ export class RdDRollTables {
}
/* -------------------------------------------- */
static async drawTextFromRollTable(tableName, toChat) {
const drawResult = await RdDRollTables.genericGetTableResult(tableName, toChat);
static async drawTextFromRollTable(tableName, options = {}) {
const drawResult = await RdDRollTables.genericGetTableResult(tableName, options.toChat);
return drawResult.text;
}
@ -103,7 +103,7 @@ export class RdDRollTables {
static async getMaladresse(options = { toChat: false, arme: false }) {
return await RdDRollTables.drawTextFromRollTable(
options.arme ? "Maladresse armé" : "Maladresses non armé",
options.toChat);
options)
}
}

View File

@ -66,11 +66,13 @@ export class RdDSheetUtility {
}
static async splitItem(item, actor, onSplit = () => { }) {
const dialog = await DialogSplitItem.create(item, async (item, split) => {
const _onSplit = async (item, split) => {
await RdDSheetUtility._onSplitItem(item, split, actor);
onSplit();
});
dialog.render(true);
await RdDSheetUtility.renderItemBranch(actor, item)
}
const dialog = await DialogSplitItem.create(item, _onSplit)
dialog.render(true)
}
static async _onSplitItem(item, split, actor) {
@ -82,4 +84,11 @@ export class RdDSheetUtility {
await actor.createEmbeddedDocuments('Item', [splitItem])
}
}
static async renderItemBranch(actor, item) {
while (item) {
await item.sheet?.render()
item = actor.getContenant(item)
}
}
}

View File

@ -16,7 +16,7 @@ import { RdDDice } from "./rdd-dice.js";
import { STATUSES } from "./settings/status-effects.js";
import { RdDRencontre } from "./item/rencontre.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { ITEM_TYPES } from "./item.js";
import { ITEM_TYPES } from "./constants.js";
import { Misc } from "./misc.js";
const TMR_DISPLAY_SIZE = {
@ -212,7 +212,7 @@ export class RdDTMRDialog extends Dialog {
}
getSortsReserve(coord) {
return this.actor.itemTypes[ITEM_TYPES.sortreserve].filter(// Reserve sur une case fleuve ou normale
return this.sortsReserve.filter(// Reserve sur une case fleuve ou normale
TMRUtility.getTMR(coord).type == 'fleuve'
? it => TMRUtility.getTMR(it.system.coord).type == 'fleuve'
: it => it.system.coord == coord
@ -241,7 +241,7 @@ export class RdDTMRDialog extends Dialog {
this._getTokensRencontres().forEach(t => this._trackToken(t))
this._getTokensSortsReserve().forEach(t => this._trackToken(t))
}
/* -------------------------------------------- */
updateTokens() {
this._removeTokens(t => true);
@ -271,8 +271,7 @@ export class RdDTMRDialog extends Dialog {
}
_getTokensSortsReserve() {
const sortsReserve = this.actor.itemTypes[ITEM_TYPES.sortreserve];
return Misc.concat(sortsReserve.map(sortReserve =>
return Misc.concat(this.sortsReserve.map(sortReserve =>
EffetsDraconiques.sortReserve.tokens(this.pixiTMR, sortReserve, () => sortReserve.system.coord)))
}
@ -908,8 +907,8 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
lancerSortEnReserve(coord, sortId) {
let sorts = this.getSortsReserve(coord);
let sort = sorts.find(it => it.id == sortId);
const sort = this.getSortsReserve(coord)
.find(it => it.id == sortId);
if (sort) {
this.processSortReserve(sort);
} else {

View File

@ -19,8 +19,12 @@ import { RdDEmpoignade } from "./rdd-empoignade.js";
import { ExperienceLog } from "./actor/experience-log.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { APP_ASTROLOGIE_REFRESH } from "./sommeil/app-astrologie.js";
import { RDD_CONFIG } from "./constants.js";
import { ITEM_TYPES, RDD_CONFIG } from "./constants.js";
import { RdDBaseActor } from "./actor/base-actor.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
import { Monnaie } from "./item-monnaie.js";
import { ItemAction } from "./item/item-actions.js";
/* -------------------------------------------- */
// This table starts at 0 -> niveau -10
@ -105,7 +109,7 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async preloadHandlebarsTemplates() {
static preloadHandlebarsTemplates() {
const templatePaths = [
//Character Sheets
'systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html',
@ -113,6 +117,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/actor-vehicule-sheet.html',
// sous-parties de feuilles de personnages
'systems/foundryvtt-reve-de-dragon/templates/actor/item-action-controls.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-buttons.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-etat.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs.html',
@ -127,48 +132,47 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-derivee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-creature.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-entitee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/comp-creature.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/comp-possession.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/comp-creature.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/comp-possession.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-total.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/competence.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/competence.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/xp-competences.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/combat.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/blessures.html',
'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/maladies-poisons.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/possessions.hbs',
'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',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/jeux.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/alchimie.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/taches.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/jeus.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/alchimie.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/astrologie.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/chirurgie.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/chirurgie.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/non-haut-revant.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/haut-revant.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-queues.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-souffles.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-tetes.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-signes-draconiques.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-rencontres.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-sorts.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-sorts-reserve.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-meditations.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-casestmr.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-queue.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-souffles.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/dragon-tetes.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-signes-draconiques.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-rencontres.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-sorts.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-sorts-reserve.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-meditations.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/hr-casetmrs.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/xp-journal.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.html',
"systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-monnaie.html",
'systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.hbs',
"systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-monnaie.hbs",
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-animaux.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-suivants.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/liens-vehicules.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.hbs',
//Items
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs',
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete.hbs',
@ -178,8 +182,9 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/item/partial-inventaire.html',
'systems/foundryvtt-reve-de-dragon/templates/item/partial-environnement.html',
'systems/foundryvtt-reve-de-dragon/templates/item/partial-tab-environnement.html',
'systems/foundryvtt-reve-de-dragon/templates/item-queue-sheet.html',
'systems/foundryvtt-reve-de-dragon/templates/header-item.html',
'systems/foundryvtt-reve-de-dragon/templates/item/queue-sheet.hbs',
// partial enums
'systems/foundryvtt-reve-de-dragon/templates/enum-aspect-tarot.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-base-competence.html',
@ -187,7 +192,8 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/enum-categories.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-ingredient.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-parade.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-potion.html',
'systems/foundryvtt-reve-de-dragon/templates/item/enum-categorie-alchimie.hbs',
'systems/foundryvtt-reve-de-dragon/templates/item/enum-etat-alchimie.hbs',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-queue.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-categorie-vehicule.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-competence.html',
@ -201,6 +207,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-effet.html',
'systems/foundryvtt-reve-de-dragon/templates/enum-tmr-type.html',
// Partials
'systems/foundryvtt-reve-de-dragon/templates/enchantement/partial-enchantement.hbs',
'systems/foundryvtt-reve-de-dragon/templates/coeur/chat-effet-tendre-moment.hbs',
'systems/foundryvtt-reve-de-dragon/templates/coeur/afficher-coeur.hbs',
'systems/foundryvtt-reve-de-dragon/templates/tirage/liste-resultats-recherche.hbs',
@ -257,11 +264,10 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-actor.html'
];
// foundry et options
Handlebars.registerHelper('RDD_CONFIG', path => RDD_CONFIG[path])
Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name));
@ -276,7 +282,7 @@ export class RdDUtility {
const html = options.fn(this);
return html.replace(rgx, "$& selected");
})
// logic
Handlebars.registerHelper('either', (a, b) => a ?? b);
// string manipulation
@ -288,22 +294,23 @@ export class RdDUtility {
Handlebars.registerHelper('grammar-apostrophe', (article, str) => Grammar.apostrophe(article, str));
Handlebars.registerHelper('grammar-un', str => Grammar.articleIndetermine(str));
Handlebars.registerHelper('grammar-accord', (genre, ...args) => Grammar.accord(genre, args));
Handlebars.registerHelper('json-stringify', object => JSON.stringify(object))
// math
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('repeat', function(n, block) {
Handlebars.registerHelper('repeat', function (n, block) {
let accum = '';
for(let i = 0; i < n; ++i){
for (let i = 0; i < n; ++i) {
accum += block.fn(i)
}
return accum
})
// tableaux, listes
Handlebars.registerHelper('array-includes', (array, value) => array.includes(value));
Handlebars.registerHelper('isLastIndex', (index, list) => index + 1 >= list.length);
Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name)));
// table de résolution
Handlebars.registerHelper('computeResolutionScore', (row, col) => RdDResolutionTable.computePercentage(row, col));
Handlebars.registerHelper('computeResolutionChances', (row, col) => RdDResolutionTable.computeChances(row, col));
@ -314,12 +321,14 @@ export class RdDUtility {
Handlebars.registerHelper('timestamp-extract', timestamp => new RdDTimestamp(timestamp).toCalendrier());
Handlebars.registerHelper('timestamp-formulesDuree', () => RdDTimestamp.formulesDuree());
Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode());
// informations sur les acteurs
Handlebars.registerHelper('actor-default', (actorType, ...path) => RdDBaseActor.getDefaultValue(actorType, path.slice(0, -1)));
Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences));
Handlebars.registerHelper('experienceLog-topic', topic => ExperienceLog.labelTopic(topic));
Handlebars.registerHelper('carac-label', (code) => RdDCarac.label(code))
// inventaire et marchands
Handlebars.registerHelper('buildLigneInventaire', (item, options) => { return new Handlebars.SafeString(RdDUtility.buildLigneInventaire(item, options)); });
Handlebars.registerHelper('buildInventaireConteneur', (actorId, itemId, options) => { return new Handlebars.SafeString(RdDUtility.buildInventaireConteneur(actorId, itemId, options)); });
@ -329,14 +338,17 @@ export class RdDUtility {
Handlebars.registerHelper('isFieldInventaireModifiable', (type, field) => RdDItem.isFieldInventaireModifiable(type, field));
// Items
Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field));
Handlebars.registerHelper('item-action-applies', (action, item, options) => ItemAction.applies(action, item, options))
Handlebars.registerHelper('item-action-icon', (action, item) => ItemAction.icon(action, item))
Handlebars.registerHelper('item-name', (item) => item.nameDisplay)
// TMRs
Handlebars.registerHelper('caseTmr-label', coord => TMRUtility.getTMRLabel(coord));
Handlebars.registerHelper('caseTmr-type', coord => TMRUtility.getTMRType(coord));
Handlebars.registerHelper('typeTmr-name', type => TMRUtility.typeTmrName(type));
Handlebars.registerHelper('effetRencontre-name', coord => TMRUtility.typeTmrName(coord));
return loadTemplates(templatePaths);
loadTemplates(templatePaths);
}
static getItem(itemId, actorId = undefined) {
@ -430,8 +442,8 @@ export class RdDUtility {
};
if (!optionsArbre.templateItem) {
optionsArbre.templateItem = item.parent?.type == 'commerce'
? "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.html"
: "systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.html";
? "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.hbs"
: "systems/foundryvtt-reve-de-dragon/templates/actor/inventaire-item.hbs";
}
item.niveau = optionsArbre.profondeur;
}
@ -459,13 +471,38 @@ export class RdDUtility {
return ligneObjet;
}
static filterItemsPerTypeForSheet(formData, itemTypes) {
Object.values(ITEM_TYPES).forEach(t => {
formData[t + 's'] = Misc.arrayOrEmpty(itemTypes[t])
itemTypes[t].forEach(item => item.actions = item.itemActions())
})
formData.maladiesPoisons = formData.maladies.concat(formData.poisons)
formData.competences = formData.competences.concat(formData.competencecreatures)
formData.monnaies = formData.monnaies.sort(Monnaie.triValeurEntiere())
formData.inventaires = RdDUtility.prepareInventaire(itemTypes)
}
static buildInventaireConteneur(actorId, itemId, options) {
const actor = game.actors.get(actorId)
const item = actor?.items.get(itemId)
if (item) {
return RdDUtility.buildContenuConteneur(item, options, { ouvert: true, profondeur: 1 });
if (item?.type == ITEM_TYPES.conteneur) {
const formData = {}
RdDUtility.filterItemsPerTypeForSheet(formData, actor.itemTypes);
RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
item.subItems = formData.conteneurs.find(it => it._id == itemId)?.subItems;
return RdDUtility.buildContenuConteneur(item, options, { ouvert: true, profondeur: 1 })
}
return '';
return ''
}
static prepareInventaire(itemTypes) {
return RdDItem.getItemTypesInventaire('all')
.map(t => Misc.arrayOrEmpty(itemTypes[t]))
.reduce((a, b) => a.concat(b), [])
.sort(Misc.ascending(it => it.name))
}
/* -------------------------------------------- */
@ -692,6 +729,7 @@ export class RdDUtility {
RdDCombat.registerChatCallbacks(html)
RdDEmpoignade.registerChatCallbacks(html)
RdDCoeur.registerChatCallbacks(html)
RdDTextEditor.registerChatCallbacks(html)
// Gestion spécifique message passeurs
html.on("click", '.tmr-passeur-coord a', event => {
@ -866,7 +904,7 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async confirmActorItemDelete(sheet, item, htmlToDelete) {
static async confirmActorItemDelete(item, actor) {
const itemId = item.id;
const confirmationSuppression = {
settingConfirmer: "confirmation-supprimer-" + item.getItemGroup(),
@ -875,8 +913,7 @@ export class RdDUtility {
buttonLabel: "Supprimer",
onAction: () => {
console.log('Delete : ', itemId);
sheet.actor.deleteEmbeddedDocuments('Item', [itemId], { renderSheet: false });
RdDUtility.slideOnDelete(sheet, htmlToDelete);
actor.deleteEmbeddedDocuments('Item', [itemId], { renderSheet: false });
}
};
if (item.isConteneurNonVide()) {
@ -889,8 +926,7 @@ export class RdDUtility {
label: "Supprimer conteneur et contenu",
callback: () => {
console.log("Delete : ", itemId);
sheet.actor.deleteAllConteneur(itemId, { renderSheet: false });
RdDUtility.slideOnDelete(sheet, htmlToDelete);
actor.deleteAllConteneur(itemId, { renderSheet: false });
}
}
});
@ -930,7 +966,7 @@ export class RdDUtility {
/*-------------------------------------------- */
static checkThanatosXP(compName) {
if (compName.includes('Thanatos')) {
let message = "Vous avez mis des points d'Expérience dans la Voie de Thanatos !<br>Vous devez réduire manuellement d'un même montant d'XP une autre compétence Draconique.";
let message = "Vous avez mis des points d'Expérience en Thanatos !<br>Vous devez réduire manuellement d'un même montant d'XP une autre compétence Draconique.";
ChatMessage.create({
whisper: ChatUtility.getUserAndGMs(),
content: message

View File

@ -168,7 +168,7 @@ export class RollDataAjustements {
// s'assurer de la correction des infos rollData
foundry.utils.mergeObject(rollData, { ajustements: {}, use: {} }, { overwrite: false })
for (var key in referenceAjustements) {
for (let key in referenceAjustements) {
const reference = referenceAjustements[key];
rollData.ajustements[key] = {
visible: reference.isVisible && reference.isVisible(rollData, actor),

View File

@ -15,6 +15,7 @@ const CONFIGURABLE_COMPENDIUMS = {
'ombres-de-thanatos': { label: "Ombres de Thanatos", type: "Item" },
'souffles-de-dragon': { label: "Souffles de Dragon", type: "Item" },
'tarot-draconique': { label: "Tarots draconiques", type: "Item" },
'races': { label: "Races", type: "Item" },
'rencontres': { label: "Rencontres dans les TMR", type: "Item" },
'tetes-de-dragon-pour-haut-revants': { label: "Têtes de dragons (haut-rêvant)", type: "Item" },
'tetes-de-dragon-pour-tous-personnages': { label: "Têtes de dragons (tous)", type: "Item" },

View File

@ -334,6 +334,10 @@ export class RdDTimestamp {
return this.nouvelleHeure().addHeures((12 + heure - this.heure) % 12);
}
debutJournee() {
return RdDTimestamp.timestamp(this.annee, this.mois, this.jour)
}
async appliquerDuree(duree, actor) {
const formule = FORMULES_DUREE.find(it => it.code == duree) ?? FORMULES_DUREE.find(it => it.code == "");
return await formule.calcul(this, actor);

View File

@ -26,7 +26,7 @@ export class TMRRencontres {
* @param {*} forcedRoll
*/
async rollRencontre(terrain, forcedRoll) {
const tmrType = TMRUtility.findTMRLike(terrain)?.type
const tmrType = TMRUtility.findTMRLike(terrain, { inclusMauvaise: true })?.type
if (tmrType == undefined) {
return undefined;
}
@ -38,7 +38,7 @@ export class TMRRencontres {
const frequence = it => it.system.frequence[tmrType];
const row = await this.table.getRandom(frequence, filtreMauvaise, forcedRoll);
if (row) {
await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item', {showSource: false});
await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item', { showSource: false });
}
return row?.document;

View File

@ -22,7 +22,7 @@ export const TMRType = {
export const FLEUVE_COORD = 'Fleuve'
const TMRMapping = {
Fleuve: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" },
Fleuve: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli", generique: 'fleuve' },
A1: { type: TMRType.cite.type, label: "Cité Vide" },
B1: { type: TMRType.plaines.type, label: "Plaines dAssorh" },
C1: { type: TMRType.necropole.type, label: "Nécropole de Kroak" },
@ -281,18 +281,18 @@ export class TMRUtility {
return Grammar.articleDetermine(tmr.type) + ' ' + tmr.label;
}
static findTMRLike(type, options = { inclusMauvaise: true }) {
static findTMRLike(type, options = { inclusMauvaise: false }) {
const choix = [...Object.values(TMRType)]
if (options.inclusMauvaise) {
choix.push({ name: 'Mauvaise', type: 'mauvaise'});
}
const selection = Misc.findAllLike(type, choix)
if (selection.length == 0) {
ui.notifications.warn(`Un type de TMR doit être indiqué, '${type}' n'est pas trouvé dans ${choix}`);
ui.notifications.warn(`Un type de TMR doit être indiqué, '${type}' n'est pas trouvé dans ${choix.map(it => it.name).reduce(Misc.joining(', '))}`);
return undefined
}
if (selection.length > 1) {
ui.notifications.warn(`Plusieurs types de TMR pourraient correspondre à '${type}': ${selection.map(it => it.name)}`);
ui.notifications.warn(`Plusieurs types de TMR pourraient correspondre à '${type}': ${selection.map(it => it.name).reduce(Misc.joining(', '))}`);
return undefined;
}
return selection[0]
@ -357,7 +357,7 @@ export class TMRUtility {
}
static filterTMR(filter) {
return Object.values(TMRMapping).filter(filter);
return Object.values(TMRMapping).filter(it => !it.generique && filter(it))
}
static getCasesType(type) {

View File

@ -1,4 +1,4 @@
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { TMRUtility } from "../tmr-utility.js";
import { PixiTMR } from "./pixi-tmr.js";

View File

@ -18,7 +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 { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
export class EffetsDraconiques {
static carteTmr = new CarteTmr();

View File

@ -5,7 +5,7 @@ import { RdDRollTables } from "../rdd-rolltables.js";
import { TMRUtility } from "../tmr-utility.js";
import { tmrTokenZIndex } from "../tmr-constants.js";
import { Draconique } from "./draconique.js";
import { ITEM_TYPES } from "../item.js";
import { ITEM_TYPES } from "../constants.js";
import { TMRAnimations } from "./animation.js";
export class UrgenceDraconique extends Draconique {

View File

@ -1,4 +1,4 @@
import { ITEM_TYPES } from "../item.js"
import { ITEM_TYPES } from "../constants.js"
import { RdDItemCompetence } from "../item-competence.js"
import { ChatUtility } from "../chat-utility.js"
import { Misc } from "../misc.js"

View File

@ -125,39 +125,39 @@ system:
carac:
taille:
type: number
value: '23'
value: 23
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '18'
value: 18
label: Constitution
xp: '0'
xp: 0
derivee: false
force:
type: number
value: '18'
value: 18
label: Force
xp: '0'
xp: 0
derivee: false
perception:
type: number
value: '11'
value: 11
label: Perception
xp: '0'
xp: 0
derivee: false
volonte:
type: number
value: '7'
value: 7
label: Volonté
xp: '0'
xp: 0
derivee: false
reve:
type: number
value: '8'
value: 8
label: Rêve
xp: '0'
xp: 0
derivee: false
sante:
vie:
@ -190,12 +190,12 @@ system:
derivee: true
encombrement:
type: number
value: '20'
value: 20
label: Encombrement
derivee: false
protection:
type: number
value: '4'
value: 4
label: Protection
derivee: false
compteurs:

View File

@ -155,37 +155,37 @@ system:
carac:
taille:
type: number
value: '3'
value: 3
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '12'
value: 12
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '7'
value: 7
label: Force
xp: 0
derivee: false
perception:
type: number
value: '15'
value: 15
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '10'
value: 10
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '7'
value: 7
label: Rêve
xp: 0
derivee: false
@ -210,7 +210,7 @@ system:
attributs:
plusdom:
type: number
value: '-2'
value: -2
label: +dom
derivee: true
vitesse:
@ -220,12 +220,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '-3'
value: -3
label: Protection
derivee: false
compteurs:
@ -257,8 +257,8 @@ system:
d&eacute;vore.</p>
<h1>Venin</h1>
<p>@UUID[Compendium.foundryvtt-reve-de-dragon.maladies-et-poisons.Item.v7yZidE9mObKO566]{Venin d'Araflate}</p>
<p></p>
<p>@UUID[Compendium.foundryvtt-reve-de-dragon.maladies-et-poisons.Item.v7yZidE9mObKO566]{Venin
d'Araflate}</p> <p></p>
race: ''
notesmj: ''
ownership:

View File

@ -155,37 +155,37 @@ system:
carac:
taille:
type: number
value: '19'
value: 19
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '18'
value: 18
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '18'
value: 18
label: Force
xp: 0
derivee: false
perception:
type: number
value: '12'
value: 12
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '4'
value: 4
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '6'
value: 6
label: Rêve
xp: 0
derivee: false
@ -220,12 +220,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '2'
value: 2
label: Protection
derivee: false
compteurs:

View File

@ -126,37 +126,37 @@ system:
carac:
taille:
type: number
value: '33'
value: 33
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '26'
value: 26
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '26'
value: 26
label: Force
xp: 0
derivee: false
perception:
type: number
value: '13'
value: 13
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '3'
value: 3
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: 0
derivee: false
@ -191,12 +191,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '2'
value: 2
label: Protection
derivee: false
compteurs:

View File

@ -154,37 +154,37 @@ system:
carac:
taille:
type: number
value: '20'
value: 20
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '15'
value: 15
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '16'
value: 16
label: Force
xp: 0
derivee: false
perception:
type: number
value: '13'
value: 13
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '9'
value: 9
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: 0
derivee: false
@ -219,12 +219,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '1'
value: 1
label: Protection
derivee: false
compteurs:

View File

@ -183,37 +183,37 @@ system:
carac:
taille:
type: number
value: '7'
value: 7
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '12'
value: 12
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '11'
value: 11
label: Force
xp: 0
derivee: false
perception:
type: number
value: '14'
value: 14
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '8'
value: 8
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '15'
value: 15
label: Rêve
xp: 0
derivee: false
@ -248,12 +248,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '0'
value: 0
label: Protection
derivee: false
compteurs:
@ -300,7 +300,7 @@ system:
d&eacute;chirure. Celle-l&agrave; se contente de chercher &agrave; fuir en
cas d&rsquo;agression, ou attaque toutes griffes dehors si elle se sent
accul&eacute;e. Pour la distinguer (visuellement) de la rieuse,
r&eacute;ussir VUE/Zoologie &agrave; -5. Les caract&eacute;ristiques de
r&eacute;ussir @roll[VUE/Zoologie/-5]. Les caract&eacute;ristiques de
combat indiqu&eacute;es ne s&rsquo;appliquent qu&rsquo;&agrave; la
pointue.</p>
race: ''

View File

@ -153,37 +153,37 @@ system:
carac:
taille:
type: number
value: '21'
value: 21
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '17'
value: 17
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '16'
value: 16
label: Force
xp: 0
derivee: false
perception:
type: number
value: '12'
value: 12
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '12'
value: 12
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '12'
value: 12
label: Rêve
xp: 0
derivee: false
@ -218,12 +218,12 @@ system:
derivee: true
encombrement:
type: number
value: '18'
value: 18
label: Encombrement
derivee: false
protection:
type: number
value: '1'
value: 1
label: Protection
derivee: false
compteurs:

View File

@ -126,37 +126,37 @@ system:
carac:
taille:
type: number
value: '25'
value: 25
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '18'
value: 18
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '20'
value: 20
label: Force
xp: 0
derivee: false
perception:
type: number
value: '12'
value: 12
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '8'
value: 8
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: 0
derivee: false
@ -191,12 +191,12 @@ system:
derivee: true
encombrement:
type: number
value: '22'
value: 22
label: Encombrement
derivee: false
protection:
type: number
value: '2'
value: 2
label: Protection
derivee: false
compteurs:
@ -215,7 +215,7 @@ system:
souffrir ses premiers malus &agrave; la course et au saut. La vitesse
indiqu&eacute;e correspond &agrave; une allure plut&ocirc;t lente.
Contrairement aux autres animaux pour qui la vitesse de base est fixe, le
gardien des r&ecirc;ves peut rajouter jusqu&rsquo;&agrave; 3d6 points
gardien des r&ecirc;ves peut rajouter jusqu&rsquo;&agrave; @roll[3d6] points
&agrave; la vitesse de course de chaque individu. Cette nouvelle vitesse de
course est rajout&eacute;e une fois pour toutes, mais peut &ecirc;tre
modul&eacute;e par un jet de course sur la table de Course animale.</p>

View File

@ -153,37 +153,37 @@ system:
carac:
taille:
type: number
value: '6'
value: 6
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '10'
value: 10
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '10'
value: 10
label: Force
xp: 0
derivee: false
perception:
type: number
value: '11'
value: 11
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '8'
value: 8
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '8'
value: 8
label: Rêve
xp: 0
derivee: false
@ -218,12 +218,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '-1'
value: -1
label: Protection
derivee: false
compteurs:

View File

@ -126,37 +126,37 @@ system:
carac:
taille:
type: number
value: '7'
value: 7
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '9'
value: 9
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '11'
value: 11
label: Force
xp: 0
derivee: false
perception:
type: number
value: '10'
value: 10
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '3'
value: 3
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '2'
value: 2
label: Rêve
xp: 0
derivee: false
@ -184,19 +184,19 @@ system:
value: '+0'
label: +dom
derivee: true
encombrement:
type: number
value: 0
label: Encombrement
derivee: false
vitesse:
type: string
value: 12/28
label: Vitesse
derivee: true
encombrement:
type: number
value: '0'
label: Encombrement
derivee: false
protection:
type: number
value: '4'
value: 4
label: Protection
derivee: false
compteurs:
@ -207,28 +207,19 @@ system:
value: 0
label: Sur-encombrement
description: >-
<h1>Description</h1>
<p>Description Le chrasme (prononcer krasme) est une sorte de cafard
g&eacute;ant &agrave; carapace de crabe aux jointures poilues. Il mesure en
moyenne 1 m de haut sur 1m50 de long, et peut peser jusqu&rsquo;&agrave; 50
kg. Dot&eacute; de mandibules ac&eacute;r&eacute;es et puissantes, il est
redout&eacute; pour son venin mortel.</p>
<h1>M&oelig;urs</h1>
<p>Le chrasme vit dans les lieux sombres, cavernes et souterrains, o&ugrave;
il se nourrit de tout. Paranos le Moindre affirme qu&rsquo;&agrave;
d&eacute;faut d&rsquo;une meilleure ch&egrave;re, il peut m&ecirc;me se
sustenter de cailloux. Il d&eacute;teste la lumi&egrave;re comme son cousin
de petite taille, mais a toutefois un comportement diff&eacute;rent: au lieu
de fuir, il entre dans une rage f&eacute;roce et se rue sur le porteur de
lumi&egrave;re pour le r&eacute;duire en charpie. Savez-vous, cher Paranos,
que vous nous faites un peu peur ?</p>
<h1>Venin</h1>
<p>@UUID[Compendium.foundryvtt-reve-de-dragon.maladies-et-poisons.Item.cFMUtU6LZG0mKeDl]{Venin de chrasme}</p>
<p></p>
<h1>Description</h1><p>Description Le chrasme (prononcer krasme) est une
sorte de cafard géant à carapace de crabe aux jointures poilues. Il mesure
en moyenne 1 m de haut sur 1m50 de long, et peut peser jusquà 50 kg. Doté
de mandibules acérées et puissantes, il est redouté pour son venin
mortel.</p><h1>Mœurs</h1><p>Le chrasme vit dans les lieux sombres, cavernes
et souterrains, où il se nourrit de tout. Paranos le Moindre affirme quà
défaut dune meilleure chère, il peut même se sustenter de cailloux. Il
déteste la lumière comme son cousin de petite taille, mais a toutefois un
comportement différent: au lieu de fuir, il entre dans une rage féroce et se
rue sur le porteur de lumière pour le réduire en charpie. Savez-vous, cher
Paranos, que vous nous faites un peu peur
?</p><h1>Venin</h1><p>@UUID[Compendium.foundryvtt-reve-de-dragon.maladies-et-poisons.Item.cFMUtU6LZG0mKeDl]{Venin
de chrasme}</p><p></p>
race: ''
notesmj: ''
ownership:
@ -311,7 +302,13 @@ prototypeToken:
texture: null
_stats:
systemId: foundryvtt-reve-de-dragon
systemVersion: 12.0.22
systemVersion: 12.0.32
coreVersion: '12.331'
createdTime: null
modifiedTime: 1736537299708
lastModifiedBy: Hp9ImM4o9YRTSdfu
compendiumSource: null
duplicateSource: null
flags: {}
_key: '!actors!yL1XStIKWxGnhKvR'

View File

@ -155,37 +155,37 @@ system:
carac:
taille:
type: number
value: '20'
value: 20
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '18'
value: 18
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '16'
value: 16
label: Force
xp: 0
derivee: false
perception:
type: number
value: '13'
value: 13
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '7'
value: 7
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '9'
value: 9
label: Rêve
xp: 0
derivee: false
@ -220,12 +220,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '3'
value: 3
label: Protection
derivee: false
compteurs:

View File

@ -154,37 +154,37 @@ system:
carac:
taille:
type: number
value: '12'
value: 12
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '12'
value: 12
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '15'
value: 15
label: Force
xp: 0
derivee: false
perception:
type: number
value: '10'
value: 10
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '6'
value: 6
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '4'
value: 4
label: Rêve
xp: 0
derivee: false
@ -219,12 +219,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '3'
value: 3
label: Protection
derivee: false
compteurs:

View File

@ -97,37 +97,37 @@ system:
carac:
taille:
type: number
value: '4'
value: 4
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '10'
value: 10
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '8'
value: 8
label: Force
xp: 0
derivee: false
perception:
type: number
value: '12'
value: 12
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '7'
value: 7
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '7'
value: 7
label: Rêve
xp: 0
derivee: false
@ -152,7 +152,7 @@ system:
attributs:
plusdom:
type: number
value: '-1'
value: -1
label: +dom
derivee: true
vitesse:
@ -162,12 +162,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '-3'
value: -3
label: Protection
derivee: false
compteurs:
@ -203,10 +203,10 @@ system:
<p>En combat, quand un drakkule r&eacute;ussit une particuli&egrave;re et
cause au moins une blessure l&eacute;g&egrave;re, il reste accroch&eacute;
&agrave; sa victime qui perd alors automatiquement 1d6 points
&agrave; sa victime qui perd alors automatiquement @roll[1d6] points
d&rsquo;endurance par round sous l&rsquo;effet de la saign&eacute;e. Quand
l&rsquo;endurance tombe &agrave; z&eacute;ro, le drakkule continue &agrave;
la vider de son sang &agrave; raison de 1d6 points de vie par round. Le
la vider de son sang &agrave; raison de @roll[1d6] points de vie par round. Le
drakkule ne se d&eacute;tache que <em>bless&eacute; gravement </em>ou
<em>sonn&eacute;</em>. Pour se d&eacute;gager, la victime ne peut utiliser
que Corps &agrave; corps (totaliser 2 points d&rsquo;empoignade) ou une

View File

@ -97,37 +97,37 @@ system:
carac:
taille:
type: number
value: '2'
value: 2
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '8'
value: 8
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '3'
value: 3
label: Force
xp: 0
derivee: false
perception:
type: number
value: '13'
value: 13
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '10'
value: 10
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '11'
value: 11
label: Rêve
xp: 0
derivee: false
@ -152,7 +152,7 @@ system:
attributs:
plusdom:
type: number
value: '-4'
value: -4
label: +dom
derivee: true
vitesse:
@ -162,12 +162,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '-6'
value: -6
label: Protection
derivee: false
compteurs:

View File

@ -184,37 +184,37 @@ system:
carac:
taille:
type: number
value: '7'
value: 7
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '12'
value: 12
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '11'
value: 11
label: Force
xp: 0
derivee: false
perception:
type: number
value: '13'
value: 13
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '10'
value: 10
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: 0
derivee: false
@ -249,12 +249,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '0'
value: 0
label: Protection
derivee: false
compteurs:

View File

@ -206,37 +206,37 @@ system:
carac:
taille:
type: number
value: '6'
value: 6
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '10'
value: 10
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '9'
value: 9
label: Force
xp: 0
derivee: false
perception:
type: number
value: '14'
value: 14
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '11'
value: 11
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '12'
value: 12
label: Rêve
xp: 0
derivee: false
@ -261,7 +261,7 @@ system:
attributs:
plusdom:
type: number
value: '-1'
value: -1
label: +dom
derivee: true
vitesse:
@ -271,12 +271,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '0'
value: 0
label: Protection
derivee: false
compteurs:

View File

@ -241,39 +241,39 @@ system:
carac:
taille:
type: number
value: '16'
value: 16
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '15'
value: 15
label: Constitution
xp: '0'
xp: 0
derivee: false
force:
type: number
value: '13'
value: 13
label: Force
xp: '0'
xp: 0
derivee: false
perception:
type: number
value: '10'
value: 10
label: Perception
xp: '0'
xp: 0
derivee: false
volonte:
type: number
value: '8'
value: 8
label: Volonté
xp: '0'
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: '0'
xp: 0
derivee: false
sante:
vie:
@ -306,12 +306,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '4'
value: 4
label: Protection
derivee: false
compteurs:

View File

@ -155,37 +155,37 @@ system:
carac:
taille:
type: number
value: '11'
value: 11
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '14'
value: 14
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '13'
value: 13
label: Force
xp: 0
derivee: false
perception:
type: number
value: '10'
value: 10
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '7'
value: 7
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: 0
derivee: false
@ -220,12 +220,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '0'
value: 0
label: Protection
derivee: false
compteurs:
@ -257,7 +257,8 @@ system:
<h1 style="box-sizing: border-box; user-select: text; color: #191813;
font-family: GoudyAcc, sans-serif;">Venin</h1>
<p>@UUID[Compendium.foundryvtt-reve-de-dragon.maladies-et-poisons.Item.IF19EUvrY1HL87lr]{Venin paralysant de goule}</p>
<p>@UUID[Compendium.foundryvtt-reve-de-dragon.maladies-et-poisons.Item.IF19EUvrY1HL87lr]{Venin
paralysant de goule}</p>
<p>La paralysie intervient quand toutes les lignes de fatigue sont pleines
et dure 6 heures.</p>

View File

@ -137,7 +137,7 @@ system:
elles-m&ecirc;mes, leur grincement involontaire est une torture. Toute
personne se trouvant dans un rayon de 10m doit manquer un jet
d&rsquo;OU&Iuml;E &agrave; +5. Si le jet r&eacute;ussit, perte de 1 point
d&rsquo;endurance, puis jet de VOLONT&Eacute; &agrave; -5. Si le jet de
d&rsquo;endurance, puis jet de @roll[volonté/-5]. Si le jet de
VOLONT&Eacute; &eacute;choue, le personnage est sonn&eacute;
jusqu&rsquo;&agrave; la fin du round suivant.</p>

View File

@ -183,37 +183,37 @@ system:
carac:
taille:
type: number
value: '23'
value: 23
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '20'
value: 20
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '20'
value: 20
label: Force
xp: 0
derivee: false
perception:
type: number
value: '11'
value: 11
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '8'
value: 8
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: 0
derivee: false
@ -248,12 +248,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '3'
value: 3
label: Protection
derivee: false
compteurs:

View File

@ -68,37 +68,37 @@ system:
carac:
taille:
type: number
value: '9'
value: 9
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '11'
value: 11
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '12'
value: 12
label: Force
xp: 0
derivee: false
perception:
type: number
value: '11'
value: 11
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '12'
value: 12
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '11'
value: 11
label: Rêve
xp: 0
derivee: false
@ -133,12 +133,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '4'
value: 4
label: Protection
derivee: false
compteurs:
@ -174,8 +174,8 @@ system:
agressivit&eacute; s&rsquo;appliquant aux humano&iuml;des. S&rsquo;agissant
d&rsquo;un pouvoir inn&eacute;, elles n&rsquo;ont ni &agrave; monter dans
les TMR, ni &agrave; d&eacute;penser de points de r&ecirc;ve. La victime
doit jouer un jet de r&eacute;sistance standard, r-8, et en cas
d&rsquo;&eacute;chec, r&eacute;ussir un jet de VOLONT&Eacute; &agrave; -3
doit jouer un jet de r&eacute;sistance standard, @roll[reve-actuel/-8], et en cas
d&rsquo;&eacute;chec, r&eacute;ussir un jet de @roll[Volonté/-3]
pour pouvoir attaquer la harpie. Le JR n&rsquo;est &agrave; jouer
qu&rsquo;une seule fois, tandis qu&rsquo;en cas d&rsquo;&eacute;chec, le jet
de VOLONT&Eacute; est &agrave; jouer tous les rounds. La non

View File

@ -154,37 +154,37 @@ system:
carac:
taille:
type: number
value: '11'
value: 11
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '13'
value: 13
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '13'
value: 13
label: Force
xp: 0
derivee: false
perception:
type: number
value: '11'
value: 11
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '6'
value: 6
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '10'
value: 10
label: Rêve
xp: 0
derivee: false
@ -219,12 +219,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '2'
value: 2
label: Protection
derivee: false
compteurs:

View File

@ -126,37 +126,37 @@ system:
carac:
taille:
type: number
value: '3'
value: 3
label: Taille
xp: 0
derivee: false
constitution:
type: number
value: '4'
value: 4
label: Constitution
xp: 0
derivee: false
force:
type: number
value: '7'
value: 7
label: Force
xp: 0
derivee: false
perception:
type: number
value: '11'
value: 11
label: Perception
xp: 0
derivee: false
volonte:
type: number
value: '2'
value: 2
label: Volonté
xp: 0
derivee: false
reve:
type: number
value: '2'
value: 2
label: Rêve
xp: 0
derivee: false
@ -181,7 +181,7 @@ system:
attributs:
plusdom:
type: number
value: '0'
value: 0
label: +dom
derivee: true
vitesse:
@ -191,12 +191,12 @@ system:
derivee: true
encombrement:
type: number
value: '0'
value: 0
label: Encombrement
derivee: false
protection:
type: number
value: '-6'
value: -6
label: Protection
derivee: false
compteurs:

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