Compare commits

..

No commits in common. "v11" and "foundryvtt-reve-de-dragon-12.0.0" have entirely different histories.

119 changed files with 1371 additions and 11783 deletions

View File

@ -1,137 +1,16 @@
# 12.0
## 12.0.19 - La témérité d'Astrobazzarh
- Fix
- les défenses des créatures sont correctement filtrées
- le lancer d'initiative pour tous les personnages/PNJs fonctionne correctement
- les lieux et commerces n'ont pas d'initiative
## 12.0.18 - A la barbe d'Astrobazzarh
- Améliorations sur la feuille de PNJ simplifiée
- Ajout du portrait
- Ajout du corps à corps
- Affichage du niveau d'esquive
- Un clic sur l'initiative permet de lancer l'initiative
- les boutons +/- pour la vie, l'endurance et la fatigue changent si on est à la valeur normale
- un clic sur l'endurance effectue un jet d'endurance
- Fix
- les achats des commerces sont de nouveau possibles
- la commande /astro fonctionne de nouveau
- le nombre d'utilisations d'items est réinitialisé à chaque round et fin de combat
- la difficulté de parade pour les armes à distances n'est plus indiquée
- les propositions d'armes de parade sont corrigées
- Ajout d'un indicateur pour les armes de parade nécessitant une significative
## 12.0.16 - Le secret d'Astrobazzarh
- Fix: les jets envoyés messages uniquement au MJ ne sont plus envoyés à tous les autres joueurs (et dupliqués)
- Les noms affichés dans les automatisations de combat sont maintenant ceux des tokens plutôt que ceux des acteurs
- Ajout d'une option pour la localisation des blessures
## 12.0.15 - Le messager d'Astrobazzarh
- Correction des faces de dés personalisés dice-so-nice
- Les messages de maladies ne sont plus publics
- Les messages privés dans les TMR sont aussi envoyés au GM
- Les informations de compétences pouvant augmenter s'affichent comme tooltips
- Amélioration du rendu des tables de compendiums (commande /table)
## 12.0.14 - Les légions d'Astrobazzarh
- Feuille de PNJ:
- boutons standard (encaissement, ...)
- boutons pour ajuster les compteurs
- visualisation des blessures
- click sur blessure pour ajouter/enlever
- gestion des armes
## 12.0.13 - La Chance d'Astrobazzarh
- Fix: jets de caractéristiques
## 12.0.12 - L'étalage d'Astrobazzarh
- Fix: On peut de nouveau vendre des items sans propriétaire, depuis les compendiums ou depuis l'onglet des Objets
- Début de Feuille PNJ au format des encarts Scriptarium
- support des jets de caractéristiques
- support des jets de compétences
## 12.0.11 - Le scriptorium d'Astrobazzarh
- ajout d'un bouton pour générer les éléments de description d'un personnage
- ajout du logo en background dans la liste des systèmes Foundry
- ajout d'un champ pour le métier
- export scriptarium
- encodage de l'export en windows-1252
- export de l'esquive avec armure et sans armure
## 12.0.10 - Le scriptorium d'Astrobazzarh
- corrections de l'export scriptarium
## 12.0.9 - Le scriptorium d'Astrobazzarh
- ajout d'une fonction avancée pour exporter les personnages dans un format csv
## 12.0.8 - La quincaillerie d'Astrobazzarh
- le propriétaire est indiqué dans les feuilles d'équipements/compétences/...
- Ecaille d'efficacité
- l'écaille d'efficacité est prise en compte même si on n'utilise pas le ciblage en combat
- l'écaille d'efficacité est prise en compte pour l'initiative
- Corrections
- l'état général est pris en compte pour les initiatives
- le tooltip de l'initiative affiche correctement l'initiative
## 12.0.7 - La propriété d'Astrobazzarh
- correction des opérations faites à la création d'un Item:
- la durée des queues/rencontres/souffles
- les effets draconiques d'un souffle/queue
- mise à jour des points de tâche des blessures lors des soins
- pas d'expérience sur les particulières quand aucun MJ n'est connecté
- Le drag&drop d'un acteur depuis la liste des acteurs sur la fiche
d'une entité incarnée permet d'accorder le personnage
- Les messages pour résister aux possessions/conjuration sont envoyées
au défenseur
- Les messages pour résister aux empoignades sont envoyées au défenseur
- la commande /voyage affiche maintenant le total de fatigue pour chaque voyageur
- la commande /voyage affiche maintenant les compétences liées au terrain
## 12.0.6 - Le bazar d'Astrobazzarh
- Corrections de l'inventaire en bazar:
- un problème pouvait survenir en déplaçant les objets
l'inventaire, qui fait qu'un conteneur se retrouve récursivement dans son
propre contenu, ce qui empêche d'ouvrir la feuille d'acteur.
- un objet non-conteneur pouvait dans certains cas avoir un pseudo contenu
- un objet pouvait être considéré comme contenu, sans être présent dans un
conteneur (et donc non affiché)
- vider les conteneurs supprime correctement toutes les informations liées
aux conteneurs/contenus
- Les messages pour les tirages dans le compendium utilisent le "roll mode"
courant pour leur visibilité
- Fix: restaurer la compatibilité Foundry 11
## 12.0.5 - Les mauvais jours d'Astrobazzarh
- Fix: on peut de nouveau ouvrir l'édition de calendrier
- Fix: on ne peut plus ouvrir plusieurs fenêtres de lancer de sort
- Fix: Failed to execute 'getComputedStyle' on 'Window'
## 12.0.4 - La plaie d'Astrobazzarh
- **Support V12**
- Fix: les boutons d'encaissement dans le tchat fonctionnent de nouveau
- Fix warnings sur "Die" et AudioHelper
## 12.0.3 - L'hémorragie d'Astrobazzarh
- **Support V12**
- On peut de nouveau ouvrir un acteur blessé après redémarrage du monde
- On peut de nouveau ouvrir les Items avec une rareté par environnement
- Le choix de ne plus afficher les demandes de suppression est bien pris en compte
## 12.0.2 - Les pluies d'Astrobazzarh
- **Support V12**
- correction des actions techniques déleguées au MJ qui bloquaient les fenêtre de lancer de dés des joueurs (et plein d'autres)
- la fenêtre de calendrier s'ouvre correctement
- les dés draconiques peuvent de nouveau faire plus que 0
- adaptation de la fenêtre de recherche
- correction des comparaisons de version pour les migrations automatiques
- correction des roll.eveluate: l'option async est maintenant standard
- correction des templates liés aux selections
- correction de l'ajustement de luminosité de la scène selon l'heure
- correction des images d'effets sur les tokens
- correction de la vente par le tchat: seul le premier acheteur pouvait acheter
- correction d'erreurs intempestives 'User ... lacks permission to update ...'
# 11.2
## 11.2.22 - Le futur d'Akarlikarlikar
- correction de la vente par le tchat: seul le premier acheteur pouvait acheter
- correction d'erreurs intempestives 'User ... lacks permission to update ...'
### Support V12
- adaptation fenêtre de recherche
- correction des comparaisons de version pour les migrations automatiques
- correction des roll.eveluate: l'option async est maintenant standard
- correction des templates liés aux selections
- correction de l'ajustement de luminosité de la scène selon l'heure
- correction des images d'effets sur les tokens
## 11.2.21 - Le questionnement d'Akarlikarlikar
- Une confirmation spécifique est demandée pour monter dans les terres médianes en cas de rencontre en attente
- L'expérience en caractéristique sur les jets de chance et rêve actuels est mise dans la caractéristique correspondante

View File

@ -9,7 +9,7 @@ export class DialogItemVente extends Dialog {
const venteData = {
item: item,
alias: item.actor?.name ?? game.user.name,
vendeurId: item.actor?.id,
vendeurId: item.actor.id,
prixOrigine: item.calculerPrixCommercant(),
prixUnitaire: item.calculerPrixCommercant(),
prixLot: item.calculerPrixCommercant(),

View File

@ -17,7 +17,6 @@ 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";
/* -------------------------------------------- */
/**
@ -128,7 +127,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
this.render(true);
});
this.html.find('.button-tmr-visu').click(async event => this.actor.displayTMR("visu"))
this.html.find('.visu-tmr').click(async event => this.actor.displayTMR("visu"))
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
@ -186,7 +185,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
this.html.find('.item-equip').click(async event => this.actor.equiperObjet(RdDSheetUtility.getItemId(event)))
this.html.find('.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('.chance-appel').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)))
@ -199,7 +198,6 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
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('.description-aleatoire').click(async event => new AppPersonnageAleatoire(this.actor).render(true))
if (game.user.isGM) {
// experience log
this.html.find('.experiencelog-delete').click(async event => {
@ -218,26 +216,26 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
}
// Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', true))
this.html.find('.ptreve-actuel a').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-arme').click(async event => this.actor.rollArme(foundry.utils.duplicate(this._getEventArmeCombat(event)), 'competence'))
this.html.find('.arme-label a').click(async event => this.actor.rollArme(foundry.utils.duplicate(this._getEventArmeCombat(event))))
// Initiative pour l'arme
this.html.find('.roll-init-arme').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id)
this.html.find('.arme-initiative a').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id);
if (combatant) {
RdDCombatManager.rollInitiativeAction(combatant._id, this._getEventArmeCombat(event));
let action = this._getEventArmeCombat(event);
RdDCombatManager.rollInitiativeAction(combatant._id, action);
} else {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
}
})
});
// Display TMR
this.html.find('.button-tmr').click(async event => this.actor.displayTMR("normal"))
this.html.find('.button-tmr-rapide').click(async event => this.actor.displayTMR("rapide"))
this.html.find('.monte-tmr').click(async event => this.actor.displayTMR("normal"))
this.html.find('.monte-tmr-rapide').click(async event => this.actor.displayTMR("rapide"))
this.html.find('.button-repos').click(async event => await this.actor.repos())
this.html.find('.repos').click(async event => await this.actor.repos())
this.html.find('.carac-xp-augmenter').click(async event => this.actor.updateCaracXPAuto(event.currentTarget.name.replace("augmenter.", "")))
this.html.find('.competence-xp-augmenter').click(async event => this.actor.updateCompetenceXPAuto(RdDSheetUtility.getItemId(event)))
@ -279,7 +277,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
this.html.find('.moral-malheureux').click(async event => this.actor.jetDeMoral('malheureuse'))
this.html.find('.moral-neutre').click(async event => this.actor.jetDeMoral('neutre'))
this.html.find('.moral-heureux').click(async event => this.actor.jetDeMoral('heureuse'))
this.html.find('.button-ethylisme').click(async event => this.actor.jetEthylisme())
this.html.find('.ethylisme-test').click(async event => this.actor.jetEthylisme())
this.html.find('.ptreve-actuel-plus').click(async event => this.actor.reveActuelIncDec(1))
this.html.find('.ptreve-actuel-moins').click(async event => this.actor.reveActuelIncDec(-1))

View File

@ -32,12 +32,10 @@ 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 { TYPES } from "./item.js";
import { RdDBaseActorSang } from "./actor/base-actor-sang.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { DialogChoixXpCarac } from "./dialog-choix-xp-carac.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDCombatManager } from "./rdd-combat.js";
export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
@ -94,7 +92,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
canReceive(item) {
return ![ITEM_TYPES.competencecreature, ITEM_TYPES.tarot, ITEM_TYPES.service].includes(item.type)
return ![TYPES.competencecreature, TYPES.tarot, TYPES.service].includes(item.type)
}
isPersonnageJoueur() {
@ -127,28 +125,11 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
getMalusArmure() {
return this.itemTypes[ITEM_TYPES.armure].filter(it => it.system.equipe)
return this.itemTypes[TYPES.armure].filter(it => it.system.equipe)
.map(it => it.system.malus)
.reduce(Misc.sum(), 0);
}
listActionsCombat() {
// Recupération des armes
const actions = RdDCombatManager.listActionsArmes(
this.itemTypes[ITEM_TYPES.arme]
.filter(it => RdDItemArme.isAttaque(it))
.concat(RdDItemArme.empoignade(this))
.concat(RdDItemArme.mainsNues(this))
,
this.itemTypes[ITEM_TYPES.competence],
this.system.carac)
if (this.system.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } });
}
return actions
}
/* -------------------------------------------- */
getTache(id) { return this.findItemLike(id, 'tache') }
getMeditation(id) { return this.findItemLike(id, 'meditation') }
@ -161,7 +142,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
getDemiReve() { return this.system.reve.tmrpos.coord }
getDraconicList() { return this.itemTypes[ITEM_TYPES.competence].filter(it => it.system.categorie == 'draconic') }
getDraconicList() { return this.itemTypes[TYPES.competence].filter(it => it.system.categorie == 'draconic') }
getBestDraconic() { return foundry.utils.duplicate(this.getDraconicList().sort(Misc.descending(it => it.system.niveau)).find(it => true)) }
getDraconicOuPossession() {
return [...this.getDraconicList().filter(it => it.system.niveau >= 0),
@ -172,13 +153,13 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async $perteRevePotionsEnchantees() {
let potions = this.itemTypes[ITEM_TYPES.potion]
let potions = this.itemTypes[TYPES.potion]
.filter(it => Grammar.includesLowerCaseNoAccent(it.system.categorie, 'enchanté') && !it.system.prpermanent)
const potionUpdates = await Promise.all(potions.map(async it => {
const nouveauReve = Math.max(it.system.pr - 1, 0)
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html`, {
pr: nouveauReve,
alias: this.name,
@ -229,7 +210,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async grisReve(nbJours) {
let message = {
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `${nbJours} jours de gris rêve sont passés. `
};
for (let i = 0; i < nbJours; i++) {
@ -283,7 +264,7 @@ export class RdDActor extends RdDBaseActorSang {
async dormirChateauDormant() {
if (!ReglesOptionnelles.isUsing("chateau-dormant-gardien") || !this.system.sommeil || this.system.sommeil.nouveaujour) {
const message = {
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: ""
};
@ -364,7 +345,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async _recupererBlessures(message, isMaladeEmpoisonne) {
const timestamp = game.system.rdd.calendrier.getTimestamp()
const blessures = this.filterItems(it => it.system.gravite > 0, ITEM_TYPES.blessure).sort(Misc.ascending(it => it.system.gravite))
const blessures = this.filterItems(it => it.system.gravite > 0, TYPES.blessure).sort(Misc.ascending(it => it.system.gravite))
await Promise.all(blessures.map(async b => b.recuperationBlessure({
actor: this,
@ -380,7 +361,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async _recupererVie(message, isMaladeEmpoisonne) {
const tData = this.system
let blessures = this.filterItems(it => it.system.gravite > 0, ITEM_TYPES.blessure);
let blessures = this.filterItems(it => it.system.gravite > 0, TYPES.blessure);
if (blessures.length > 0) {
return
}
@ -419,7 +400,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async remiseANeuf() {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: 'Remise à neuf de ' + this.name
});
await this.supprimerBlessures(it => true);
@ -436,7 +417,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async dormir(heures, options = { grisReve: false, chateauDormant: false }) {
const message = {
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: this.name + ': '
};
const insomnie = this.system.sommeil?.insomnie || heures == 0;
@ -507,7 +488,7 @@ export class RdDActor extends RdDBaseActorSang {
else {
if (!ReglesOptionnelles.isUsing("recuperation-reve")) {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `Pas de récupération de rêve (${reve} points ignorés)`
});
jetsReve.push(0);
@ -655,7 +636,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async sortMisEnReserve(sort, draconic, coord, ptreve) {
await this.createEmbeddedDocuments("Item", [{
type: ITEM_TYPES.sortreserve,
type: TYPES.sortreserve,
name: sort.name,
img: sort.img,
system: { sortid: sort._id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
@ -938,7 +919,7 @@ export class RdDActor extends RdDBaseActorSang {
await this.createEmbeddedDocuments('Item', [souffle]);
if (options.chat) {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: this.name + " subit un Souffle de Dragon : " + souffle.name
});
}
@ -958,7 +939,7 @@ export class RdDActor extends RdDBaseActorSang {
await this.createEmbeddedDocuments('Item', [queue]);
if (options.chat) {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: this.name + " subit une Queue de Dragon : " + queue.name
});
}
@ -996,7 +977,7 @@ export class RdDActor extends RdDBaseActorSang {
let tmr = await TMRUtility.getTMRAleatoire(tmr => accessible(tmr) && !innaccessible.includes(tmr.coord));
ChatMessage.create({
content: `${raison} : ré-insertion aléatoire.`,
whisper: ChatUtility.getOwners(this)
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name)
});
await this.forcerPositionTMRInconnue(tmr);
return tmr;
@ -1010,24 +991,24 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
buildTMRInnaccessible() {
return this.items.filter(it => it.type == ITEM_TYPES.casetmr).filter(it => EffetsDraconiques.isInnaccessible(it)).map(it => it.system.coord)
return this.items.filter(it => it.type == TYPES.casetmr).filter(it => EffetsDraconiques.isInnaccessible(it)).map(it => it.system.coord)
}
/* -------------------------------------------- */
getRencontresTMR() {
return this.itemTypes[ITEM_TYPES.rencontre];
return this.itemTypes[TYPES.rencontre];
}
/* -------------------------------------------- */
async deleteRencontreTMRAtPosition() {
const rencontreIds = this.itemTypes[ITEM_TYPES.rencontre].filter(this.filterRencontreTMRDemiReve()).map(it => it.id)
const rencontreIds = this.itemTypes[TYPES.rencontre].filter(this.filterRencontreTMRDemiReve()).map(it => it.id)
if (rencontreIds.length > 0) {
await this.deleteEmbeddedDocuments('Item', rencontreIds)
}
}
getRencontreTMREnAttente() {
return this.itemTypes[ITEM_TYPES.rencontre].find(this.filterRencontreTMRDemiReve())
return this.itemTypes[TYPES.rencontre].find(this.filterRencontreTMRDemiReve())
}
filterRencontreTMRDemiReve() {
@ -1101,7 +1082,7 @@ export class RdDActor extends RdDBaseActorSang {
const jetMoral = await this._jetDeMoral(situation);
const finMessage = (jetMoral.succes ? messageReussi : messageManque) ?? (jetMoral.ajustement == 0 ? "Vous gardez votre moral" : jetMoral.ajustement > 0 ? "Vous gagnez du moral" : "Vous perdez du moral");
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `${finMessage} - jet ${jetMoral.succes ? "réussi" : "manqué"} en situation ${situation} (${jetMoral.jet}/${jetMoral.difficulte}).`
});
return jetMoral.ajustement;
@ -1165,7 +1146,8 @@ export class RdDActor extends RdDBaseActorSang {
diffNbDoses: -Number(this.system.compteurs.ethylisme.nb_doses || 0),
finalLevel: 0,
diffConditions: 0,
ajustementsForce: CONFIG.RDD.difficultesLibres
ajustementsForce: CONFIG.RDD.difficultesLibres,
config: game.system.rdd.config
}
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-ethylisme.html', rollData);
new RdDRollDialogEthylisme(html, rollData, this, r => this.saouler(r.forceAlcool)).render(true);
@ -1179,11 +1161,11 @@ export class RdDActor extends RdDBaseActorSang {
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);
case TYPES.potion: return await this.consommerPotion(item, onActionItem);
case TYPES.livre: return await this.actionLire(item);
case TYPES.conteneur: return await item.sheet.render(true);
case TYPES.herbe: return await this.actionHerbe(item, onActionItem);
case TYPES.queue: case TYPES.ombre: return await this.actionRefoulement(item);
}
return undefined
}
@ -1438,7 +1420,7 @@ export class RdDActor extends RdDBaseActorSang {
};
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-transformer-stress.html`, stressRollData)
});
@ -1524,7 +1506,7 @@ export class RdDActor extends RdDBaseActorSang {
}
if (display) {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html`, checkXp)
});
}
@ -1556,7 +1538,7 @@ export class RdDActor extends RdDBaseActorSang {
}
if (display) {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html`, checkXp)
});
}
@ -1567,9 +1549,6 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') {
if (!Misc.isFirstConnectedGM()) {
return
}
hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
let xpData = await this._appliquerExperience(rollData.rolled, rollData.selectedCarac.label, rollData.competence, rollData.jetResistance);
if (xpData.length) {
@ -1578,11 +1557,11 @@ export class RdDActor extends RdDBaseActorSang {
xpData
})
if (hideChatMessage) {
ChatUtility.blindMessageToGM({ content: content })
ChatUtility.blindMessageToGM({ content: content });
}
else {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: content
});
}
@ -1621,7 +1600,7 @@ export class RdDActor extends RdDBaseActorSang {
computeDraconicAndSortIndex(sortList) {
let draconicList = this.getDraconicList();
for (let sort of sortList) {
let draconicsSort = RdDItemSort.getDraconicsSort(draconicList, sort).map(it => it.name);
let draconicsSort = this.getDraconicsSort(draconicList, sort).map(it => it.name);
for (let index = 0; index < draconicList.length && sort.system.listIndex == undefined; index++) {
if (draconicsSort.includes(draconicList[index].name)) {
sort.system.listIndex = index;
@ -1631,6 +1610,19 @@ export class RdDActor extends RdDBaseActorSang {
return draconicList;
}
/* -------------------------------------------- */
getDraconicsSort(draconicList, sort) {
//console.log(draconicList, bestDraconic, draconic, voie);
switch (Grammar.toLowerCaseNoAccent(sort.name)) {
case "lecture d'aura":
case "detection d'aura":
return draconicList;
case "annulation de magie":
return draconicList.filter(it => !RdDItemCompetence.isThanatos(it));
}
return [RdDItemCompetence.getVoieDraconic(draconicList, sort.system.draconic)];
}
/* -------------------------------------------- */
async rollUnSort(coord) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
@ -1685,7 +1677,7 @@ export class RdDActor extends RdDBaseActorSang {
}
ChatMessage.create({
content: "Vous êtes sous le coup d'une Mauvaise Rencontre en Persective." + addMsg,
whisper: ChatUtility.getOwners(this)
whisper: ChatMessage.getWhisperRecipients(this.name)
});
}
return rencSpecial;
@ -1697,7 +1689,7 @@ export class RdDActor extends RdDBaseActorSang {
if (countInertieDraconique > 0) {
ChatMessage.create({
content: `Vous êtes sous le coup d'Inertie Draconique : vous perdrez ${countInertieDraconique + 1} cases de Fatigue par déplacement au lieu d'une.`,
whisper: ChatUtility.getOwners(this)
whisper: ChatMessage.getWhisperRecipients(this.name)
});
}
return countInertieDraconique + 1;
@ -1709,7 +1701,7 @@ export class RdDActor extends RdDBaseActorSang {
await this.reveActuelIncDec(-1);
ChatMessage.create({
content: "Vous êtes sous le coup d'un Péage : l'entrée sur cette case vous a coûté 1 Point de Rêve (déduit automatiquement).",
whisper: ChatUtility.getOwners(this)
whisper: ChatMessage.getWhisperRecipients(this.name)
});
}
}
@ -1832,7 +1824,7 @@ export class RdDActor extends RdDBaseActorSang {
}
blessuresASoigner() {
return (this.itemTypes[ITEM_TYPES.blessure])
return (this.itemTypes[TYPES.blessure])
.filter(it => it.system.gravite > 0 && it.system.gravite <= 6)
.filter(it => !(it.system.premierssoins.done && it.system.soinscomplets.done))
.sort(Misc.descending(b => (b.system.premierssoins.done ? "A" : "B") + b.system.gravite))
@ -2289,7 +2281,7 @@ export class RdDActor extends RdDBaseActorSang {
// Cas de désir lancinant, pas d'expérience sur particulière
ChatMessage.create({
content: `Vous souffrez au moins d'un Désir Lancinant, vous ne pouvez pas gagner d'expérience sur une Particulière tant que le désir n'est pas assouvi`,
whisper: ChatUtility.getOwners(this)
whisper: ChatMessage.getWhisperRecipients(this.name)
});
return []
}
@ -2424,7 +2416,7 @@ export class RdDActor extends RdDBaseActorSang {
if (countMonteeLaborieuse > 0) {
ChatMessage.create({
content: `Vous êtes sous le coup d'une Montée Laborieuse : vos montées en TMR coûtent ${countMonteeLaborieuse} Point de Rêve de plus.`,
whisper: ChatUtility.getOwners(this)
whisper: ChatMessage.getWhisperRecipients(this.name)
});
}
return countMonteeLaborieuse;
@ -2472,7 +2464,7 @@ export class RdDActor extends RdDBaseActorSang {
if (this.getReveActuel() < minReveValue) {
ChatMessage.create({
content: `Vous n'avez les ${minReveValue} Points de Reve nécessaires pour monter dans les Terres Médianes`,
whisper: ChatUtility.getOwners(this)
whisper: ChatMessage.getWhisperRecipients(this.name)
});
return;
}
@ -2634,7 +2626,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async resetItemUse() {
await this.unsetFlag(SYSTEM_RDD, 'itemUse');
await this.setFlag(SYSTEM_RDD, 'itemUse', {});
}
/* -------------------------------------------- */
@ -2869,7 +2861,7 @@ export class RdDActor extends RdDBaseActorSang {
await this.setBonusPotionSoin(potionData.system.herbebonus);
}
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-soin.html`, potionData)
});
}
@ -2906,7 +2898,7 @@ export class RdDActor extends RdDBaseActorSang {
this.bonusRepos = potionData.system.herbebonus;
}
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-repos.html`, potionData)
});
}
@ -2938,7 +2930,7 @@ export class RdDActor extends RdDBaseActorSang {
this.diminuerQuantiteObjet(herbeData._id, herbeData.nbBrins);
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html`, messageData)
});
}
@ -2963,7 +2955,7 @@ export class RdDActor extends RdDBaseActorSang {
}
}
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-generique.html`, potionData)
});
}
@ -3030,7 +3022,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async onCreateOwnedDraconique(item, options, id) {
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item));
if (draconique) {
await draconique.onActorCreateOwned(this, item)
@ -3042,7 +3034,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async onDeleteOwnedDraconique(item, options, id) {
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item));
if (draconique) {
await draconique.onActorDeleteOwned(this, item)
@ -3052,7 +3044,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async onDeleteOwnedCaseTmr(item, options, id) {
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
let draconique = Draconique.all().find(it => it.isCase(item));
if (draconique) {
await draconique.onActorDeleteCaseTmr(this, item)
@ -3063,7 +3055,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
notifyGestionTeteSouffleQueue(item, manualMessage = true) {
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `${this.name} a reçu un/une ${item.type}: ${item.name}, qui ${manualMessage ? "n'est pas" : "est"} géré(e) automatiquement. ${manualMessage ? manualMessage : ''}`
});
}
@ -3071,7 +3063,7 @@ export class RdDActor extends RdDBaseActorSang {
async nouvelleIncarnation() {
let incarnation = this.toObject();
incarnation.items = Array.from(this.items.filter(it => it.type == ITEM_TYPES.competence),
incarnation.items = Array.from(this.items.filter(it => it.type == TYPES.competence),
it => {
it = it.toObject();
it.id = undefined;
@ -3086,7 +3078,7 @@ export class RdDActor extends RdDBaseActorSang {
incarnation.name = 'Réincarnation de ' + incarnation.name
incarnation.system = {
carac: foundry.utils.duplicate(this.system.carac),
heure: RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "selfroll", showDice: SHOW_DICE })).key,
heure: RdDTimestamp.defHeure(await RdDDice.rollTotal("1dh", { rollMode: "selfroll", showDice: SHOW_DICE })).key,
age: 18,
biographie: '',
notes: '',

View File

@ -24,15 +24,14 @@ export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
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.html.find('.roll-competence').click(async event => this.actor.rollCompetence(RdDSheetUtility.getItemId(event)));
this.html.find('.encaisser-direct').click(async event => this.actor.encaisser())
this.html.find('.carac-label a').click(async event => this.actor.rollCarac(Grammar.toLowerCaseNoAccent(event.currentTarget.attributes.name.value)));
this.html.find('a.competence-label').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));
if (game.user.isGM) {
this.html.find('.button-remise-a-neuf').click(async event => this.actor.remiseANeuf())
this.html.find('.remise-a-neuf').click(async event => this.actor.remiseANeuf())
this.html.find('.delete-active-effect').click(async event => this.actor.removeEffect(this.html.find(event.currentTarget).parents(".active-effect").data('effect')));
this.html.find('.enlever-tous-effets').click(async event => await this.actor.removeEffects());
}

View File

@ -12,10 +12,10 @@ import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { RdDBaseActor } from "./base-actor.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { StatusEffects } from "../settings/status-effects.js";
import { ITEM_TYPES } from "../item.js";
import { TYPES } from "../item.js";
import { Targets } from "../targets.js";
import { RdDPossession } from "../rdd-possession.js";
import { RdDCombat, RdDCombatManager } from "../rdd-combat.js";
import { RdDCombat } from "../rdd-combat.js";
import { RdDConfirm } from "../rdd-confirm.js";
import { ENTITE_INCARNE, SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { RdDItemArme } from "../item-arme.js";
@ -78,27 +78,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } }
isDead() { return false }
isSonne() { return false }
blessuresASoigner() { return [] }
getEtatGeneral(options = { ethylisme: false }) { return 0 }
isActorCombat() { return true }
getCaracInit(competence) {
if (!competence){
return 0
}
if (competence.type == ITEM_TYPES.competencecreature) {
return competence.system.carac_value
}
return this.system.carac[competence.system.defaut_carac].value;
}
listActionsCombat() {
return this.itemTypes[ITEM_TYPES.competencecreature]
.filter(it => RdDItemCompetenceCreature.isAttaque(it))
.map(it => RdDItemCompetenceCreature.armeCreature(it))
.filter(it => it != undefined);
}
async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
async remiseANeuf() { }
@ -127,14 +108,14 @@ export class RdDBaseActorReve extends RdDBaseActor {
async $finDeRoundSupprimerObsoletes() {
const obsoletes = []
.concat(this.itemTypes[ITEM_TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
.concat(this.itemTypes[ITEM_TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
.concat(this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
.concat(this.itemTypes[TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
.map(it => it.id);
await this.deleteEmbeddedDocuments('Item', obsoletes);
}
async $finDeRoundEmpoignade() {
const immobilisations = this.itemTypes[ITEM_TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
const immobilisations = this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
immobilisations.forEach(emp => RdDEmpoignade.onImmobilisation(this,
game.actors.get(emp.system.empoigneid),
emp
@ -170,10 +151,13 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
getPossession(possessionId) {
return this.itemTypes[ITEM_TYPES.possession].find(it => it.system.possessionid == possessionId);
return this.itemTypes[TYPES.possession].find(it => it.system.possessionid == possessionId);
}
getPossessions() {
return this.itemTypes[TYPES.possession];
}
getEmpoignades() {
return this.itemTypes[ITEM_TYPES.empoignade];
return this.itemTypes[TYPES.empoignade];
}
/* -------------------------------------------- */
@ -312,12 +296,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
/* -------------------------------------------- */
async rollCarac(caracName, jetResistance = undefined) {
if (Grammar.equalsInsensitive(caracName, 'taille')) {
return
}
RdDEmpoignade.checkEmpoignadeEnCours(this)
let selectedCarac = this.getCaracByName(caracName)
console.log("selectedCarac", selectedCarac)
await this.openRollDialog({
name: 'jet-' + caracName,
label: 'Jet ' + Grammar.apostrophe('de', selectedCarac.label),
@ -338,20 +318,19 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
/* -------------------------------------------- */
async rollCompetence(idOrName, options = { tryTarget: true, arme: undefined }) {
async rollCompetence(idOrName, options = { tryTarget: true }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(idOrName);
let rollData = { carac: this.system.carac, competence: competence, arme: options.arme }
if (competence.type == ITEM_TYPES.competencecreature) {
const token = RdDUtility.getSelectedToken(this)
let rollData = { carac: this.system.carac, competence: competence }
if (competence.type == TYPES.competencecreature) {
const arme = RdDItemCompetenceCreature.armeCreature(competence)
if (arme && options.tryTarget && Targets.hasTargets()) {
Targets.selectOneTargetToken(target => {
Targets.selectOneToken(target => {
if (arme.action == "possession") {
RdDPossession.onAttaquePossession(target, this, competence)
}
else {
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
}
});
return;
@ -381,10 +360,9 @@ export class RdDBaseActorReve extends RdDBaseActor {
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
* @returns
*/
rollArme(arme, categorieArme, token) {
token = token ?? RdDUtility.getSelectedToken(this)
const compToUse = this.$getCompetenceArme(arme, categorieArme)
if (!RdDItemArme.isUtilisable(arme)) {
rollArme(arme, categorieArme = "competence") {
let compToUse = this.$getCompetenceArme(arme, categorieArme)
if (!RdDItemArme.isArmeUtilisable(arme)) {
ui.notifications.warn(`Arme inutilisable: ${arme.name} a une résistance de 0 ou moins`)
return
}
@ -397,13 +375,13 @@ export class RdDBaseActorReve extends RdDBaseActor {
title: 'Ne pas utiliser les automatisation de combat',
buttonLabel: "Pas d'automatisation",
onAction: async () => {
this.rollCompetence(compToUse, { tryTarget: false, arme: arme })
this.rollCompetence(compToUse, { tryTarget: false })
}
});
return
}
Targets.selectOneTargetToken(target => {
Targets.selectOneToken(target => {
if (Targets.isTargetEntite(target)) {
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
return
@ -413,74 +391,89 @@ export class RdDBaseActorReve extends RdDBaseActor {
if (competence.isCompetencePossession()) {
return RdDPossession.onAttaquePossession(target, this, competence);
}
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme);
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme);
})
}
$getCompetenceArme(arme, competenceName) {
return RdDItemArme.getCompetenceArme(arme, competenceName)
switch (arme.type) {
case TYPES.competencecreature:
return arme.name
case TYPES.arme:
switch (competenceName) {
case 'competence': return arme.system.competence;
case 'unemain': return RdDItemArme.competence1Mains(arme);
case 'deuxmains': return RdDItemArme.competence2Mains(arme);
case 'tir': return arme.system.tir;
case 'lancer': return arme.system.lancer;
}
}
return undefined
}
verifierForceMin(item) { }
verifierForceMin(item) {
}
/* -------------------------------------------- */
async resetItemUse() { }
async incDecItemUse(itemId, inc = 1) { }
getItemUse(itemId) { return 0; }
/* -------------------------------------------- */
async encaisser() { await RdDEncaisser.encaisser(this) }
async encaisserDommages(rollData, attacker = undefined, show = undefined, attackerToken = undefined, defenderToken = undefined) {
async encaisserDommages(rollData, attacker = undefined, show = undefined) {
if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
return;
}
const armure = await this.computeArmure(rollData);
if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
await this.encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken);
if (ReglesOptionnelles.isUsing('validation-encaissement-gr')){
await this.encaisserDommagesValidationGR(rollData, armure, attacker?.id, show);
}
else {
const jet = await RdDUtility.jetEncaissement(this, rollData, armure, { showDice: SHOW_DICE });
await this.$onEncaissement(jet, show, attackerToken, defenderToken)
const jet = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
await this.$onEncaissement(jet, show, attacker);
}
}
async encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken) {
async encaisserDommagesValidationGR(rollData, armure, attackerId, show) {
if (!game.user.isGM) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'encaisserDommagesValidationGR',
args: [rollData, armure, show, attackerToken, defenderToken]
})
args: [rollData, armure, attackerId, show]
});
} else {
const attacker = game.actors.get(attackerId);
DialogValidationEncaissement.validerEncaissement(this, rollData, armure,
jet => this.$onEncaissement(jet, show, attackerToken, defenderToken));
jet => this.$onEncaissement(jet, show, attacker));
}
}
async $onEncaissement(jet, show, attackerToken, defenderToken) {
await this.onAppliquerJetEncaissement(jet, attackerToken);
await this.$afficherEncaissement(jet, show, defenderToken);
async $onEncaissement(jet, show, attacker) {
await this.onAppliquerJetEncaissement(jet, attacker);
await this.$afficherEncaissement(jet, show);
}
async onAppliquerJetEncaissement(encaissement, attackerToken) { }
async onAppliquerJetEncaissement(encaissement, attacker) { }
async $afficherEncaissement(encaissement, show, defenderToken) {
async $afficherEncaissement(encaissement, show) {
foundry.utils.mergeObject(encaissement, {
alias: defenderToken?.name ?? this.name,
alias: this.name,
hasPlayerOwner: this.hasPlayerOwner,
show: show ?? {}
}, { overwrite: false });
});
await ChatUtility.createChatWithRollMode(
{
roll: encaissement.roll,
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
},
this
)
await ChatUtility.createChatWithRollMode(this.name, {
roll: encaissement.roll,
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
});
if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) {
encaissement = foundry.utils.duplicate(encaissement)
encaissement.isGM = true
encaissement = foundry.utils.duplicate(encaissement);
encaissement.isGM = true;
ChatMessage.create({
whisper: ChatUtility.getGMs(),
whisper: ChatMessage.getWhisperRecipients("GM"),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement)
});
}
@ -515,8 +508,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
isEntiteAccordee(attacker) { return true }
async setEntiteReveAccordee(actor) {
ui.notifications.error("Impossible de s'accorder à " + this.name + ": ce n'est pas une entité incarnée");
async setEntiteReveAccordee(attacker) {
ui.notifications.error("Impossible de s'accorder à " + this.name + ": ce n'est pas une entite de cauchemer/rêve");
}
}

View File

@ -39,8 +39,8 @@ export class RdDBaseActorSangSheet extends RdDBaseActorReveSheet {
ChatMessage.create({
content: `Jet d'Endurance : ${result.jetEndurance} / ${endurance}
<br>${this.actor.name} a ${result.sonne ? 'échoué' : 'réussi'} son Jet d'Endurance ${result.sonne ? 'et devient Sonné' : ''}`,
whisper: ChatUtility.getOwners(this.actor)
})
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.actor.name)
});
}
}

View File

@ -1,11 +1,10 @@
import { MAX_ENDURANCE_FATIGUE, 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 { TYPES } from "../item.js";
import { RdDBaseActorReve } from "./base-actor-reve.js";
import { RdDDice } from "../rdd-dice.js";
import { RdDItemBlessure } from "../item/blessure.js";
import { ChatUtility } from "../chat-utility.js";
/**
* Classe de base pour les acteurs qui peuvent subir des blessures
@ -51,9 +50,9 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
isDead() { return this.system.sante.vie.value < -this.getSConst() }
nbBlessuresLegeres() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isLegere()).length }
nbBlessuresGraves() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isGrave()).length }
nbBlessuresCritiques() { return this.itemTypes[ITEM_TYPES.blessure].filter(it => it.isCritique()).length }
nbBlessuresLegeres() { return this.itemTypes[TYPES.blessure].filter(it => it.isLegere()).length }
nbBlessuresGraves() { return this.itemTypes[TYPES.blessure].filter(it => it.isGrave()).length }
nbBlessuresCritiques() { return this.itemTypes[TYPES.blessure].filter(it => it.isCritique()).length }
/* -------------------------------------------- */
computeResumeBlessure() {
@ -89,9 +88,9 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */
async onAppliquerJetEncaissement(encaissement, attackerToken) {
async onAppliquerJetEncaissement(encaissement, attacker) {
const santeOrig = foundry.utils.duplicate(this.system.sante);
const blessure = await this.ajouterBlessure(encaissement, attackerToken); // Will update the result table
const blessure = await this.ajouterBlessure(encaissement, attacker); // Will update the result table
const perteVie = await this.santeIncDec("vie", -encaissement.vie);
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique());
@ -169,7 +168,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */
async ajouterBlessure(encaissement, attackerToken = undefined) {
async ajouterBlessure(encaissement, attacker = undefined) {
if (encaissement.gravite < 0) return;
if (encaissement.gravite > 0) {
while (this.countBlessures(it => it.system.gravite == encaissement.gravite) >= RdDItemBlessure.maxBlessures(encaissement.gravite) && encaissement.gravite <= 6) {
@ -181,7 +180,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
}
}
const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken);
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg.loc.label, attacker);
if (blessure.isCritique()) {
encaissement.endurance = endActuelle;
}
@ -197,30 +196,20 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
return blessure;
}
async supprimerBlessure({ gravite }) {
const toDelete = this.itemTypes[ITEM_TYPES.blessure].find(it => it.system.gravite == gravite)?.id
if (toDelete) {
await this.deleteEmbeddedDocuments('Item', [toDelete]);
}
}
async supprimerBlessures(filterToDelete) {
const toDelete = this.filterItems(filterToDelete, ITEM_TYPES.blessure)
const toDelete = this.filterItems(filterToDelete, TYPES.blessure)
.map(it => it.id);
await this.deleteEmbeddedDocuments('Item', toDelete);
}
countBlessures(filter = it => !it.isContusion()) {
return this.filterItems(filter, ITEM_TYPES.blessure).length
return this.filterItems(filter, 'blessure').length
}
/* -------------------------------------------- */
async jetDeVie() {
if (this.isDead()) {
ChatMessage.create({
content: `Jet de Vie: ${this.name} est déjà mort, ce n'est pas la peine d'en rajouter !!!!!`,
whisper: ChatUtility.getOwners(this)
})
ChatMessage.create({ content: `Jet de Vie: ${this.name} est déjà mort, ce n'est pas la peine d'en rajouter !!!!!`, whisper: ChatMessage.getWhisperRecipients(this.name) });
return
}
const jetDeVie = await RdDDice.roll("1d20");
@ -247,10 +236,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
else if (prochainJet > 0) {
msgText += `<br>Prochain jet de vie dans ${prochainJet} ${isCritique ? 'round' : 'minute'}${prochainJet > 1 ? 's' : ''} ${isCritique ? '(état critique)' : '(état grave)'}`
}
ChatMessage.create({
content: msgText,
whisper: ChatUtility.getOwners(this)
});
ChatMessage.create({ content: msgText, whisper: ChatMessage.getWhisperRecipients(this.name) });
}
/* -------------------------------------------- */
@ -276,11 +262,11 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
ui.notifications.info(`${this.name} est hors combat, il ne reste donc pas sonné`);
return;
}
await this.setEffect(STATUSES.StatusStunned, sonne)
await this.setEffect(STATUSES.StatusStunned, sonne);
}
isSonne() {
return this.getEffect(STATUSES.StatusStunned)
getSonne() {
return this.getEffect(STATUSES.StatusStunned);
}
isEffectAllowed(effectId) { return true }

View File

@ -3,7 +3,7 @@ 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 { RdDItem, TYPES } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
/* -------------------------------------------- */
@ -38,7 +38,8 @@ export class RdDBaseActorSheet extends ActorSheet {
description: await TextEditor.enrichHTML(this.actor.system.description, { async: true }),
notesmj: await TextEditor.enrichHTML(this.actor.system.notesmj, { async: true }),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.actor, this.isEditable),
effects: this.actor.effects
effects: this.actor.effects,
config: game.system.rdd.config
}
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
@ -51,7 +52,7 @@ export class RdDBaseActorSheet extends ActorSheet {
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
this._appliquerRechercheObjets(formData.conteneurs, formData.inventaires);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
formData.competences.filter(it => it.type == ITEM_TYPES.competencecreature)
formData.competences.filter(it => it.type == TYPES.competencecreature)
.forEach(it => it.isdommages = RdDItemCompetenceCreature.isDommages(it))
return formData;
}

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 { TYPES } from "../item.js";
import { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js";
import { RdDConfirm } from "../rdd-confirm.js";
@ -121,7 +121,6 @@ export class RdDBaseActor extends Actor {
return new ActorConstructor(docData, context);
}
}
context.rdd = undefined
super(docData, context);
}
@ -205,8 +204,11 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) { }
async onDeleteItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { }
async onTimeChanging(oldTimestamp, newTimestamp) {
@ -215,7 +217,7 @@ export class RdDBaseActor extends Actor {
}
async creerObjetParMJ(object) {
if (!Misc.isFirstConnectedGM()) {
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
@ -229,13 +231,11 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
async cleanupConteneurs() {
if (Misc.isOwnerPlayerOrUniqueConnectedGM(this)) {
let updates = this.itemTypes['conteneur']
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
if (updates.length > 0) {
await this.updateEmbeddedDocuments("Item", updates)
}
let updates = this.itemTypes['conteneur']
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
if (updates.length > 0) {
await this.updateEmbeddedDocuments("Item", updates)
}
}
@ -267,7 +267,6 @@ export class RdDBaseActor extends Actor {
}
let fortune = this.getFortune();
console.log("payer", game.user.character, depense, fortune);
// TODO: passer en handlebars
let msg = "";
if (fortune >= depense) {
await Monnaie.optimiserFortune(this, fortune - depense);
@ -277,10 +276,11 @@ export class RdDBaseActor extends Actor {
msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
}
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
let message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: msg
})
};
ChatMessage.create(message);
}
async depenserSols(sols) {
@ -314,7 +314,7 @@ export class RdDBaseActor extends Actor {
RdDAudio.PlayContextAudio("argent"); // Petit son
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
});
}
@ -332,7 +332,7 @@ export class RdDBaseActor extends Actor {
ui.notifications.info("Inutile de se vendre à soi-même");
return;
}
if (!Misc.isFirstConnectedGM()) {
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
actorId: achat.vendeurId ?? achat.acheteurId,
method: 'achatVente',
@ -368,7 +368,7 @@ export class RdDBaseActor extends Actor {
ChatMessage.create({
user: achat.userId,
speaker: { alias: (acheteur ?? vendeur).name },
whisper: ChatUtility.getOwners(this),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem)
});
@ -539,7 +539,7 @@ export class RdDBaseActor extends Actor {
// Calculer le total actuel des contenus
const encContenu = dest.getEncContenu();
const newEnc = moved.getEncTotal(); // Calculer le total actuel du nouvel objet
const placeDisponible = Misc.keepDecimals(dest.system.capacite - encContenu - newEnc, 4)
const placeDisponible = Math.roundDecimals(dest.system.capacite - encContenu - newEnc, 4)
// Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
if (placeDisponible < 0) {
@ -554,15 +554,15 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
/** Ajoute un item dans un conteneur, sur la base de leurs ID */
async ajouterDansConteneur(item, conteneur, onAjouterDansConteneur) {
if (conteneur?.isConteneur()) {
if (!conteneur) {
// TODO: afficher
item.estContenu = false;
}
else if (conteneur.isConteneur()) {
item.estContenu = true;
const nouveauContenu = [...conteneur.system.contenu, item.id];
await conteneur.update({ 'system.contenu': nouveauContenu });
onAjouterDansConteneur(item.id, conteneur.id)
}
else {
item.estContenu = false;
await conteneur?.update({ 'system.-=contenu': undefined })
onAjouterDansConteneur(item.id, conteneur.id);
}
}
@ -580,13 +580,8 @@ export class RdDBaseActor extends Actor {
if (item.estContenu) {
item.estContenu = undefined;
}
if (item.system.contenu != undefined) {
if (item.type == 'conteneur') {
corrections.push({ _id: item.id, 'system.contenu': [] });
}
else {
corrections.push({ _id: item.id, 'system.-=contenu': undefined });
}
if (item.type == 'conteneur' && item.system.contenu.length > 0) {
corrections.push({ _id: item.id, 'system.contenu': [] });
}
}
if (corrections.length > 0) {
@ -621,21 +616,15 @@ export class RdDBaseActor extends Actor {
}
/* -------------------------------------------- */
/**
* Supprime un item d'un conteneur, sur la base de leurs ID
*/
/** Supprime un item d'un conteneur, sur la base
* de leurs ID */
async enleverDeConteneur(item, conteneur, onEnleverDeConteneur) {
if (conteneur) {
if (conteneur.isConteneur()) {
const contenu = conteneur.system.contenu.filter(id => id != item.id);
await conteneur.update({ 'system.contenu': contenu });
onEnleverDeConteneur();
}
else {
await conteneur.update({ 'system.-=contenu': undefined })
}
if (conteneur?.isConteneur()) {
item.estContenu = false;
const contenu = conteneur.system.contenu.filter(id => id != item.id);
await conteneur.update({ 'system.contenu': contenu });
onEnleverDeConteneur();
}
item.estContenu = false;
}
/* -------------------------------------------- */
@ -701,28 +690,9 @@ export class RdDBaseActor extends Actor {
async actionPrincipale(item, onActionItem = async () => { }) {
switch (item.type) {
case ITEM_TYPES.conteneur: return await item.sheet.render(true);
case 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]
.map(p => {
return {
name: p.name,
action: 'possession',
system: {
competence: p.name,
possessionid: p.system.possessionid,
}
}
})
}
}

View File

@ -1,5 +1,5 @@
import { ENTITE_INCARNE } from "../constants.js";
import { ITEM_TYPES } from "../item.js";
import { TYPES } from "../item.js";
import { STATUSES } from "../settings/status-effects.js";
import { RdDBaseActorSang } from "./base-actor-sang.js";
@ -12,7 +12,7 @@ export class RdDCreature extends RdDBaseActorSang {
isCreature() { return true }
canReceive(item) {
return item.type == ITEM_TYPES.competencecreature || item.isInventaire();
return item.type == TYPES.competencecreature || item.isInventaire();
}
async remiseANeuf() {
@ -33,4 +33,27 @@ export class RdDCreature extends RdDBaseActorSang {
}
}
isEntiteAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
let resonnance = this.system.sante.resonnance
return (resonnance.actors.find(it => it == attacker.id))
}
return true
}
/* -------------------------------------------- */
async setEntiteReveAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
let resonnance = foundry.utils.duplicate(this.system.sante.resonnance);
if (resonnance.actors.find(it => it == attacker.id)) {
// déjà accordé
return;
}
await this.update({ "system.sante.resonnance": [...resonnance, attacker.id] });
}
else {
super.setEntiteReveAccordee(attacker)
}
}
}

View File

@ -54,12 +54,6 @@ export class RdDActorEntiteSheet extends RdDBaseActorReveSheet {
});
}
async _onDropActor(event, dragData) {
const dropActor = fromUuidSync(dragData.uuid)
await this.actor.setEntiteReveAccordee(dropActor)
super._onDropActor(event, dragData)
}
async deleteSubActeur(actorId) {
let newResonances = this.actor.system.sante.resonnance.actors.filter(id => id != actorId);
await this.actor.update({ 'system.sante.resonnance.actors': newResonances }, { renderSheet: false });

View File

@ -1,5 +1,5 @@
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
import { ITEM_TYPES } from "../item.js";
import { TYPES } from "../item.js";
import { Misc } from "../misc.js";
import { RdDEncaisser } from "../rdd-roll-encaisser.js";
import { STATUSES } from "../settings/status-effects.js";
@ -12,7 +12,7 @@ export class RdDEntite extends RdDBaseActorReve {
}
canReceive(item) {
return item.type == ITEM_TYPES.competencecreature
return item.type == TYPES.competencecreature
}
isEntite(typeentite = []) {
@ -29,7 +29,7 @@ export class RdDEntite extends RdDBaseActorReve {
getChance() { return this.getReve() }
getDraconicOuPossession() {
return this.itemTypes[ITEM_TYPES.competencecreature]
return this.itemTypes[TYPES.competencecreature]
.filter(it => it.system.categorie == 'possession')
.sort(Misc.descending(it => it.system.niveau))
.find(it => true);
@ -74,7 +74,7 @@ export class RdDEntite extends RdDBaseActorReve {
return [STATUSES.StatusComma].includes(effectId);
}
async onAppliquerJetEncaissement(encaissement, attackerToken) {
async onAppliquerJetEncaissement(encaissement, attacker) {
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
foundry.utils.mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue,
@ -91,16 +91,20 @@ export class RdDEntite extends RdDBaseActorReve {
}
/* -------------------------------------------- */
async setEntiteReveAccordee(actor) {
async setEntiteReveAccordee(attacker) {
if (this.isEntite([ENTITE_INCARNE])) {
if (this.system.sante.resonnance.actors.find(it => it == actor.id)) {
let resonnance = foundry.utils.duplicate(this.system.sante.resonnance);
if (resonnance.actors.find(it => it == attacker.id)) {
// déjà accordé
return
return;
}
await this.update({ "system.sante.resonnance.actors": [...this.system.sante.resonnance.actors, actor.id] })
resonnance.actors.push(attacker.id);
await this.update({ "system.sante.resonnance": resonnance });
}
else {
super.setEntiteReveAccordee(actor)
super.setEntiteReveAccordee(attacker)
}
}
}

View File

@ -1,115 +0,0 @@
import { RdDActorSheet } from "../../actor-sheet.js"
import { SYSTEM_RDD } from "../../constants.js";
import { Misc } from "../../misc.js";
import { EXPORT_CSV_SCRIPTARIUM, OptionsAvancees } from "../../settings/options-avancees.js";
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([
"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",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-compteur.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee-compteur.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/competences.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/esquive.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/fatigue.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/protection.hbs",
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs",
])
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",
width: 550,
showCompNiveauBase: false,
vueArchetype: false,
}, { inplace: false })
}
constructor(actor, options) {
super(actor, options)
}
async getData() {
const formData = await super.getData()
// Add any structured, precomputed list of data
formData.context = Mapping.prepareContext(this.actor)
formData.export = this.getMappingValues(formData.context, this.actor)
formData.competences = this.getCompetences(CATEGORIES_COMPETENCES)
formData.draconic = this.getCompetences(CATEGORIES_DRACONIC)
const legeres = this.actor.nbBlessuresLegeres()
const graves = this.actor.nbBlessuresGraves()
const critiques = this.actor.nbBlessuresCritiques()
formData.etat = {
surenc: this.actor.computeMalusSurEncombrement(),
fatigue: {
value: this.actor.getFatigueActuelle(),
max: this.actor.getFatigueMax(),
malus: this.actor.malusFatigue()
},
blessures: legeres + graves + critiques,
blessure: [legeres > 0, legeres > 1, legeres > 2, legeres > 3, legeres > 4, graves > 0, graves > 1, critiques > 0],
}
formData.options.exportScriptarium = OptionsAvancees.isUsing(EXPORT_CSV_SCRIPTARIUM)
return formData
}
getMappingValues(context, actor) {
return Object.fromEntries(Mapping.getMapping().map(it => [it.column, {
colName: it.colName ?? it.column,
column: it.column,
rollClass: it.rollClass,
value: String(it.getter(actor, context))
}]))
}
getCompetences(categories) {
const competences = Mapping.getCompetencesCategorie(this.actor, categories)
if (competences.length == 0) {
return ''
}
const byCategories = Mapping.competencesByCategoriesByNiveau(competences, categories)
const listByCategories = Object.values(byCategories)
.map(it => it.competencesParNiveau)
.map(byNiveau => {
const niveaux = Object.keys(byNiveau).map(it => Number(it)).sort(Misc.ascending())
if (niveaux.length == 0) {
return undefined
}
const listCategorieByNiveau = niveaux.map(niveau => {
const list = byNiveau[niveau].sort(Misc.ascending(it => it.name))
return { niveau, list }
})
return Misc.concat(listCategorieByNiveau)
}).filter(it => it != undefined)
return Misc.concat(listByCategories)
}
activateListeners(html) {
super.activateListeners(html);
this.html.find('.click-blessure-remove').click(async event =>
await this.actor.supprimerBlessure({
gravite: this.html.find(event.currentTarget).data('gravite')
})
)
this.html.find('.click-blessure-add').click(async event =>
await this.actor.ajouterBlessure({
gravite: this.html.find(event.currentTarget).data('gravite')
// event.currentTarget.attributes['data-gravite'].value
})
)
this.html.find('.button-export').click(async event => {
ExportScriptarium.INSTANCE.exportActors([this.actor],
`${this.actor.uuid}-${this.actor.name}`
)
})
}
}

View File

@ -1,78 +0,0 @@
import { ACTOR_TYPES } from "../../item.js"
import { Misc } from "../../misc.js"
import { EXPORT_CSV_SCRIPTARIUM, OptionsAvancees } from "../../settings/options-avancees.js"
import { Mapping } from "./mapping.js"
const IMG_SCRIPTARIUM = '<img class="context-menu-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/scriptarium.svg">'
export class ExportScriptarium {
static init() {
ExportScriptarium.INSTANCE = new ExportScriptarium()
}
constructor() {
Hooks.on("getActorDirectoryFolderContext", (actorDirectory, menus) => { ExportScriptarium.INSTANCE.onActorDirectoryMenu(actorDirectory, menus) })
Hooks.on("getActorDirectoryEntryContext", (actorDirectory, menus) => { ExportScriptarium.INSTANCE.onActorDirectoryMenu(actorDirectory, menus) })
}
onActorDirectoryMenu(actorDirectory, menus) {
menus.push({
name: 'Export Personnages <i class="fa-regular fa-file-csv"></i>',
icon: IMG_SCRIPTARIUM,
condition: (target) => game.user.isGM &&
OptionsAvancees.isUsing(EXPORT_CSV_SCRIPTARIUM) &&
this.$getActors(actorDirectory, target).length > 0,
callback: target => this.exportActors(this.$getActors(actorDirectory, target), this.$getTargetName(actorDirectory, target))
})
}
$getTargetName(actorDirectory, target) {
const li = target.closest(".directory-item")
const folderId = li.data("folderId")
const actorId = li.data("documentId")
return actorId
? game.actors.get(actorId).name
: actorDirectory.folders.find(it => it.id == folderId).name
}
$getActors(actorDirectory, target) {
const li = target.closest(".directory-item")
const folderId = li.data("folderId")
const actorId = li.data("documentId")
const actors = actorId
? [game.actors.get(actorId)]
: folderId
? actorDirectory.folders.find(it => it.id == folderId).contents
: []
return actors.filter(it => it.type == ACTOR_TYPES.personnage)
}
exportActors(actors, targetName) {
const eol = '\n\r'
const header = Misc.join(this.getHeaderLine(), ';')
const actorLines = actors.map(actor => Misc.join(this.getActorLine(actor), ';'))
const data = Misc.join([header, ...actorLines], eol)
const filename = `scriptarium-${targetName?.slugify()}.csv`;
saveDataToFile(data, "text/csv;charset=windows-1252", `${filename}`);
}
getHeaderLine() {
return Mapping.getColumns()
}
getActorLine(actor) {
const values = Mapping.getValues(actor)
return values
.map(it => this.$escapeQuotes(it))
.map(it => it.replaceAll("\n", " ").replaceAll("\r", ""))
}
$escapeQuotes(it) {
it = '' + it
if (it.includes('"') || it.includes(';')) {
return `"${it.replaceAll('"', '\\"')}"`
}
return it
}
}

View File

@ -1,384 +0,0 @@
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 { Misc } from "../../misc.js"
import { RdDTimestamp } from "../../time/rdd-timestamp.js"
import { RdDBonus } from "../../rdd-bonus.js"
import { TMRType } from "../../tmr-utility.js"
export const CATEGORIES_COMPETENCES = [
"generale",
"particuliere",
"specialisee",
"connaissance",
]
export const CATEGORIES_DRACONIC = [
"draconic",
]
const CATEGORIES_COMBAT = [
"melee",
"tir",
"lancer"
]
const NIVEAU_BASE = {
"generale": -4,
"particuliere": -8,
"specialisee": -11,
"connaissance": -11,
"draconic": -11,
"melee": -6,
"tir": -8,
"lancer": -8,
}
class ColumnMappingFactory {
static createMappingArme(part, i) {
return { column: `arme_${part}_${i}`, getter: (actor, context) => Mapping.getArme(actor, context, part, i) }
}
static createMappingSort(part, i) {
return { column: `sort_${part}_${i}`, getter: (actor, context) => Mapping.getSort(actor, context, part, i) }
}
}
const NB_ARMES = 10
const NB_SORTS = 20
const TABLEAU_ARMES = [...Array(NB_ARMES).keys()]
const TABLEAU_SORTS = [...Array(NB_SORTS).keys()]
const MAPPING_BASE = [
{ column: "ID", colName: 'ID', getter: (actor, context) => actor.id },
{ column: "name", getter: (actor, context) => actor.name },
{ column: "metier", colName: 'Métier', getter: (actor, context) => actor.system.metier },
{ column: "biographie", colName: 'Biographie', getter: (actor, context) => actor.system.biographie },
{ column: "taille", getter: (actor, context) => actor.system.carac.taille.value },
{ column: "apparence", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.apparence.value },
{ column: "constitution", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.constitution.value },
{ column: "force", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.force.value },
{ column: "agilite", rollClass: 'roll-carac', colName: 'Agilité', getter: (actor, context) => actor.system.carac.agilite.value },
{ column: "dexterite", rollClass: 'roll-carac', colName: 'Dextérité', getter: (actor, context) => actor.system.carac.dexterite.value },
{ column: "vue", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.vue.value },
{ column: "ouie", rollClass: 'roll-carac', colName: 'Ouïe', getter: (actor, context) => actor.system.carac.ouie.value },
{ column: "odoratgout", rollClass: 'roll-carac', colName: 'Odo-goût', getter: (actor, context) => actor.system.carac.odoratgout.value },
{ column: "volonte", rollClass: 'roll-carac', colName: 'Volonté', getter: (actor, context) => actor.system.carac.volonte.value },
{ column: "intellect", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.intellect.value },
{ column: "empathie", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.empathie.value },
{ column: "reve", rollClass: 'roll-carac', colName: 'Rêve', getter: (actor, context) => actor.system.carac.reve.value },
{ column: "chance", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.chance.value },
{ column: "melee", rollClass: 'roll-carac', colName: 'Mêlée', getter: (actor, context) => actor.system.carac.melee.value },
{ column: "tir", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.tir.value },
{ column: "lancer", rollClass: 'roll-carac', getter: (actor, context) => actor.system.carac.lancer.value },
{ column: "derobee", rollClass: 'roll-carac', colName: 'Dérobée', getter: (actor, context) => actor.system.carac.derobee.value },
{ column: "vie", getter: (actor, context) => actor.system.sante.vie.max },
{ column: "endurance", getter: (actor, context) => actor.system.sante.endurance.max },
{ column: "plusdom", colName: '+dom', getter: (actor, context) => actor.system.attributs.plusdom.value },
{ column: "protectionnaturelle", colName: 'Protection naturelle', getter: (actor, context) => actor.system.attributs.protection.value > 0 ? actor.system.attributs.protection.value : '' },
{ column: "description", getter: (actor, context) => Mapping.getDescription(actor) },
{ column: "armure", getter: (actor, context) => Mapping.getArmure(actor, context) },
{ column: "protectionarmure", colName: 'Protection', getter: (actor, context) => Mapping.getProtectionArmure(actor, context) },
{ column: "malus_armure", getter: (actor, context) => Mapping.getMalusArmure(actor, context) },
{ column: "reve_actuel", rollClass: 'roll-reve-actuel', colName: 'Rêve actuel', getter: (actor, context) => actor.system.reve.reve.value },
{ column: "vie_actuel", rollClass: 'jet-vie', getter: (actor, context) => actor.system.sante.vie.value },
{ column: "endurance_actuel", rollClass: 'jet-endurance', getter: (actor, context) => actor.system.sante.endurance.value },
{ column: "esquive", getter: (actor, context) => Mapping.getEsquive(context) },
{ column: "esquive_armure", getter: (actor, context) => Mapping.getEsquiveArmure(context) },
{ column: "competences", getter: (actor, context) => Mapping.getCompetences(actor, CATEGORIES_COMPETENCES) },
{ column: "draconic", getter: (actor, context) => Mapping.getCompetences(actor, CATEGORIES_DRACONIC) },
]
const MAPPING_ARMES = TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('name', i))
.concat(TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('niveau', i)))
.concat(TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('init', i)))
.concat(TABLEAU_ARMES.map(i => ColumnMappingFactory.createMappingArme('dommages', i)))
const MAPPING_SORTS = TABLEAU_SORTS.map(i => ColumnMappingFactory.createMappingSort('voie', i))
.concat(TABLEAU_SORTS.map(i => ColumnMappingFactory.createMappingSort('description', i)))
.concat(TABLEAU_SORTS.map(i => ColumnMappingFactory.createMappingSort('bonus', i)))
const MAPPING = MAPPING_BASE
.concat(MAPPING_ARMES)
.concat(MAPPING_SORTS)
export class Mapping {
static getMapping() {
return MAPPING
}
static getColumns() {
return MAPPING.map(it => it.column)
}
static getValues(actor) {
const context = Mapping.prepareContext(actor)
return MAPPING.map(it => it.getter(actor, context))
}
static getAsObject(actor) {
const context = Mapping.prepareContext(actor)
return Object.fromEntries(MAPPING.map(it => [it.column, {
colName: it.colName ?? it.column,
value: it.getter(actor, context)
}]))
}
static getValues(actor) {
const context = Mapping.prepareContext(actor)
return MAPPING.map(it => it.getter(actor, context))
}
static prepareContext(actor) {
return {
armes: Mapping.prepareArmes(actor),
armure: Mapping.prepareArmure(actor),
esquive: Mapping.prepareEsquive(actor),
sorts: Mapping.prepareSorts(actor)
}
}
static prepareArmes(actor) {
const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme)
RdDItemArme.ajoutCorpsACorps(armes, actor)
return armes.map(arme => [
arme.system.unemain ? Mapping.prepareArme(actor, arme, 'unemain') : undefined,
arme.system.deuxmains ? Mapping.prepareArme(actor, arme, 'deuxmains') : undefined,
!(arme.system.unemain || arme.system.deuxmains) ? Mapping.prepareArme(actor, arme, 'competence') : undefined,
arme.system.lancer != "" ? Mapping.prepareArme(actor, arme, 'lancer') : undefined,
arme.system.tir != "" ? Mapping.prepareArme(actor, arme, 'tir') : undefined]
.filter(it => it != undefined))
.reduce((a, b) => a.concat(b), [])
}
static prepareArme(actor, arme, maniement) {
const nameCompetenceArme = RdDItemArme.getCompetenceArme(arme, maniement)
const competence = actor.getCompetence(nameCompetenceArme)
if (RdDItemCompetence.isNiveauBase(competence)) {
return undefined
}
const categorie = Mapping.complementCategorie(arme, maniement)
const dommages = Mapping.dommagesArme(actor, arme, maniement)
return {
name: arme.name + categorie,
niveau: Misc.toSignedString(competence.system.niveau),
init: Mapping.calculBaseInit(actor, competence.system.categorie) + competence.system.niveau,
dommages: dommages,
competence: competence,
arme: arme
}
}
static dommagesArme(actor, arme, maniement){
const dmgArme = RdDItemArme.dommagesReels(arme, maniement)
const dommages = Misc.toSignedString(dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme))
switch(arme.system.mortalite) {
case 'non-mortel': return `(${dommages})`
case 'empoignade': return '-'
}
return dommages
}
static complementCategorie(arme, maniement) {
switch (maniement) {
case 'unemain': return (arme.system.deuxmains) ? ' 1 main' : (arme.system.lancer || arme.system.tir) ? ' mêlée' : ''
case 'deuxmains': return (arme.system.unemain) ? ' 2 mains' : (arme.system.lancer || arme.system.tir) ? ' mêlée' : ''
case 'lancer': return (arme.system.unemain || arme.system.deuxmains || arme.system.tir) ? ' jet' : ''
case 'tir': return (arme.system.unemain || arme.system.deuxmains || arme.system.lancer) ? ' tir' : ''
}
return ''
}
static calculBaseInit(actor, categorie) {
const mapping = MAPPING_BASE.find(it => it.column == categorie)
if (mapping) {
switch (categorie) {
case 'melee':
case 'tir':
case 'lancer':
const caracteristique = Number(actor.system.carac[categorie].value)
return Math.floor(caracteristique / 2)
}
}
return 0
}
static prepareArmure(actor) {
const armures = actor.itemTypes[ITEM_TYPES.armure].filter(it => it.system.equipe)
if (armures.length > 1) {
console.warn(`${actor.name} a équipé ${armures.length} armures, seule la première sera considérée`)
}
if (armures.length > 0) {
const armure = armures[0]
return {
name: armure.name,
protection: armure.system.protection,
malus: armure.system.malus ?? 0
}
}
return {
name: '',
protection: actor.system.attributs.protection.value,
malus: 0
}
}
static prepareEsquive(actor) {
const esquives = actor.getCompetences("Esquive")
if (esquives.length > 0) {
const esquive = esquives[0]
return {
name: esquive.name,
niveau: esquive.system.niveau,
competence: esquive
}
}
return undefined
}
static prepareSorts(actor) {
const codeVoies = Mapping.getCompetencesCategorie(actor, CATEGORIES_DRACONIC)
.map(it => RdDItemSort.getVoieCode(it))
return actor.itemTypes[ITEM_TYPES.sort].map(it => Mapping.prepareSort(it, codeVoies))
.sort(Misc.ascending(it => `${it.voie} : ${it.description}`))
}
static prepareSort(sort, voies) {
return {
voie: RdDItemSort.getCodeDraconic(sort, voies),
description: Mapping.descriptionSort(sort),
bonus: Mapping.bonusCase(sort)
}
}
static descriptionSort(sort) {
const ptSeuil = Array(sort.system.coutseuil).map(it => '*')
const caseTMR = sort.system.caseTMRspeciale.length > 0 ? Mapping.toVar(sort.system.caseTMRspeciale) : Misc.upperFirst(TMRType[sort.system.caseTMR].name)
const ptreve = Mapping.addSpaceToNonNumeric(sort.system.ptreve)
const diff = Mapping.addSpaceToNonNumeric(sort.system.difficulte)
return `${sort.name}${ptSeuil} (${caseTMR}) R${diff} r${ptreve}`
}
static addSpaceToNonNumeric(value) {
return Number.isNumeric(value) ? value : ' ' + Mapping.toVar(value)
}
static toVar(value) {
return value.replace('variable', 'var')
}
static bonusCase(sort) {
const list = RdDItemSort.bonuscaseStringToList(sort.system.bonuscase).sort(Misc.descending(it => it.bonus))
if (list.length > 0) {
const bonus = list[0]
return `+${bonus.bonus}% en ${bonus.case}`
}
return ''
}
static getDescription(actor) {
const sexe = actor.system.sexe
const sexeFeminin = sexe.length > 0 && sexe.charAt(0).toLowerCase() == 'f' ? 'Née' : 'Né'
const race = ['', 'humain'].includes(Grammar.toLowerCaseNoAccent(actor.system.race)) ? '' : (actor.system.race + ' ')
const heure = actor.system.heure
const hn = `${sexeFeminin} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
const age = actor.system.age ? `${actor.system.age} ans` : undefined
const taille = actor.system.taille
const poids = actor.system.poids
const cheveux = actor.system.cheveux ? `cheveux ${actor.system.cheveux}` : undefined
const yeux = actor.system.yeux ? `yeux ${actor.system.yeux}` : undefined
const beaute = actor.system.beaute ? `beauté ${actor.system.beaute}` : undefined
const list = [race, hn, age, taille, poids, cheveux, yeux, beaute]
return Misc.join(list.filter(it => it), ', ')
}
static getArmure(actor, context) {
return context.armure?.name ?? ''
}
static getProtectionArmure(actor, context) {
const naturelle = Number(actor.system.attributs.protection.value)
if (context?.armure?.protection == undefined) {
return naturelle
}
if (Number.isNumeric(context?.armure?.protection)) {
return Number(context?.armure?.protection ?? 0) + naturelle
}
return context?.armure.protection + (naturelle > 0 ? `+${naturelle}` : '')
}
static getMalusArmure(actor, context) {
return context?.armure?.malus ?? 0
}
static getEsquive(context) {
if (context.esquive) {
return Misc.toSignedString(context.esquive.niveau)
}
return ''
}
static getEsquiveArmure(context) {
if (context.esquive) {
const niveau = context.esquive.niveau + context.armure.malus
return Misc.toSignedString(niveau)
}
return ''
}
static getCompetences(actor, categories) {
const competences = Mapping.getCompetencesCategorie(actor, categories)
if (competences.length == 0) {
return ''
}
const byCategories = Mapping.competencesByCategoriesByNiveau(competences, categories)
const txtByCategories = Object.values(byCategories)
.map(it => it.competencesParNiveau)
.map(byNiveau => {
const niveaux = Object.keys(byNiveau)
.map(it => Number(it)).sort(Misc.ascending())
if (niveaux.length == 0) {
return ''
}
const txtCategorieByNiveau = niveaux.map(niveau => {
const names = Misc.join(byNiveau[niveau].map(it => it.name).sort(Misc.ascending()), ', ')
return names + ' ' + Misc.toSignedString(niveau)
})
const txtCategorie = Misc.join(txtCategorieByNiveau, ' / ')
return txtCategorie
}).filter(it => it != '')
return Misc.join(txtByCategories, ' / ')
}
static competencesByCategoriesByNiveau(competences, categories) {
return categories.map(c => {
return {
categorie: c,
competencesParNiveau: Misc.classify(
competences.filter(comp => comp.system.categorie == c),
comp => comp.system.niveau)
}
})
}
static getArme(actor, context, part, numero) {
if (numero < context.armes.length) {
return context.armes[numero][part] ?? ''
}
return ''
}
static getCompetencesCategorie(actor, categories) {
return actor.itemTypes[ITEM_TYPES.competence]
.filter(it => categories.includes(it.system.categorie))
.filter(it => !RdDItemCompetence.isNiveauBase(it))
}
static getSort(actor, context, part, numero) {
if (numero < context.sorts.length) {
return context.sorts[numero][part]
}
return ''
}
}

View File

@ -1,188 +0,0 @@
import { SHOW_DICE } from "../../constants.js";
import { Misc } from "../../misc.js";
import { RdDCarac } from "../../rdd-carac.js";
import { RdDDice } from "../../rdd-dice.js";
import { RdDNameGen } from "../../rdd-namegen.js";
import { RdDTimestamp } from "../../time/rdd-timestamp.js";
const PATHS = [
'name',
'system.sexe',
'system.age',
'system.taille',
'system.poids',
'system.main',
'system.heure',
'system.cheveux',
'system.yeux'
]
const RANDOM_VALUES = {
'system.sexe': { 'masculin': 1, 'féminin': 1 },
'system.main': { 'droitier': 51, 'gaucher': 15, 'ambidectre': 6 },
'system.cheveux': { 'noirs': 2, 'bruns': 5, 'châtains clair': 5, 'blonds': 4, 'blonds très clair': 1, 'roux carotte': 1, 'roux cuivré': 3 },
'system.yeux': { 'noirs': 2, 'noisettes': 3, 'bruns vert': 4, 'verts': 3, 'bleus clair': 3, 'bleus gris': 2, 'gris': 1, 'mauves': 1, 'indigos': 1 },
}
export class AppPersonnageAleatoire extends FormApplication {
static preloadHandlebars() {
loadTemplates([
'systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs',
])
}
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/actor/random/app-personnage-aleatoire.hbs",
title: "Génération aléatoire",
width: 'fit-content',
height: 'fit-content',
classes: ['app-personnage-aleatoire'],
popOut: true,
resizable: true
}, { inplace: false })
}
constructor(actor) {
super({})
this.actor = actor
this.current = foundry.utils.duplicate(actor)
this.checked = {
'name': false,
'system.sexe': true,
'system.age': true,
'system.taille': true,
'system.poids': true,
'system.main': true,
'system.heure': true,
'system.cheveux': true,
'system.yeux': true
}
}
async getData(options) {
return foundry.utils.mergeObject(await super.getData(options), {
actor: this.actor,
current: this.current,
checked: this.checked,
options: { isGM: game.user.isGM }
})
}
activateListeners(html) {
super.activateListeners(html)
this.html = html
this.html.find("button.button-cancel").click(async event => await this.close())
this.html.find("button.button-apply").click(async event => await this.onApply())
this.html.find("input.current-value").change(async event => await this.onChange(event))
this.html.find("div.random-field[data-path='system.heure'] select.current-value").change(async event => await this.onChange(event))
this.html.find("a.random").click(async event => await this.onRandom(event))
this.html.find("a.reset").click(async event => await this.onReset(event))
this.html.find("a.randomize-selected").click(async event => await this.onRandomizeSelected())
this.html.find("input.check-for-random").click(async event => await this.onCheckForRandom(event))
}
async _updateObject(event, formData) { }
async onApply() {
const updates = Object.fromEntries(
PATHS.filter(path => game.user.isGM || path != 'name')
.map(path => [path, this.current[path]])
)
await this.actor.update(updates)
await this.close()
}
getPath(selector) {
const fields = this.html.find(selector).parents("div.random-field:first")
return fields[0].attributes['data-path'].value
}
async onChange(event) {
const path = this.getPath(event.currentTarget)
this.current[path] = event.currentTarget.value
}
async onRandom(event) {
const path = this.getPath(event.currentTarget)
await this.setRandom(path);
this.render()
}
async onReset(event) {
const path = this.getPath(event.currentTarget)
this.current[path] = this.actor[path]
await this.render()
}
async onCheckForRandom(event) {
const path = this.getPath(event.currentTarget)
this.checked[path] = event.currentTarget.checked
this.render()
}
async onRandomizeSelected() {
const paths = this.html.find("input.check-for-random:checked")
.parents("div.random-field")
.toArray()
.map(it => it.attributes['data-path'].value)
await Promise.all(paths.map(path => this.setRandom(path)))
this.render()
}
async setRandom(path) {
this.current[path] = await this.random(path);
}
async random(path) {
switch (path) {
case 'name':
return await RdDNameGen.generate()
case 'system.sexe':
case 'system.main':
case 'system.cheveux':
case 'system.yeux':
return await this.randomFromMap(RANDOM_VALUES[path])
case 'system.poids':
return await this.randomPoids()
case 'system.taille':
return await this.randomTaille()
case 'system.age':
return await RdDDice.rollTotal('(2d4kl)*10 + 1d7xo + 2d20kl')
case 'system.heure':
return RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "selfroll", showDice: SHOW_DICE })).key
}
return 'unknown'
}
async randomFromMap(valuesMap) {
const max = Object.values(valuesMap).reduce(Misc.sum(), 0)
const total = await RdDDice.rollTotal(`1d${max}`)
let sum = 0
for (let entry of Object.entries(valuesMap)) {
sum = sum + entry[1]
if (sum >= total) {
return entry[0]
}
}
return Object.keys(valuesMap)[0]
}
async randomPoids() {
const caracTaille = RdDCarac.getCaracDerivee(this.current.system.carac.taille.value)
const range = caracTaille.poidsMax - caracTaille.poidsMin + 1
const total = await RdDDice.rollTotal(`1d${range} + ${caracTaille.poidsMin}`)
return total + ' kg'
}
async randomTaille() {
const caracTaille = RdDCarac.getCaracDerivee(this.current.system.carac.taille.value)
const base = this.current.system.carac.taille.value * 2 + 60 + caracTaille.poidsMin
const variation = Math.floor((caracTaille.poidsMax - caracTaille.poidsMin + base / 5) / 2)
const total = await RdDDice.rollTotal(`2d${variation} + ${base}`)
const cm = total % 100
const m = (total - cm) / 100
return `${m}m${cm}`
}
}

View File

@ -1,433 +0,0 @@
/************************************************************************************/
import "./xregexp-all.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { RdDBaseActorReve } from "../actor/base-actor-reve.js";
/************************************************************************************/
// Some internal test strings
let statBlock01 = `+$16(/, baron de Sylvedire, né à lheure du
Roseau, 40 ans, 1m78, 65 kg, Beauté 13.
TAILLE
10
Mêlée
14
APPARENCE
13
Tir
11
CONSTITUTION
12
Lancer
11
FORCE
12
Dérobée
13
AGILITÉ
16
Vie
11
DEXTÉRITÉ
13
Endurance
25
VUE
10
+dom
0
OUÏE
11
Protection
2 ou 4
ODO-GOÛT
9
cuir souple
VOLONTÉ
14
ou cuir / métal
INTELLECT
9
EMPATHIE
11
RÊVE
13
CHANCE
10
niv
init
+dom
Épée dragonne
+5
12
+3
Hache de bataille
+6
13
+3
Bouclier moyen
+5
Dague mêlée
+4
11
+1
Corps à corps
+4
11
(0)
Esquive
+8
Escalade +4 / Saut +5 / Commerce +3 / Équitation
+6 / Chirurgie 0 / Survie en extérieur +4 / Survie fo-
rêt +6 / Acrobatie -2 / Métallurgie +2 / Natation +3 /
Légendes -1 / Écriture -4
`;
let statBlock02 = `/HVJDUGHV
TAILLE
11
Mêlée
12
CONSTITUTION
11
Tir
11
FORCE
12
Lancer
11
AGILITÉ
12
Dérobée
11
DEXTERITÉ
11
Vie
11
VUE
11
Endurance
22
OUÏE
11
Vitesse
12
VOLONTÉ
10
+dom
0
Protection
4
cuir / métal
niv
init
+dom
Hache de bataille
+4
10
+3
Bouclier moyen
+4
Dague mêlée
+3
9
+1
Arc
+5
10
+2
Corps à corps
+3
9
(0)
Esquive avec armure
+2
Course +1/ Vigilance +4
`;
let statBlock03 = `rencontres sont laissées à /HVFKLHQVORXSVGXEDURQ
chaque gardien des rêves.
TAILLE
8
Vie
10
CONSTITUTION FORCE
12
11
Endurance
Vitesse
12/38
21
/HVFKLHQV]RPELV
PERCEPTION 13
+dom
0
VOLONTÉ
10
Protection
0
Les « monstres » apparaîtront un soir, durant
RÊVE
10
lheure du Serpent, et attaqueront les voya-
niv
init
+dom
geurs à leur campement. Si ces derniers ne
Morsure
13
+4
10
+1
campent pas, ils apparaîtront tout de même à
Esquive
11
+3
lheure du Serpent. Le feu ne les effraie pas. Ils
Course, Saut
12
+3
ne sont pas très rapides, mais en revanche, très
Discrétion
12
+3
silencieux : ils naboient pas. Les voyageurs
Vigilance
13
+3
`
// Skill parser depending on the type of actor
const compParser = { personnage: "\\s+(?<value>[\\+\\-]?\\d+)", creature: "\\s+(?<carac>\\d+)\\s+(?<value>[\\+\\-]?\\d+)\\s?(?<init>\\d+)?\\s+?(?<dommages>\\+\\d+)?" };
// Main class for parsing a stat block
export class RdDStatBlockParser {
static openInputDialog() {
let dialog = new Dialog({
title: "Import de stats de PNJ/Créatures",
content: `
<div>
<p>Coller le texte de la stat ici</p>
<textarea id="statBlock" style="width: 100%; height: 200px;"></textarea>
</div>
`,
buttons: {
ok: {
label: "OK",
callback: async (html) => {
let statBlock = html.find("#statBlock")[0].value;
await RdDStatBlockParser.parseStatBlock(statBlock);
dialog.close();
}
},
cancel: {
label: "Cancel"
}
}
});
dialog.render(true);
}
static fixWeirdPDF(statString) {
// Split the statString into lines
let lines = statString.split("\n");
let newLines = [];
let index = 0;
let nextType = "string";
// Loop through each line
for (let i = 0; i < lines.length; i++) {
// remove trailing spaces
lines[i] = lines[i].trim();
// Is it text ?
if (lines[i].match(/^[a-zA-Zéêè\s]+/)) {
if ( nextType == "string" ) {
newLines[index] = lines[i];
nextType = "number";
} else {
console.log("Wrong sequence string detected...", lines[i], nextType);
}
}
// Is it a number ?
if (lines[i].match(/^[\d\s]+/)) {
if ( nextType == "number" ) {
newLines[index] = newLines[index] + lines[i];
nextType = "string";
index++;
} else {
console.log("Wrong sequence number detected...", lines[i], nextType);
}
}
}
}
static async parseStatBlock(statString, type = "npc") {
//statString = statBlock03;
if (!statString) {
return;
}
// Special function to fix strange/weird copy/paste from PDF readers
// Unused up to now : this.fixWeirdPDF(statString);
// Replace all endline by space in the statString
statString = statString.replace(/\n/g, " ");
// Remove all multiple spaces
statString = statString.replace(/\s{2,}/g, " ");
// Remove all leading and trailing spaces
statString = statString.trim();
let actorType = "personnage";
let perception = XRegExp.exec(statString.toLowerCase(), XRegExp("perception\\s+(?<value>\\d+)", 'gi'));
if (perception?.value ) {
actorType = "creature";
}
// Now start carac
let actorData = foundry.utils.deepClone(game.model.Actor[actorType]);
for (let key in game.model.Actor.personnage.carac) {
let caracDef = game.model.Actor.personnage.carac[key];
// Parse the stat string for each caracteristic
let carac = XRegExp.exec(statString.toLowerCase(), XRegExp(caracDef.label.toLowerCase()+"\\s+(?<value>\\d+)", 'gi'));
if (carac?.value) {
actorData.carac[key].value = Number(carac.value);
}
}
// If creature we need to setup additionnal fields
if (actorType == "creature") {
let plusDom = XRegExp.exec(statString.toLowerCase(), XRegExp("\\+dom\\s+(?<value>\\+\\d+)", 'gi'));
if (plusDom?.values) {
actorData.attributs.plusdom.value = Number(plusDom.value);
}
let protection = XRegExp.exec(statString.toLowerCase(), XRegExp("protection\\s+(?<value>\\d+)", 'gi'));
if (protection?.value) {
actorData.attributs.protection.value = Number(protection.value);
}
let endurance = XRegExp.exec(statString.toLowerCase(), XRegExp("endurance\\s+(?<value>\\d+)", 'gi'));
if (endurance?.value) {
actorData.sante.endurance.value = Number(endurance.value);
actorData.sante.endurance.max = Number(endurance.value);
}
let vie = XRegExp.exec(statString.toLowerCase(), XRegExp("vie\\s+(?<value>\\d+)", 'gi'));
if (vie.value) {
actorData.sante.vie.value = Number(vie.value);
actorData.sante.vie.max = Number(vie.value);
}
let vitesse = XRegExp.exec(statString.toLowerCase(), XRegExp("vitesse\\s+(?<value>[\\d\\/]+)", 'gi'));
if (vitesse?.value) {
actorData.attributs.vitesse.value = vitesse.value;
}
}
let items = [];
// Get skills from compendium
const competences = await SystemCompendiums.getCompetences(actorType);
//console.log("Competences : ", competences);
let allComp = competences.map(i => i.toObject())
for (let comp of allComp) {
let skill = XRegExp.exec(statString.toLowerCase(), XRegExp(comp.name.toLowerCase()+compParser[actorType], 'gi'));
if (skill) {
comp.system.niveau = Number(skill.value);
if (actorType == "creature") {
comp.system.carac_value = Number(skill.carac);
if (skill.init) {
comp.system.dommages = Number(skill.dommages);
comp.system.iscombat = true;
}
items.push(comp); // Only selective push
}
}
if (actorType == "personnage") {
items.push(comp); // Always push
}
}
// Now process weapons
const weapons = await SystemCompendiums.getWorldOrCompendiumItems("arme", "equipement")
//console.log("Equipement : ", equipment);
for (let w of weapons) {
let weapon = XRegExp.exec(statString.toLowerCase(), XRegExp(w.name.toLowerCase()+"\\s+(?<value>\\+\\d+)", 'gi'));
if (weapon) {
w.system.equipe = true
items.push(w.toObject());
// now process the skill
if ( w.system?.competence != "") {
let wComp = items.find(i => i.name.toLowerCase() == w.system.competence.toLowerCase());
if (wComp) {
wComp.system.niveau = Number(weapon.value);
}
}
if ( w.system?.tir != "") {
let wComp = items.find(i => i.name.toLowerCase() == w.system.tir.toLowerCase());
if (wComp) {
wComp.system.niveau = Number(weapon.value);
}
}
if ( w.system?.lancer != "") {
let wComp = items.find(i => i.name.toLowerCase() == w.system.lancer.toLowerCase());
if (wComp) {
wComp.system.niveau = Number(weapon.value);
}
}
}
}
// Now process armors
const armors = await SystemCompendiums.getWorldOrCompendiumItems("armure", "equipement")
for (let a of armors) {
let armor = XRegExp.exec(statString.toLowerCase(), XRegExp(a.name.toLowerCase(), 'gi'));
if (armor) {
a.system.equipe = true
items.push(a.toObject());
}
}
// Get hour name : heure du XXXXX
let heure = XRegExp.exec(statString.toLowerCase(), XRegExp("heure du\\s+(?<value>\\w+)", 'gi'));
if (heure?.value) {
actorData.heure = heure.value;
}
// Get age
let age = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>\\d+) ans", 'gi'));
if (age?.value) {
actorData.age = Number(age.value);
}
// Get height
let taille = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>\\d+)m\\d+", 'gi'));
if (taille?.value) {
actorData.taille = taille.value;
}
// Get weight
let poids = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>\\d+) kg", 'gi'));
if (poids?.value) {
actorData.poids = poids.value;
}
// Get beauty
let beaute = XRegExp.exec(statString.toLowerCase(), XRegExp("beauté\\s+(?<value>\\d+)", 'gi'));
if (beaute?.value) {
actorData.beaute = Number(beaute.value);
}
// Name is all string before ', né'
let name
if (actorType == "personnage") {
name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>[\\w\\s\\d]+),", 'gi'));
if (!name?.value) {
name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>.+)\\s+taille", 'gi'));
}
name = name?.value || "Importé";
}
if (actorType == "creature") {
name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>.+)\\s+taille", 'gi'));
name = name?.value || "Importé";
}
let newActor = RdDBaseActorReve.create({name: name || "Importé", type:actorType, system: actorData, items: items});
// DUmp....
console.log(actorData);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,20 +8,15 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
*/
export class ChatUtility {
static async init() {
Hooks.on("renderChatMessage", async (app, html, msg) => await ChatUtility.onRenderChatMessage(app, html, msg))
Hooks.on("createChatMessage", async (chatMessage, options, id) => await ChatUtility.onCreateChatMessage(chatMessage, options, id))
}
/* -------------------------------------------- */
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
case "msg_gm_chat_message": return ChatUtility.handleGMChatMessage(sockmsg.data)
case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.data)
case "msg_user_ui_notifications": return ChatUtility.onNotifyUser(sockmsg.data)
case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.data);
case "msg_user_ui_notifications": return ChatUtility.onNotifyUser(sockmsg.data);
}
}
/* -------------------------------------------- */
static notifyUser(userId, level = 'info', message) {
const socketData = {
@ -49,7 +44,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static onRemoveMessages(socketData) {
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
if (socketData.part) {
const toDelete = game.messages.filter(it => it.content.includes(socketData.part));
toDelete.forEach(it => it.delete());
@ -63,7 +58,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static removeMessages(socketData) {
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
ChatUtility.onRemoveMessages(socketData);
}
else {
@ -83,90 +78,73 @@ export class ChatUtility {
}
/* -------------------------------------------- */
static async createChatWithRollMode(messageData, actor = undefined) {
switch (game.settings.get("core", "rollMode")) {
static async createChatWithRollMode(name, chatOptions) {
let rollMode = game.settings.get("core", "rollMode")
switch (rollMode) {
case "blindroll": // GM only
if (!game.user.isGM) {
ChatUtility.blindMessageToGM(messageData)
messageData.whisper = [game.user];
messageData.content = "Message envoyé en aveugle au Gardien"
ChatUtility.blindMessageToGM(chatOptions);
chatOptions.whisper = [game.user.id];
chatOptions.content = "Message envoyé en aveugle au Gardien";
}
else {
messageData.whisper = ChatUtility.getGMs()
chatOptions.whisper = ChatUtility.getUsers(user => user.isGM);
}
break
case "gmroll":
messageData.whisper = ChatUtility.getOwners(actor)
break
case "selfroll":
messageData.whisper = [game.user]
break
break;
default:
chatOptions.whisper = ChatUtility.getWhisperRecipients(rollMode, name);
break;
}
messageData.alias = messageData.alias ?? actor?.name ?? game.user.name
return await ChatMessage.create(messageData)
}
static getOwners(document) {
return game.users.filter(it => document.getUserLevel(it) == CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
}
static getUserAndGMs() {
return [game.user, ...ChatUtility.getGMs()]
chatOptions.alias = chatOptions.alias || name;
return await ChatMessage.create(chatOptions);
}
/* -------------------------------------------- */
static getMultipleActorsOwners(...actors) {
return Misc.concat(actors.map(it => it == undefined ? [] : ChatUtility.getOwners(it)))
static prepareChatMessage(rollMode, name) {
return {
user: game.user.id,
whisper: ChatUtility.getWhisperRecipients(rollMode, name)
}
}
/* -------------------------------------------- */
static getWhisperRecipients(rollMode, name) {
switch (rollMode) {
case "blindroll": return ChatUtility.getUsers(user => user.isGM);
case "gmroll": return ChatUtility.getWhisperRecipientsAndGMs(name);
case "selfroll": return [game.user.id];
}
return undefined;
}
/* -------------------------------------------- */
static getWhisperRecipientsAndGMs(...names) {
let recipients = [...ChatMessage.getWhisperRecipients('GM')]
names.forEach(name => recipients.push(...ChatMessage.getWhisperRecipients(name)))
return recipients
}
/* -------------------------------------------- */
static getUsers(filter) {
return game.users.filter(filter)
}
static getGMs() {
return game.users.filter(user => user.isGM)
}
static applyRollMode(chatMessageData = {}, rollMode = game.settings.get("core", "rollMode")) {
switch (rollMode) {
case "blindroll":
chatMessageData.blind = true
chatMessageData.whisper = ChatUtility.getGMs()
break
case "gmroll":
chatMessageData.whisper = ChatUtility.getGMs()
chatMessageData.blind = false
break
case "roll":
chatMessageData.whisper = ChatUtility.getUsers(user => user.active)
chatMessageData.blind = false
break
case "selfroll":
chatMessageData.whisper = [game.user]
chatMessageData.blind = false
break
}
return chatMessageData
return game.users.filter(filter).map(user => user.id);
}
/* -------------------------------------------- */
static blindMessageToGM(chatOptions) {
const chatGM = foundry.utils.duplicate(chatOptions)
chatGM.content = "Message aveugle de " + game.user.name + "<br>" + chatOptions.content
console.log("blindMessageToGM", chatGM)
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_gm_chat_message", data: chatGM })
let chatGM = foundry.utils.duplicate(chatOptions);
chatGM.whisper = ChatUtility.getUsers(user => user.isGM);
chatGM.content = "Message aveugle de " + game.user.name + "<br>" + chatOptions.content;
console.log("blindMessageToGM", chatGM);
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_gm_chat_message", data: chatGM });
}
/* -------------------------------------------- */
static handleGMChatMessage(socketData) {
console.log("blindMessageToGM", socketData);
if (Misc.isFirstConnectedGM()) {
ChatMessage.create({
user: game.user.id,
whisper: ChatUtility.getGMs(),
content: socketData.content
})
if (game.user.isGM) { // message privé pour GM only
socketData.user = game.user.id;
ChatMessage.create(socketData);
}
}

View File

@ -30,8 +30,7 @@ export class RdDCoeur {
}
static extractInfoCoeur(event) {
const chatMesage = ChatUtility.getChatMessage(event);
return ChatUtility.getMessageData(chatMesage, INFO_COEUR)
return ChatUtility.getMessageData(ChatUtility.getChatMessage(event), INFO_COEUR)
}
static getInfoCoeur(sourceActorId, targetActorId) {
@ -99,11 +98,12 @@ export class RdDCoeur {
static async startSubActeurTendreMoment(actorId, subActeurId) {
const infoCoeur = RdDCoeur.getInfoCoeur(actorId, subActeurId)
if (infoCoeur.target?.actor?.id) {
if (infoCoeur.target?.actor.id) {
// TODO: passer par une fenêtre pour saisir sa proposition (lieu, heure, ...)
const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-proposer-tendre-moment.hbs`, infoCoeur)
const chatMessage = await ChatMessage.create({
whisper: ChatUtility.getOwners(infoCoeur.target.actor),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-proposer-tendre-moment.hbs`, infoCoeur)
whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.target?.actor.name),
content: chatHtml
})
RdDCoeur.addTagsInfoCoeur(infoCoeur, chatMessage)
}
@ -127,7 +127,7 @@ export class RdDCoeur {
}
const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-accepter-tendre-moment.hbs`, infoCoeur)
const chatMessage = await ChatMessage.create({
whisper: ChatUtility.getMultipleActorsOwners(infoCoeur.source?.actor, infoCoeur.target?.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.source?.actor.name, infoCoeur.target?.actor.name),
content: chatHtml
})
RdDCoeur.addTagsInfoCoeur(infoCoeur, chatMessage)
@ -142,7 +142,7 @@ export class RdDCoeur {
ChatUtility.removeChatMessageId(infoCoeur.chatMessageId)
const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-refuser-tendre-moment.hbs`, infoCoeur)
await ChatMessage.create({
whisper: ChatUtility.getMultipleActorsOwners(infoCoeur.source?.actor, infoCoeur.target?.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.source?.actor.name, infoCoeur.target?.actor.name),
content: chatHtml
});
}

View File

@ -8,7 +8,7 @@ const LATEST_USED_JOURNAL_ID = "chronologie-dernier-journal";
export class DialogChronologie extends Dialog {
static initSettings() {
static init() {
game.settings.register(SYSTEM_RDD, LATEST_USED_JOURNAL_ID, {
name: "Dernier article de journal utilisé pour enregistrer la chronologie",
scope: "client",

View File

@ -24,7 +24,7 @@ export class DialogCreateSigneDraconique extends Dialog {
}
constructor(dialogData, html) {
let options = { classes: ["DialogCreateSigneDraconiqueActors"], width: 500, height: 650, 'z-index': 99999 };
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 500, height: 650, 'z-index': 99999 };
let conf = {
title: "Créer un signe",
content: html,
@ -48,7 +48,7 @@ export class DialogCreateSigneDraconique extends Dialog {
async _createSigneForActor(actor, signe) {
actor.createEmbeddedDocuments("Item", [signe]);
ChatMessage.create({
whisper: ChatUtility.getOwners(actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name),
content: await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-actor.html", {
signe: signe,
alias: actor.name

View File

@ -8,13 +8,14 @@ import { RdDUtility } from "./rdd-utility.js";
export class DialogValidationEncaissement extends Dialog {
static async validerEncaissement(actor, rollData, armure, onEncaisser) {
const encaissement = await RdDUtility.jetEncaissement(actor, rollData, armure, { showDice: HIDE_DICE });
let encaissement = await RdDUtility.jetEncaissement(rollData, armure, { showDice: HIDE_DICE });
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.html', {
actor: actor,
rollData: rollData,
encaissement: encaissement
});
new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser).render(true);
const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser);
dialog.render(true);
}
/* -------------------------------------------- */
@ -55,14 +56,14 @@ export class DialogValidationEncaissement extends Dialog {
this.html = html;
this.html.find('input.encaissement-roll-result').keyup(async event => {
this.forceDiceResult.total = event.currentTarget.value;
this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.rollData, this.armure, { showDice: HIDE_DICE, forceDiceResult: this.forceDiceResult});
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: HIDE_DICE, forceDiceResult: this.forceDiceResult});
this.html.find('label.encaissement-total').text(this.encaissement.total);
this.html.find('label.encaissement-blessure').text(this.encaissement.blessures)
});
}
async onValider() {
this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
this.onEncaisser(this.encaissement)
}
}

View File

@ -7,7 +7,7 @@ import { CompendiumTableHelpers, CompendiumTable, SystemCompendiums } from "./se
const COMPENDIUMS_RECHERCHE = 'compendiums-recherche';
export class Environnement {
static initSettings() {
static init() {
game.settings.register(SYSTEM_RDD, COMPENDIUMS_RECHERCHE, {
name: COMPENDIUMS_RECHERCHE,
default: [

View File

@ -1,5 +1,5 @@
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
import { ITEM_TYPES } from "./item.js";
import { TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.js";
const nomCategorieParade = {
@ -20,35 +20,19 @@ const nomCategorieParade = {
export class RdDItemArme extends Item {
static isArme(item) {
return item.type == ITEM_TYPES.arme || RdDItemCompetenceCreature.getCategorieAttaque(item);
return item.type == TYPES.arme || RdDItemCompetenceCreature.getCategorieAttaque(item);
}
/* -------------------------------------------- */
static getArme(arme) {
switch (arme ? arme.type : '') {
case ITEM_TYPES.arme: return arme;
case ITEM_TYPES.competencecreature:
case TYPES.arme: return arme;
case TYPES.competencecreature:
return RdDItemCompetenceCreature.armeCreature(arme);
}
return RdDItemArme.mainsNues();
}
static getCompetenceArme(arme, maniement) {
switch (arme.type) {
case ITEM_TYPES.competencecreature:
return arme.name
case ITEM_TYPES.arme:
switch (maniement) {
case 'competence': return arme.system.competence;
case 'unemain': return RdDItemArme.competence1Mains(arme);
case 'deuxmains': return RdDItemArme.competence2Mains(arme);
case 'tir': return arme.system.tir;
case 'lancer': return arme.system.lancer;
}
}
return undefined
}
static computeNiveauArmes(armes, competences) {
for (const arme of armes) {
arme.system.niveau = RdDItemArme.niveauCompetenceArme(arme, competences);
@ -81,59 +65,37 @@ export class RdDItemArme extends Item {
/* -------------------------------------------- */
static getCategorieParade(armeData) {
if (armeData.system.categorie_parade) {
return armeData.system.categorie_parade
return armeData.system.categorie_parade;
}
// pour compatibilité avec des personnages existants
if (armeData.type == ITEM_TYPES.competencecreature || armeData.system.categorie == 'creature') {
return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '')
if (armeData.type == TYPES.competencecreature || armeData.system.categorie == 'creature') {
return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '');
}
if (!armeData.type.match(/arme|competencecreature/)) {
return ''
return '';
}
if (armeData.system.competence == undefined) {
return ITEM_TYPES.competencecreature;
return TYPES.competencecreature;
}
let compname = armeData.system.competence.toLowerCase();
if (compname.match(/^(dague de jet|javelot|fouet|arc|arbalête|fronde|hache de jet|fléau)$/)) {
return ''
}
if (compname.match(/^(dague de jet|javelot|fouet|arc|arbalête|fronde|hache de jet|fléau)$/)) return '';
if (compname.match('hache')) return 'haches'
if (compname.match('hast')) return 'hast'
if (compname.match('lance')) return 'lances'
if (compname.match('bouclier')) return 'boucliers'
if (compname.match('masse')) return 'masses'
if (compname.match('hache')) return 'haches';
if (compname.match('hast')) return 'hast';
if (compname.match('lance')) return 'lances';
if (compname.match('bouclier')) return 'boucliers';
if (compname.match('masse')) return 'masses';
if (compname.match('epée') || compname.match('épée')) {
if (armeData.name.toLowerCase().match(/(gnome)/))
return 'epees-courtes'
return 'epees-courtes';
if (armeData.name.toLowerCase().match(/((e|é)pée dragone|esparlongue|demi-dragonne)/))
return 'epees-longues'
return 'epees-lourdes'
return 'epees-longues';
return 'epees-lourdes';
}
if (compname.match('dague')) {
return 'dagues'
}
return 'sans-armes'
}
static defenseArmeParade(armeAttaque, armeParade) {
const defCategory = RdDItemArme.getCategorieParade(armeParade)
if (defCategory == 'bouclier') {
return 'norm'
}
if (armeAttaque.system.competence.toLowerCase().match(/(fléau)/)) {
return ''
}
if (armeParade.system.tir) {
return ''
}
const attCategory = RdDItemArme.getCategorieParade(armeAttaque)
switch (attCategory) {
case 'armes-naturelles': case 'sans-armes':
return defCategory == 'sans-armes' ? 'norm' : ''
default:
return RdDItemArme.needParadeSignificative(armeAttaque, armeParade) ? 'sign' : 'norm'
return 'dagues';
}
return 'sans-armes';
}
/* -------------------------------------------- */
@ -142,8 +104,8 @@ export class RdDItemArme extends Item {
return false;
}
// categories d'armes à la parade (cf. page 115 )
const attCategory = RdDItemArme.getCategorieParade(armeAttaque)
const defCategory = RdDItemArme.getCategorieParade(armeParade)
let attCategory = RdDItemArme.getCategorieParade(armeAttaque);
let defCategory = RdDItemArme.getCategorieParade(armeParade);
// bouclier et mêmes catégorie: peuvent se parer sans difficulté
if (defCategory == 'boucliers') {
return false;
@ -170,66 +132,43 @@ export class RdDItemArme extends Item {
return true;
}
static dommagesReels(arme, maniement) {
switch (maniement) {
case 'tir':
case 'lancer':
case 'competence':
return Number(arme.system.dommages)
}
if (arme.system.unemain && arme.system.deuxmains) {
const containsSlash = !Number.isInteger(arme.system.dommages) && arme.system.dommages.includes("/")
if (!containsSlash) {
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)");
return Number(arme.system.dommages)
}
const tableauDegats = arme.system.dommages.split("/");
return Number(tableauDegats[maniement == 'unemain' ? 0 : 1])
}
return Number(arme.system.dommages);
}
/* -------------------------------------------- */
static armeUneOuDeuxMains(arme, aUneMain) {
if (arme && !arme.system.cac) {
arme = foundry.utils.duplicate(arme);
arme.system.dommagesReels = RdDItemArme.dommagesReels(arme, aUneMain ? 'unemain' : 'deuxmains')
static armeUneOuDeuxMains(armeData, aUneMain) {
if (armeData && !armeData.system.cac) {
armeData.system.unemain = armeData.system.unemain || !armeData.system.deuxmains;
const uneOuDeuxMains = armeData.system.unemain && armeData.system.deuxmains;
const containsSlash = !Number.isInteger(armeData.system.dommages) && armeData.system.dommages.includes("/");
if (containsSlash) { // Sanity check
armeData = foundry.utils.duplicate(armeData);
const tableauDegats = armeData.system.dommages.split("/");
if (aUneMain)
armeData.system.dommagesReels = Number(tableauDegats[0]);
else // 2 mains
armeData.system.dommagesReels = Number(tableauDegats[1]);
}
else {
armeData.system.dommagesReels = Number(armeData.system.dommages);
}
if (uneOuDeuxMains != containsSlash) {
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + armeData.name + " ne sont pas corrects (ie sous la forme X/Y)");
}
}
return arme;
return armeData;
}
static competence2Mains(arme) {
return arme.system.competence.replace(" 1 main", " 2 mains");
}
static competence1Mains(arme) {
return arme.system.competence.replace(" 2 mains", " 1 main");
}
static competence2Mains(arme) {
return arme.system.competence.replace(" 1 main", " 2 mains");
}
static isUtilisable(arme) {
static isArmeUtilisable(arme) {
switch (arme.type) {
case ITEM_TYPES.arme: return arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0)
case ITEM_TYPES.competencecreature: return true
}
return false
}
static isAttaque(arme) {
switch (arme.type) {
case ITEM_TYPES.arme:
return arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0)
case ITEM_TYPES.competencecreature:
return arme.system.iscombat && RdDItemCompetenceCreature.isAttaque(item)
}
return false
}
static isParade(arme) {
switch (arme.type) {
case ITEM_TYPES.arme:
return arme.system.equipe && arme.system.resistance > 0 && true/* TODO: regarder la categorie d'arme?*/
case ITEM_TYPES.competencecreature:
return arme.system.iscombat && RdDItemCompetenceCreature.isParade(arme)
case TYPES.arme: return arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0)
case TYPES.competencecreature: return true
}
return false
}
@ -241,11 +180,11 @@ export class RdDItemArme extends Item {
static corpsACorps(actor) {
let competence = actor?.getCompetenceCorpsACorps() ?? { system: { niveau: -6 } };
let melee = actor ? actor.system.carac['melee'].value : 0
let melee = actor? actor.system.carac['melee'].value : 0
return {
_id: competence?.id,
name: 'Corps à corps',
type: ITEM_TYPES.arme,
type: TYPES.arme,
img: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp',
system: {
initiative: RdDCombatManager.calculInitiative(competence.system.niveau, melee),

View File

@ -1,5 +1,4 @@
import { Grammar } from "./grammar.js";
import { RdDItem } from "./item.js";
import { Misc } from "./misc.js";
const competenceTroncs = [["Esquive", "Dague", "Corps à corps"],
@ -24,12 +23,12 @@ const limitesArchetypes = [
];
/* -------------------------------------------- */
export const CATEGORIES_COMPETENCES = {
const categoriesCompetences = {
"generale": { base: -4, label: "Générales" },
"particuliere": { base: -8, label: "Particulières" },
"specialisee": { base: -11, label: "Spécialisées" },
"connaissance": { base: -11, label: "Connaissances" },
"draconic": { base: -11, label: "Draconic" },
"draconic": { base: -11, label: "Draconics" },
"melee": { base: -6, label: "Mêlée" },
"tir": { base: -8, label: "Tir" },
"lancer": { base: -8, label: "Lancer" }
@ -50,13 +49,15 @@ const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static getLabelCategorie(category) {
return CATEGORIES_COMPETENCES[category].label;
static getCategories() {
return categoriesCompetences;
}
/* -------------------------------------------- */
static getLabelCategorie(category) {
return categoriesCompetences[category].label;
}
/* -------------------------------------------- */
static getNiveauBase(category, itemType) {
let categories = RdDItem.getCategories(itemType)
static getNiveauBase(category, categories = categoriesCompetences) {
return categories[category]?.base ?? 0;
}
@ -190,7 +191,7 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static isNiveauBase(item) {
return item.system.niveau == undefined || Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.type);
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.getCategories());
}
/* -------------------------------------------- */

View File

@ -1,8 +1,8 @@
import { ITEM_TYPES } from "./item.js";
import { TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.js";
export const CATEGORIES_COMPETENCES_CREATURES = {
const categories = {
"generale": { base: 0, label: "Générale" },
"naturelle": { base: 0, label: "Arme naturelle" },
"melee": { base: 0, label: "Mêlée" },
@ -15,6 +15,10 @@ export const CATEGORIES_COMPETENCES_CREATURES = {
/* -------------------------------------------- */
export class RdDItemCompetenceCreature extends Item {
static getCategories() {
return categories;
}
/* -------------------------------------------- */
static setRollDataCreature(rollData) {
rollData.carac = { "carac_creature": { label: rollData.competence.name, value: rollData.competence.system.carac_value } }
@ -27,29 +31,30 @@ export class RdDItemCompetenceCreature extends Item {
static armeCreature(item) {
const categorieAttaque = RdDItemCompetenceCreature.getCategorieAttaque(item)
if (categorieAttaque != undefined) {
// cloner pour ne pas modifier la compétence
return foundry.utils.mergeObject(item, {
// si c'est un Item compétence: cloner pour ne pas modifier la compétence
let arme = item.clone();
return foundry.utils.mergeObject(arme, {
action: item.isCompetencePossession() ? 'possession' : 'attaque',
system: {
competence: item.name,
competence: arme.name,
cac: categorieAttaque == "naturelle" ? "naturelle" : "",
niveau: item.system.niveau,
initiative: RdDCombatManager.calculInitiative(item.system.niveau, item.system.carac_value),
equipe: true,
resistance: 100,
dommagesReels: item.system.dommages,
dommagesReels: arme.system.dommages,
penetration: 0,
force: 0,
rapide: true,
}
}, { inplace: false, });
}, { inplace: false });
}
return undefined;
}
/* -------------------------------------------- */
static isAttaque(item) {
if (item.type == ITEM_TYPES.competencecreature) {
static isCompetenceAttaque(item) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
@ -59,11 +64,11 @@ export class RdDItemCompetenceCreature extends Item {
return true
}
}
return false
return undefined
}
static getCategorieAttaque(item) {
if (item.type == ITEM_TYPES.competencecreature) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
@ -76,9 +81,8 @@ export class RdDItemCompetenceCreature extends Item {
}
return undefined
}
static isDommages(item) {
if (item.type == ITEM_TYPES.competencecreature) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "tir":
@ -89,9 +93,8 @@ export class RdDItemCompetenceCreature extends Item {
}
return false
}
static isParade(item) {
if (item.type == ITEM_TYPES.competencecreature) {
if (item.type == TYPES.competencecreature) {
switch (item.system.categorie) {
case "melee":
case "naturelle":
@ -102,4 +105,8 @@ export class RdDItemCompetenceCreature extends Item {
return false
}
/* -------------------------------------------- */
static isCompetenceParade(item) {
return item.type == 'competencecreature' && item.system.categorie_parade !== "";
}
}

View File

@ -12,7 +12,7 @@ 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 { TYPES } from "./item.js";
/**
* Extend the basic ItemSheet for RdD specific items
@ -53,8 +53,7 @@ export class RdDItemSheet extends ItemSheet {
}
get title() {
const owner = (this.item.parent instanceof Actor) ? `(${this.item.parent.name})` : '';
return `${Misc.typeName('Item', this.item.type)}: ${this.item.name} ${owner}`;
return `${Misc.typeName('Item', this.item.type)}: ${this.item.name}`;
}
/* -------------------------------------------- */
@ -99,15 +98,15 @@ export class RdDItemSheet extends ItemSheet {
description: await TextEditor.enrichHTML(this.item.system.description, { async: true }),
descriptionmj: await TextEditor.enrichHTML(this.item.system.descriptionmj, { async: true }),
isComestible: this.item.getUtilisationCuisine(),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable)
}
if (this.item.type == ITEM_TYPES.competencecreature) {
if (this.item.type == TYPES.competencecreature) {
formData.isparade = RdDItemCompetenceCreature.isParade(this.item)
formData.isdommages = RdDItemCompetenceCreature.isDommages(this.item)
}
const competences = await SystemCompendiums.getCompetences('personnage');
formData.categories = RdDItem.getCategories(this.item.type)
formData.categories = this.item.getCategories()
if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') {
formData.caracList = foundry.utils.duplicate(game.model.Actor.personnage.carac)
formData.caracList["reve-actuel"] = foundry.utils.duplicate(game.model.Actor.personnage.reve.reve)
@ -258,7 +257,7 @@ export class RdDItemSheet extends ItemSheet {
if (this.item.isCompetence()) {
const categorie = event.currentTarget.value;
const level = RdDItemCompetence.getNiveauBase(categorie, this.item.type);
const level = RdDItemCompetence.getNiveauBase(categorie, this.item.getCategories());
this.item.system.base = level;
this.html.find('[name="system.base"]').val(level);
}
@ -267,17 +266,10 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
switch (this.item.type) {
case ITEM_TYPES.sort:
// Données de bonus de cases ?
formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue)
break
case ITEM_TYPES.competence:
if (formData['system.niveau'] == undefined) {
formData['system.niveau'] = formData['system.base']
}
if (this.item.type == 'sort') {
// Données de bonus de cases ?
formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue);
}
return this.item.update(formData);
}

View File

@ -1,59 +1,9 @@
import { Grammar } from "./grammar.js";
import { RdDItemCompetence } from "./item-competence.js";
import { ITEM_TYPES } from "./item.js";
import { Misc } from "./misc.js";
import { TMRUtility } from "./tmr-utility.js";
const VOIES_DRACONIC = [
{ code: 'O', label: "Voie d'Oniros", short: 'Oniros', ordre: 'a' },
{ code: 'H', label: "Voie d'Hypnos", short: 'Hypnos', ordre: 'b' },
{ code: 'N', label: "Voie de Narcos", short: 'Narcos', ordre: 'c' },
{ code: 'T', label: "Voie de Thanatos", short: 'Thanatos', ordre: 'd' },
{ code: 'O/H/N/T', label: "Oniros/Hypnos/Narcos/Thanatos", short: 'Oniros/Hypnos/Narcos/Thanatos', ordre: 'e' },
{ code: 'O/H/N', label: "Oniros/Hypnos/Narcos", short: "Oniros/Hypnos/Narcos", ordre: 'f' }
]
/* -------------------------------------------- */
export class RdDItemSort extends Item {
static getDraconicsSort(draconicList, sort) {
switch (Grammar.toLowerCaseNoAccent(sort.name)) {
case "lecture d'aura":
case "detection d'aura":
return draconicList;
case "annulation de magie":
return draconicList.filter(it => !RdDItemCompetence.isThanatos(it));
}
return [RdDItemCompetence.getVoieDraconic(draconicList, sort.system.draconic)];
}
static getOrdreCode(code) {
return (VOIES_DRACONIC.find(it => it.code == code)?.ordre ?? '?')
}
static getVoieCode(voie) {
return VOIES_DRACONIC.find(it => voie.name.includes(it.short))?.code ?? '?'
}
static getCodeDraconic(sort, voies = ['O', 'H', 'N', 'T']) {
switch (Grammar.toLowerCaseNoAccent(sort.name)) {
case "lecture d'aura":
case "detection d'aura":
return RdDItemSort.$voiesConnues('O/H/N/T', voies)
case "annulation de magie":
return RdDItemSort.$voiesConnues('O/H/N', voies)
}
const voie = VOIES_DRACONIC.find(it => it.label.includes(sort.system.draconic))
return voie?.code ?? sort.system.draconic
}
static $voiesConnues(voiesSort, voies) {
const codes = voies.filter(it => voiesSort.includes(it))
.sort(Misc.ascending(it => RdDItemSort.getOrdreCode(it)))
return Misc.join(codes ?? [''], '/');
}
/* -------------------------------------------- */
static isDifficulteVariable(sort) {
return sort && (sort.system.difficulte.toLowerCase() == "variable");
@ -81,9 +31,9 @@ export class RdDItemSort extends Item {
/* -------------------------------------------- */
static buildBonusCaseList(bonuscase, newCase) {
const list = RdDItemSort.bonuscaseStringToList(bonuscase)
const list = RdDItemSort._bonuscaseStringToList(bonuscase)
if (newCase) {
list.push({ case: "Nouvelle", bonus: 0 })
return list.concat({ case: "Nouvelle", bonus: 0 });
}
return list;
}
@ -94,7 +44,7 @@ export class RdDItemSort extends Item {
*/
static getBonusCaseList(item, newCase = false) {
// Gestion spéciale case bonus
if (item.type == ITEM_TYPES.sort) {
if (item.type == 'sort') {
return RdDItemSort.buildBonusCaseList(item.system.bonuscase, newCase);
}
return undefined;
@ -154,11 +104,8 @@ export class RdDItemSort extends Item {
.sort(Misc.ascending())
.join(',');
}
static bonuscaseStringToList(bonuscase) {
if (bonuscase == undefined || bonuscase == '') {
return []
}
return bonuscase.split(',').map(it => {
static _bonuscaseStringToList(bonuscase) {
return (bonuscase ?? '').split(',').map(it => {
const b = it.split(':');
return { case: b[0], bonus: b[1] };
});

View File

@ -6,18 +6,10 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDUtility } from "./rdd-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { RdDRaretes } from "./item/raretes.js";
import { CATEGORIES_COMPETENCES } from "./item-competence.js";
import { CATEGORIES_COMPETENCES_CREATURES } from "./item-competencecreature.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
export const ACTOR_TYPES = {
personnage: 'personnage',
creature: 'creature',
entite: 'entite',
commerce: 'commerce',
vehicule: 'vehicule'
}
export const ITEM_TYPES = {
export const TYPES = {
competence: 'competence',
competencecreature: 'competencecreature',
empoignade: 'empoignade',
@ -64,33 +56,33 @@ export const ITEM_TYPES = {
}
const typesInventaireMateriel = [
ITEM_TYPES.arme,
ITEM_TYPES.armure,
ITEM_TYPES.conteneur,
ITEM_TYPES.faune,
ITEM_TYPES.gemme,
ITEM_TYPES.herbe,
ITEM_TYPES.plante,
ITEM_TYPES.ingredient,
ITEM_TYPES.livre,
ITEM_TYPES.monnaie,
ITEM_TYPES.munition,
ITEM_TYPES.nourritureboisson,
ITEM_TYPES.objet,
ITEM_TYPES.potion,
TYPES.arme,
TYPES.armure,
TYPES.conteneur,
TYPES.faune,
TYPES.gemme,
TYPES.herbe,
TYPES.plante,
TYPES.ingredient,
TYPES.livre,
TYPES.monnaie,
TYPES.munition,
TYPES.nourritureboisson,
TYPES.objet,
TYPES.potion,
]
const typesInventaire = {
materiel: typesInventaireMateriel,
all: ['service'].concat(typesInventaireMateriel),
}
const typesObjetsOeuvres = [ITEM_TYPES.oeuvre, ITEM_TYPES.recettecuisine, ITEM_TYPES.musique, ITEM_TYPES.chant, ITEM_TYPES.danse, ITEM_TYPES.jeu]
const typesObjetsDraconiques = [ITEM_TYPES.queue, ITEM_TYPES.ombre, ITEM_TYPES.souffle, ITEM_TYPES.tete, ITEM_TYPES.signedraconique, ITEM_TYPES.sortreserve, ITEM_TYPES.rencontre]
const typesObjetsConnaissance = [ITEM_TYPES.meditation, ITEM_TYPES.recettealchimique, ITEM_TYPES.sort]
const typesObjetsEffet = [ITEM_TYPES.possession, ITEM_TYPES.poison, ITEM_TYPES.maladie, ITEM_TYPES.blessure]
const typesObjetsCompetence = [ITEM_TYPES.competence, ITEM_TYPES.competencecreature]
const typesObjetsTemporels = [ITEM_TYPES.blessure, ITEM_TYPES.poison, ITEM_TYPES.maladie, ITEM_TYPES.queue, ITEM_TYPES.ombre, ITEM_TYPES.souffle, ITEM_TYPES.signedraconique, ITEM_TYPES.rencontre]
const typesObjetsEquipable = [ITEM_TYPES.arme, ITEM_TYPES.armure, ITEM_TYPES.objet];
const typesObjetsOeuvres = [TYPES.oeuvre, TYPES.recettecuisine, TYPES.musique, TYPES.chant, TYPES.danse, TYPES.jeu]
const typesObjetsDraconiques = [TYPES.queue, TYPES.ombre, TYPES.souffle, TYPES.tete, TYPES.signedraconique, TYPES.sortreserve, TYPES.rencontre]
const typesObjetsConnaissance = [TYPES.meditation, TYPES.recettealchimique, TYPES.sort]
const typesObjetsEffet = [TYPES.possession, TYPES.poison, TYPES.maladie, TYPES.blessure]
const typesObjetsCompetence = [TYPES.competence, TYPES.competencecreature]
const typesObjetsTemporels = [TYPES.blessure, TYPES.poison, TYPES.maladie, TYPES.queue, TYPES.ombre, TYPES.souffle, TYPES.signedraconique, TYPES.rencontre]
const typesObjetsEquipable = [TYPES.arme, TYPES.armure, TYPES.objet];
const typesEnvironnement = typesInventaireMateriel;
const encBrin = 0.00005; // un brin = 1 décigramme = 1/10g = 1/10000kg = 1/20000 enc
const encPepin = 0.0007; /* un pépin de gemme = 1/10 cm3 = 1/1000 l = 3.5/1000 kg = 7/2000 kg = 7/1000 enc
@ -149,12 +141,12 @@ export class RdDItem extends Item {
static isFieldInventaireModifiable(type, field) {
switch (field) {
case 'quantite':
if ([ITEM_TYPES.conteneur].includes(type)) {
if ([TYPES.conteneur].includes(type)) {
return false;
}
break;
case 'cout':
if ([ITEM_TYPES.monnaie].includes(type)) {
if ([TYPES.monnaie].includes(type)) {
return game.user.isGM;
}
break;
@ -172,11 +164,9 @@ export class RdDItem extends Item {
static getItemTypesInventaire(mode = 'materiel') {
return typesInventaire[mode ?? 'materiel']
}
static getItemTypesDraconiques() {
return typesObjetsDraconiques;
}
static getItemTypesEnvironnement() {
return typesEnvironnement;
}
@ -185,16 +175,6 @@ export class RdDItem extends Item {
return typesObjetsOeuvres
}
static getCategories(itemType) {
switch (itemType) {
case ITEM_TYPES.competence:
return CATEGORIES_COMPETENCES
case ITEM_TYPES.competencecreature:
return CATEGORIES_COMPETENCES_CREATURES
}
return {}
}
constructor(docData, context = {}) {
if (!context.rdd?.ready) {
foundry.utils.mergeObject(context, { rdd: { ready: true } });
@ -209,21 +189,20 @@ export class RdDItem extends Item {
if (!docData.img) {
docData.img = RdDItem.getDefaultImg(docData.type);
}
context.rdd = undefined
super(docData, context);
}
getUniteQuantite() {
switch (this.type) {
case ITEM_TYPES.monnaie: return "(Pièces)"
case ITEM_TYPES.herbe:
case TYPES.monnaie: return "(Pièces)"
case TYPES.herbe:
switch (this.system.categorie) {
case 'Alchimie': case 'Repos': case 'Soin':
return "(Brins)"
case 'Cuisine': return '';
}
return '';
case ITEM_TYPES.ingredient: return "(Pépins ou Brins)"
case TYPES.ingredient: return "(Pépins ou Brins)"
}
return '';
}
@ -232,13 +211,13 @@ export class RdDItem extends Item {
return typesObjetsEquipable.includes(this.type)
}
isCompetencePersonnage() { return this.type == ITEM_TYPES.competence }
isCompetenceCreature() { return this.type == ITEM_TYPES.competencecreature }
isConteneur() { return this.type == ITEM_TYPES.conteneur; }
isMonnaie() { return this.type == ITEM_TYPES.monnaie; }
isPotion() { return this.type == ITEM_TYPES.potion; }
isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; }
isService() { return this.type == ITEM_TYPES.service; }
isCompetencePersonnage() { return this.type == TYPES.competence }
isCompetenceCreature() { return this.type == TYPES.competencecreature }
isConteneur() { return this.type == TYPES.conteneur; }
isMonnaie() { return this.type == TYPES.monnaie; }
isPotion() { return this.type == TYPES.potion; }
isNourritureBoisson() { return this.type == TYPES.nourritureboisson; }
isService() { return this.type == TYPES.service; }
isCompetence() { return typesObjetsCompetence.includes(this.type) }
isEsquive() {
@ -257,23 +236,30 @@ export class RdDItem extends Item {
return this.isCompetence() && ['melee', 'tir', 'lancer'].includes(this.system.categorie)
}
isCompetencePossession() { return ITEM_TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isCompetencePossession() { return TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
isDraconique() { return RdDItem.getItemTypesDraconiques().includes(this.type) }
isQueueDragon() { return [ITEM_TYPES.queue, ITEM_TYPES.ombre].includes(this.type) }
isQueueDragon() { return [TYPES.queue, TYPES.ombre].includes(this.type) }
isEffet() { return typesObjetsEffet.includes(this.type) }
isConnaissance() { return typesObjetsConnaissance.includes(this.type) }
isInventaire(mode = 'materiel') { return RdDItem.getItemTypesInventaire(mode).includes(this.type); }
isBoisson() { return this.isNourritureBoisson() && this.system.boisson; }
isAlcool() { return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise; }
isHerbeAPotion() { return this.type == ITEM_TYPES.herbe && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
isBlessure() { return this.type == ITEM_TYPES.blessure }
isHerbeAPotion() { return this.type == TYPES.herbe && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); }
isBlessure() { return this.type == TYPES.blessure }
isPresentDansMilieux(milieux) {
return this.getEnvironnements(milieux).length > 0
}
getCategories() {
switch (this.type) {
case TYPES.competence: return RdDItemCompetence.getCategories()
case TYPES.competencecreature: return RdDItemCompetenceCreature.getCategories()
}
return {}
}
getEnvironnements(milieux = undefined) {
const environnements = this.isInventaire() ? this.system.environnement : undefined;
@ -354,15 +340,15 @@ export class RdDItem extends Item {
getUtilisation() {
switch (this.type) {
case ITEM_TYPES.potion:
case TYPES.potion:
switch (this.system.categorie) {
case 'Alchimie': case 'AlchimieEnchante': case 'AlchimieAutre': return 'alchimie'
case 'Cuisine': return 'cuisine'
case 'Remede': case 'Repos': case 'ReposEnchante': case 'Soin': case 'SoinEnchante': return 'soins'
}
return '';
case ITEM_TYPES.nourritureboisson: return 'cuisine';
case ITEM_TYPES.herbe: case ITEM_TYPES.faune: case ITEM_TYPES.ingredient: case ITEM_TYPES.plante:
case TYPES.nourritureboisson: return 'cuisine';
case TYPES.herbe: case TYPES.faune: case TYPES.ingredient: case TYPES.plante:
switch (this.system.categorie) {
case 'Cuisine': return 'cuisine';
case 'Toxique': case 'Poison': return 'poison';
@ -377,9 +363,9 @@ export class RdDItem extends Item {
getUtilisationCuisine() {
if (this.getUtilisation() == 'cuisine') {
switch (this.type) {
case ITEM_TYPES.nourritureboisson:
case TYPES.nourritureboisson:
return 'pret';
case ITEM_TYPES.herbe: case ITEM_TYPES.faune: case ITEM_TYPES.ingredient: case ITEM_TYPES.plante:
case TYPES.herbe: case TYPES.faune: case TYPES.ingredient: case TYPES.plante:
return 'brut';
}
}
@ -387,7 +373,7 @@ export class RdDItem extends Item {
}
isCristalAlchimique() {
return this.type == ITEM_TYPES.objet && Grammar.includesLowerCaseNoAccent(this.name, 'cristal alchimique') && this.system.quantite > 0;
return this.type == TYPES.objet && Grammar.includesLowerCaseNoAccent(this.name, 'cristal alchimique') && this.system.quantite > 0;
}
isMagique() {
@ -415,11 +401,11 @@ export class RdDItem extends Item {
getEnc() {
switch (this.type) {
case ITEM_TYPES.service:
case TYPES.service:
return 0;
case ITEM_TYPES.herbe:
case TYPES.herbe:
return this.getEncHerbe();
case ITEM_TYPES.gemme:
case TYPES.gemme:
return encPepin * this.system.taille;
}
return Math.max(this.system.encombrement ?? 0, 0);
@ -497,7 +483,7 @@ export class RdDItem extends Item {
getActionPrincipale(options = { warnIfNot: true }) {
switch (this.type) {
case ITEM_TYPES.conteneur: return 'Ouvrir';
case TYPES.conteneur: return 'Ouvrir';
}
if (this.actor?.isPersonnage()) {
const warn = options.warnIfNot;
@ -505,11 +491,11 @@ export class RdDItem extends Item {
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;
case TYPES.nourritureboisson: return this._actionOrWarnQuantiteZero(this.system.boisson ? 'Boire' : 'Manger', warn);
case TYPES.potion: return this._actionOrWarnQuantiteZero('Consommer', warn);
case TYPES.livre: return this._actionOrWarnQuantiteZero('Lire', warn);
case TYPES.herbe: return this.isHerbeAPotion() ? this._actionOrWarnQuantiteZero('Décoction', warn) : undefined;
case TYPES.queue: case TYPES.ombre: return this.system.refoulement > 0 ? 'Refouler' : undefined;
}
}
return undefined;

View File

@ -62,12 +62,12 @@ export class RdDItemBlessure extends RdDItem {
content: `Blessure ${definition.label} appliquée à ${actor.name}`+
`<br>Perte d'endurance : ${lostEndurance}`+
`<br>Perte de Vie : ${lostVie}`,
whisper: ChatUtility.getOwners(actor)
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name)
});
}
static async createBlessure(actor, gravite, localisation = '', attackerToken) {
static async createBlessure(actor, gravite, localisation = '', attacker) {
const definition = RdDItemBlessure.getDefinition(gravite)
const blessure = {
name: definition.label,
@ -77,7 +77,7 @@ export class RdDItemBlessure extends RdDItem {
gravite: gravite,
difficulte: - gravite,
localisation: localisation,
origine: attackerToken?.name ?? ""
origine: attacker?.name ?? ""
}
}
const blessures = await actor.createEmbeddedDocuments('Item', [blessure])

View File

@ -1,4 +1,3 @@
import { ChatUtility } from "../chat-utility.js";
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDTimestamp } from "../time/rdd-timestamp.js";
@ -22,12 +21,9 @@ export class RdDItemMaladie extends RdDItem {
const souffrance = mal.system.identifie
? `de ${mal.name}`
: `d'un mal inconnu`
ChatMessage.create({
whisper: ChatUtility.getOwners(mal.actor),
content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !`
})
mal.postItemToChat('gmroll')
await RdDItemMaladie.prolongerPeriode(mal, oldTimestamp, newTimestamp)
ChatMessage.create({ content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !` });
mal.postItemToChat('gmroll');
await RdDItemMaladie.prolongerPeriode(mal,oldTimestamp, newTimestamp);
}
}

View File

@ -2,7 +2,7 @@ import { RdDBaseActor } from "./actor/base-actor.js";
import { LOG_HEAD, SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Monnaie } from "./item-monnaie.js";
import { RdDItem, ITEM_TYPES } from "./item.js";
import { RdDItem, TYPES } from "./item.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
import { RdDCalendrier } from "./time/rdd-calendrier.js";
@ -465,7 +465,7 @@ class _10_7_19_CategorieCompetenceCreature extends Migration {
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => ITEM_TYPES.competencecreature == it.type)
.filter(it => TYPES.competencecreature == it.type)
.map(it => this.migrateCompetenceCreature(it))
);
}
@ -502,7 +502,7 @@ class _10_7_19_PossessionsEntiteVictime extends Migration {
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => ITEM_TYPES.possession == it.type)
.filter(it => TYPES.possession == it.type)
.map(it => this.migratePossession(it))
);
}
@ -569,7 +569,7 @@ export class Migrations {
}
if (foundry.utils.isNewerVersion(game.system.version, currentVersion)) {
// if (true) { /* comment previous and uncomment here to test before upgrade */
const migrations = Migrations.getMigrations().filter(m => foundry.utils.isNewerVersion(m.version, currentVersion));
const migrations = Migrations.getMigrations().filter(m => isNewerVersion(m.version, currentVersion));
if (migrations.length > 0) {
migrations.sort((a, b) => this.compareVersions(a, b));
migrations.forEach(async (m) => {

View File

@ -139,7 +139,7 @@ export class Misc {
}
static join(params, separator = '') {
return (!params || params.length == 0) ? '' : params.reduce(Misc.joining(separator))
return params?.reduce(Misc.joining(separator)) ?? '';
}
static joining(separator = '') {
@ -166,12 +166,8 @@ export class Misc {
}
static firstConnectedGM() {
if (foundry.utils.isNewerVersion(game.release.version, '12.0')) {
return game.users.activeGM
}
return game.users.find(u => u.isGM && u.active);
return game.users.sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
}
static connectedGMs() {
return game.users.filter(u => u.isGM && u.active);
}
@ -189,31 +185,27 @@ export class Misc {
* and there is no connected GM
*/
static documentIfResponsible(document) {
if (foundry.utils.isNewerVersion(game.release.version, '12.0')) {
if (game.users.activeGM || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document)))
{
if (document instanceof Document) {
if (Misc.isUniqueConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) {
return document
}
}
else if (Misc.isFirstConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) {
return document
}
return undefined
}
static isOwnerPlayer(document) {
return document.testUserPermission && document.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)
static isOwnerPlayer(actor) {
return actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)
}
static isOwnerPlayerOrUniqueConnectedGM(actor) {
return Misc.isOwnerPlayer(actor) ?? Misc.isFirstConnectedGM();
return Misc.isOwnerPlayer(actor) ?? Misc.isUniqueConnectedGM();
}
/**
* @returns true pour un seul utilisateur: le premier GM connecté par ordre d'id
*/
static isFirstConnectedGM() {
return game.user == Misc.firstConnectedGM();
static isUniqueConnectedGM() {
return game.user.id == Misc.firstConnectedGMId();
}
static firstConnectedGMId() {

View File

@ -15,7 +15,7 @@ export class RdDAudio {
if ( audioData ) {
let audioPath = "systems/foundryvtt-reve-de-dragon/sounds/" + audioData.file;
console.log(`foundryvtt-reve-de-dragon | Playing Sound: ${audioPath}`)
foundry.audio.AudioHelper.play({ src: audioPath }, audioData.isGlobal);
AudioHelper.play({ src: audioPath }, audioData.isGlobal);
}
}
}

View File

@ -31,8 +31,8 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static dmg(rollData, actor, isEntiteIncarnee = false) {
const dmgArme = RdDBonus.dmgArme(rollData.arme)
static dmg(rollData, dmgActor, isEntiteIncarnee = false) {
const dmgArme = RdDBonus._dmgArme(rollData)
let dmg = {
total: 0,
dmgArme: dmgArme,
@ -41,7 +41,7 @@ export class RdDBonus {
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: RdDBonus.dmgBonus(rollData.ajustements?.attaqueDefenseurSurpris.used),
mortalite: RdDBonus._calculMortalite(rollData, isEntiteIncarnee),
dmgActor: RdDBonus.bonusDmg(actor, rollData.selectedCarac?.label.toLowerCase(), dmgArme)
dmgActor: RdDBonus._dmgPerso(dmgActor, rollData.selectedCarac?.label, dmgArme)
}
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere;
return dmg;
@ -71,11 +71,11 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static dmgArme(arme) {
if (arme) {
let dmgBase = arme.system.dommagesReels ?? Number(arme.system.dommages ?? 0);
static _dmgArme(rollData) {
if (rollData.arme) {
let dmgBase = rollData.arme.system.dommagesReels ?? Number(rollData.arme.system.dommages ?? 0);
//Le bonus dégats magiques ne peut pas faire dépasser le bonus de l'arme (cf p.278)
return dmgBase + Math.min(dmgBase, arme.system.magique ? arme.system.ecaille_efficacite : 0);
return dmgBase + Math.min(dmgBase, rollData.arme.system.magique ? rollData.arme.system.ecaille_efficacite : 0);
}
return 0;
}
@ -86,14 +86,13 @@ export class RdDBonus {
}
/* -------------------------------------------- */
static bonusDmg(actor, categorie, dmgArme) {
const dmgActor = actor.getBonusDegat()
static _dmgPerso(dmgActor, categorie, dmgArme) {
if (categorie == undefined) {
return 0
}
switch (categorie) {
case "tir": return 0;
case "lancer": return Math.max(0, Math.min(dmgArme, dmgActor));
case "Tir": return 0;
case "Lancer": return Math.max(0, Math.min(dmgArme, dmgActor));
}
return dmgActor;
}

View File

@ -3,38 +3,38 @@ import { Misc } from "./misc.js";
const TABLE_CARACTERISTIQUES_DERIVEES = {
// xp: coût pour passer du niveau inférieur à ce niveau
1: { xp: 3, poids: "moins de 1kg", poidsMin: 0, poidsMax: 1, plusdom: -5, sconst: 0.5, sust: 0.1 },
2: { xp: 3, poids: "1-5", poidsMin: 1, poidsMax: 5, plusdom: -4, sconst: 0.5, sust: 0.3 },
3: { xp: 4, poids: "6-10", poidsMin: 6, poidsMax: 10, plusdom: -3, sconst: 1, sust: 0.5, beaute: 'hideux' },
4: { xp: 4, poids: "11-20", poidsMin: 11, poidsMax: 20, plusdom: -3, sconst: 1, sust: 1, beaute: 'repoussant' },
5: { xp: 5, poids: "21-30", poidsMin: 21, poidsMax: 30, plusdom: -2, sconst: 1, sust: 1, beaute: 'franchement très laid' },
6: { xp: 5, poids: "31-40", poidsMin: 31, poidsMax: 40, plusdom: -1, sconst: 2, sust: 2, beaute: 'laid' },
7: { xp: 6, poids: "41-50", poidsMin: 41, poidsMax: 50, plusdom: -1, sconst: 2, sust: 2, beaute: 'très désavantagé' },
8: { xp: 6, poids: "51-60", poidsMin: 51, poidsMax: 60, plusdom: 0, sconst: 2, sust: 2, beaute: 'désavantagé' },
9: { xp: 7, poids: "61-65", poidsMin: 61, poidsMax: 65, plusdom: 0, sconst: 3, sust: 2, beaute: 'pas terrible' },
10: { xp: 7, poids: "66-70", poidsMin: 66, poidsMax: 70, plusdom: 0, sconst: 3, sust: 3, beaute: 'commun' },
11: { xp: 8, poids: "71-75", poidsMin: 71, poidsMax: 75, plusdom: 0, sconst: 3, sust: 3, beaute: 'pas mal' },
12: { xp: 8, poids: "76-80", poidsMin: 76, poidsMax: 80, plusdom: +1, sconst: 4, sust: 3, beaute: 'avantagé' },
13: { xp: 9, poids: "81-90", poidsMin: 81, poidsMax: 90, plusdom: +1, sconst: 4, sust: 3, beaute: 'mignon' },
14: { xp: 9, poids: "91-100", poidsMin: 91, poidsMax: 100, plusdom: +2, sconst: 4, sust: 4, beaute: 'beau' },
15: { xp: 10, poids: "101-110", poidsMin: 101, poidsMax: 110, plusdom: +2, sconst: 5, sust: 4, beaute: 'très beau' },
16: { xp: 20, poids: "111-120", poidsMin: 111, poidsMax: 120, plusdom: +3, sconst: 5, sust: 4, beaute: 'éblouissant' },
17: { xp: 30, poids: "121-131", poidsMin: 121, poidsMax: 131, plusdom: +3, sconst: 5, sust: 5 },
18: { xp: 40, poids: "131-141", poidsMin: 131, poidsMax: 141, plusdom: +4, sconst: 6, sust: 5 },
19: { xp: 50, poids: "141-150", poidsMin: 141, poidsMax: 150, plusdom: +4, sconst: 6, sust: 5 },
20: { xp: 60, poids: "151-160", poidsMin: 151, poidsMax: 160, plusdom: +4, sconst: 6, sust: 6 },
21: { xp: 70, poids: "161-180", poidsMin: 161, poidsMax: 180, plusdom: +5, sconst: 7, sust: 6 },
22: { xp: 80, poids: "181-200", poidsMin: 181, poidsMax: 200, plusdom: +5, sconst: 7, sust: 7 },
23: { xp: 90, poids: "201-300", poidsMin: 201, poidsMax: 300, plusdom: +6, sconst: 7, sust: 8 },
24: { xp: 100, poids: "301-400", poidsMin: 301, poidsMax: 400, plusdom: +6, sconst: 8, sust: 9 },
25: { xp: 110, poids: "401-500", poidsMin: 401, poidsMax: 500, plusdom: +7, sconst: 8, sust: 10 },
26: { xp: 120, poids: "501-600", poidsMin: 501, poidsMax: 600, plusdom: +7, sconst: 8, sust: 11 },
27: { xp: 130, poids: "601-700", poidsMin: 601, poidsMax: 700, plusdom: +8, sconst: 9, sust: 12 },
28: { xp: 140, poids: "701-800", poidsMin: 701, poidsMax: 800, plusdom: +8, sconst: 9, sust: 13 },
29: { xp: 150, poids: "801-900", poidsMin: 801, poidsMax: 900, plusdom: +9, sconst: 9, sust: 14 },
30: { xp: 160, poids: "901-1000", poidsMin: 901, poidsMax: 1000, plusdom: +9, sconst: 10, sust: 15 },
31: { xp: 170, poids: "1001-1500", poidsMin: 1001, poidsMax: 1500, plusdom: +10, sconst: 10, sust: 16 },
32: { xp: 180, poids: "1501-2000", poidsMin: 1501, poidsMax: 2000, plusdom: +11, sconst: 10, sust: 17 }
1: { xp: 3, poids: "moins de 1kg", plusdom: -5, sconst: 0.5, sust: 0.1 },
2: { xp: 3, poids: "1-5", plusdom: -4, sconst: 0.5, sust: 0.3 },
3: { xp: 4, poids: "6-10", plusdom: -3, sconst: 1, sust: 0.5, beaute: 'hideux' },
4: { xp: 4, poids: "11-20", plusdom: -3, sconst: 1, sust: 1, beaute: 'repoussant' },
5: { xp: 5, poids: "21-30", plusdom: -2, sconst: 1, sust: 1, beaute: 'franchement très laid' },
6: { xp: 5, poids: "31-40", plusdom: -1, sconst: 2, sust: 2, beaute: 'laid' },
7: { xp: 6, poids: "41-50", plusdom: -1, sconst: 2, sust: 2, beaute: 'très désavantagé' },
8: { xp: 6, poids: "51-60", plusdom: 0, sconst: 2, sust: 2, beaute: 'désavantagé' },
9: { xp: 7, poids: "61-65", plusdom: 0, sconst: 3, sust: 2, beaute: 'pas terrible' },
10: { xp: 7, poids: "66-70", plusdom: 0, sconst: 3, sust: 3, beaute: 'commun' },
11: { xp: 8, poids: "71-75", plusdom: 0, sconst: 3, sust: 3, beaute: 'pas mal' },
12: { xp: 8, poids: "76-80", plusdom: +1, sconst: 4, sust: 3, beaute: 'avantagé' },
13: { xp: 9, poids: "81-90", plusdom: +1, sconst: 4, sust: 3, beaute: 'mignon' },
14: { xp: 9, poids: "91-100", plusdom: +2, sconst: 4, sust: 4, beaute: 'beau' },
15: { xp: 10, poids: "101-110", plusdom: +2, sconst: 5, sust: 4, beaute: 'très beau' },
16: { xp: 20, poids: "111-120", plusdom: +3, sconst: 5, sust: 4, beaute: 'éblouissant' },
17: { xp: 30, poids: "121-131", plusdom: +3, sconst: 5, sust: 5 },
18: { xp: 40, poids: "131-141", plusdom: +4, sconst: 6, sust: 5 },
19: { xp: 50, poids: "141-150", plusdom: +4, sconst: 6, sust: 5 },
20: { xp: 60, poids: "151-160", plusdom: +4, sconst: 6, sust: 6 },
21: { xp: 70, poids: "161-180", plusdom: +5, sconst: 7, sust: 6 },
22: { xp: 80, poids: "181-200", plusdom: +5, sconst: 7, sust: 7 },
23: { xp: 90, poids: "201-300", plusdom: +6, sconst: 7, sust: 8 },
24: { xp: 100, poids: "301-400", plusdom: +6, sconst: 8, sust: 9 },
25: { xp: 110, poids: "401-500", plusdom: +7, sconst: 8, sust: 10 },
26: { xp: 120, poids: "501-600", plusdom: +7, sconst: 8, sust: 11 },
27: { xp: 130, poids: "601-700", plusdom: +8, sconst: 9, sust: 12 },
28: { xp: 140, poids: "701-800", plusdom: +8, sconst: 9, sust: 13 },
29: { xp: 150, poids: "801-900", plusdom: +9, sconst: 9, sust: 14 },
30: { xp: 160, poids: "901-1000", plusdom: +9, sconst: 10, sust: 15 },
31: { xp: 170, poids: "1001-1500", plusdom: +10, sconst: 10, sust: 16 },
32: { xp: 180, poids: "1501-2000", plusdom: +11, sconst: 10, sust: 17 }
};
export class RdDCarac {

View File

@ -42,8 +42,7 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
Hooks.on("getCombatTrackerEntryContext", (html, options) => { RdDCombatManager.pushInitiativeOptions(html, options); });
Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) });
Hooks.on("preDeleteCombat", (combat, html, id) => { combat.onPreDeleteCombat() })
Hooks.on("deleteCombat", (combat, html, id) => { combat.onDeleteCombat() })
Hooks.on("preDeleteCombat", (combat, html, id) => { combat.onPreDeleteCombat() });
}
/* -------------------------------------------- */
@ -54,77 +53,78 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
async onPreDeleteCombat() {
if (Misc.isFirstConnectedGM()) {
await this.finDeRound({ terminer: true })
if (Misc.isUniqueConnectedGM()) {
await this.finDeRound({ terminer: true });
ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
.forEach(it => it.delete())
.forEach(it => it.delete());
RdDEmpoignade.deleteAllEmpoignades()
}
}
async onDeleteCombat() {
if (Misc.isFirstConnectedGM()) {
if (game.combats.size <= 1) {
game.actors.forEach(actor => actor.resetItemUse())
}
}
}
/* -------------------------------------------- */
async finDeRound(options = { terminer: false }) {
this.combatants.map(it => RdDCombatManager.getActorCombatant(it, { warning: false }))
.filter(it => it != undefined)
.forEach(async actor => {
await actor.finDeRound(options)
await actor.resetItemUse()
})
}
this.turns.forEach(turn => turn.actor.resetItemUse());
static getActorCombatant(combatant, options = { warning: true }) {
if (!combatant.actor) {
if (options.warning) {
for (let combatant of this.combatants) {
if (combatant.actor) {
await combatant.actor.finDeRound(options);
}
else {
ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur!`)
}
return undefined
}
else if (!combatant.actor.isActorCombat()) {
if (options.warning) {
ui.notifications.warn(`${combatant.name} ne peut pas combattre!`)
}
return undefined
}
return combatant.actor
}
static calculAjustementInit(actor, arme) {
const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
const etatGeneral = actor.getEtatGeneral() ?? 0
return efficacite + etatGeneral
}
/************************************************************************************/
async rollInitiative(ids, messageOptions = {}) {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, messageOptions)
ids = typeof ids === "string" ? [ids] : ids
ids.forEach(async id =>
await this.rollInitRdD(id, undefined, messageOptions)
)
return this
}
async rollInitiative(ids, formula = undefined, messageOptions = {}) {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, formula, messageOptions);
async rollInitRdD(id, formula, messageOptions = {}) {
const combatant = this.combatants.get(id);
const actor = RdDCombatManager.getActorCombatant(combatant)
if (actor) {
const rollFormula = formula ?? RdDCombatManager.getFirstInitRollFormula(actor)
ids = typeof ids === "string" ? [ids] : ids;
// calculate initiative
for (let cId = 0; cId < ids.length; cId++) {
const combatant = this.combatants.get(ids[cId]);
let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, 0);
if (!formula) {
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
const competence = combatant.actor.items.find(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
if (competence) {
rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, 0);
}
} else {
const armeCombat = combatant.actor.itemTypes['arme'].find(it => it.system.equipe)
let compName = "Corps à corps"
if (armeCombat) {
if (armeCombat.system.competence != "") {
compName = armeCombat.system.competence
}
if (armeCombat.system.lancer != "") {
compName = armeCombat.system.lancer
}
if (armeCombat.system.tir != "") {
compName = armeCombat.system.tir
}
}
const competence = RdDItemCompetence.findCompetence(combatant.actor.items, compName);
if (competence && competence.system.defaut_carac) {
const carac = combatant.actor.system.carac[competence.system.defaut_carac].value;
const niveau = competence.system.niveau;
const bonusEcaille = (armeCombat?.system.magique) ? armeCombat.system.ecaille_efficacite : 0;
rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, bonusEcaille);
} else {
ui.notifications.warn(`Votre arme ${armeCombat.name} n'a pas de compétence renseignée`);
}
}
}
//console.log("Combatat", c);
const roll = combatant.getInitiativeRoll(rollFormula);
if (!roll.total) {
await roll.evaluate();
}
const total = Math.max(roll.total, 0.00);
console.log("Compute init for", rollFormula, roll, total, combatant);
await this.updateEmbeddedDocuments("Combatant", [{ _id: combatant._id || combatant.id, initiative: total }]);
let id = combatant._id || combatant.id;
await this.updateEmbeddedDocuments("Combatant", [{ _id: id, initiative: total }]);
// Send a chat message
let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode");
@ -136,7 +136,7 @@ export class RdDCombatManager extends Combat {
alias: combatant.token.name,
sound: CONFIG.sounds.dice,
},
flavor: `${combatant.token.name} a fait son jet d'Initiative (${messageOptions.info})<br>`
flavor: `${combatant.token.name} a fait son jet d'Initiative (${messageOptions.initInfo})<br>`,
},
messageOptions);
roll.toMessage(messageData, { rollMode, create: true });
@ -144,20 +144,7 @@ export class RdDCombatManager extends Combat {
RdDCombatManager.processPremierRoundInit();
}
return this;
}
static getFirstInitRollFormula(actor) {
const actions = actor.listActionsCombat()
if (actions.length > 0) {
const action = actions[0]
const init = RdDCombatManager.getInitData(actor, action)
const ajustement = RdDCombatManager.calculAjustementInit(actor, action)
return RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement);
}
let ajustement = RdDCombatManager.calculAjustementInit(actor, undefined);
return RdDCombatManager.formuleInitiative(2, 10, 0, ajustement);
}
};
static formuleInitiative(rang, carac, niveau, bonusMalus) {
return `${rang} +( (${RdDCombatManager.calculInitiative(niveau, carac, bonusMalus)} )/100)`;
@ -227,23 +214,72 @@ export class RdDCombatManager extends Combat {
static $prepareAttaqueArme(infoAttaque) {
const comp = infoAttaque.competences.find(c => c.name == infoAttaque.competence);
const arme = infoAttaque.arme;
const attaque = foundry.utils.duplicate(arme);
const attaque = foundry.utils.duplicate(infoAttaque.arme);
attaque.action = 'attaque';
attaque.system.competence = infoAttaque.competence;
attaque.system.dommagesReels = infoAttaque.dommagesReel;
attaque.system.infoMain = infoAttaque.infoMain;
attaque.system.niveau = comp.system.niveau;
const ajustement = (arme?.parent?.getEtatGeneral() ?? 0) + (arme?.system.magique) ? arme.system.ecaille_efficacite : 0;
attaque.system.initiative = RdDCombatManager.calculInitiative(comp.system.niveau, infoAttaque.carac[comp.system.defaut_carac].value, ajustement);
attaque.system.initiative = RdDCombatManager.calculInitiative(comp.system.niveau, infoAttaque.carac[comp.system.defaut_carac].value);
return attaque;
}
static listActionsCreature(competences) {
return competences
.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(it => RdDItemCompetenceCreature.armeCreature(it))
.filter(it => it != undefined);
}
static listActionsPossessions(actor) {
return RdDCombatManager._indexActions(actor.getPossessions().map(p => {
return {
name: p.name,
action: 'possession',
system: {
competence: p.name,
possessionid: p.system.possessionid,
}
}
}));
}
/* -------------------------------------------- */
static listActionsCombat(combatant) {
const actor = combatant.actor;
let actions = RdDCombatManager.listActionsPossessions(actor);
if (actions.length > 0) {
return actions;
}
if (actor.isCreatureEntite()) {
actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']);
} else if (actor.isPersonnage()) {
// Recupération des items 'arme'
const competences = actor.itemTypes['competence'];
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
.concat(RdDItemArme.empoignade(actor))
.concat(RdDItemArme.mainsNues(actor));
actions = RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac);
if (actor.system.attributs.hautrevant.value) {
actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } });
}
}
return RdDCombatManager._indexActions(actions);
}
static _indexActions(actions) {
for (let index = 0; index < actions.length; index++) {
actions[index].index = index;
}
return actions;
}
/* -------------------------------------------- */
static processPremierRoundInit() {
// Check if we have the whole init !
if (Misc.isFirstConnectedGM() && game.combat.current.round == 1) {
if (Misc.isUniqueConnectedGM() && game.combat.current.round == 1) {
let initMissing = game.combat.combatants.find(it => !it.initiative);
if (!initMissing) { // Premier round !
for (let combatant of game.combat.combatants) {
@ -277,13 +313,13 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */
static pushInitiativeOptions(html, options) {
for (let i = 0; i < options.length; i++) {
let option = options[i]
let option = options[i];
if (option.name == 'COMBAT.CombatantReroll') { // Replace !
option.name = "Sélectionner l'initiative..."
option.condition = true
option.icon = '<i class="far fa-question-circle"></i>'
option.name = "Sélectionner l'initiative...";
option.condition = true;
option.icon = '<i class="far fa-question-circle"></i>';
option.callback = target => {
RdDCombatManager.displayInitiativeMenu(html, target.data('combatant-id'))
RdDCombatManager.displayInitiativeMenu(html, target.data('combatant-id'));
}
}
}
@ -294,83 +330,97 @@ export class RdDCombatManager extends Combat {
}
/* -------------------------------------------- */
static rollInitiativeAction(combatantId, action) {
const combatant = game.combat.combatants.get(combatantId)
const actor = RdDCombatManager.getActorCombatant(combatant)
if (actor == undefined) { return [] }
const combatant = game.combat.combatants.get(combatantId);
if (combatant.actor == undefined) {
ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return [];
}
let initInfo = "";
let initOffset = 0;
let caracForInit = 0;
let compNiveau = 0;
let compData = { name: "Aucune" };
if (combatant.actor.getSurprise() == "totale") {
initOffset = -1; // To force 0
initInfo = "Surprise Totale"
} else if (combatant.actor.getSurprise() == "demi") {
initOffset = 0;
initInfo = "Demi Surprise"
} else if (action.action == 'possession') {
initOffset = 10;
caracForInit = combatant.actor.getReveActuel();
initInfo = "Possession"
} else if (action.action == 'autre') {
initOffset = 2;
initInfo = "Autre Action"
} else if (action.action == 'haut-reve') {
initOffset = 9;
initInfo = "Draconic"
} else {
compData = RdDItemCompetence.findCompetence(combatant.actor.items, action.system.competence);
compNiveau = compData.system.niveau;
initInfo = action.name + " / " + action.system.competence;
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
caracForInit = compData.system.carac_value;
} else {
caracForInit = combatant.actor.system.carac[compData.system.defaut_carac].value;
}
initOffset = RdDCombatManager._baseInitOffset(compData.system.categorie, action);
}
let malus = combatant.actor.getEtatGeneral(); // Prise en compte état général
// Cas des créatures et entités vs personnages
let rollFormula = RdDCombatManager.formuleInitiative(initOffset, caracForInit, compNiveau, malus);
// Garder la trace de l'arme/compétence utilisée pour l'iniative
combatant.initiativeData = { arme: action } // pour reclasser l'init au round 0
const init = RdDCombatManager.getInitData(actor, action)
const ajustement = RdDCombatManager.calculAjustementInit(actor, action)
const rollFormula = RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement);
game.combat.rollInitRdD(combatantId, rollFormula, init);
game.combat.rollInitiative(combatantId, rollFormula, { initInfo: initInfo });
}
static getInitData(actor, action) {
if (actor.getSurprise() == "totale") { return { offset: -1, info: "Surprise Totale", carac: 0, niveau: 0 } }
if (actor.getSurprise() == "demi") { return { offset: 0, info: "Demi Surprise", carac: 0, niveau: 0 } }
if (action.action == 'autre') { return { offset: 2, info: "Autre Action", carac: 0, niveau: 0 } }
if (action.action == 'possession') { return { offset: 10, info: "Possession", carac: actor.getReveActuel(), niveau: 0 } }
if (action.action == 'haut-reve') { return { offset: 9, info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } }
const comp = RdDItemCompetence.findCompetence(actor.items, action.system.competence);
return {
offset: RdDCombatManager.initOffset(comp?.system.categorie, action),
info: action.name + " / " + action.system.competence,
carac: actor.getCaracInit(comp),
niveau: comp?.system.niveau ?? -8
/* -------------------------------------------- */
static _baseInitOffset(categorie, arme) {
if (categorie == "tir") { // Offset de principe pour les armes de jet
return 8;
}
}
static initOffset(categorie, arme) {
switch (categorie) {
case "tir": return 8
case "lancer": return 7
default:
switch (arme.system.cac) {
case "empoignade": return 3
case "pugilat": return 4
case "naturelle": return 4
default: return 5
}
if (categorie == "lancer") { // Offset de principe pour les armes de jet
return 7;
}
switch (arme.system.cac) {
case "empoignade":
return 3;
case "pugilat":
case "naturelle":
return 4;
}
return 5;
}
/* -------------------------------------------- */
static displayInitiativeMenu(html, combatantId) {
const combatant = game.combat.combatants.get(combatantId)
const actor = RdDCombatManager.getActorCombatant(combatant, { warning: false })
if (actor) {
const actions = RdDCombatManager.listActionsActorCombatant(actor)
// Build the relevant submenu
const menuItems = actions.map(action => {
return {
console.log("Combatant ; ", combatantId);
const combatant = game.combat.combatants.get(combatantId);
if (!(combatant?.actor)) {
ui.notifications.warn(`Le combatant ${combatant.name ?? combatantId} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return;
}
let actions = RdDCombatManager.listActionsCombat(combatant);
// Build the relevant submenu
if (actions) {
let menuItems = [];
for (let action of actions) {
menuItems.push({
name: action.system.competence,
icon: "<i class='fas fa-dice-d6'></i>",
callback: target => { RdDCombatManager.rollInitiativeAction(combatantId, action) }
}
})
if (menuItems.length > 0) {
new ContextMenu(html, ".directory-list", menuItems).render();
});
}
new ContextMenu(html, ".directory-list", menuItems).render();
}
}
/* -------------------------------------------- */
static listActionsActorCombatant( actor) {
const possessions = actor.listActionsPossessions()
const actions = possessions.length > 0
? possessions
: actor.listActionsCombat()
for (let index = 0; index < actions.length; index++) {
actions[index].index = index
}
return actions
}
}
/* -------------------------------------------- */
@ -393,11 +443,11 @@ export class RdDCombat {
/* -------------------------------------------- */
static combatNouveauTour(combat) {
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId);
if (turn?.actor) {
RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token.id);
// TODO Playaudio for player??
RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token);
}
}
}
@ -408,51 +458,52 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static rddCombatTarget(target, attacker, attackerToken) {
return new RdDCombat(attacker, attackerToken?.id, target?.actor, target?.id, target)
static rddCombatTarget(target, attacker) {
const defender = target?.actor;
const defenderTokenId = target?.id;
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static rddCombatForAttackerAndDefender(attackerId, attackerTokenId, defenderTokenId) {
const attacker = game.actors.get(attackerId)
const defenderToken = defenderTokenId ? canvas.tokens.get(defenderTokenId) : undefined
let defender = defenderToken?.actor;
static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) {
const attacker = game.actors.get(attackerId);
let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined;
let target = undefined
if (!defenderTokenId || !defender) {
console.warn(`RdDCombat.rddCombatForAttackerAndDefender: appel avec defenderTokenId ${defenderTokenId} incorrect, ou pas de defender correspondant`);
target = Targets.getTarget()
if (!target) {
return
return;
}
defenderTokenId = target.id;
defender = target.actor;
if (!defenderTokenId || !defender) {
return
return;
}
}
return new RdDCombat(attacker, attackerTokenId, defender, defenderTokenId, target)
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static onMsgEncaisser(msg) {
let defender = canvas.tokens.get(msg.defenderTokenId).actor;
if (Misc.isOwnerPlayerOrUniqueConnectedGM()) {
let attackerRoll = msg.attackerRoll;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
let defender = canvas.tokens.get(msg.defenderToken.id).actor;
defender.encaisserDommages(attackerRoll, attacker, msg.attackerToken);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id);
defender.encaisserDommages(attackerRoll, attacker);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
}
/* -------------------------------------------- */
static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderToken.id)
if (defenderToken && Misc.isFirstConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id)
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme)
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
let defenderToken = canvas.tokens.get(msg.defenderTokenId);
if (defenderToken && Misc.isUniqueConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme);
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
}
}
@ -483,7 +534,6 @@ export class RdDCombat {
html.on("click", button, event => {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-attackerTokenId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) {
rddCombat.onEvent(button, event);
@ -499,38 +549,22 @@ export class RdDCombat {
}
/* -------------------------------------------- */
constructor(attacker, attackerTokenId, defender, defenderTokenId, target) {
constructor(attacker, defender, defenderTokenId, target) {
this.attacker = attacker
this.defender = defender
this.target = target
this.attackerId = this.attacker.id
this.defenderId = this.defender.id
this.attackerTokenId = attackerTokenId
this.defenderTokenId = defenderTokenId
this.attackerToken = RdDCombat.$extractAttackerTokenData(attacker, attackerTokenId)
this.defenderToken = RdDCombat.$extractDefenderTokenData(defender, defenderTokenId, target)
}
static $extractAttackerTokenData(attacker, attackerTokenId) {
const token = canvas.tokens.get(attackerTokenId);
return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(attackerTokenId, attacker)
}
static $extractDefenderTokenData(defender, defenderTokenId, target) {
if (target) {
return Targets.extractTokenData(target)
}
const token = canvas.tokens.get(defenderTokenId);
return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(defenderTokenId, defender)
}
/* -------------------------------------------- */
async onEvent(button, event) {
const chatMessage = ChatUtility.getChatMessage(event);
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll');
console.log('RdDCombat', attackerRoll, defenderRoll);
const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value;
const armeParadeId = event.currentTarget.attributes['data-armeid']?.value;
const competence = event.currentTarget.attributes['data-competence']?.value;
@ -540,7 +574,7 @@ export class RdDCombat {
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 '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll, defenderTokenId);
case '#echec-total-attaque': return this._onEchecTotal(attackerRoll);
case '#appel-chance-attaque': return this.attacker.rollAppelChance(
@ -653,11 +687,11 @@ export class RdDCombat {
if (this.defender.isEntite([ENTITE_BLURETTE])) {
ChatMessage.create({
content: `<strong>La cible est une blurette, l'arme à distance sera perdue dans le blurêve`,
whisper: ChatUtility.getGMs()
whisper: ChatMessage.getWhisperRecipients("GM")
})
}
else {
const defenderToken = canvas.tokens.get(this.defenderTokenId)
const defenderToken = canvas.tokens.get(this.defenderTokenId);
const dist = this.distance(_token, defenderToken)
const isVisible = this.isVisible(_token, defenderToken)
const portee = this._ajustementPortee(dist, rollData.arme)
@ -676,7 +710,7 @@ export class RdDCombat {
activite: activite,
total: total
}),
whisper: ChatUtility.getGMs()
whisper: ChatMessage.getWhisperRecipients("GM")
})
}
}
@ -724,7 +758,7 @@ export class RdDCombat {
}
RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
let rollData = this._prepareAttaque(competence, arme)
let rollData = this._prepareAttaque(competence, arme);
console.log("RdDCombat.attaque >>>", rollData);
if (arme) {
this.attacker.verifierForceMin(arme);
@ -750,18 +784,15 @@ export class RdDCombat {
dialog.render(true);
}
/* -------------------------------------------- */
_prepareAttaque(competence, arme) {
let rollData = {
alias: this.attackerToken.name,
passeArme: foundry.utils.randomID(16),
passeArme: randomID(16),
mortalite: arme?.system.mortalite,
competence: competence,
surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
sourceToken: this.attackerToken,
targetToken: this.defenderToken,
targetToken: Targets.extractTokenData(this.target),
essais: {}
};
@ -804,12 +835,11 @@ export class RdDCombat {
const choixParticuliere = await ChatMessage.create({
alias: this.attacker.name,
whisper: ChatUtility.getOwners(this.attacker),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html', {
alias: this.attackerToken.name,
alias: this.attacker.name,
attackerId: this.attackerId,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
defenderTokenId: this.defenderTokenId,
isForce: isForce,
isFinesse: isFinesse,
isRapide: isRapide,
@ -823,10 +853,10 @@ export class RdDCombat {
async _onAttaqueNormale(attackerRoll) {
console.log("RdDCombat.onAttaqueNormale >>>", attackerRoll);
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite());
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), this.defender.isEntite());
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
attackerRoll.show = {
cible: this.defenderToken?.name ?? 'la cible',
cible: this.target ? this.defender.name : 'la cible',
isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
}
await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
@ -857,7 +887,7 @@ export class RdDCombat {
// # utilisation esquive
const corpsACorps = this.defender.getCompetenceCorpsACorps({ onMessage: it => console.info(it, this.defender) });
const esquives = foundry.utils.duplicate(this.defender.getCompetencesEsquive())
esquives.forEach(e => e.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0);
esquives.forEach(e => e.system.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0);
const paramChatDefense = {
passeArme: attackerRoll.passeArme,
@ -867,8 +897,7 @@ export class RdDCombat {
attacker: this.attacker,
attackerId: this.attackerId,
esquives: esquives,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
defenderTokenId: this.defenderTokenId,
mainsNues: attackerRoll.dmg.mortalite != 'mortel' && corpsACorps,
armes: this._filterArmesParade(this.defender, attackerRoll.competence, attackerRoll.arme),
diffLibre: attackerRoll.ajustements?.diffLibre?.value ?? 0,
@ -879,7 +908,7 @@ export class RdDCombat {
dmg: attackerRoll.dmg,
};
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
await this._chatMessageDefense(paramChatDefense, defenderRoll);
}
else {
@ -892,8 +921,8 @@ export class RdDCombat {
const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attackerToken.name,
whisper: ChatUtility.getOwners(this.defender),
alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.defender.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense),
});
// flag pour garder les jets d'attaque/defense
@ -906,9 +935,8 @@ export class RdDCombat {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_defense", data: {
attackerId: this.attacker?.id,
attackerToken: this.attackerToken,
defenderId: this.defender?.id,
defenderToken: this.defenderToken,
defenderTokenId: this.defenderTokenId,
defenderRoll: defenderRoll,
paramChatDefense: paramChatDefense,
rollMode: true
@ -917,33 +945,31 @@ export class RdDCombat {
}
/* -------------------------------------------- */
_filterArmesParade(defender, competence, armeAttaque) {
let defenses = defender.items.filter(it => RdDItemArme.isParade(it))
defenses = foundry.utils.duplicate(defenses)
defenses.forEach(armeDefense => {
// Ajout du # d'utilisation ce round
armeDefense.nbUsage = defender.getItemUse(armeDefense.id)
armeDefense.typeParade = RdDItemArme.defenseArmeParade(armeAttaque, armeDefense)
})
_filterArmesParade(defender, competence) {
let items = defender.items.filter(it => RdDItemArme.isArmeUtilisable(it) || RdDItemCompetenceCreature.isCompetenceParade(it))
items.forEach(item => item.system.nbUsage = defender.getItemUse(item.id)); // Ajout du # d'utilisation ce round
switch (competence.system.categorie) {
case 'tir':
case 'lancer':
return defenses.filter(armeDefense => RdDItemArme.getCategorieParade(armeDefense) == 'boucliers')
return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
default:
return defenses.filter(armeDefense => armeDefense.typeParade != '')
// Le fléau ne peut être paré quau bouclier p115
if (competence.name == "Fléau") {
return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
}
return items.filter(item => RdDItemArme.getCategorieParade(item));
}
}
/* -------------------------------------------- */
async _onAttaqueEchecTotal(attackerRoll) {
const choixEchecTotal = await ChatMessage.create({
whisper: ChatUtility.getOwners(this.attacker),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
attackerId: this.attackerId,
attacker: this.attacker,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
defenderTokenId: this.defenderTokenId,
essais: attackerRoll.essais
})
});
@ -957,9 +983,9 @@ export class RdDCombat {
const arme = rollData.arme;
const avecArme = !['', 'sans-armes', 'armes-naturelles'].includes(arme?.system.categorie_parade ?? '');
const action = (rollData.attackerRoll ? (arme ? "la parade" : "l'esquive") : "l'attaque");
ChatUtility.createChatWithRollMode(
{ content: `<strong>Maladresse à ${action}!</strong> ` + await RdDRollTables.getMaladresse({ arme: avecArme }) },
this.defender)
ChatUtility.createChatWithRollMode(this.defender.name, {
content: `<strong>Maladresse à ${action}!</strong> ` + await RdDRollTables.getMaladresse({ arme: avecArme })
});
}
/* -------------------------------------------- */
@ -1015,11 +1041,8 @@ export class RdDCombat {
/* -------------------------------------------- */
_prepareParade(attackerRoll, armeParade, competenceParade) {
let defenderRoll = {
alias: this.defenderToken?.name,
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
attackerRoll: attackerRoll,
competence: this.defender.getCompetence(competenceParade),
arme: armeParade,
@ -1042,9 +1065,9 @@ export class RdDCombat {
console.log("RdDCombat._onParadeParticuliere >>>", defenderRoll);
if (!defenderRoll.attackerRoll.isPart) {
// TODO: attaquant doit jouer résistance et peut être désarmé p132
ChatUtility.createChatWithRollMode(
{ content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` },
this.defender)
ChatUtility.createChatWithRollMode(this.defender.name, {
content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)`
});
}
}
@ -1099,11 +1122,8 @@ export class RdDCombat {
/* -------------------------------------------- */
_prepareEsquive(attackerRoll, competence) {
let rollData = {
alias: this.defenderToken?.name,
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
attackerRoll: attackerRoll,
competence: competence,
surprise: this.defender.getSurprise(true),
@ -1121,9 +1141,9 @@ export class RdDCombat {
/* -------------------------------------------- */
_onEsquiveParticuliere(rollData) {
console.log("RdDCombat._onEsquiveParticuliere >>>", rollData);
ChatUtility.createChatWithRollMode(
{ content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" },
this.defender);
ChatUtility.createChatWithRollMode(this.defender.name, {
content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>"
});
}
/* -------------------------------------------- */
@ -1245,8 +1265,9 @@ export class RdDCombat {
}
/* -------------------------------------------- */
async encaisser(attackerRoll, defenderRoll) {
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderRoll);
async encaisser(attackerRoll, defenderRoll, defenderTokenId) {
defenderTokenId = defenderTokenId || this.defenderTokenId;
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) {
this._onEchecTotal(defenderRoll);
@ -1254,19 +1275,18 @@ export class RdDCombat {
if (Misc.isOwnerPlayerOrUniqueConnectedGM(this.defender)) {
attackerRoll.attackerId = this.attackerId;
attackerRoll.defenderTokenId = this.defenderToken.id;
attackerRoll.defenderTokenId = defenderTokenId;
await this.computeRecul(defenderRoll);
await this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show, this.attackerToken, this.defenderToken);
await this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show);
}
else { // envoi à un GM: les joueurs n'ont pas le droit de modifier les personnages qu'ils ne possèdent pas
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_encaisser",
data: {
attackerId: this.attackerId,
attackerRoll: attackerRoll,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken
defenderTokenId: defenderTokenId,
attackerRoll: attackerRoll
}
});
}
@ -1274,31 +1294,28 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static async displayActorCombatStatus(combat, actor, token) {
if (!actor?.isActorCombat()) {
return
}
const formData = {
static async displayActorCombatStatus(combat, actor, tokenId) {
let formData = {
combatId: combat._id,
alias: token.name ?? actor.name,
alias: actor.name,
etatGeneral: actor.getEtatGeneral(),
isSonne: actor.isSonne(),
isSonne: actor.getSonne(),
blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(),
actorId: actor.id,
actor: actor,
tokenId: token.id,
tokenId: tokenId,
isGrave: actor.countBlessures(it => it.isGrave()) > 0,
isCritique: actor.countBlessures(it => it.isCritique()) > 0
}
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs`, formData),
alias: token.name ?? actor.name
})
alias: actor.name
});
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData),
whisper: ChatUtility.getOwners(actor),
alias: token.name ?? actor.name
})
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name),
alias: actor.name
});
}
}

View File

@ -17,7 +17,6 @@ import { RdDUtility } from "./rdd-utility.js";
import { FenetreRechercheTirage } from "./tirage/fenetre-recherche-tirage.js";
import { TMRUtility } from "./tmr-utility.js";
import { DialogFatigueVoyage } from "./voyage/dialog-fatigue-voyage.js";
import { ChatUtility } from "./chat-utility.js";
const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/;
@ -82,7 +81,7 @@ export class RdDCommands {
this.registerCommand({ path: ["/sommeil"], func: (content, msg, params) => this.sommeil(msg, params), descr: "Prépare le passage de journée pour chateau dormant" });
this.registerCommand({ path: ["/meteo"], func: (content, msg, params) => this.getMeteo(msg, params), descr: "Propose une météo marine" });
this.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.proposeName(msg, params), descr: "Génère un nom aléatoire" });
this.registerCommand({ path: ["/nom"], func: (content, msg, params) => RdDNameGen.getName(msg, params), descr: "Génère un nom aléatoire" });
this.registerCommand({
path: ["/tmr"], func: (content, msg, params) => this.findTMR(msg, params),
@ -207,20 +206,26 @@ export class RdDCommands {
/* Manage chat commands */
processChatCommand(commandLine, content = '', msg = {}) {
// Setup new message's visibility
ChatUtility.applyRollMode(msg)
msg.type = 0;
let rollMode = game.settings.get("core", "rollMode");
if (["gmroll", "blindroll"].includes(rollMode)) {
msg["whisper"] = ChatMessage.getWhisperRecipients("GM");
}
if (rollMode === "blindroll") {
msg["blind"] = true;
}
msg["type"] = 0;
if (!this.commandsTable) {
this._registerCommands()
this._registerCommands();
}
let command = commandLine[0].toLowerCase();
if (this._isCommandHandled(command)) {
let params = commandLine.slice(1);
this._processCommand(this.commandsTable, command, params, content, msg)
return true
this._processCommand(this.commandsTable, command, params, content, msg);
return true;
}
return false
return false;
}
_isCommandHandled(command) {

View File

@ -1,11 +1,9 @@
import { Grammar } from "./grammar.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
export class RdDConfirm {
/* -------------------------------------------- */
static confirmer(options, autresActions) {
if (options.settingConfirmer && !ReglesOptionnelles.isSet(options.settingConfirmer)) {
return options.onAction()
}
let buttons = {
"action": RdDConfirm._createButtonAction(options),
"cancel": RdDConfirm._createButtonCancel()

View File

@ -2,14 +2,19 @@ import { ChatUtility } from "./chat-utility.js";
import { HIDE_DICE, SHOW_DICE } from "./constants.js";
import { Misc } from "./misc.js";
const imgHeures = [1, 2, 3, 4, 5, 6, 7, 9, 9, 10, 11, 12].map(heure => {
function img(src) {
return `<img src="${src}" class="dice-img" />`
}
function iconHeure(heure) {
if (heure < 10) {
heure = '0' + heure;
}
return `<img src="systems/foundryvtt-reve-de-dragon/icons/heures/hd${heure}.webp" class="dice-img" />`
})
return `systems/foundryvtt-reve-de-dragon/icons/heures/hd${heure}.webp`
}
const imagesHeures = [1, 2, 3, 4, 5, 6, 7, 9, 9, 10, 11, 12].map(it => iconHeure(it));
const imgSigneDragon = imgHeures[4]
const imgSigneDragon = img(imagesHeures[4]);
/** De pour les jets de rencontre */
export class DeTMR extends Die {
@ -20,7 +25,7 @@ export class DeTMR extends Die {
return {
type: "dt",
font: "HeuresDraconiques",
fontScale: 0.8,
fontScale: 0.7,
labels: ['1', '2', '3', '4', '5', '6', 'd', '0'],
system: system
}
@ -31,14 +36,14 @@ export class DeTMR extends Die {
super(termData);
}
async evaluate(options) {
await super.evaluate(options)
await this.reroll('r=8', { recursive: true })
async evaluate() {
super.evaluate();
this.explode("x=8");
return this;
}
get total() {
return this.values.map(it => Misc.modulo(it, 8)).reduce(Misc.sum(), 0);
return this.values.filter(it => it != 8).reduce(Misc.sum(), 0);
}
getResultLabel(diceTerm) {
@ -51,14 +56,13 @@ export class DeTMR extends Die {
/** DeDraconique pour le D8 sans limite avec 8=>0 */
export class DeDraconique extends Die {
/** @override */
static DENOMINATION = "r";
static diceSoNiceData(system) {
return {
type: "dr",
font: "HeuresDraconiques",
fontScale: 0.8,
fontScale: 0.7,
labels: ['1', '2', '3', '4', '5', '6', 'd', '0'],
system: system
}
@ -69,9 +73,9 @@ export class DeDraconique extends Die {
super(termData);
}
async evaluate(options) {
await super.evaluate(options);
await this.explode("x=7");
async evaluate() {
super.evaluate();
this.explode("x=7");
return this;
}
@ -81,7 +85,7 @@ export class DeDraconique extends Die {
getResultLabel(diceTerm) {
switch (diceTerm.result) {
case 7: return imgSigneDragon
case 7: return imgSigneDragon;
case 8: return '0';
}
return diceTerm.result.toString();
@ -98,7 +102,6 @@ export class DeHeure extends Die {
return {
type: "dh",
font: "HeuresDraconiques",
fontScale: 1.2,
labels: ['v', 'i', 'f', 'o', 'd', 'e', 'l', 's', 'p', 'a', 'r', 'c'],
system: system
}
@ -110,15 +113,15 @@ export class DeHeure extends Die {
}
getResultLabel(diceTerm) {
return imgHeures[diceTerm.result - 1]
return img(imagesHeures[diceTerm.result - 1]);
}
}
export class RdDDice {
static init() {
CONFIG.Dice.terms[DeTMR.DENOMINATION] = DeTMR
CONFIG.Dice.terms[DeDraconique.DENOMINATION] = DeDraconique
CONFIG.Dice.terms[DeHeure.DENOMINATION] = DeHeure
CONFIG.Dice.terms[DeTMR.DENOMINATION] = DeTMR;
CONFIG.Dice.terms[DeDraconique.DENOMINATION] = DeDraconique;
CONFIG.Dice.terms[DeHeure.DENOMINATION] = DeHeure;
}
static onReady() {
@ -129,18 +132,6 @@ export class RdDDice {
}
}
static diceSoNiceReady(dice3d) {
dice3d.DiceFactory.systems.keys().forEach(system => {
dice3d.addDicePreset(DeTMR.diceSoNiceData(system));
dice3d.addDicePreset(DeDraconique.diceSoNiceData(system));
dice3d.addDicePreset(DeHeure.diceSoNiceData(system));
})
}
static async rollHeure(options = { showDice: HIDE_DICE }) {
return await RdDDice.rollTotal("1dh", options) - 1
}
static async rollTotal(formula, options = { showDice: HIDE_DICE }) {
return (await RdDDice.roll(formula, options)).total;
}
@ -160,13 +151,21 @@ export class RdDDice {
return array[roll - 1];
}
static diceSoNiceReady(dice3d) {
for (const system of Object.keys(dice3d.DiceFactory.systems)) {
dice3d.addDicePreset(DeTMR.diceSoNiceData(system));
dice3d.addDicePreset(DeDraconique.diceSoNiceData(system));
dice3d.addDicePreset(DeHeure.diceSoNiceData(system));
}
}
/* -------------------------------------------- */
static async showDiceSoNice(roll, options) {
if (options.showDice == HIDE_DICE || !game.modules.get("dice-so-nice")?.active || !game.dice3d) {
return;
}
let { whisper, blind } = ChatUtility.applyRollMode({}, options?.rollMode);
let { whisper, blind } = RdDDice._getWhisperBlind(options);
if (options.forceDiceResult?.total) {
let terms = await RdDDice._getForcedTerms(options);
if (terms) {
@ -220,4 +219,24 @@ export class RdDDice {
await roll.evaluate();
return roll.total;
}
static _getWhisperBlind(options) {
let whisper = undefined;
let blind = false;
let rollMode = options.rollMode ?? game.settings.get("core", "rollMode");
switch (rollMode) {
case "blindroll": //GM only
blind = true;
case "gmroll": //GM + rolling player
whisper = ChatUtility.getUsers(user => user.isGM);
break;
case "roll": //everybody
whisper = ChatUtility.getUsers(user => user.active);
break;
case "selfroll":
whisper = [game.user.id];
break;
}
return { whisper, blind };
}
}

View File

@ -5,7 +5,7 @@ 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 { TYPES } from "./item.js";
/* -------------------------------------------- */
@ -92,18 +92,18 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static isEmpoignadeEnCours(actor) {
return actor.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.pointsemp > 0)
return actor.itemTypes[TYPES.empoignade].find(it => it.system.pointsemp > 0)
}
/* -------------------------------------------- */
static getEmpoignadeById(actor, id) {
let emp = actor.itemTypes[ITEM_TYPES.empoignade].find(it => it.system.empoignadeid == id)
let emp = actor.itemTypes[TYPES.empoignade].find(it => it.system.empoignadeid == id)
return emp && foundry.utils.duplicate(emp) || undefined;
}
/* -------------------------------------------- */
static getEmpoignade(attacker, defender) {
let emp = attacker.itemTypes[ITEM_TYPES.empoignade].find(it =>
let emp = attacker.itemTypes[TYPES.empoignade].find(it =>
(it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
(it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
)
@ -158,12 +158,9 @@ export class RdDEmpoignade {
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
//console.log("W.", empoignade, defender.hasArmeeMeleeEquipee())
if ((isNouvelle || empoignade.system.pointsemp == 0) && defender.hasArmeeMeleeEquipee()) {
ChatUtility.createChatWithRollMode(
{
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.html`, { attacker: attacker, defender: defender })
},
attacker
)
ChatUtility.createChatWithRollMode(attacker.name, {
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.html`, { attacker: attacker, defender: defender })
})
} else {
await this.onAttaqueEmpoignadeValidee(attacker, defender)
}
@ -216,7 +213,7 @@ export class RdDEmpoignade {
competence: attacker.getCompetenceCorpsACorps()
}
const msg = await ChatMessage.create({
whisper: ChatUtility.getOwners(attacker),
whisper: ChatUtility.getWhisperRecipientsAndGMs(attacker.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.html`, rollData)
})
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
@ -251,7 +248,7 @@ export class RdDEmpoignade {
if (rollData.rolled.isPart) {
rollData.particuliere = "finesse";
}
let msg = await RdDResolutionTable.displayRollData(rollData, defender, 'chat-empoignade-resultat.html');
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-resultat.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
@ -430,7 +427,7 @@ export class RdDEmpoignade {
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 }
system: { description: "", empoignadeid: randomID(16), compteempoigne: 0, empoigneurid: attacker.id, empoigneid: defender.id, ptsemp: 0, empoigneurname: attacker.name, empoignename: defender.name }
},
{
temporary: true

View File

@ -6,7 +6,7 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
export class RdDHerbes extends Item {
/* -------------------------------------------- */
static async onReady() {
static async initializeHerbes() {
this.herbesSoins = await RdDHerbes.listCategorieHerbes('Soin');
this.herbesRepos = await RdDHerbes.listCategorieHerbes('Repos');
}

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 { TYPES } from "./item.js";
export class RdDHotbar {
@ -35,7 +35,7 @@ export class RdDHotbar {
static async addToHotbar(item, slot) {
switch (item?.type ?? '') {
case ITEM_TYPES.arme:
case TYPES.arme:
{
// Les armes peuvent avoir plusieurs usages
if (item.system.competence != '') {
@ -54,12 +54,12 @@ export class RdDHotbar {
}
}
return
case ITEM_TYPES.competencecreature:
case TYPES.competencecreature:
const categorie = RdDItemCompetenceCreature.getCategorieAttaque(item) ?? 'competence';
await this.createItemMacro(item, slot, categorie)
return
default:
case ITEM_TYPES.competence:
case TYPES.competence:
await this.createItemMacro(item, slot++, 'competence')
if (item.isCorpsACorps()) {
await this.createItemMacro(item, slot++, 'pugilat')
@ -79,7 +79,8 @@ export class RdDHotbar {
* Actor - open actor sheet
* Journal - open journal sheet
*/
static initHooks() {
static initDropbar() {
Hooks.on('hotbarDrop', (bar, documentData, slot) => {
// Create item macro if rollable item - weapon, spell, prayer, trait, or skill
@ -87,9 +88,9 @@ export class RdDHotbar {
const item = fromUuidSync(documentData.uuid) ?? this.actor.items.get(documentData.uuid)
console.log('DROP', documentData, item)
switch (item?.type) {
case ITEM_TYPES.arme:
case ITEM_TYPES.competence:
case ITEM_TYPES.competencecreature:
case TYPES.arme:
case TYPES.competence:
case TYPES.competencecreature:
this.addToHotbar(item, slot)
return false
}
@ -115,9 +116,9 @@ export class RdDHotbar {
// Trigger the item roll
switch (item.type) {
case ITEM_TYPES.arme:
case TYPES.arme:
return actor.rollArme(item, categorieArme);
case ITEM_TYPES.competence:
case TYPES.competence:
if (item.isCorpsACorps()) {
switch (categorieArme) {
case 'pugilat':
@ -127,7 +128,7 @@ export class RdDHotbar {
}
}
return actor.rollCompetence(item);
case ITEM_TYPES.competencecreature:
case TYPES.competencecreature:
return item.system.iscombat && !item.system.isparade
? actor.rollArme(item, categorieArme)
: actor.rollCompetence(item);

View File

@ -1,74 +1,69 @@
import { SYSTEM_RDD, SYSTEM_SOCKET_ID, RDD_CONFIG } from "./constants.js"
import { Migrations } from './migrations.js'
import { SYSTEM_RDD, SYSTEM_SOCKET_ID, RDD_CONFIG } from "./constants.js";
import { Migrations } from './migrations.js';
import { RdDUtility } from "./rdd-utility.js"
import { TMRUtility } from "./tmr-utility.js"
import { TMRRencontres } from "./tmr-rencontres.js"
import { RdDCalendrier } from "./time/rdd-calendrier.js"
import { RdDTimestamp } from "./time/rdd-timestamp.js"
import { DialogChronologie } from "./dialog-chronologie.js"
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { RdDCalendrier } from "./time/rdd-calendrier.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { DialogChronologie } from "./dialog-chronologie.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js"
import { RdDTokenHud } from "./rdd-token-hud.js"
import { RdDCommands } from "./rdd-commands.js"
import { RdDCombatManager, RdDCombat } from "./rdd-combat.js"
import { ChatUtility } from "./chat-utility.js"
import { StatusEffects } from "./settings/status-effects.js"
import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js"
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTokenHud } from "./rdd-token-hud.js";
import { RdDCommands } from "./rdd-commands.js";
import { RdDCombatManager, RdDCombat } from "./rdd-combat.js";
import { ChatUtility } from "./chat-utility.js";
import { StatusEffects } from "./settings/status-effects.js";
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"
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";
import { SystemCompendiums } from "./settings/system-compendiums.js"
import { Environnement } from "./environnement.js"
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Environnement } from "./environnement.js";
import { RdDActor } from "./actor.js"
import { RdDBaseActor } from "./actor/base-actor.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"
import { RdDActorEntiteSheet } from "./actor/entite-sheet.js"
import { RdDActorVehiculeSheet } from "./actor/vehicule-sheet.js"
import { RdDActor } from "./actor.js";
import { RdDBaseActor } from "./actor/base-actor.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";
import { RdDActorEntiteSheet } from "./actor/entite-sheet.js";
import { RdDActorVehiculeSheet } from "./actor/vehicule-sheet.js";
import { RdDItem } from "./item.js"
import { RdDItemBlessure } from "./item/blessure.js"
import { RdDItemService } from "./item/service.js"
import { RdDItemMaladie } from "./item/maladie.js"
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 { RdDItemSouffle } from "./item/souffle.js"
import { RdDRencontre } from "./item/rencontre.js"
import { RdDItem } from "./item.js";
import { RdDItemBlessure } from "./item/blessure.js";
import { RdDItemService } from "./item/service.js";
import { RdDItemMaladie } from "./item/maladie.js";
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 { RdDItemSouffle } from "./item/souffle.js";
import { RdDRencontre } from "./item/rencontre.js";
import { RdDItemSheet } from "./item-sheet.js"
import { RdDBlessureItemSheet } from "./item/sheet-blessure.js"
import { RdDServiceItemSheet } from "./item/sheet-service.js"
import { RdDRencontreItemSheet } from "./item/sheet-rencontre.js"
import { RdDHerbeItemSheet } from "./item/sheet-herbe.js"
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 { 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 { RdDItemSheet } from "./item-sheet.js";
import { RdDBlessureItemSheet } from "./item/sheet-blessure.js";
import { RdDServiceItemSheet } from "./item/sheet-service.js";
import { RdDRencontreItemSheet } from "./item/sheet-rencontre.js";
import { RdDHerbeItemSheet } from "./item/sheet-herbe.js";
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 { AppAstrologie } from "./sommeil/app-astrologie.js";
import { RdDItemArmure } from "./item/armure.js";
import { AutoAdjustDarkness as AutoAdjustDarkness } from "./time/auto-adjust-darkness.js";
import { RdDCreature } from "./actor/creature.js";
import { RdDTMRDialog } from "./rdd-tmr-dialog.js";
/**
* RdD system
@ -81,14 +76,12 @@ export class SystemReveDeDragon {
const system = new SystemReveDeDragon()
Hooks.once('init', async () => await system.onInit())
Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d))
Hooks.once('ready', async () => await system.onReady())
Hooks.once('ready', () => system.onReady())
}
constructor() {
this.config = RDD_CONFIG
this.RdDUtility = RdDUtility
this.RdDHotbar = RdDHotbar
this.RdDStatBlockParser = RdDStatBlockParser
this.RdDUtility = RdDUtility;
this.RdDHotbar = RdDHotbar;
this.itemClasses = {
armure: RdDItemArmure,
blessure: RdDItemBlessure,
@ -114,51 +107,43 @@ export class SystemReveDeDragon {
/* Foundry VTT Initialization */
/* -------------------------------------------- */
async onInit() {
game.system.rdd = this
this.AppAstrologie = AppAstrologie
game.system.rdd = this;
game.system.rdd.config = RDD_CONFIG;
this.AppAstrologie = AppAstrologie;
console.log(`Initializing Reve de Dragon System Settings`)
console.log(`Initializing Reve de Dragon System`);
// preload handlebars templates
RdDUtility.preloadHandlebarsTemplates()
AppPersonnageAleatoire.preloadHandlebars()
RdDUtility.preloadHandlebarsTemplates();
/* -------------------------------------------- */
ReglesOptionnelles.initSettings()
OptionsAvancees.initSettings()
AutoAdjustDarkness.initSettings()
RdDTimestamp.initSettings()
RdDCalendrier.initSettings()
SystemCompendiums.initSettings()
DialogChronologie.initSettings()
RdDTMRDialog.initSettings()
Environnement.initSettings()
this.initSettings()
this.initSystemSettings();
/* -------------------------------------------- */
// Set an initiative formula for the system
CONFIG.Combat.initiative = { formula: "1+(1d6/10)", decimals: 2 }
CONFIG.Combat.initiative = {
formula: "1+(1d6/10)",
decimals: 2
};
/* -------------------------------------------- */
console.log(`Initializing Reve de Dragon Socket handlers`)
game.socket.on(SYSTEM_SOCKET_ID, async (sockmsg) => {
console.log(">>>>> MSG RECV", sockmsg)
console.log(">>>>> MSG RECV", sockmsg);
try {
RdDUtility.onSocketMessage(sockmsg)
RdDCombat.onSocketMessage(sockmsg)
ChatUtility.onSocketMessage(sockmsg)
RdDBaseActor.onSocketMessage(sockmsg)
RdDUtility.onSocketMessage(sockmsg);
RdDCombat.onSocketMessage(sockmsg);
ChatUtility.onSocketMessage(sockmsg);
RdDBaseActor.onSocketMessage(sockmsg);
} catch (e) {
console.error('game.socket.on(SYSTEM_SOCKET_ID) Exception: ', sockmsg, ' => ', e)
}
})
});
/* -------------------------------------------- */
// Define custom Entity classes
console.log(`Initializing Reve de Dragon Documents`)
CONFIG.Actor.documentClass = RdDBaseActor
CONFIG.Item.documentClass = RdDItem
CONFIG.Actor.documentClass = RdDBaseActor;
CONFIG.Item.documentClass = RdDItem;
CONFIG.RDD = {
resolutionTable: RdDResolutionTable.resolutionTable,
carac_array: RdDUtility.getCaracArray(),
@ -168,31 +153,30 @@ export class SystemReveDeDragon {
/* -------------------------------------------- */
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet)
Actors.registerSheet(SYSTEM_RDD, RdDCommerceSheet, { types: ["commerce"], makeDefault: true })
Actors.registerSheet(SYSTEM_RDD, RdDActorSheet, { types: ["personnage"], makeDefault: true })
Actors.registerSheet(SYSTEM_RDD, RdDCreatureSheet, { types: ["creature"], makeDefault: true })
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()
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet(SYSTEM_RDD, RdDCommerceSheet, { types: ["commerce"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorSheet, { types: ["personnage"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDCreatureSheet, { types: ["creature"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorVehiculeSheet, { types: ["vehicule"], makeDefault: true });
Actors.registerSheet(SYSTEM_RDD, RdDActorEntiteSheet, { types: ["entite"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
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)
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);
Items.registerSheet(SYSTEM_RDD, RdDItemInventaireSheet, {
types: [
"objet", "arme", "armure", "livre", "potion", "munition",
"monnaie", "nourritureboisson", "gemme",
], makeDefault: true
})
});
Items.registerSheet(SYSTEM_RDD, RdDItemSheet, {
types: [
"competence", "competencecreature",
@ -201,29 +185,34 @@ export class SystemReveDeDragon {
"nombreastral", "tache", "maladie", "poison", "possession",
"tarot", "extraitpoetique", "empoignade"
], makeDefault: true
})
});
CONFIG.Combat.documentClass = RdDCombatManager;
// préparation des différents modules
console.log(`Initializing Reve de Dragon Hooks and handlers`)
CONFIG.Combat.documentClass = RdDCombatManager
ChatUtility.init()
RdDUtility.initHooks()
RdDDice.init()
RdDCommands.init()
RdDCombatManager.init()
RdDTokenHud.init()
RdDBaseActor.init()
RdDCompendiumOrganiser.init()
AutoAdjustDarkness.init();
RdDTimestamp.init();
RdDCalendrier.init();
SystemCompendiums.init();
DialogChronologie.init();
ReglesOptionnelles.init();
RdDUtility.init();
RdDDice.init();
RdDCommands.init();
RdDCombatManager.init();
RdDTokenHud.init();
RdDBaseActor.init();
RdDCompendiumOrganiser.init();
EffetsDraconiques.init()
TMRUtility.init()
RdDHotbar.initHooks()
RdDPossession.init()
TMRRencontres.init()
ExportScriptarium.init()
TMRUtility.init();
await RdDTMRDialog.init()
RdDHotbar.initDropbar();
RdDPossession.init();
TMRRencontres.init();
Environnement.init();
}
initSettings() {
// TODO: déplacer vers les modules correspondants
initSystemSettings() {
game.settings.register(SYSTEM_RDD, "accorder-entite-cauchemar", {
name: "Accorder le rêve aux entités",
hint: "A quel moment les personnages doivent accorder leur rêve aux entités de cauchemar",
@ -236,7 +225,7 @@ export class SystemReveDeDragon {
"avant-encaissement": "Avant l'encaissement",
},
default: "avant-encaissement"
})
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "supprimer-dialogues-combat-chat", {
@ -246,7 +235,7 @@ export class SystemReveDeDragon {
config: true,
default: true,
type: Boolean
})
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "activer-sons-audio", {
@ -256,8 +245,7 @@ export class SystemReveDeDragon {
config: true,
default: true,
type: Boolean
})
});
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "appliquer-famine-soif", {
name: "Notifier de la famine et la soif pour",
@ -271,17 +259,7 @@ export class SystemReveDeDragon {
"famine-soif": "la famine et la soif",
},
default: "aucun"
})
}
static async setupAccueil() {
let exists = game.scenes.find(j => j.name == "Accueil RdD");
if (!exists) {
const scenes = await SystemCompendiums.loadCompendium("foundryvtt-reve-de-dragon.scenes-rdd")
let newDocuments = scenes.filter(i => i.name == "Accueil RdD");
await game.scenes.documentClass.create(newDocuments);
game.scenes.find(i => i.name == "Accueil RdD").activate();
}
});
}
async onReady() {
@ -289,50 +267,47 @@ export class SystemReveDeDragon {
/* -------------------------------------------- */
/* Foundry VTT Initialization */
/* -------------------------------------------- */
game.system.rdd.calendrier = new RdDCalendrier()
if (Misc.isFirstConnectedGM()) {
new Migrations().migrate()
this.messageDeBienvenue()
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
game.system.rdd.calendrier = new RdDCalendrier();
if (Misc.isUniqueConnectedGM()) {
new Migrations().migrate();
this.messageDeBienvenue();
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter=>{
console.log("ClassCounter loaded", moduleCounter)
moduleCounter.ClassCounter.registerUsageCount()
}).catch(err =>
}).catch(err=>
console.log("No stats available, giving up.")
)
}
StatusEffects.onReady()
RdDHerbes.onReady()
RdDDice.onReady()
RdDStatBlockParser.parseStatBlock()
StatusEffects.onReady();
RdDHerbes.initializeHerbes();
RdDDice.onReady();
/* -------------------------------------------- */
/* Affiche/Init le calendrier */
game.system.rdd.calendrier.display()
game.system.rdd.calendrier.display();
// Avertissement si joueur sans personnage
if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Vous n'êtes connecté à aucun personnage !")
ui.notifications.info("Attention ! Vous n'êtes connecté à aucun personnage !");
ChatMessage.create({
content: "<b>ATTENTION</b> Le joueur " + game.user.name + " n'est connecté à aucun personnage !",
user: game.user.id
})
});
}
SystemReveDeDragon.setupAccueil()
}
/* -------------------------------------------- */
messageDeBienvenue() {
if (game.user.isGM) {
ChatUtility.removeChatMessageContaining('<div id="message-bienvenue-rdd">')
ChatUtility.removeChatMessageContaining('<div id="message-bienvenue-rdd">');
ChatMessage.create({
user: game.user.id,
content: `<div id="message-bienvenue-rdd"><span class="rdd-roll-part">Bienvenue dans le Rêve des Dragons !</span>
<br>Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}
<br>La commande <code>/aide</code> dans le chat permet de voir les commandes spécifiques à Rêve de Dragon.</div>
` })
` });
}
}
}
SystemReveDeDragon.start()
SystemReveDeDragon.start();

View File

@ -1,4 +1,3 @@
import { ChatUtility } from "./chat-utility.js"
const vents = [
{ min: 0, max: 0, valeur: 'Calme' },
@ -118,7 +117,7 @@ export class RdDMeteo {
ChatMessage.create({
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-meteo.html', meteo),
whisper: ChatUtility.getGMs()
whisper: ChatMessage.getWhisperRecipients('GM')
});
}

View File

@ -1,5 +1,4 @@
import { RdDBaseActor } from "./actor/base-actor.js";
import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
@ -12,15 +11,11 @@ const words = ['pore', 'pre', 'flor', 'lane', 'turlu', 'pin', 'a', 'alph', 'i',
/* -------------------------------------------- */
export class RdDNameGen {
static async proposeName(msg, params) {
static async getName(msg, params) {
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-command-nom.html`, {
nom: await RdDNameGen.generate()
nom: Misc.upperFirst(await RdDDice.rollOneOf(words) + await RdDDice.rollOneOf(words))
});
ChatMessage.create({ content: html, whisper: ChatUtility.getGMs() });
}
static async generate() {
return Misc.upperFirst(await RdDDice.rollOneOf(words) + await RdDDice.rollOneOf(words));
ChatMessage.create({ content: html, whisper: ChatMessage.getWhisperRecipients("GM") });
}
static async onCreerActeur(event) {

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 { TYPES } from "./item.js";
/* -------------------------------------------- */
/* On part du principe qu'une entité démarre tjs
@ -20,9 +20,9 @@ export class RdDPossession {
/* -------------------------------------------- */
static searchPossessionFromEntite(attacker, defender) {
let poss = attacker.items.find(poss => poss.type == ITEM_TYPES.possession && poss.system.victime.actorid == defender.id);
let poss = attacker.items.find(poss => poss.type == TYPES.possession && poss.system.victime.actorid == defender.id);
if (!poss) {
poss = defender.items.find(poss => poss.type == ITEM_TYPES.possession && poss.system.victime.actorid == defender.id);
poss = defender.items.find(poss => poss.type == TYPES.possession && poss.system.victime.actorid == defender.id);
}
return poss && foundry.utils.duplicate(poss) || undefined;
}
@ -131,7 +131,7 @@ export class RdDPossession {
}
const possession = (rollData.isECNIDefender ? rollData.attacker : rollData.defender).getPossession(rollData.possession.system.possessionid)
RdDPossession.storePossessionAttaque(possession, rollData)
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.html');
await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html');
}
/* -------------------------------------------- */
@ -171,7 +171,7 @@ export class RdDPossession {
rollData.possession = possession
RdDPossession.$updateEtatPossession(rollData.possession)
await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-resultat-possession.html')
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-resultat-possession.html')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
// conjuration
victime.deleteEmbeddedDocuments("Item", [rollData.possession._id])
@ -230,7 +230,7 @@ export class RdDPossession {
system: {
description: "", typepossession: attacker.name,
possede: false,
possessionid: foundry.utils.randomID(16),
possessionid: randomID(16),
entite: { actorid: attacker.id },
victime: { actorid: defender.id },
compteur: 0

View File

@ -91,14 +91,13 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
return await ChatUtility.createChatWithRollMode(
{ content: await RdDResolutionTable.buildRollDataHtml(rollData, template) },
actor
)
return await ChatUtility.createChatWithRollMode(RdDResolutionTable.actorChatName(actor), {
content: await RdDResolutionTable.buildRollDataHtml(rollData, template)
});
}
static actorChatName(actor) {
return actor ?? game.user.name;
return actor?.userName ?? game.user.name;
}
/* -------------------------------------------- */
@ -139,14 +138,14 @@ export class RdDResolutionTable {
if (carac == 0) {
return NaN;
}
if (rolled >= carac) {
const upper = Math.ceil(rolled / carac);
return 2 * upper - 10
if (rolled >= carac){
const upper = Math.ceil(rolled/carac);
return 2*upper -10
}
if (rolled > Math.floor(carac / 2)) {
if (rolled > Math.floor(carac/2)) {
return -8
}
if (rolled > Math.floor(carac / 4)) {
if (rolled > Math.floor(carac/4)) {
return -9
}
if (rolled > 1) {
@ -266,7 +265,7 @@ export class RdDResolutionTable {
}
/* -------------------------------------------- */
static subTable(carac, level, delta = { carac: 2, level: 5 }) {
static subTable(carac, level, delta = { carac: 2, level: 5}) {
return {
carac,
level,
@ -288,8 +287,8 @@ export class RdDResolutionTable {
carac: carac,
difficulte: level,
min: minLevel,
rows: Misc.intArray(minCarac, maxCarac + 1),
cols: Misc.intArray(minLevel, maxLevel + 1)
rows: Misc.intArray(minCarac, maxCarac+1),
cols: Misc.intArray(minLevel, maxLevel+1)
});
}

View File

@ -72,6 +72,6 @@ export class RdDEncaisser extends Dialog {
encaisserSpecial: this.encaisserSpecial,
mortalite: mortalite
}
})
});
}
}

View File

@ -307,7 +307,7 @@ export class RdDRoll extends Dialog {
async updateRollResult(html) {
const rollData = this.rollData;
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor)
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor.getBonusDegat())
rollData.caracValue = parseInt(rollData.selectedCarac.value)
rollData.dmg.mortalite = rollData.dmg.mortalite ?? 'mortel';
rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac);

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 { TYPES } from "./item.js";
import { Misc } from "./misc.js";
const TMR_DISPLAY_SIZE = {
@ -34,7 +34,7 @@ const TMR_DISPLAY_SIZE = {
/* -------------------------------------------- */
export class RdDTMRDialog extends Dialog {
static initSettings() {
static async init() {
game.settings.register(SYSTEM_RDD, TMR_DISPLAY_SIZE.code, {
name: 'Taille des cases des TMR',
hint: "Taille en pixel des cases des TMR (réglable directement dans la fenêtre des TMR)",
@ -50,7 +50,7 @@ export class RdDTMRDialog extends Dialog {
await PixiTMR.init()
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', tmrData);
if (tmrData.mode != 'visu' && !game.user.isGM) {
ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatUtility.getGMs() });
ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatMessage.getWhisperRecipients("GM") });
}
return new RdDTMRDialog(html, actor, tmrData)
}
@ -82,7 +82,7 @@ export class RdDTMRDialog extends Dialog {
this.rencontreState = 'aucune';
this.subdialog = undefined
this.displaySize = undefined
if (!this.viewOnly && !game.user.isGM) {
if (!this.viewOnly) {
this._tellToGM(this.actor.name + " monte dans les terres médianes (" + tmrData.mode + ")");
}
this.callbacksOnAnimate = [];
@ -129,7 +129,7 @@ export class RdDTMRDialog extends Dialog {
this.html.find('form.tmr-dialog *').click(event => this.subdialog?.bringToTop());
// Roll Sort
this.html.find('.lancer-sort').click(event => this.lancerUnSort());
this.html.find('.lancer-sort').click(event => this.actor.rollUnSort(this._getCoordActor()));
this.html.find('.lire-signe-draconique').click(event => this.actor.rollLireSigneDraconique(this._getCoordActor()));
this.html.find('img.tmr-move').click(event => this.deplacementTMR(this.html.find(event.currentTarget)?.data('move')));
@ -142,13 +142,6 @@ export class RdDTMRDialog extends Dialog {
this.updateValuesDisplay();
}
lancerUnSort() {
if (this.subdialog) {
return this.forceTMRContinueAction();
}
return this.actor.rollUnSort(this._getCoordActor());
}
async onDeplacement() {
await this.manageRencontre(TMRUtility.getTMR(this._getCoordActor()));
}
@ -171,25 +164,23 @@ export class RdDTMRDialog extends Dialog {
async forceTMRDisplay() {
if (this.rendered) {
this.bringToTop()
this.bringSubDialogToTop();
}
}
bringSubDialogToTop() {
if (this.subdialog?.bringToTop && this.subdialog?.element[0]) {
this.subdialog.bringToTop();
if (this.subdialog?.bringToTop) {
this.subdialog.bringToTop();
}
}
}
async restoreTMRAfterAction() {
this.subdialog = undefined
await this.maximize()
this.bringToTop()
await this.maximize();
this.bringToTop();
}
forceTMRContinueAction() {
ui.notifications.warn('Vous devez finir votre action avant de continuer dans les TMR');
this.bringSubDialogToTop();
if (this.subdialog?.bringToTop) {
this.subdialog.bringToTop();
}
return;
}
@ -208,11 +199,11 @@ export class RdDTMRDialog extends Dialog {
}
get sortsReserve() {
return this.actor.itemTypes[ITEM_TYPES.sortreserve];
return this.actor.itemTypes[TYPES.sortreserve];
}
getSortsReserve(coord) {
return this.actor.itemTypes[ITEM_TYPES.sortreserve].filter(// Reserve sur une case fleuve ou normale
return this.actor.itemTypes[TYPES.sortreserve].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
@ -270,7 +261,7 @@ export class RdDTMRDialog extends Dialog {
}
_getTokensSortsReserve() {
const sortsReserve = this.actor.itemTypes[ITEM_TYPES.sortreserve];
const sortsReserve = this.actor.itemTypes[TYPES.sortreserve];
return Misc.concat(sortsReserve.map(sortReserve =>
EffetsDraconiques.sortReserve.tokens(this.pixiTMR, sortReserve, () => sortReserve.system.coord)))
}
@ -496,7 +487,7 @@ export class RdDTMRDialog extends Dialog {
rencData.message = this.formatMessageRencontre(rencData, result.message);
ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.html`, rencData)
});
@ -571,20 +562,12 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
_tellToGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getGMs()
});
ChatMessage.create({ content: message, user: game.user.id, whisper: ChatMessage.getWhisperRecipients("GM") });
}
/* -------------------------------------------- */
_tellToUserAndGM(message) {
ChatMessage.create({
user: game.user.id,
content: message,
whisper: ChatUtility.getUserAndGMs()
})
ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients("GM")) });
}
/* -------------------------------------------- */
@ -723,7 +706,7 @@ export class RdDTMRDialog extends Dialog {
}
rollData.poesie = await Poetique.getExtrait();
ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData)
});
if (rollData.rolled.isEchec) {
@ -747,7 +730,7 @@ export class RdDTMRDialog extends Dialog {
if (this.isCaseMaitrisee(tmr.coord)) {
ChatMessage.create({
content: tmr.label + ": cette case humide est déja maitrisée grâce à votre Tête <strong>Quête des Eaux</strong>",
whisper: ChatUtility.getOwners(this.actor)
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return false;
}
@ -759,14 +742,14 @@ export class RdDTMRDialog extends Dialog {
if (tmr.type == 'pont' && EffetsDraconiques.isPontImpraticable(this.actor)) {
ChatMessage.create({
content: tmr.label + ": Vous êtes sous le coup d'une Impraticabilité des Ponts : ce pont doit être maîtrisé comme une case humide.",
whisper: ChatUtility.getOwners(this.actor)
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return true;
}
if (this.isCaseInondee(tmr.coord)) {
ChatMessage.create({
content: tmr.label + ": cette case est inondée, elle doit être maîtrisée comme une case humide.",
whisper: ChatUtility.getOwners(this.actor)
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return true;
}
@ -840,7 +823,7 @@ export class RdDTMRDialog extends Dialog {
}
rollData.poesie = await Poetique.getExtrait();
ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData)
});
if (rollData.rolled.isEchec) {
@ -890,16 +873,17 @@ export class RdDTMRDialog extends Dialog {
const reserveSecurite = EffetsDraconiques.isReserveEnSecurite(this.actor);
const reserveExtensible = this.isReserveExtensible(coord);
if (!EffetsDraconiques.isUrgenceDraconique(this.actor) && (reserveSecurite || reserveExtensible)) {
ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-demande-declencher-sort.hbs`, {
actor: this.actor,
sorts: sorts,
coord: coord,
tete: { reserveSecurite: reserveSecurite, reserveExtensible: reserveExtensible }
}),
whisper: ChatUtility.getOwners(this.actor)
const msg = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-demande-declencher-sort.hbs`, {
actor: this.actor,
sorts: sorts,
coord: coord,
tete: { reserveSecurite: reserveSecurite, reserveExtensible: reserveExtensible }
})
return
ChatMessage.create({
content: msg,
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
return;
}
await this.processSortReserve(sorts[0]);
}
@ -913,8 +897,9 @@ export class RdDTMRDialog extends Dialog {
this.processSortReserve(sort);
} else {
ChatMessage.create({
content: "Une erreur est survenue : impossible de récupérer le sort en réserve demandé.",
whisper: ChatUtility.getOwners(this.actor)
content:
"Une erreur est survenue : impossible de récupérer le sort en réserve demandé.",
whisper: ChatMessage.getWhisperRecipients(game.user.name),
});
}
}
@ -1097,7 +1082,7 @@ export class RdDTMRDialog extends Dialog {
async notifierResonanceSigneDraconique(coord) {
if (!this.viewOnly && this.actor.isResonanceSigneDraconique(coord)) {
ChatMessage.create({
whisper: ChatUtility.getOwners(this.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-resonance.html`, { alias: this.actor.name, typeTMR: TMRUtility.getTMRType(coord) })
});
}

View File

@ -28,15 +28,16 @@ export class RdDTokenHud {
await RdDTokenHud.addExtensionHudSoins(html, actor);
if (isCombat) {
const combatant = game.combat.combatants.find(c => c.tokenId == tokenId)
const actor = RdDCombatManager.getActorCombatant(combatant, { warning: false })
if (actor) {
let actions = RdDCombatManager.listActionsActorCombatant(actor)
// initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions)
// combat
await RdDTokenHud.addExtensionHudCombat(html, combatant, token, actions)
let combatant = game.combat.combatants.find(c => c.tokenId == tokenId);
if (!(combatant?.actor)) {
ui.notifications.warn(`Le combatant ${token.name} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return;
}
let actions = RdDCombatManager.listActionsCombat(combatant);
// initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions);
// combat
await RdDTokenHud.addExtensionHudCombat(html, combatant, actions);
}
@ -67,8 +68,8 @@ export class RdDTokenHud {
});
}
static async addExtensionHudCombat(html, combatant, token, actions) {
const hudData = { combatant, token, actions, commandes: [] };
static async addExtensionHudCombat(html, combatant, actions) {
const hudData = { combatant, actions, commandes: [] };
const controlIconTarget = html.find('.control-icon[data-action=target]');
await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData,
(event) => {
@ -79,7 +80,7 @@ export class RdDTokenHud {
combatant.actor.conjurerPossession(possession);
}
else {
combatant.actor.rollArme(action, 'competence', token)
combatant.actor.rollArme(action);
}
});
}

View File

@ -19,7 +19,6 @@ 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";
/* -------------------------------------------- */
// This table starts at 0 -> niveau -10
@ -99,7 +98,9 @@ export class RdDUtility {
// persistent handling of conteneur show/hide
static afficheContenu = {}
/* -------------------------------------------- */
static async initHooks() {
static async init() {
Hooks.on("renderChatMessage", async (app, html, msg) => await ChatUtility.onRenderChatMessage(app, html, msg))
Hooks.on("createChatMessage", async (chatMessage, options, id) => await ChatUtility.onCreateChatMessage(chatMessage, options, id))
Hooks.on('renderChatLog', (log, html, chatLog) => RdDUtility.chatListeners(html))
}
@ -261,6 +262,8 @@ export class RdDUtility {
];
Handlebars.registerHelper('either', (a, b) => a ?? b);
Handlebars.registerHelper('computeResolutionScore', (row, col) => RdDResolutionTable.computePercentage(row, col));
Handlebars.registerHelper('computeResolutionChances', (row, col) => RdDResolutionTable.computeChances(row, col));
Handlebars.registerHelper('upperFirst', str => Misc.upperFirst(str ?? 'Null'));
Handlebars.registerHelper('lowerFirst', str => Misc.lowerFirst(str ?? 'Null'));
Handlebars.registerHelper('upper', str => str?.toUpperCase() ?? '');
@ -269,10 +272,6 @@ export class RdDUtility {
Handlebars.registerHelper('apostrophe', (article, str) => Grammar.apostrophe(article, str));
Handlebars.registerHelper('un', str => Grammar.articleIndetermine(str));
Handlebars.registerHelper('accord', (genre, ...args) => Grammar.accord(genre, args));
Handlebars.registerHelper('RDD_CONFIG', path => RDD_CONFIG[path])
Handlebars.registerHelper('computeResolutionScore', (row, col) => RdDResolutionTable.computePercentage(row, col));
Handlebars.registerHelper('computeResolutionChances', (row, col) => RdDResolutionTable.computeChances(row, col));
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)); });
Handlebars.registerHelper('buildContenuConteneur', (item, options) => { return new Handlebars.SafeString(RdDUtility.buildContenuConteneur(item, options)); });
@ -290,7 +289,6 @@ export class RdDUtility {
Handlebars.registerHelper('array-includes', (array, value) => array.includes(value));
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('isLastIndex', (index, list) => index + 1 >= list.length);
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option));
Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name)));
Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences));
@ -357,15 +355,13 @@ export class RdDUtility {
let objetVersConteneur = {};
// Attribution des objets aux conteneurs
for (let conteneur of conteneurs) {
if (conteneur.isConteneur()) {
conteneur.subItems = [];
for (let id of conteneur.system.contenu ?? []) {
let objet = inventaires.find(objet => (id == objet._id));
if (objet) {
objet.estContenu = true;
objetVersConteneur[id] = conteneur._id;
conteneur.subItems.push(objet);
}
conteneur.subItems = [];
for (let id of conteneur.system.contenu ?? []) {
let objet = inventaires.find(objet => (id == objet._id));
if (objet) {
objet.estContenu = true; // Permet de filtrer ce qui est porté dans le template
objetVersConteneur[id] = conteneur._id;
conteneur.subItems.push(objet);
}
}
}
@ -563,14 +559,14 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async jetEncaissement(actor, rollData, armure, options = { showDice: HIDE_DICE }) {
static async jetEncaissement(rollData, armure, options = { showDice: HIDE_DICE }) {
const diff = Math.abs(rollData.diffLibre);
let formula = RdDUtility.formuleEncaissement(diff, options)
const roll = await RdDDice.roll(formula, options);
RdDUtility.remplaceDeMinParDifficulte(roll, diff, options);
return await RdDUtility.prepareEncaissement(actor, rollData, roll, armure);
return await RdDUtility.prepareEncaissement(rollData, roll, armure);
}
static remplaceDeMinParDifficulte(roll, diff, options) {
@ -602,20 +598,15 @@ export class RdDUtility {
}
/* -------------------------------------------- */
static async prepareEncaissement(actor, rollData, roll, armure) {
static async prepareEncaissement(rollData, roll, armure) {
// La difficulté d'ataque s'ajoute aux dégâts
const bonusDegatsDiffLibre = ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(rollData.diffLibre ?? 0) : 0
const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre
const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite);
const over20 = Math.max(jetTotal - 20, 0);
encaissement.dmg = rollData.dmg
if (ReglesOptionnelles.isUsing('localisation-aleatoire')) {
encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(actor.type)
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'
}
else {
encaissement.dmg.loc = { label: '' }
}
encaissement.dmg = rollData.dmg;
encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(this.type);
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;';
encaissement.dmg.bonusDegatsDiffLibre = bonusDegatsDiffLibre
encaissement.roll = roll;
encaissement.armure = armure;
@ -648,16 +639,18 @@ export class RdDUtility {
/* -------------------------------------------- */
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
case "msg_gm_chat_message":
return ChatUtility.handleGMChatMessage(sockmsg.data);
case "msg_app_astrologie_refresh":
return Hooks.callAll(APP_ASTROLOGIE_REFRESH)
return Hooks.callAll(APP_ASTROLOGIE_REFRESH);
case "msg_request_nombre_astral":
return game.system.rdd.calendrier.requestNombreAstral(sockmsg.data)
return game.system.rdd.calendrier.requestNombreAstral(sockmsg.data);
case "msg_tmr_move":
let actor = game.actors.get(sockmsg.data.actorId);
if (actor.isOwner || game.user.isGM) {
actor.refreshTMRView()
actor.refreshTMRView();
}
break
break;
}
}
@ -746,15 +739,6 @@ export class RdDUtility {
return undefined;
}
static getSelectedToken(actor) {
if (canvas.tokens.controlled.length > 0) {
const tokens = canvas.tokens.controlled
.filter(it => it.actor.id == actor.id)
return tokens[0]
}
return undefined
}
static getSelectedActor(msgPlayer = undefined) {
if (canvas.tokens.controlled.length == 1) {
let token = canvas.tokens.controlled[0];
@ -780,7 +764,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static createMonnaie(name, cout, img = "", enc = 0.01) {
let piece = {
name: name, type: 'monnaie', img: img, _id: foundry.utils.randomID(16),
name: name, type: 'monnaie', img: img, _id: randomID(16),
dasystemta: {
quantite: 0,
cout: cout,
@ -818,13 +802,17 @@ export class RdDUtility {
user: game.user.id,
rollMode: modeOverride || game.settings.get("core", "rollMode"),
content: content
}
ChatUtility.applyRollMode(chatData)
};
if (["gmroll", "blindroll"].includes(chatData.rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id);
if (chatData.rollMode === "blindroll") chatData["blind"] = true;
else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user];
if (forceWhisper) { // Final force !
chatData.speaker = ChatMessage.getSpeaker();
chatData.whisper = ChatMessage.getWhisperRecipients(forceWhisper);
chatData["speaker"] = ChatMessage.getSpeaker();
chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper);
}
return chatData;
}
@ -881,13 +869,13 @@ export class RdDUtility {
/* -------------------------------------------- */
static afficherHeuresChanceMalchance(heureNaissance) {
if (game.user.isGM) {
const heure = RdDTimestamp.findHeure(heureNaissance)
const heure = RdDTimestamp.findHeure(heureNaissance - 1);
if (heureNaissance && heure) {
const ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance)
const current = game.system.rdd.calendrier.heureCourante()
let ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance);
const current = game.system.rdd.calendrier.heureCourante();
ChatMessage.create({
content: `A l'heure de <strong>${current.label}</strong>, le modificateur de Chance/Malchance est de <strong>${Misc.toSignedString(ajustement)}</strong> pour l'heure de naissance <strong>${heure.label}</strong>.`,
whisper: ChatUtility.getGMs()
whisper: ChatMessage.getWhisperRecipients("GM")
});
}
else if (heureNaissance) {
@ -906,7 +894,7 @@ export class RdDUtility {
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.";
ChatMessage.create({
whisper: ChatUtility.getUserAndGMs(),
whisper: ChatMessage.getWhisperRecipients(game.user.name),
content: message
});
}

View File

@ -111,7 +111,7 @@ export const referenceAjustements = {
isVisible: (rollData, actor) => rollData.arme?.system.magique && Number(rollData.arme?.system.ecaille_efficacite) > 0,
isUsed: (rollData, actor) => rollData.arme?.system.magique && Number(rollData.arme?.system.ecaille_efficacite) > 0,
getLabel: (rollData, actor) => "Ecaille d'Efficacité: ",
getValue: (rollData, actor) => rollData.arme?.system.magique ? Math.max(Number(rollData.arme?.system.ecaille_efficacite), 0) : 0,
getValue: (rollData, actor) => Math.max(Number(rollData.arme?.system.ecaille_efficacite), 0),
},
finesse: {
isUsed: (rollData, actor) => RdDBonus.isDefenseAttaqueFinesse(rollData),

View File

@ -1,91 +0,0 @@
import { SYSTEM_RDD } from "../constants.js"
import { Misc } from "../misc.js"
export const EXPORT_CSV_SCRIPTARIUM = 'export-csv-scriptarium'
const OPTIONS_AVANCEES = [
{ group: 'Menus', name: EXPORT_CSV_SCRIPTARIUM, descr: "Proposer le menu d'export csv Scriptarium" },
]
export class OptionsAvancees extends FormApplication {
static initSettings() {
for (const regle of OPTIONS_AVANCEES) {
const name = regle.name
const id = OptionsAvancees._getId(name)
game.settings.register(SYSTEM_RDD, id, { name: id, scope: regle.scope ?? "world", config: false, default: regle.default == undefined ? true : regle.default, type: Boolean })
}
game.settings.registerMenu(SYSTEM_RDD, "rdd-options-avancees", {
name: "Configurer les options avancées",
label: "Options avancées",
hint: "Ouvre la fenêtre de configuration des options avancées",
icon: "fas fa-bars",
type: OptionsAvancees
})
}
constructor(...args) {
super(...args)
}
static _getId(name) {
return `rdd-advanced-${name}`
}
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
id: "options-avancees",
template: "systems/foundryvtt-reve-de-dragon/templates/settings/options-avancees.hbs",
height: 650,
width: 550,
minimizable: false,
closeOnSubmit: true,
title: "Options avancées"
}, { inplace: false })
}
getData() {
let formData = super.getData()
const regles = OPTIONS_AVANCEES.filter(it => game.user.isGM || it.scope == "client")
.map(it => {
it = foundry.utils.duplicate(it)
it.id = OptionsAvancees._getId(it.name)
it.active = OptionsAvancees.isSet(it.name)
return it
})
formData.regles = regles
formData.groups = Misc.classify(regles, it => it.group)
return formData
}
static getSettingKey(name){
return `${SYSTEM_RDD}.${this._getId(name)}`
}
static isUsing(name) {
return OptionsAvancees.isSet(name)
}
static isSet(name) {
return game.settings.get(SYSTEM_RDD, OptionsAvancees._getId(name))
}
static set(name, value) {
return game.settings.set(SYSTEM_RDD, OptionsAvancees._getId(name), value ? true : false)
}
activateListeners(html) {
html.find(".select-option").click((event) => {
if (event.currentTarget.attributes.name) {
let id = event.currentTarget.attributes.name.value
let isChecked = event.currentTarget.checked
game.settings.set(SYSTEM_RDD, id, isChecked)
}
})
}
async _updateObject(event, formData) {
this.close()
}
}

View File

@ -11,7 +11,6 @@ const listeReglesOptionnelles = [
{ group: 'Récupération', name: 'recuperation-moral', descr: "Le moral revient vers 0 durant Château Dormant"},
{ group: 'Règles de combat', name: 'localisation-aleatoire', descr: "Proposer une localisation aléatoire des blessures" },
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
{ group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },
@ -47,7 +46,7 @@ const listeReglesOptionnelles = [
const uniquementJoueur = listeReglesOptionnelles.filter(it => it.uniquementJoueur).map(it=>it.name);
export class ReglesOptionnelles extends FormApplication {
static initSettings() {
static init() {
for (const regle of listeReglesOptionnelles) {
const name = regle.name;
const id = ReglesOptionnelles._getIdRegle(name);
@ -74,7 +73,7 @@ export class ReglesOptionnelles extends FormApplication {
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
id: "regles-optionnelles",
template: "systems/foundryvtt-reve-de-dragon/templates/settings/regles-optionnelles.hbs",
template: "systems/foundryvtt-reve-de-dragon/templates/settings/regles-optionnelles.html",
height: 650,
width: 550,
minimizable: false,

View File

@ -1,4 +1,3 @@
import { ChatUtility } from "../chat-utility.js";
import { HIDE_DICE, SYSTEM_RDD } from "../constants.js";
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
@ -25,7 +24,7 @@ const CONFIGURABLE_COMPENDIUMS = {
* ======= Gestion des accès aux compendiums systèmes (ou surchargés) =======
*/
export class SystemCompendiums extends FormApplication {
static initSettings() {
static init() {
Object.keys(CONFIGURABLE_COMPENDIUMS).forEach(compendium => {
const definition = CONFIGURABLE_COMPENDIUMS[compendium];
foundry.utils.mergeObject(definition, {
@ -112,19 +111,6 @@ export class SystemCompendiums extends FormApplication {
return elements;
}
/* -------------------------------------------- */
static async loadCompendiumData(compendium) {
const pack = game.packs.get(compendium);
return await pack?.getDocuments() ?? [];
}
/* -------------------------------------------- */
static async loadCompendium(compendium, filter = item => true) {
let compendiumData = await SystemCompendiums.loadCompendiumData(compendium);
return compendiumData.filter(filter);
}
static async getDefaultItems(compendium) {
const pack = game.packs.get(SystemCompendiums._getDefaultCompendium(compendium));
if (pack.metadata.type == 'Item') {
@ -249,12 +235,12 @@ export class CompendiumTableHelpers {
let max = 0;
const total = rows.map(it => it.frequence).reduce(Misc.sum(), 0);
return rows.map(row => {
const frequence = row.frequence
row.min = max + 1
row.max = max + frequence
const frequence = row.frequence;
row.min = max + 1;
row.max = max + frequence;
row.total = total
max += frequence
return row
max += frequence;
return row;
})
}
static async getRandom(table, type, subTypes = ['objet'], forcedRoll = undefined, localisation = undefined) {
@ -273,8 +259,8 @@ export class CompendiumTableHelpers {
}
const total = table[0].total;
const formula = `1d${total}`;
if (forcedRoll != undefined && (forcedRoll > total || forcedRoll <= 0)) {
ui.notifications.warn(`Jet forcé ${forcedRoll} en dehors de la table [1..${total}], le jet est relancé`);
if (forcedRoll == undefined && (forcedRoll > total || forcedRoll <= 0)) {
ui.notifications.warn(`Jet de rencontre ${forcedRoll} en dehors de la table [1..${total}], le jet est relancé`);
forcedRoll = undefined;
}
const roll = forcedRoll ? { total: forcedRoll, formula } : await RdDDice.roll(formula, { showDice: HIDE_DICE });
@ -289,7 +275,7 @@ export class CompendiumTableHelpers {
return;
}
const percentages = (row.total == 100) ? '%' : ''
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll.hbs', {
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll.html', {
roll: row.roll,
document: row.document,
percentages,
@ -304,12 +290,12 @@ export class CompendiumTableHelpers {
sound: CONFIG.sounds.dice,
content: flavorContent
};
await ChatUtility.createChatWithRollMode(messageData)
ChatMessage.create(messageData, { rollMode: "gmroll" });
}
/* -------------------------------------------- */
static async tableToChatMessage(table, type, subTypes, typeName = undefined) {
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table.hbs', {
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table.html', {
img: RdDItem.getDefaultImg(subTypes[0]),
typeName: typeName ?? Misc.typeName(type, subTypes[0]),
table,
@ -317,10 +303,10 @@ export class CompendiumTableHelpers {
});
const messageData = {
user: game.user.id,
whisper: [game.user],
whisper: game.user.id,
content: flavorContent
};
await ChatUtility.createChatWithRollMode(messageData)
ChatMessage.create(messageData, { rollMode: "gmroll" });
}
}

View File

@ -186,7 +186,7 @@ export class AppAstrologie extends Application {
date: this.html.find('[name="joursAstrologie"]').val(),
userId: game.user.id
}
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
game.system.rdd.calendrier.requestNombreAstral(socketData);
} else {
game.socket.emit(SYSTEM_SOCKET_ID, {

View File

@ -13,21 +13,18 @@ export class Targets {
static extractTokenData(target) {
return { id: target?.id, name: target?.document.name, img: target?.document.texture.src ?? target?.actor.img ?? 'icons/svg/mystery-man.svg' };
}
static buildActorTokenData(tokenId, actor) {
return { id: tokenId, name: actor.name, img: actor.img ?? 'icons/svg/mystery-man.svg' };
}
static isTargetEntite(target) {
return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE;
}
static async selectOneTargetToken(onSelectTarget = target => { }) {
const targets = Targets.listTargets()
static async selectOneToken(onSelectTarget = target => { }) {
const targets = Targets.listTargets();
switch (targets.length) {
case 0:
return
case 0: return;
case 1:
onSelectTarget(targets[0])
return
onSelectTarget(targets[0]);
return;
default:
{
const selectData = {
@ -35,7 +32,7 @@ export class Targets {
label: "Choisir une seule des cibles",
list: targets.map(it => Targets.extractTokenData(it))
};
DialogSelect.select(selectData, onSelectTarget)
DialogSelect.select(selectData, onSelectTarget);
}
}
}

View File

@ -4,7 +4,7 @@ export const AUTO_ADJUST_DARKNESS = "auto-adjust-darkness";
export class AutoAdjustDarkness {
static initSettings() {
static init() {
game.settings.register(SYSTEM_RDD, AUTO_ADJUST_DARKNESS, {
name: AUTO_ADJUST_DARKNESS,
scope: "world",
@ -15,7 +15,7 @@ export class AutoAdjustDarkness {
}
static async adjust(darkness) {
if (game.user.isGM && AutoAdjustDarkness.isAuto()) {
if (AutoAdjustDarkness.isAuto()) {
const scene = game.scenes.viewed;
if (scene?.environment?.globalLight?.enabled && scene?.tokenVision) {
await scene.update({ darkness });

View File

@ -1,6 +1,7 @@
import { MAX_NOMBRE_ASTRAL, RdDTimestamp, WORLD_TIMESTAMP_SETTING } from "./rdd-timestamp.js";
import { RdDCalendrierEditor } from "./rdd-calendrier-editor.js";
import { RdDResolutionTable } from "../rdd-resolution-table.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDDice } from "../rdd-dice.js";
import { Misc } from "../misc.js";
import { DialogChronologie } from "../dialog-chronologie.js";
@ -15,7 +16,7 @@ const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/ca
const INITIAL_CALENDAR_POS = { top: 200, left: 200, horlogeAnalogique: true };
/* -------------------------------------------- */
export class RdDCalendrier extends Application {
static initSettings() {
static init() {
game.settings.register(SYSTEM_RDD, "liste-nombre-astral", {
name: "liste-nombre-astral",
scope: "world",
@ -48,7 +49,7 @@ export class RdDCalendrier extends Application {
constructor() {
super();
this.timestamp = RdDTimestamp.getWorldTime();
if (Misc.isFirstConnectedGM()) { // Uniquement si GM
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp);
this.rebuildNombresAstraux(); // Ensure always up-to-date
}
@ -122,9 +123,9 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
fillCalendrierData(formData = {}) {
foundry.utils.mergeObject(formData, this.timestamp.toCalendrier());
formData.isGM = game.user.isGM
formData.isGM = game.user.isGM;
formData.heures = RdDTimestamp.definitions()
formData.horlogeAnalogique = this.horlogeAnalogique
formData.horlogeAnalogique = this.horlogeAnalogique;
formData.autoDarkness = AutoAdjustDarkness.isAuto()
return formData;
}
@ -227,7 +228,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async ajouterNombreAstral(indexDate) {
const nombreAstral = await RdDDice.rollHeure( { showDice: HIDE_DICE, rollMode: "selfroll" });
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: HIDE_DICE, rollMode: "selfroll" });
return {
nombreAstral: nombreAstral,
lectures: [],
@ -258,7 +259,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async rebuildNombresAstraux() {
if (Misc.isFirstConnectedGM()) {
if (Misc.isUniqueConnectedGM()) {
const nombresAstraux = this.getNombresAstraux()
let newNombresAstraux = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
@ -337,7 +338,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */
async requestNombreAstral(request) {
const actor = game.actors.get(request.id);
if (Misc.isFirstConnectedGM()) { // Only once
if (Misc.isUniqueConnectedGM()) { // Only once
console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);

View File

@ -15,18 +15,18 @@ export const RDD_MINUTES_PAR_JOUR = 1440; //RDD_HEURES_PAR_JOUR * RDD_MINUTES_PA
const ROUNDS_PAR_MINUTE = 10;
const DEFINITION_HEURES = [
{ key: "vaisseau", article: "du ", label: "Vaisseau", lettreFont: 'v', saison: "Printemps", darkness: 0.9 },
{ key: "sirene", article: "de la ", label: "Sirène", lettreFont: 'i', saison: "Printemps", darkness: 0.1 },
{ key: "faucon", article: "du ", label: "Faucon", lettreFont: 'f', saison: "Printemps", darkness: 0 },
{ key: "couronne", article: "de la ", label: "Couronne", lettreFont: '', saison: "Eté", darkness: 0 },
{ key: "dragon", article: "du ", label: "Dragon", lettreFont: 'd', saison: "Eté", darkness: 0 },
{ key: "epees", article: "des ", label: "Epées", lettreFont: 'e', saison: "Eté", darkness: 0 },
{ key: "lyre", article: "de la ", label: "Lyre", lettreFont: 'l', saison: "Automne", darkness: 0.1 },
{ key: "serpent", article: "du ", label: "Serpent", lettreFont: 's', saison: "Automne", darkness: 0.9 },
{ key: "poissonacrobate", article: "du ", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne", darkness: 1 },
{ key: "araignee", article: "de l'", label: "Araignée", lettreFont: 'a', saison: "Hiver", darkness: 1 },
{ key: "roseau", article: "du ", label: "Roseau", lettreFont: 'r', saison: "Hiver", darkness: 1 },
{ key: "chateaudormant", article: "du ", label: "Château Dormant", lettreFont: 'c', saison: "Hiver", darkness: 1 },
{ key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "Printemps", darkness: 0.9 },
{ key: "sirene", label: "Sirène", lettreFont: 'i', saison: "Printemps", darkness: 0.1 },
{ key: "faucon", label: "Faucon", lettreFont: 'f', saison: "Printemps", darkness: 0 },
{ key: "couronne", label: "Couronne", lettreFont: '', saison: "Eté", darkness: 0 },
{ key: "dragon", label: "Dragon", lettreFont: 'd', saison: "Eté", darkness: 0 },
{ key: "epees", label: "Epées", lettreFont: 'e', saison: "Eté", darkness: 0 },
{ key: "lyre", label: "Lyre", lettreFont: 'l', saison: "Automne", darkness: 0.1 },
{ key: "serpent", label: "Serpent", lettreFont: 's', saison: "Automne", darkness: 0.9 },
{ key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne", darkness: 1 },
{ key: "araignee", label: "Araignée", lettreFont: 'a', saison: "Hiver", darkness: 1 },
{ key: "roseau", label: "Roseau", lettreFont: 'r', saison: "Hiver", darkness: 1 },
{ key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "Hiver", darkness: 1 },
]
const FORMULES_DUREE = [
@ -49,7 +49,7 @@ const FORMULES_PERIODE = [
export class RdDTimestamp {
static initSettings() {
static init() {
game.settings.register(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING, {
name: WORLD_TIMESTAMP_SETTING,
scope: "world",
@ -64,7 +64,6 @@ export class RdDTimestamp {
DEFINITION_HEURES[i].hh = RdDTimestamp.hh(i);
DEFINITION_HEURES[i].icon = RdDTimestamp.iconeHeure(i);
DEFINITION_HEURES[i].webp = DEFINITION_HEURES[i].icon.replace(".svg", ".webp");
DEFINITION_HEURES[i].avecArticle = DEFINITION_HEURES[i].article + DEFINITION_HEURES[i].label
}
}
@ -160,21 +159,15 @@ export class RdDTimestamp {
}
static findHeure(heure) {
let filtered
if (Number.isInteger(Number(heure))) {
filtered = DEFINITION_HEURES.filter(it => it.heure == Misc.modulo(Number(heure) - 1, RDD_HEURES_PAR_JOUR))
heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
let parHeureOuLabel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure || it.heure == Misc.modulo(parseInt(heure), RDD_HEURES_PAR_JOUR));
if (parHeureOuLabel.length == 1) {
return parHeureOuLabel[0];
}
else {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
filtered = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure || it.heure == Misc.modulo(parseInt(heure), RDD_HEURES_PAR_JOUR));
}
if (filtered.length == 1) {
return filtered[0]
}
filtered = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
if (filtered.length > 0) {
filtered.sort(Misc.ascending(h => h.label.length));
return filtered[0]
let parLabelPartiel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
if (parLabelPartiel.length > 0) {
parLabelPartiel.sort(Misc.ascending(h => h.label.length));
return parLabelPartiel[0];
}
return undefined;
}
@ -248,7 +241,7 @@ export class RdDTimestamp {
get darkness() {
const darknessDebut = 100 * RdDTimestamp.definition(this.heure).darkness
const darknessFin = 100 * RdDTimestamp.definition(this.heure + 1).darkness
const darknessFin = 100 * RdDTimestamp.definition(this.heure + 1).darkness
const darknessMinute = Math.round((darknessFin - darknessDebut) * this.minute / RDD_MINUTES_PAR_HEURES);
return (darknessDebut + darknessMinute) / 100
}

View File

@ -105,7 +105,7 @@ export class TMRRencontres {
/* -------------------------------------------- */
async $chatRolledRencontre(row, rencontre, tmr) {
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll-rencontre.hbs',
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll-rencontre.html',
{
roll: row.roll,
rencontre,

View File

@ -1,4 +1,4 @@
import { ITEM_TYPES } from "../item.js";
import { TYPES } from "../item.js";
import { TMRUtility } from "../tmr-utility.js";
import { PixiTMR } from "./pixi-tmr.js";
@ -12,10 +12,10 @@ export class Draconique {
static init() {
}
static isCaseTMR(item) { return item.type == ITEM_TYPES.casetmr; }
static isCaseTMR(item) { return item.type == TYPES.casetmr; }
static isQueueDragon(item) { return item.isQueueDragon(); }
static isSouffleDragon(item) { return item.type == ITEM_TYPES.souffle; }
static isTeteDragon(item) { return item.type == ITEM_TYPES.tete; }
static isSouffleDragon(item) { return item.type == TYPES.souffle; }
static isTeteDragon(item) { return item.type == TYPES.tete; }
static isQueueSouffle(item) { return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item); }
static register(draconique, code = undefined) {

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 { TYPES } from "../item.js";
export class EffetsDraconiques {
static carteTmr = new CarteTmr();
@ -122,11 +122,11 @@ export class EffetsDraconiques {
}
static tetesDragon(actor, name) {
return actor.itemTypes[ITEM_TYPES.tete].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
return actor.itemTypes[TYPES.tete].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
}
static soufflesDragon(actor, name) {
return actor.itemTypes[ITEM_TYPES.souffle].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
return actor.itemTypes[TYPES.souffle].filter(it => Grammar.includesLowerCaseNoAccent(it.name, name));
}
static queuesDragon(actor, name) {
@ -134,7 +134,7 @@ export class EffetsDraconiques {
}
static queuesSoufflesDragon(actor, name) {
return actor.filterItems(it => [ITEM_TYPES.queue, ITEM_TYPES.ombre, ITEM_TYPES.souffle].includes(it.type) && Grammar.includesLowerCaseNoAccent(it.name, name));
return actor.filterItems(it => [TYPES.queue, TYPES.ombre, TYPES.souffle].includes(it.type) && Grammar.includesLowerCaseNoAccent(it.name, name));
}
static countAugmentationSeuil(actor) {

View File

@ -30,7 +30,7 @@ export class EffetsRencontre {
static $reve_plus = async (actor, reve) => {
if (!ReglesOptionnelles.isUsing("recuperation-reve") && reve < 0) {
ChatMessage.create({
whisper: ChatUtility.getOwners(actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name),
content: `Pas de récupération de rêve (${reve} points ignorés)`
});
return
@ -112,7 +112,7 @@ export class EffetsRencontre {
poesie: await Poetique.getExtrait()
})
ChatMessage.create({
whisper: ChatUtility.getOwners(context.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(context.actor.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, context)
});
}
@ -127,7 +127,7 @@ export class EffetsRencontre {
}
ChatMessage.create({
whisper: ChatUtility.getOwners(context.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, context)
});
}

View File

@ -28,7 +28,7 @@ export class PresentCites extends Draconique {
let existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord);
if (existants.length > 0) {
ChatMessage.create({
whisper: ChatUtility.getOwners(actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: "Vous avez encore des présents dans des cités, vous devrez tirer une autre tête pour remplacer celle ci!"
})
}

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 { TYPES } from "../item.js";
import { TMRAnimations } from "./animation.js";
export class UrgenceDraconique extends Draconique {
@ -14,12 +14,12 @@ export class UrgenceDraconique extends Draconique {
match(item) { return Draconique.isQueueDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('urgence draconique'); }
manualMessage() { return false }
async onActorCreateOwned(actor, queue) {
const coordSortsReserve = actor.itemTypes[ITEM_TYPES.sortreserve].map(it => it.system.coord) ?? [];
const coordSortsReserve = actor.itemTypes[TYPES.sortreserve].map(it => it.system.coord) ?? [];
if (coordSortsReserve.length == 0) {
// La queue se transforme en idée fixe
const ideeFixe = await RdDRollTables.getIdeeFixe();
ChatMessage.create({
whisper: ChatUtility.getOwners(actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: `En l'absence de sorts en réserve, l'urgence draconique de ${actor.name} se transforme en ${ideeFixe.name}`
});
await actor.createEmbeddedDocuments('Item', [ideeFixe]);

View File

@ -1,7 +1,6 @@
import { ITEM_TYPES } from "../item.js"
import { TYPES } from "../item.js"
import { RdDItemCompetence } from "../item-competence.js"
import { ChatUtility } from "../chat-utility.js"
import { Misc } from "../misc.js"
const CODES_COMPETENCES_VOYAGE = ['Extérieur', 'Forêt', 'Montagne', 'Marais', 'Glace', 'Equitation']
const TABLEAU_FATIGUE_MARCHE = [
@ -37,7 +36,7 @@ export class DialogFatigueVoyage extends Dialog {
const parameters = {
tableauFatigueMarche: TABLEAU_FATIGUE_MARCHE,
playerActors: game.actors.filter(actor => actor.isPersonnageJoueur())
.map(actor => DialogFatigueVoyage.prepareActorParameters(actor)),
.map(actor => DialogFatigueVoyage.prepareActor(actor)),
nombreHeures: 1,
}
DialogFatigueVoyage.setModeDeplacement(parameters, undefined, undefined)
@ -54,37 +53,21 @@ export class DialogFatigueVoyage extends Dialog {
parameters.typeTerrain = ligneFatigueMarche
parameters.vitesseDeplacement = rythme.vitesse
parameters.fatigueHoraire = rythme.fatigue
parameters.playerActors.forEach(voyageur =>
DialogFatigueVoyage.selectSurvie(voyageur, parameters.typeTerrain.code)
)
}
static prepareActorParameters(actor) {
const actorParameters = {
id: actor.id,
static prepareActor(actor) {
const competencesVoyage = {}
CODES_COMPETENCES_VOYAGE.forEach(codeSurvie =>
competencesVoyage[codeSurvie] = RdDItemCompetence.findCompetence(actor.itemTypes[TYPES.competence], codeSurvie, { onMessage: () => { } })
)
return {
actor: actor,
selected: true,
ajustementFatigue: 0,
survies: {}
competencesVoyage: competencesVoyage
}
const competencesVoyage = {}
CODES_COMPETENCES_VOYAGE.forEach(codeSurvie => {
competencesVoyage[codeSurvie] = RdDItemCompetence.findCompetence(actor.itemTypes[ITEM_TYPES.competence], codeSurvie, { onMessage: () => { } })
})
TABLEAU_FATIGUE_MARCHE.forEach(terrain => {
actorParameters.survies[terrain.code] = Misc.join(
terrain.survies.map(survie => {
const niveau = competencesVoyage[survie]?.system.niveau
return `${survie}: ${niveau}`
}),
', ')
})
return actorParameters
}
static selectSurvie(actorParameters, code) {
actorParameters.survieCourante = actorParameters.survies[code]
}
constructor(html, parameters) {
const options = {
@ -114,7 +97,6 @@ export class DialogFatigueVoyage extends Dialog {
this.html.find('select[name="code-terrain"]').change(event => this.changeParameters())
this.html.find('select[name="vitesse-deplacement"]').change(event => this.changeParameters())
this.html.find('input[name="nombre-heures"]').change(event => this.changeParameters())
this.html.find('.list-item input[name="ajustement-fatigue"]').change(event => this.changeParameters())
this.html.find('button[name="appliquer-fatigue"]').click(event => this.appliquerFatigue())
}
@ -136,10 +118,6 @@ export class DialogFatigueVoyage extends Dialog {
selectVitesseDeplacement.append(await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/voyage/option-vitesse-fatigue.hbs', rythme))
})
selectVitesseDeplacement.val(this.parameters.vitesseDeplacement).change()
Promise.all(this.getActorRows()
.map(async row => row.find('label.voyage-liste-survies').text(this.$extractActorParameters(row).survieCourante)
))
}
}
@ -154,28 +132,20 @@ export class DialogFatigueVoyage extends Dialog {
}
async setFatigue() {
const baseFatigue = this.parameters.nombreHeures * this.parameters.fatigueHoraire
this.html.find('input[name="base-fatigue"]').val(baseFatigue)
this.updateActorTotalFatigue(baseFatigue)
}
async updateActorTotalFatigue(baseFatigue) {
Promise.all(this.getActorRows()
.map(async row => {
const actor = this.$extractActorParameters(row)
row.find('input[name="total-fatigue"]').val(actor.ajustement + baseFatigue)
}))
this.html.find('input[name="base-fatigue"]').val(this.parameters.nombreHeures * this.parameters.fatigueHoraire)
}
async appliquerFatigue() {
const fatigueBase = parseInt(this.html.find('input[name="base-fatigue"]').val() ?? 0)
this.getActorRows()
.map(row => this.$extractActorParameters(row))
.filter(it => it.selected)
const actors = jQuery.map(
this.html.find('div.fatigue-actors-list li.list-item'),
it => this.$extractActor(this.html.find(it))
)
actors.filter(it => it.selected)
.forEach(async it => {
const perteFatigue = fatigueBase + it.ajustement
ChatMessage.create({
whisper: ChatUtility.getOwners(it.actor),
whisper: ChatUtility.getWhisperRecipientsAndGMs(it.actor.name),
content: await renderTemplate(
'systems/foundryvtt-reve-de-dragon/templates/voyage/chat-fatigue_voyage.hbs',
foundry.utils.mergeObject(it,
@ -191,24 +161,16 @@ export class DialogFatigueVoyage extends Dialog {
})
}
getActorRows() {
return jQuery.map(
this.html.find('div.fatigue-actors-list li.list-item'),
it => this.html.find(it))
}
$extractActorParameters(actorRow) {
const actorId = actorRow.data('actor-id')
const actorParameters = this.parameters.playerActors.find(it => it.id == actorId)
const actor = game.actors.get(actorId)
$extractActor(actorRow) {
const actor = game.actors.get(actorRow.data('actor-id'))
if (!actor) {
ui.notifications.warn(`Acteur ${it.actorId} introuvable`)
return {}
}
actorParameters.ajustement = parseInt(actorRow.find('input[name="ajustement-fatigue"]').val() ?? 0)
actorParameters.selected = actor && actorRow.find('input[name="selectionner-acteur"]').is(':checked')
return actorParameters
return {
actor: actor,
ajustement: parseInt(actorRow.find('input[name="ajustement-fatigue"]').val() ?? 0),
selected: actor && actorRow.find('input[name="selectionner-acteur"]').is(':checked')
}
}
async close() {

View File

@ -64,7 +64,6 @@
{"_id":"MGvyXFQJpv6nNynl","name":"Masse lourde","type":"arme","img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/masse_lourde.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.MGvyXFQJpv6nNynl"}},"system":{"description":"","descriptionmj":"","equipe":false,"encombrement":3,"quantite":1,"qualite":0,"cout":4,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Rare","frequence":6},{"milieu":"Villes","rarete":"Frequente","frequence":18}],"resistance":10,"categorie_parade":"","dommages":"3/4","penetration":0,"force":"12/11","competence":"Masse à 1 main","lancer":"","tir":"","portee_courte":0,"portee_moyenne":0,"portee_extreme":0,"magique":false,"ecaille_efficacite":0,"resistance_magique":0,"rapide":false,"deuxmains":true,"unemain":true,"initpremierround":"masse"},"ownership":{"default":0,"Q4cUvqxCxMoTJXDL":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.15","coreVersion":"10.291","createdTime":1668808206023,"modifiedTime":1676074487966,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
{"_id":"MQxgfYTEQEUhG116","name":"Épée bâtarde","type":"arme","img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/epee_batarde.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.MQxgfYTEQEUhG116"}},"system":{"description":"<p>Comme son nom l&rsquo;indique, c&rsquo;est une <em>b&acirc;tarde </em>de l'&eacute;p&eacute;e longue et de l'&eacute;p&eacute;e sorde, &agrave; la fois longue et large, pouvant s&rsquo;utiliser &agrave; une ou deux mains.</p>","descriptionmj":"","equipe":false,"encombrement":3,"quantite":1,"qualite":0,"cout":30,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Rarissime","frequence":2},{"milieu":"Villes","rarete":"Rare","frequence":6}],"resistance":14,"categorie_parade":"epees-lourdes","dommages":"4/5","penetration":0,"force":"13/12","competence":"Epée à 1 main","lancer":"","tir":"","portee_courte":0,"portee_moyenne":0,"portee_extreme":0,"magique":false,"ecaille_efficacite":null,"resistance_magique":null,"rapide":false,"deuxmains":true,"unemain":true,"initpremierround":"epeebatarde"},"ownership":{"default":0,"Q4cUvqxCxMoTJXDL":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.15","coreVersion":"10.291","createdTime":1668808206023,"modifiedTime":1676074487966,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
{"_id":"Mbh2M8JS1Rf0vxEX","name":"Harpe","type":"objet","img":"systems/foundryvtt-reve-de-dragon/icons/objets/harpe.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.Mbh2M8JS1Rf0vxEX"}},"system":{"description":"","descriptionmj":"","encombrement":1,"quantite":1,"qualite":0,"cout":5,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Rarissime","frequence":2},{"milieu":"Villes","rarete":"Rare","frequence":6}],"equipe":false,"resistance":1},"ownership":{"default":0,"rYShh2P1DNavdoBD":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.15","coreVersion":"10.291","createdTime":1668808206023,"modifiedTime":1676074487966,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
{"_id":"NCp2kdTKmJVdFuit","name":"Dague mêlée","type":"arme","img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/dague.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sheetClass":"","sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.NCp2kdTKmQGyAh1U"}},"system":{"description":"","descriptionmj":"","equipe":false,"encombrement":0.5,"quantite":1,"qualite":0,"cout":3,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Frequente","frequence":18},{"milieu":"Villes","rarete":"Frequente","frequence":18}],"resistance":8,"categorie_parade":"dagues","dommages":"1","penetration":0,"force":"7","competence":"Dague","lancer":"Dague de jet","tir":"","portee_courte":3,"portee_moyenne":8,"portee_extreme":15,"magique":false,"ecaille_efficacite":null,"resistance_magique":null,"rapide":true,"deuxmains":false,"unemain":true,"initpremierround":"dague"},"ownership":{"default":0,"Q4cUvqxCxMoTJXDL":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.15","coreVersion":"10.291","createdTime":1668808206023,"modifiedTime":1676074487966,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
{"_id":"NCp2kdTKmQGyAh1U","name":"Dague","type":"arme","img":"systems/foundryvtt-reve-de-dragon/icons/armes_armures/dague.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sheetClass":"","sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.NCp2kdTKmQGyAh1U"}},"system":{"description":"","descriptionmj":"","equipe":false,"encombrement":0.5,"quantite":1,"qualite":0,"cout":3,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Frequente","frequence":18},{"milieu":"Villes","rarete":"Frequente","frequence":18}],"resistance":8,"categorie_parade":"dagues","dommages":"1","penetration":0,"force":"7","competence":"Dague","lancer":"Dague de jet","tir":"","portee_courte":3,"portee_moyenne":8,"portee_extreme":15,"magique":false,"ecaille_efficacite":null,"resistance_magique":null,"rapide":true,"deuxmains":false,"unemain":true,"initpremierround":"dague"},"ownership":{"default":0,"Q4cUvqxCxMoTJXDL":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.15","coreVersion":"10.291","createdTime":1668808206023,"modifiedTime":1676074487966,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
{"_id":"NNLhxjFsoJVdFuit","name":"Bouteille de verre (1 litre)","type":"conteneur","img":"systems/foundryvtt-reve-de-dragon/icons/objets/bouteille_verre.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.NNLhxjFsoJVdFuit"}},"system":{"description":"","descriptionmj":"","encombrement":0.2,"quantite":1,"qualite":0,"cout":0.7,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Rare","frequence":6},{"milieu":"Villes","rarete":"Frequente","frequence":18}],"contenu":[],"capacite":0.5,"equipe":false},"ownership":{"default":0,"rYShh2P1DNavdoBD":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.15","coreVersion":"10.291","createdTime":1668808206023,"modifiedTime":1676074487966,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}
{"_id":"OXFFRZOqlhZDJas3","name":"Béret de velours","type":"objet","img":"systems/foundryvtt-reve-de-dragon/icons/objets/beret_velours.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.OXFFRZOqlhZDJas3"}},"system":{"description":"","descriptionmj":"","encombrement":0.05,"quantite":1,"qualite":0,"cout":3,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Rare","frequence":6},{"milieu":"Villes","rarete":"Frequente","frequence":18}],"equipe":false,"resistance":1},"ownership":{"default":0,"rYShh2P1DNavdoBD":3},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.15","coreVersion":"10.291","createdTime":1668808206023,"modifiedTime":1676074487966,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"}}

View File

@ -11,3 +11,4 @@
{"name":"Thème astral","type":"script","scope":"global","author":"Hp9ImM4o9YRTSdfu","img":"icons/magic/nature/symbol-moon-stars-white.webp","command":"game.system.rdd.AppAstrologie.create()","ownership":{"default":0,"Hp9ImM4o9YRTSdfu":3},"flags":{"core":{"sourceId":"Macro.oA0HPFeFK6YMspAX"}},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"10.6.19","coreVersion":"10.291","createdTime":1678127868791,"modifiedTime":1678237392810,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"},"folder":null,"sort":0,"_id":"koqhiDJSGP4gQ4vf"}
{"name":"Jet d'éthylisme","type":"script","scope":"global","author":"Hp9ImM4o9YRTSdfu","img":"icons/consumables/drinks/alcohol-beer-stein-wooden-metal-brown.webp","command":"const selected = game.system.rdd.RdDUtility.getSelectedActor();\nif (selected) {\n selected.jetEthylisme();\n}\nelse {\n ui.notifications.info('Pas de personnage sélectionné');\n}","ownership":{"default":0,"Hp9ImM4o9YRTSdfu":3},"flags":{"core":{"sourceId":"Macro.XHNbjnGKXaCiCadq"}},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"11.1.0","coreVersion":"10.291","createdTime":1671220038331,"modifiedTime":1671233646086,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"},"folder":null,"sort":0,"_id":"mvub1dRHNFmWjRr7"}
{"name":"Tirer le tarot","type":"chat","scope":"global","author":"Hp9ImM4o9YRTSdfu","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp","command":"/tirer tarot","ownership":{"default":0,"Hp9ImM4o9YRTSdfu":3},"flags":{"core":{"sourceId":"Macro.HBZSKR9OHCQbLcTC"}},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"11.1.0","coreVersion":"10.291","createdTime":1669469547231,"modifiedTime":1671237401618,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"},"folder":null,"sort":0,"_id":"vTfJTFYYiRu8X5LM"}
{"name": "Mon personnage","type": "script","author": "Hp9ImM4o9YRTSdfu","img": "systems/foundryvtt-reve-de-dragon/icons/voyageurs/token_hr_dilettante.webp","scope": "global","command": "if (game.user.isGM) {\n ui.notifications.warn(\"En tant que gardien, vous n'avez pas de personnage attitré\")\n return\n}\nconst actor = game.users.get(game.userId)?.character\nif (!actor) {\n ui.notifications.warn(\"Vous n'avez pas de personnage attitré\")\n return\n}\nactor.sheet.render(true)","folder": null,"flags": {"core": {},"exportSource": {"world": "graine","system": "foundryvtt-reve-de-dragon","coreVersion": "11.313","systemVersion": "11.1.1"}},"_stats": {"systemId": "foundryvtt-reve-de-dragon","systemVersion": "11.1.1","coreVersion": "11.313","createdTime": 1699477824379,"modifiedTime": 1699485023429,"lastModifiedBy": "Hp9ImM4o9YRTSdfu"}}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 56 KiB

View File

@ -326,7 +326,7 @@ table {border: 1px solid #7a7971;}
display: grid;
grid-column: span 2 / span 2;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 30px;
gap: 10px;
margin: 10px 0;
padding: 0;
}
@ -411,11 +411,6 @@ table {border: 1px solid #7a7971;}
text-align: left;
}
.flex-group-top {
vertical-align: top;
align-self: start;
}
.flex-group-right {
-webkit-box-pack: end;
-ms-flex-pack: end;
@ -567,11 +562,7 @@ input:is(.blessure-premiers_soins, .blessure-soins_complets) {
.dice-img {
border-width: 0;
max-width: 1.5rem;
max-height: 1.5rem;
vertical-align: top;
}
.in-text-img {
max-width: 1.2em;
max-height: 1.2em;
@ -581,15 +572,6 @@ input:is(.blessure-premiers_soins, .blessure-soins_complets) {
border: none;
padding: 0.1rem;
}
.context-menu-img {
max-width: 2rem;
max-height: 1rem;
flex-grow: 0;
margin: 0.2rem 0.3rem 0 0;
vertical-align: middle;
border: none;
padding: 0rem;
}
.button-img {
vertical-align: baseline;
@ -684,7 +666,6 @@ input:is(.blessure-premiers_soins, .blessure-soins_complets) {
}
.carac-label {
font-weight: bold;
flex-basis: 40%;
}
.rdd.sheet .window-content .sheet-body .carac-list .caracteristique > .utiliser-attribut {
@ -1193,6 +1174,9 @@ ul.chat-list li:nth-child(odd) {
padding: 0.5rem;
cursor: pointer;
}
.carac-label {
font-weight: bold;
}
.list-item {
margin: 0.1rem;
box-shadow: inset 0px 0px 1px #00000096;
@ -1390,7 +1374,7 @@ table.table-nombres-astraux tr:hover {
flex-direction: column;
position: absolute;
top: 4.6rem;
left: -19rem;
right: 3.5rem;
}
.token-hud-ext.soins {
flex-direction: column;
@ -1561,11 +1545,6 @@ div.control-icon.token-hud-icon {
height: 100%;
object-fit: contain;
}
.chat-inline-icon {
border: 0;
padding: 1px;
vertical-align: text-top;
}
#sidebar-tabs {
flex: 0 0 28px;

View File

@ -1,8 +1,8 @@
{
"id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon",
"version": "12.0.20",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-12.0.20.zip",
"version": "12.0.0",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-12.0.0.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json",
"changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"compatibility": {
@ -534,7 +534,6 @@
},
"primaryTokenAttribute": "sante.vie",
"secondaryTokenAttribute": "sante.endurance",
"background": "systems/foundryvtt-reve-de-dragon/styles/img/reve-de-dragon-cover.webp",
"media": [
{
"type": "icon",

View File

@ -15,14 +15,12 @@
}
},
"background": {
"metier": "",
"biographie": "Histoire personnelle...",
"notes": "Notes",
"notesmj": "Notes du MJ",
"race": "Humain",
"yeux": "",
"cheveux": "",
"taille": "",
"poids": "",
"heure": "",
"sexe": "",

View File

@ -9,9 +9,9 @@
<div class="flexrow">
<h1 class="charname"><input name="name" type="text" value="{{name}}" placeholder="Name" /></h1>
<div class="header-buttons">
<span><a class="button-encaissement"><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a></span>
<span class="encaisser-direct"><a><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a></span>
{{#if @root.options.isGM}}
<span><a class="button-remise-a-neuf"><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a></span>
<span class="remise-a-neuf"><a><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a></span>
{{/if}}
</div>
</div>

View File

@ -9,9 +9,9 @@
<div class="flexrow">
<h1 class="charname"><input name="name" type="text" value="{{name}}" placeholder="Name" /></h1>
<div class="header-buttons">
<span><a class="button-encaissement"><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a></span>
<span class="encaisser-direct"><a><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a></span>
{{#if @root.options.isGM}}
<span><a class="button-remise-a-neuf"><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a></span>
<span class="remise-a-neuf"><a><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a></span>
{{/if}}
</div>
</div>

View File

@ -172,10 +172,6 @@
<label for="system.poids">Poids :</label>
<input class="flex-grow" type="text" name="system.poids" value="{{system.poids}}" data-dtype="String"/>
</li>
<li class="item flexrow list-item">
<label for="system.metier">Métier :</label>
<input class="flex-grow" type="text" name="system.metier" value="{{system.metier}}" data-dtype="String"/>
</li>
</ul>
<ul class="item-list alterne-list">
<li class="item flexrow list-item">
@ -205,7 +201,6 @@
<label for="system.yeux">Yeux :</label>
<input class="flex-grow" type="text" name="system.yeux" value="{{system.yeux}}" data-dtype="String"/>
</li>
<a class="chat-card-button description-aleatoire" data-tooltip="Déterminer aléatoirement les détails de description">Description <i class="fa-solid fa-dice-d20"></i></a>
</ul>
</article>

View File

@ -55,7 +55,7 @@
<li class="caracteristique flexrow list-item" data-tooltip="Niveau d'éthylisme">
<label class="derivee-label" for="system.compteurs.ethylisme.value">{{system.compteurs.ethylisme.label}}</label>
<select class="derivee-value" name="system.compteurs.ethylisme.value" data-dtype="Number">
{{selectOptions (RDD_CONFIG 'niveauEthylisme') selected=system.compteurs.ethylisme.value valueAttr="value" nameAttr="value" labelAttr="label"}}
{{selectOptions @root.config.niveauEthylisme selected=system.compteurs.ethylisme.value valueAttr="value" nameAttr="value" labelAttr="label"}}
</select>
</li>

View File

@ -2,13 +2,13 @@
<li class="caracteristique flexrow list-item">
<span class="carac-label" name="catEntite">Catégorie : </span>
<select name="system.definition.categorieentite" value="{{system.definition.categorieentite}}" data-dtype="String" {{#unless @root.options.vueDetaillee}}disabled{{/unless}}>
{{selectOptions (RDD_CONFIG 'categorieEntite') selected=system.definition.categorieentite}}
{{selectOptions @root.config.categorieEntite selected=system.definition.categorieentite}}
</select>
</li>
<li class="caracteristique flexrow list-item">
<span class="carac-label" name="typeEntite">Type d'entité : </span>
<select name="system.definition.typeentite" value="{{system.definition.typeentite}}" data-dtype="String" {{#unless @root.options.vueDetaillee}}disabled{{/unless}}>
{{selectOptions (RDD_CONFIG 'typeEntite') selected=system.definition.typeentite}}
{{selectOptions @root.config.typeEntite selected=system.definition.typeentite}}
</select>
</li>
{{#each system.attributs as |attr key|}}

View File

@ -7,7 +7,7 @@
<label class="carac-xp"/>
{{else}}
{{#if carac.derivee}}
<span class="carac-label" name="system.carac.{{key}}.label"><a class="roll-carac" data-carac-name={{key}}>{{carac.label}}</a></span>
<span class="carac-label" name="system.carac.{{key}}.label"><a name={{key}}>{{carac.label}}</a></span>
<label class="competence-value">{{carac.value}}</label>
<label class="carac-xp"/>
{{else}}
@ -22,7 +22,7 @@
</a>
</span>
{{else}}
<span class="carac-label tooltip" name="system.carac.{{key}}.label"><a class="roll-carac" data-carac-name={{key}}>{{carac.label}}</a></span>
<span class="carac-label tooltip" name="system.carac.{{key}}.label"><a name={{key}}>{{carac.label}}</a></span>
{{/if}}
<input class="carac-value" type="text" name="system.carac.{{key}}.value" value="{{carac.value}}" data-dtype="{{carac.type}}" {{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
<span class="carac-xp tooltip">

View File

@ -13,7 +13,7 @@
data-competence-name="{{arme.system.competence}}"
data-tooltip="{{arme.name}}: niveau {{plusMoins arme.system.niveau}}">
<span class="arme-label">
<a class="roll-arme">
<a>
{{#if arme.img}}
<img class="sheet-competence-img" src="{{arme.img}}" data-tooltip="{{arme.name}}"/>
{{/if}}
@ -25,14 +25,14 @@
<span class="competence-value">{{plusMoins arme.system.niveau}}</span>
<span class="competence-value">{{plusMoins arme.system.dommagesReels}}</span>
<span class="competence-value"></span>
<span class="initiative-value"><a class="roll-init-arme" data-tooltip="{{arme.name}}: initiative {{arme.system.initiative}}">{{arme.system.initiative}}</a></span>
<span class="initiative-value arme-initiative"><a data-tooltip="{{arme.name}}: initiative {{plusMoins arme.system.initiative}}">{{arme.system.initiative}}</a></span>
</li>
{{/each}}
{{#each esquives as |esq key|}}
<li class="item flexrow list-item" data-item-id="{{esq._id}}"
data-tooltip="{{esq.name}}: niveau {{plusMoins esq.system.niveau}}">
<span class="competence-label">
<a class="roll-competence" name="{{esq.name}}">
<a class="competence-label" name="{{esq.name}}">
<img class="sheet-competence-img" src="{{esq.img}}" />
<span>{{esq.name}}</span>
</a>

View File

@ -1,7 +1,7 @@
<ol class="item-list alterne-list">
{{#each (trier competences) as |comp key|}}
<li class="item flexrow list-item" data-item-id="{{comp._id}}">
<a class="competence-label roll-competence">
<a class="competence-label">
<img class="sheet-competence-img" src="{{comp.img}}" data-tooltip="{{comp.name}}"/>
<span>{{comp.name}}</span>
</a>

View File

@ -1,18 +1,16 @@
{{#unless system.isHidden}}
<li class="item flexrow list-item {{#if system.isLevelUp}}xp-level-up{{/if}}" data-item-id="{{_id}}">
<span class="tooltip">
<a class="competence-label roll-competence" name="{{name}}" data-tooltip="Niveau {{plusMoins system.niveau}} en {{name}}">
<img class="sheet-competence-img" src="{{img}}"/>
<span>{{name}}</span>
</a>
<li class="item flexrow list-item {{#if system.isLevelUp}}xp-level-up tooltip{{/if}}" data-item-id="{{_id}}">
<a class="competence-label" name="{{name}}" data-tooltip="Niveau {{plusMoins system.niveau}} en {{name}}">
<img class="sheet-competence-img" src="{{img}}"/>
<span>{{name}}</span>
</a>
{{#if system.isLevelUp}}
<span class="tooltiptext ttt-levelup">Vous pouvez dépenser {{system.xpNext}} points d'Experience pour augmenter de 1 votre compétence {{name}}</span>
<a class="competence-xp-augmenter" compname="{{name}}">
<i class="fas fa-arrow-alt-circle-up"></i>
</a>
{{/if}}
</span>
{{#if system.isLevelUp}}
<span class="tooltiptext ttt-levelup">Vous pouvez dépenser {{system.xpNext}} points d'Experience pour augmenter de 1 votre compétence {{name}}</span>
<a class="competence-xp-augmenter" compname="{{name}}">
<i class="fas fa-arrow-alt-circle-up"></i>
</a>
{{/if}}
<input class="competence-value" type="text" compname="{{name}}" name="comp-value-{{name}}"
value="{{plusMoins system.niveau}}" data-dtype="number"
@ -52,7 +50,7 @@
{{/if}}
&nbsp;
<a class="item-montrer" data-tooltip="Montrer"><i class="fas fa-comment"></i></a>
</div>
</div>
{{/if}}
</li>
</li>
{{/unless}}

View File

@ -1,132 +0,0 @@
<form class="{{cssClass}}" autocomplete="off" >
<section class="sheet-header flexrow">
<div class="flex-grow-0-5">
<img class="profile-img" src="{{img}}" data-edit="img" data-tooltip="{{name}}" />
</div>
<div class="flexcol flex-grow-2">
<div>
<h1 class="charname">{{name}}</h1>
</div>
<div>
<a class="button-appel-chance"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/appel-chance.svg" data-tooltip="Appel à la chance"/></a>
<a class="button-encaissement"><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a>
<a class="button-ethylisme"><img class="button-img" src="icons/svg/tankard.svg" data-tooltip="Boire"/></a>
<a class="button-repos"><img class="button-img" src="icons/svg/sleep.svg" data-tooltip="Se reposer"/></a>
{{#if system.attributs.hautrevant.value}}
<a class="button-tmr" data-tooltip="Montée dans les Terres M&eacute;dianes !" {{#if hautreve.isDemiReve}}disabled{{/if}}>
<img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-normal.svg"/>
</a>
<a class="button-tmr-rapide" data-tooltip="Montée accélérée dans les Terres M&eacute;dianes !" {{#if hautreve.isDemiReve}}disabled{{/if}}>
<img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-rapide.svg"/>
</a>
<a class="button-tmr-visu" data-tooltip="Regarder les Terres M&eacute;dianes">
<img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-view.svg"/>
</a>
{{/if}}
{{#if @root.options.isGM}}
<a class="button-remise-a-neuf"><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a>
{{/if}}
{{#if options.exportScriptarium}}
<a class="button-export">
<img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/scriptarium.svg" data-tooltip="Export format Scriptarium"/>
</a>
{{/if}}
</div>
</div>
</section>
<section class="sheet-body" style=:"margin-bottom: 3rem;">
<div>
<strong>{{export.name.value}}</strong>{{#if export.description.value}}, {{export.description.value}}{{/if}}
</div>
<hr>
<div class="grid-2col">
<div class="flexcol flex-group-top">
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.taille}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.apparence}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.constitution}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.force}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.agilite}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.dexterite}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.vue}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.ouie}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.odoratgout}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.volonte}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.empathie}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.intellect}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-compteur.hbs" carac=export.reve actuel=export.reve_actuel button-name='ptreve-actuel'}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs" carac=export.chance}}
</div>
<div class="flexcol flex-group-top">
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs" carac=export.melee}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs" carac=export.tir}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs" carac=export.lancer}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs" carac=export.derobee}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee-compteur.hbs" carac=export.vie actuel=export.vie_actuel}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee-compteur.hbs" carac=export.endurance actuel=export.endurance_actuel}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/fatigue.hbs" }}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs" carac=export.plusdom}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/protection.hbs" export}}
<div class="flexrow">&nbsp;</div>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessures.hbs" }}
</div>
</div>
<hr>
<div>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/arme.hbs" name='' niveau='Niv' init='Init' dommages='+dom'}}
{{#each context.armes as |arme|}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/arme.hbs"
name=arme.name niveau=arme.niveau init=arme.init dommages=arme.dommages
arme=arme.arme competence=arme.competence
}}
{{/each}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/esquive.hbs" name='Esquive' niveau=context.esquive.value competence=context.esquive.competence}}
{{#if (gt export.malue_armure.value 0)}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/esquive.hbs" name='Esquive (avec armure)' niveau=export.esquive_armure.value
competence=context.esquive.competence}}
{{/if}}
</div>
<hr>
<div>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/competences.hbs" competences=competences}}
</div>
<hr>
<div>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/competences.hbs" competences=draconic }}
</div>
<hr>
<div>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_0.value description=export.sort_description_0.value bonus=export.sort_bonus_0.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_1.value description=export.sort_description_1.value bonus=export.sort_bonus_1.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_2.value description=export.sort_description_2.value bonus=export.sort_bonus_2.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_3.value description=export.sort_description_3.value bonus=export.sort_bonus_3.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_4.value description=export.sort_description_4.value bonus=export.sort_bonus_4.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_5.value description=export.sort_description_5.value bonus=export.sort_bonus_5.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_6.value description=export.sort_description_6.value bonus=export.sort_bonus_6.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_7.value description=export.sort_description_7.value bonus=export.sort_bonus_7.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_8.value description=export.sort_description_8.value bonus=export.sort_bonus_8.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_9.value description=export.sort_description_9.value bonus=export.sort_bonus_9.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_10.value description=export.sort_description_10.value bonus=export.sort_bonus_10.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_11.value description=export.sort_description_11.value bonus=export.sort_bonus_11.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_12.value description=export.sort_description_12.value bonus=export.sort_bonus_12.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_13.value description=export.sort_description_13.value bonus=export.sort_bonus_13.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_14.value description=export.sort_description_14.value bonus=export.sort_bonus_14.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_15.value description=export.sort_description_15.value bonus=export.sort_bonus_15.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_16.value description=export.sort_description_16.value bonus=export.sort_bonus_16.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_17.value description=export.sort_description_17.value bonus=export.sort_bonus_17.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_18.value description=export.sort_description_18.value bonus=export.sort_bonus_18.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_19.value description=export.sort_description_19.value bonus=export.sort_bonus_19.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_20.value description=export.sort_description_20.value bonus=export.sort_bonus_20.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_21.value description=export.sort_description_21.value bonus=export.sort_bonus_21.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_22.value description=export.sort_description_22.value bonus=export.sort_bonus_22.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_23.value description=export.sort_description_23.value bonus=export.sort_bonus_23.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_24.value description=export.sort_description_24.value bonus=export.sort_bonus_24.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_25.value description=export.sort_description_25.value bonus=export.sort_bonus_25.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_26.value description=export.sort_description_26.value bonus=export.sort_bonus_26.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_27.value description=export.sort_description_27.value bonus=export.sort_bonus_27.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_28.value description=export.sort_description_28.value bonus=export.sort_bonus_28.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs" voie=export.sort_voie_29.value description=export.sort_description_29.value bonus=export.sort_bonus_29.value}}
<div>&nbsp;</div>
<div>&nbsp;</div>
</div>
</section>
</form>

View File

@ -1,24 +0,0 @@
<div class="flexrow item"
data-item-id="{{arme._id}}"
data-arme-name="{{arme.name}}"
data-competence-name="{{competence.name}}">
{{#if name}}
<a class="roll-arme">{{upperFirst name}}</a>
{{else}}
<div></div>
{{/if}}
<div class="flexrow">
<div>{{niveau}}</div>
{{#if init}}
{{#if name}}
<div><a class="roll-init-arme">{{init}}</a></div>
{{else}}
<div>{{init}}</div>
{{/if}}
<div>{{dommages}}</div>
{{else}}
<div></div>
<div></div>
{{/if}}
</div>
</div>

View File

@ -1,5 +0,0 @@
{{#if blessure}}
<a class="click-blessure-remove" data-gravite="{{gravite}}"><i class="fa-regular fa-face-head-bandage blessure-active-{{gravite}}"></i></a>
{{else}}
<a class="click-blessure-add" data-gravite="{{gravite}}"><i class="fa-regular fa-circle"></i></a>
{{/if}}

View File

@ -1,26 +0,0 @@
<div class="flexcol">
<div class="flexrow">
<div>Légères</div>
<div class="flex-group-right">
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[0] gravite=2}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[1] gravite=2}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[2] gravite=2}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[3] gravite=2}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[4] gravite=2}}
</div>
</div>
<div class="flexrow">
<div>Graves</div>
<div class="flex-group-right">
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[5] gravite=4}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[6] gravite=4}}
</div>
</div>
<div class="flexrow">
<div>Critiques</div>
<div class="flex-group-right">
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs" blessure=etat.blessure.[7] gravite=6}}
</div>
</div>
</div>

View File

@ -1,16 +0,0 @@
<div class="flexrow">
<div>
{{#if carac.rollClass}}
<a class="{{carac.rollClass}}" data-carac-name="{{carac.column}}">
{{upper carac.colName}}
</a>
{{else}}
{{upper carac.colName}}
{{/if}}
<a class="{{button-name}}-moins"><i class="fa-regular fa-square-minus"></i></a>
<a class="{{button-name}}-plus"><i class="fa-regular fa-square-plus"></i></a>
</div>
<div class="flex-group-right">
<a class="{{actuel.rollClass}}" data-carac-name="{{actuel.column}}">{{carac.value}} {{#unless (eq carac.value actuel.value)}}(Actuel : {{actuel.value}}){{/unless}}</a>
</div>
</div>

View File

@ -1,14 +0,0 @@
<div class="flexrow">
<div>
{{upperFirst carac.colName}}
<a class="{{carac.column}}-moins"><i class="fa-regular fa-square-minus"></i></a>
{{#if (eq carac.value actuel.value)}}
<i class="fa-regular fa-square"></i>
{{else}}
<a class="{{carac.column}}-plus"><i class="fa-regular fa-square-plus"></i></a>
{{/if}}
</div>
<div class="flex-group-right">
<a class="{{actuel.rollClass}}" data-carac-name="{{actuel.column}}">{{carac.value}} {{#unless (eq carac.value actuel.value)}}(Actuel : {{actuel.value}}){{/unless}}</a>
</div>
</div>

View File

@ -1,12 +0,0 @@
<div class="flexrow">
<div>
{{#if carac.rollClass}}
<a class="{{carac.rollClass}}" data-carac-name="{{carac.column}}">
{{upperFirst carac.colName}}
</a>
{{else}}
{{upperFirst carac.colName}}
{{/if}}
</div>
<div class="flex-group-right">{{carac.value}}</div>
</div>

View File

@ -1,10 +0,0 @@
<div class="flexrow">
<div>
{{#if carac.rollClass}}
<a class="{{carac.rollClass}}" data-carac-name="{{carac.column}}">{{upper carac.colName}}</a>
{{else}}
{{upper carac.colName}}
{{/if}}
</div>
<div class="flex-group-right">{{carac.value}}</div>
</div>

View File

@ -1,9 +0,0 @@
{{#each competences as |group|}}
{{#if @index}}/{{/if}}
{{#with group}}
{{#each list as |comp|}}
<span class="item" data-item-id="{{comp._id}}"><a class="roll-competence">{{comp.name}}</a></span>{{~#unless (isLastIndex @index ../list)~}},{{/unless~}}
{{/each}}
{{numberFormat niveau decimals=0 sign=true}}
{{/with}}
{{/each}}

View File

@ -1,10 +0,0 @@
{{#if name}}
<div class="flexrow item" data-item-id="{{competence._id}}">
<a class="roll-competence" name="Esquive">{{upperFirst name}}</a>
<div class="flexrow">
<div>{{numberFormat competence.system.niveau decimals=0 sign=true}}</div>
<div></div>
<div></div>
</div>
</div>
{{/if}}

View File

@ -1,16 +0,0 @@
<div class="flexrow">
<div>Fatigue
{{#if etat.fatigue.malus}}
({{etat.fatigue.malus}})
{{/if}}
{{#if (eq etat.fatigue.value 0)}}
<i class="fa-regular fa-square"></i>
{{else}}
<a class="fatigue-moins"><i class="fa-regular fa-square-minus"></i></a>
{{/if}}
<a class="fatigue-plus"><i class="fa-regular fa-square-plus"></i></a>
</div>
<div class="flex-group-right">
{{etat.fatigue.value}}{{#if etat.fatigue.value}} / {{etat.fatigue.max}}{{/if}}
</div>
</div>

View File

@ -1,7 +0,0 @@
{{#if protectionnaturelle.value}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs" carac=protectionnaturelle}}
{{/if}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs" carac=protectionarmure}}
{{#if armure.value}}
<div class="flexrow flex-group-right"><i>{{armure.value}}</i></div>
{{/if}}

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