Compare commits

...

25 Commits

Author SHA1 Message Date
ed222c7b6d Version 12.0.18 2024-11-04 00:20:12 +01:00
70198a5727 Correction des armes de défense 2024-11-04 00:19:21 +01:00
bfe70c6634 Fix difficulté défense distance 2024-11-03 23:37:21 +01:00
09365eb744 Fix: reset des utilisations d'items 2024-11-03 23:31:56 +01:00
894d4f3941 Fix: jet d'endurance feuille simplifiée 2024-11-03 22:01:04 +01:00
6e234411ca Fix: Commande /astro 2024-11-03 22:01:04 +01:00
85acb5a255 Fix: message de tchat des commerces 2024-11-03 22:01:04 +01:00
6106e2a19e Fix changelog feuille PNJ 2024-11-03 22:01:04 +01:00
f896f1da6e Fix rename attackerToken
correction oubli sans gravité
2024-11-03 22:01:03 +01:00
9bb45c2349 PNJ: boutons des compteurs
Pas de bouton + au max de vie/endurance.
Pas de bouton - au min, de fatigue
2024-11-03 22:01:03 +01:00
5cab219e0e PNJ: affichage niveau d'esquive 2024-11-03 22:01:03 +01:00
2de9ea49c8 PNJ: ajout mains nues et empoignade 2024-11-03 22:01:03 +01:00
1760d26014 PNJ: support initiative 2024-11-03 22:01:03 +01:00
92185d4a5b Portrait dans les feuilles simplifiées 2024-11-03 22:01:03 +01:00
210b129934 Fix: niveau de compétence non défini
- quand le niveau de compétence n'était pas défini, la feuille de
personnage simplifiée et l'export scriptarium ne marchaient pas
- on pouvait saisitr une compétence avec un niveau non défini
2024-11-03 22:01:02 +01:00
b892068b38 Utilisation du nom du Nom du token
suite (pour les encaissements)
2024-11-03 22:01:02 +01:00
3906cb0a7b Localisation des blessures en regle optionnelle 2024-11-03 22:01:02 +01:00
5c58932a0d Amélioration message encaissement 2024-11-03 22:01:02 +01:00
3b06bd382b Afficher le nom du token au lieu du nom d'Acteur
Dans les messages d'automatisation de combat, le nom des
tokens est utilisé au lieu d'utiliser le nom de l'acteur.

Ceci permet de ne pas dévoiler un nom générique (Villageois)
si le token a un nom personnalisé.
2024-11-03 22:01:02 +01:00
b4a725ff12 Fix: Message uniquement MJ
L'utilisation de firstConnectedGM renvoie le GM, pas true si
un GM est connecté.
2024-11-03 22:01:01 +01:00
05df6a68cc Amelioration parser 2024-10-31 23:38:02 +01:00
025c3483a9 Amelioration parser 2024-10-31 23:36:16 +01:00
97138b25c7 Parser de personnage/creature, WIP 2024-10-30 22:45:47 +01:00
c0066f79c1 Move to v12.0.17 2024-10-26 10:56:52 +02:00
4db8bf95f9 Move to v12.0.16 2024-10-25 19:02:23 +02:00
41 changed files with 9115 additions and 293 deletions

View File

@ -1,4 +1,25 @@
# 12.0 # 12.0
## 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 ## 12.0.15 - Le messager d'Astrobazzarh
- Correction des faces de dés personalisés dice-so-nice - Correction des faces de dés personalisés dice-so-nice
- Les messages de maladies ne sont plus publics - Les messages de maladies ne sont plus publics

View File

@ -220,18 +220,18 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
// Points de reve actuel // Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', true)) this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', true))
this.html.find('.empoignade-label a').click(async event => RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor))) this.html.find('.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))))
this.html.find('.roll-arme').click(async event => this.actor.rollArme(foundry.utils.duplicate(this._getEventArmeCombat(event)), 'competence'))
// Initiative pour l'arme // Initiative pour l'arme
this.html.find('.arme-initiative a').click(async event => { this.html.find('.roll-init-arme').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id); let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id)
if (combatant) { if (combatant) {
let action = this._getEventArmeCombat(event); RdDCombatManager.rollInitiativeAction(combatant._id, this._getEventArmeCombat(event));
RdDCombatManager.rollInitiativeAction(combatant._id, action);
} else { } else {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat."); ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
} }
}); })
// Display TMR // Display TMR
this.html.find('.button-tmr').click(async event => this.actor.displayTMR("normal")) this.html.find('.button-tmr').click(async event => this.actor.displayTMR("normal"))

View File

@ -1548,7 +1548,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { async appliquerAjoutExperience(rollData, hideChatMessage = 'show') {
if (!Misc.firstConnectedGM()){ if (!Misc.isFirstConnectedGM()){
return return
} }
hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM) hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
@ -2615,7 +2615,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async resetItemUse() { async resetItemUse() {
await this.setFlag(SYSTEM_RDD, 'itemUse', {}); await this.unsetFlag(SYSTEM_RDD, 'itemUse');
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -3011,7 +3011,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onCreateOwnedDraconique(item, options, id) { async onCreateOwnedDraconique(item, options, id) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item)); let draconique = Draconique.all().find(it => it.match(item));
if (draconique) { if (draconique) {
await draconique.onActorCreateOwned(this, item) await draconique.onActorCreateOwned(this, item)
@ -3023,7 +3023,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onDeleteOwnedDraconique(item, options, id) { async onDeleteOwnedDraconique(item, options, id) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.match(item)); let draconique = Draconique.all().find(it => it.match(item));
if (draconique) { if (draconique) {
await draconique.onActorDeleteOwned(this, item) await draconique.onActorDeleteOwned(this, item)
@ -3033,7 +3033,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onDeleteOwnedCaseTmr(item, options, id) { async onDeleteOwnedCaseTmr(item, options, id) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
let draconique = Draconique.all().find(it => it.isCase(item)); let draconique = Draconique.all().find(it => it.isCase(item));
if (draconique) { if (draconique) {
await draconique.onActorDeleteCaseTmr(this, item) await draconique.onActorDeleteCaseTmr(this, item)

View File

@ -78,8 +78,10 @@ export class RdDBaseActorReve extends RdDBaseActor {
} }
async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } } async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } }
isDead() { return false } isDead() { return false }
isSonne() { return false }
blessuresASoigner() { return [] } blessuresASoigner() { return [] }
getEtatGeneral(options = { ethylisme: false }) { return 0 } getEtatGeneral(options = { ethylisme: false }) { return 0 }
isActorCombat() { return true }
async computeArmure(attackerRoll) { return this.getProtectionNaturelle() } async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
async remiseANeuf() { } async remiseANeuf() { }
@ -327,14 +329,15 @@ export class RdDBaseActorReve extends RdDBaseActor {
const competence = this.getCompetence(idOrName); const competence = this.getCompetence(idOrName);
let rollData = { carac: this.system.carac, competence: competence, arme: options.arme } let rollData = { carac: this.system.carac, competence: competence, arme: options.arme }
if (competence.type == ITEM_TYPES.competencecreature) { if (competence.type == ITEM_TYPES.competencecreature) {
const token = RdDUtility.getSelectedToken(this)
const arme = RdDItemCompetenceCreature.armeCreature(competence) const arme = RdDItemCompetenceCreature.armeCreature(competence)
if (arme && options.tryTarget && Targets.hasTargets()) { if (arme && options.tryTarget && Targets.hasTargets()) {
Targets.selectOneToken(target => { Targets.selectOneTargetToken(target => {
if (arme.action == "possession") { if (arme.action == "possession") {
RdDPossession.onAttaquePossession(target, this, competence) RdDPossession.onAttaquePossession(target, this, competence)
} }
else { else {
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme) RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
} }
}); });
return; return;
@ -364,7 +367,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession * @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
* @returns * @returns
*/ */
rollArme(arme, categorieArme = "competence") { rollArme(arme, categorieArme, token) {
token = token ?? RdDUtility.getSelectedToken(this)
const compToUse = this.$getCompetenceArme(arme, categorieArme) const compToUse = this.$getCompetenceArme(arme, categorieArme)
if (!RdDItemArme.isArmeUtilisable(arme)) { if (!RdDItemArme.isArmeUtilisable(arme)) {
ui.notifications.warn(`Arme inutilisable: ${arme.name} a une résistance de 0 ou moins`) ui.notifications.warn(`Arme inutilisable: ${arme.name} a une résistance de 0 ou moins`)
@ -385,7 +389,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
return return
} }
Targets.selectOneToken(target => { Targets.selectOneTargetToken(target => {
if (Targets.isTargetEntite(target)) { if (Targets.isTargetEntite(target)) {
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`); ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
return return
@ -395,7 +399,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
if (competence.isCompetencePossession()) { if (competence.isCompetencePossession()) {
return RdDPossession.onAttaquePossession(target, this, competence); return RdDPossession.onAttaquePossession(target, this, competence);
} }
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme); RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme);
}) })
} }
@ -403,58 +407,52 @@ export class RdDBaseActorReve extends RdDBaseActor {
return RdDItemArme.getCompetenceArme(arme, competenceName) return RdDItemArme.getCompetenceArme(arme, competenceName)
} }
verifierForceMin(item) { verifierForceMin(item) { }
}
/* -------------------------------------------- */
async resetItemUse() { }
async incDecItemUse(itemId, inc = 1) { }
getItemUse(itemId) { return 0; }
/* -------------------------------------------- */ /* -------------------------------------------- */
async encaisser() { await RdDEncaisser.encaisser(this) } async encaisser() { await RdDEncaisser.encaisser(this) }
async encaisserDommages(rollData, attacker = undefined, show = undefined) { async encaisserDommages(rollData, attacker = undefined, show = undefined, attackerToken = undefined, defenderToken = undefined) {
if (attacker && !await attacker.accorder(this, 'avant-encaissement')) { if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
return; return;
} }
const armure = await this.computeArmure(rollData); const armure = await this.computeArmure(rollData);
if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) { if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
await this.encaisserDommagesValidationGR(rollData, armure, attacker?.id, show); await this.encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken);
} }
else { else {
const jet = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE }); const jet = await RdDUtility.jetEncaissement(this, rollData, armure, { showDice: SHOW_DICE });
await this.$onEncaissement(jet, show, attacker); await this.$onEncaissement(jet, show, attackerToken, defenderToken)
} }
} }
async encaisserDommagesValidationGR(rollData, armure, attackerId, show) { async encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken) {
if (!game.user.isGM) { if (!game.user.isGM) {
RdDBaseActor.remoteActorCall({ RdDBaseActor.remoteActorCall({
tokenId: this.token?.id, tokenId: this.token?.id,
actorId: this.id, actorId: this.id,
method: 'encaisserDommagesValidationGR', method: 'encaisserDommagesValidationGR',
args: [rollData, armure, attackerId, show] args: [rollData, armure, show, attackerToken, defenderToken]
}); })
} else { } else {
const attacker = game.actors.get(attackerId);
DialogValidationEncaissement.validerEncaissement(this, rollData, armure, DialogValidationEncaissement.validerEncaissement(this, rollData, armure,
jet => this.$onEncaissement(jet, show, attacker)); jet => this.$onEncaissement(jet, show, attackerToken, defenderToken));
} }
} }
async $onEncaissement(jet, show, attacker) { async $onEncaissement(jet, show, attackerToken, defenderToken) {
await this.onAppliquerJetEncaissement(jet, attacker); await this.onAppliquerJetEncaissement(jet, attackerToken);
await this.$afficherEncaissement(jet, show); await this.$afficherEncaissement(jet, show, defenderToken);
} }
async onAppliquerJetEncaissement(encaissement, attacker) { } async onAppliquerJetEncaissement(encaissement, attackerToken) { }
async $afficherEncaissement(encaissement, show) { async $afficherEncaissement(encaissement, show, defenderToken) {
foundry.utils.mergeObject(encaissement, { foundry.utils.mergeObject(encaissement, {
alias: this.name, alias: defenderToken?.name ?? this.name,
hasPlayerOwner: this.hasPlayerOwner, hasPlayerOwner: this.hasPlayerOwner,
show: show ?? {} show: show ?? {}
}); }, {overwrite: false});
await ChatUtility.createChatWithRollMode( await ChatUtility.createChatWithRollMode(
{ {

View File

@ -89,9 +89,9 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onAppliquerJetEncaissement(encaissement, attacker) { async onAppliquerJetEncaissement(encaissement, attackerToken) {
const santeOrig = foundry.utils.duplicate(this.system.sante); const santeOrig = foundry.utils.duplicate(this.system.sante);
const blessure = await this.ajouterBlessure(encaissement, attacker); // Will update the result table const blessure = await this.ajouterBlessure(encaissement, attackerToken); // Will update the result table
const perteVie = await this.santeIncDec("vie", -encaissement.vie); const perteVie = await this.santeIncDec("vie", -encaissement.vie);
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique()); const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance, blessure?.isCritique());
@ -169,7 +169,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
/* -------------------------------------------- */ /* -------------------------------------------- */
async ajouterBlessure(encaissement, attacker = undefined) { async ajouterBlessure(encaissement, attackerToken = undefined) {
if (encaissement.gravite < 0) return; if (encaissement.gravite < 0) return;
if (encaissement.gravite > 0) { if (encaissement.gravite > 0) {
while (this.countBlessures(it => it.system.gravite == encaissement.gravite) >= RdDItemBlessure.maxBlessures(encaissement.gravite) && encaissement.gravite <= 6) { while (this.countBlessures(it => it.system.gravite == encaissement.gravite) >= RdDItemBlessure.maxBlessures(encaissement.gravite) && encaissement.gravite <= 6) {
@ -181,7 +181,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
} }
} }
const endActuelle = this.getEnduranceActuelle(); const endActuelle = this.getEnduranceActuelle();
const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attacker); const blessure = await RdDItemBlessure.createBlessure(this, encaissement.gravite, encaissement.dmg?.loc.label ?? '', attackerToken);
if (blessure.isCritique()) { if (blessure.isCritique()) {
encaissement.endurance = endActuelle; encaissement.endurance = endActuelle;
} }
@ -276,11 +276,11 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
ui.notifications.info(`${this.name} est hors combat, il ne reste donc pas sonné`); ui.notifications.info(`${this.name} est hors combat, il ne reste donc pas sonné`);
return; return;
} }
await this.setEffect(STATUSES.StatusStunned, sonne); await this.setEffect(STATUSES.StatusStunned, sonne)
} }
getSonne() { isSonne() {
return this.getEffect(STATUSES.StatusStunned); return this.getEffect(STATUSES.StatusStunned)
} }
isEffectAllowed(effectId) { return true } isEffectAllowed(effectId) { return true }

View File

@ -205,11 +205,8 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { } async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) { } async onCreateItem(item, options, id) { }
async onDeleteItem(item, options, id) { } async onDeleteItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { } async onUpdateActor(update, options, actorId) { }
async onTimeChanging(oldTimestamp, newTimestamp) { async onTimeChanging(oldTimestamp, newTimestamp) {
@ -218,7 +215,7 @@ export class RdDBaseActor extends Actor {
} }
async creerObjetParMJ(object) { async creerObjetParMJ(object) {
if (!Misc.isUniqueConnectedGM()) { if (!Misc.isFirstConnectedGM()) {
RdDBaseActor.remoteActorCall({ RdDBaseActor.remoteActorCall({
tokenId: this.token?.id, tokenId: this.token?.id,
actorId: this.id, actorId: this.id,
@ -317,7 +314,7 @@ export class RdDBaseActor extends Actor {
RdDAudio.PlayContextAudio("argent"); // Petit son RdDAudio.PlayContextAudio("argent"); // Petit son
ChatMessage.create({ ChatMessage.create({
whisper: ChatUtility.getOwners(this.name), whisper: ChatUtility.getOwners(this),
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.` content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
}); });
} }
@ -335,7 +332,7 @@ export class RdDBaseActor extends Actor {
ui.notifications.info("Inutile de se vendre à soi-même"); ui.notifications.info("Inutile de se vendre à soi-même");
return; return;
} }
if (!Misc.isUniqueConnectedGM()) { if (!Misc.isFirstConnectedGM()) {
RdDBaseActor.remoteActorCall({ RdDBaseActor.remoteActorCall({
actorId: achat.vendeurId ?? achat.acheteurId, actorId: achat.vendeurId ?? achat.acheteurId,
method: 'achatVente', method: 'achatVente',
@ -708,5 +705,9 @@ export class RdDBaseActor extends Actor {
} }
return undefined return undefined
} }
async resetItemUse() { }
async incDecItemUse(itemId, inc = 1) { }
getItemUse(itemId) { return 0; }
async finDeRound(options = { terminer: false }) { }
isActorCombat() { return false }
} }

View File

@ -74,7 +74,7 @@ export class RdDEntite extends RdDBaseActorReve {
return [STATUSES.StatusComma].includes(effectId); return [STATUSES.StatusComma].includes(effectId);
} }
async onAppliquerJetEncaissement(encaissement, attacker) { async onAppliquerJetEncaissement(encaissement, attackerToken) {
const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance); const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
foundry.utils.mergeObject(encaissement, { foundry.utils.mergeObject(encaissement, {
resteEndurance: perteEndurance.newValue, resteEndurance: perteEndurance.newValue,

View File

@ -84,7 +84,7 @@ const MAPPING_BASE = [
{ column: "malus_armure", getter: (actor, context) => Mapping.getMalusArmure(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: "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: "vie_actuel", rollClass: 'jet-vie', getter: (actor, context) => actor.system.sante.vie.value },
{ column: "endurance_actuel", rollClass: 'jet-vie', getter: (actor, context) => actor.system.sante.endurance.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", getter: (actor, context) => Mapping.getEsquive(context) },
{ column: "esquive_armure", getter: (actor, context) => Mapping.getEsquiveArmure(context) }, { column: "esquive_armure", getter: (actor, context) => Mapping.getEsquiveArmure(context) },
{ column: "competences", getter: (actor, context) => Mapping.getCompetences(actor, CATEGORIES_COMPETENCES) }, { column: "competences", getter: (actor, context) => Mapping.getCompetences(actor, CATEGORIES_COMPETENCES) },
@ -140,16 +140,15 @@ export class Mapping {
static prepareArmes(actor) { static prepareArmes(actor) {
const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme) const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme)
return armes.map(arme => RdDItemArme.ajoutCorpsACorps(armes, actor)
[ return armes.map(arme => [
arme.system.unemain ? Mapping.prepareArme(actor, arme, 'unemain') : undefined, arme.system.unemain ? Mapping.prepareArme(actor, arme, 'unemain') : undefined,
arme.system.deuxmains ? Mapping.prepareArme(actor, arme, 'deuxmains') : undefined, arme.system.deuxmains ? Mapping.prepareArme(actor, arme, 'deuxmains') : undefined,
!(arme.system.unemain || arme.system.deuxmains) ? Mapping.prepareArme(actor, arme, 'competence') : undefined, !(arme.system.unemain || arme.system.deuxmains) ? Mapping.prepareArme(actor, arme, 'competence') : undefined,
arme.system.lancer != "" ? Mapping.prepareArme(actor, arme, 'lancer') : undefined, arme.system.lancer != "" ? Mapping.prepareArme(actor, arme, 'lancer') : undefined,
arme.system.tir != "" ? Mapping.prepareArme(actor, arme, 'tir') : undefined arme.system.tir != "" ? Mapping.prepareArme(actor, arme, 'tir') : undefined]
] .filter(it => it != undefined))
.filter(it => it != undefined) .reduce((a, b) => a.concat(b), [])
).reduce((a, b) => a.concat(b), [])
} }
static prepareArme(actor, arme, maniement) { static prepareArme(actor, arme, maniement) {
@ -158,23 +157,31 @@ export class Mapping {
if (RdDItemCompetence.isNiveauBase(competence)) { if (RdDItemCompetence.isNiveauBase(competence)) {
return undefined return undefined
} }
const dmgArme = RdDItemArme.dommagesReels(arme, maniement)
const dommages = dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme)
const categorie = Mapping.complementCategorie(arme, maniement) const categorie = Mapping.complementCategorie(arme, maniement)
const dommages = Mapping.dommagesArme(actor, arme, maniement)
return { return {
name: arme.name + categorie, name: arme.name + categorie,
niveau: Misc.toSignedString(competence.system.niveau), niveau: Misc.toSignedString(competence.system.niveau),
init: Mapping.calculBaseInit(actor, competence.system.categorie) + competence.system.niveau, init: Mapping.calculBaseInit(actor, competence.system.categorie) + competence.system.niveau,
dommages: Misc.toSignedString(dommages), dommages: dommages,
competence: competence, competence: competence,
arme: arme 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) { static complementCategorie(arme, maniement) {
switch (maniement) { switch (maniement) {
case 'unemain': return (arme.system.deuxmains) ? ' 1 main' : (arme.system.lancer||arme.system.tir) ? ' mêlée': '' 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 '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 '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' : '' case 'tir': return (arme.system.unemain || arme.system.deuxmains || arme.system.lancer) ? ' tir' : ''
} }
@ -327,7 +334,8 @@ export class Mapping {
const txtByCategories = Object.values(byCategories) const txtByCategories = Object.values(byCategories)
.map(it => it.competencesParNiveau) .map(it => it.competencesParNiveau)
.map(byNiveau => { .map(byNiveau => {
const niveaux = Object.keys(byNiveau).map(it => Number(it)).sort(Misc.ascending()) const niveaux = Object.keys(byNiveau)
.map(it => Number(it)).sort(Misc.ascending())
if (niveaux.length == 0) { if (niveaux.length == 0) {
return '' return ''
} }

View File

@ -0,0 +1,433 @@
/************************************************************************************/
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);
}
}

8225
module/apps/xregexp-all.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,7 @@ export class ChatUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static onRemoveMessages(socketData) { static onRemoveMessages(socketData) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
if (socketData.part) { if (socketData.part) {
const toDelete = game.messages.filter(it => it.content.includes(socketData.part)); const toDelete = game.messages.filter(it => it.content.includes(socketData.part));
toDelete.forEach(it => it.delete()); toDelete.forEach(it => it.delete());
@ -63,7 +63,7 @@ export class ChatUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static removeMessages(socketData) { static removeMessages(socketData) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
ChatUtility.onRemoveMessages(socketData); ChatUtility.onRemoveMessages(socketData);
} }
else { else {
@ -161,7 +161,7 @@ export class ChatUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static handleGMChatMessage(socketData) { static handleGMChatMessage(socketData) {
console.log("blindMessageToGM", socketData); console.log("blindMessageToGM", socketData);
if (Misc.firstConnectedGM()) { if (Misc.isFirstConnectedGM()) {
ChatMessage.create({ ChatMessage.create({
user: game.user.id, user: game.user.id,
whisper: ChatUtility.getGMs(), whisper: ChatUtility.getGMs(),

View File

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

View File

@ -81,37 +81,59 @@ export class RdDItemArme extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static getCategorieParade(armeData) { static getCategorieParade(armeData) {
if (armeData.system.categorie_parade) { if (armeData.system.categorie_parade) {
return armeData.system.categorie_parade; return armeData.system.categorie_parade
} }
// pour compatibilité avec des personnages existants // pour compatibilité avec des personnages existants
if (armeData.type == ITEM_TYPES.competencecreature || armeData.system.categorie == 'creature') { if (armeData.type == ITEM_TYPES.competencecreature || armeData.system.categorie == 'creature') {
return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : ''); return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '')
} }
if (!armeData.type.match(/arme|competencecreature/)) { if (!armeData.type.match(/arme|competencecreature/)) {
return ''; return ''
} }
if (armeData.system.competence == undefined) { if (armeData.system.competence == undefined) {
return ITEM_TYPES.competencecreature; return ITEM_TYPES.competencecreature;
} }
let compname = armeData.system.competence.toLowerCase(); 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('hache')) return 'haches'
if (compname.match('hast')) return 'hast'; if (compname.match('hast')) return 'hast'
if (compname.match('lance')) return 'lances'; if (compname.match('lance')) return 'lances'
if (compname.match('bouclier')) return 'boucliers'; if (compname.match('bouclier')) return 'boucliers'
if (compname.match('masse')) return 'masses'; if (compname.match('masse')) return 'masses'
if (compname.match('epée') || compname.match('épée')) { if (compname.match('epée') || compname.match('épée')) {
if (armeData.name.toLowerCase().match(/(gnome)/)) if (armeData.name.toLowerCase().match(/(gnome)/))
return 'epees-courtes'; return 'epees-courtes'
if (armeData.name.toLowerCase().match(/((e|é)pée dragone|esparlongue|demi-dragonne)/)) if (armeData.name.toLowerCase().match(/((e|é)pée dragone|esparlongue|demi-dragonne)/))
return 'epees-longues'; return 'epees-longues'
return 'epees-lourdes'; return 'epees-lourdes'
} }
if (compname.match('dague')) { if (compname.match('dague')) {
return 'dagues'; 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 'sans-armes';
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -120,8 +142,8 @@ export class RdDItemArme extends Item {
return false; return false;
} }
// categories d'armes à la parade (cf. page 115 ) // categories d'armes à la parade (cf. page 115 )
let attCategory = RdDItemArme.getCategorieParade(armeAttaque); const attCategory = RdDItemArme.getCategorieParade(armeAttaque)
let defCategory = RdDItemArme.getCategorieParade(armeParade); const defCategory = RdDItemArme.getCategorieParade(armeParade)
// bouclier et mêmes catégorie: peuvent se parer sans difficulté // bouclier et mêmes catégorie: peuvent se parer sans difficulté
if (defCategory == 'boucliers') { if (defCategory == 'boucliers') {
return false; return false;

View File

@ -190,7 +190,7 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static isNiveauBase(item) { static isNiveauBase(item) {
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.type); return item.system.niveau == undefined || Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie, item.type);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -267,10 +267,17 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
/** @override */ /** @override */
_updateObject(event, formData) { _updateObject(event, formData) {
if (this.item.type == 'sort') { switch (this.item.type) {
case ITEM_TYPES.sort:
// Données de bonus de cases ? // Données de bonus de cases ?
formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue); 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']
} }
}
return this.item.update(formData); return this.item.update(formData);
} }

View File

@ -67,7 +67,7 @@ export class RdDItemBlessure extends RdDItem {
} }
static async createBlessure(actor, gravite, localisation = '', attacker) { static async createBlessure(actor, gravite, localisation = '', attackerToken) {
const definition = RdDItemBlessure.getDefinition(gravite) const definition = RdDItemBlessure.getDefinition(gravite)
const blessure = { const blessure = {
name: definition.label, name: definition.label,
@ -77,7 +77,7 @@ export class RdDItemBlessure extends RdDItem {
gravite: gravite, gravite: gravite,
difficulte: - gravite, difficulte: - gravite,
localisation: localisation, localisation: localisation,
origine: attacker?.name ?? "" origine: attackerToken?.name ?? ""
} }
} }
const blessures = await actor.createEmbeddedDocuments('Item', [blessure]) const blessures = await actor.createEmbeddedDocuments('Item', [blessure])

View File

@ -139,7 +139,7 @@ export class Misc {
} }
static join(params, separator = '') { static join(params, separator = '') {
return params?.reduce(Misc.joining(separator)) ?? ''; return (!params || params.length == 0) ? '' : params.reduce(Misc.joining(separator))
} }
static joining(separator = '') { static joining(separator = '') {
@ -195,7 +195,7 @@ export class Misc {
return document return document
} }
} }
else if (Misc.isUniqueConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) { else if (Misc.isFirstConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) {
return document return document
} }
return undefined return undefined
@ -206,14 +206,14 @@ export class Misc {
} }
static isOwnerPlayerOrUniqueConnectedGM(actor) { static isOwnerPlayerOrUniqueConnectedGM(actor) {
return Misc.isOwnerPlayer(actor) ?? Misc.isUniqueConnectedGM(); return Misc.isOwnerPlayer(actor) ?? Misc.isFirstConnectedGM();
} }
/** /**
* @returns true pour un seul utilisateur: le premier GM connecté par ordre d'id * @returns true pour un seul utilisateur: le premier GM connecté par ordre d'id
*/ */
static isUniqueConnectedGM() { static isFirstConnectedGM() {
return game.user.id == Misc.firstConnectedGMId(); return game.user == Misc.firstConnectedGM();
} }
static firstConnectedGMId() { static firstConnectedGMId() {

View File

@ -42,7 +42,8 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */ /* -------------------------------------------- */
Hooks.on("getCombatTrackerEntryContext", (html, options) => { RdDCombatManager.pushInitiativeOptions(html, options); }); Hooks.on("getCombatTrackerEntryContext", (html, options) => { RdDCombatManager.pushInitiativeOptions(html, options); });
Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) }); Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) });
Hooks.on("preDeleteCombat", (combat, html, id) => { combat.onPreDeleteCombat() }); Hooks.on("preDeleteCombat", (combat, html, id) => { combat.onPreDeleteCombat() })
Hooks.on("deleteCombat", (combat, html, id) => { combat.onDeleteCombat() })
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -53,7 +54,7 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async onPreDeleteCombat() { async onPreDeleteCombat() {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
await this.finDeRound({ terminer: true }) await this.finDeRound({ terminer: true })
ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`) ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined) game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
@ -61,20 +62,31 @@ export class RdDCombatManager extends Combat {
RdDEmpoignade.deleteAllEmpoignades() RdDEmpoignade.deleteAllEmpoignades()
} }
} }
async onDeleteCombat() {
if (Misc.isFirstConnectedGM()) {
if (game.combats.size <= 1) {
game.actors.forEach(actor => actor.resetItemUse())
}
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async finDeRound(options = { terminer: false }) { async finDeRound(options = { terminer: false }) {
this.turns.forEach(turn => turn.actor.resetItemUse());
for (let combatant of this.combatants) { for (let combatant of this.combatants) {
if (combatant.actor) { if (!combatant.actor) {
await combatant.actor.finDeRound(options);
}
else {
ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur!`) ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur!`)
} }
else if (!combatant.actor.isActorCombat()) {
ui.notifications.warn(`Le combatant ${combatant.name} ne peut pas combattre!`)
}
else {
await combatant.actor.finDeRound(options)
await combatant.actor.resetItemUse()
} }
} }
}
static calculAjustementInit(actor, arme) { static calculAjustementInit(actor, arme) {
const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0 const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
const etatGeneral = actor.getEtatGeneral() ?? 0 const etatGeneral = actor.getEtatGeneral() ?? 0
@ -125,7 +137,6 @@ export class RdDCombatManager extends Combat {
} }
} }
} }
//console.log("Combatat", c);
const roll = combatant.getInitiativeRoll(rollFormula); const roll = combatant.getInitiativeRoll(rollFormula);
if (!roll.total) { if (!roll.total) {
await roll.evaluate(); await roll.evaluate();
@ -236,29 +247,12 @@ export class RdDCombatManager extends Combat {
return attaque; 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) { static listActionsCombat(combatant) {
const actor = combatant.actor; const actor = combatant.actor;
if (!actor.isActorCombat()) {
return
}
let actions = RdDCombatManager.listActionsPossessions(actor); let actions = RdDCombatManager.listActionsPossessions(actor);
if (actions.length > 0) { if (actions.length > 0) {
return actions; return actions;
@ -281,6 +275,26 @@ export class RdDCombatManager extends Combat {
return RdDCombatManager._indexActions(actions); return RdDCombatManager._indexActions(actions);
} }
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 _indexActions(actions) { static _indexActions(actions) {
for (let index = 0; index < actions.length; index++) { for (let index = 0; index < actions.length; index++) {
actions[index].index = index; actions[index].index = index;
@ -291,7 +305,7 @@ export class RdDCombatManager extends Combat {
/* -------------------------------------------- */ /* -------------------------------------------- */
static processPremierRoundInit() { static processPremierRoundInit() {
// Check if we have the whole init ! // Check if we have the whole init !
if (Misc.isUniqueConnectedGM() && game.combat.current.round == 1) { if (Misc.isFirstConnectedGM() && game.combat.current.round == 1) {
let initMissing = game.combat.combatants.find(it => !it.initiative); let initMissing = game.combat.combatants.find(it => !it.initiative);
if (!initMissing) { // Premier round ! if (!initMissing) { // Premier round !
for (let combatant of game.combat.combatants) { for (let combatant of game.combat.combatants) {
@ -454,11 +468,11 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
static combatNouveauTour(combat) { static combatNouveauTour(combat) {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId); let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId);
if (turn?.actor) { if (turn?.actor) {
RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token.id);
// TODO Playaudio for player?? // TODO Playaudio for player??
RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token);
} }
} }
} }
@ -469,52 +483,51 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static rddCombatTarget(target, attacker) { static rddCombatTarget(target, attacker, attackerToken) {
const defender = target?.actor; return new RdDCombat(attacker, attackerToken?.id, target?.actor, target?.id, target)
const defenderTokenId = target?.id;
return new RdDCombat(attacker, defender, defenderTokenId, target)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) { static rddCombatForAttackerAndDefender(attackerId, attackerTokenId, defenderTokenId) {
const attacker = game.actors.get(attackerId); const attacker = game.actors.get(attackerId)
let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined; const defenderToken = defenderTokenId ? canvas.tokens.get(defenderTokenId) : undefined
let defender = defenderToken?.actor;
let target = undefined let target = undefined
if (!defenderTokenId || !defender) { if (!defenderTokenId || !defender) {
console.warn(`RdDCombat.rddCombatForAttackerAndDefender: appel avec defenderTokenId ${defenderTokenId} incorrect, ou pas de defender correspondant`); console.warn(`RdDCombat.rddCombatForAttackerAndDefender: appel avec defenderTokenId ${defenderTokenId} incorrect, ou pas de defender correspondant`);
target = Targets.getTarget() target = Targets.getTarget()
if (!target) { if (!target) {
return; return
} }
defenderTokenId = target.id; defenderTokenId = target.id;
defender = target.actor; defender = target.actor;
if (!defenderTokenId || !defender) { if (!defenderTokenId || !defender) {
return; return
} }
} }
return new RdDCombat(attacker, defender, defenderTokenId, target) return new RdDCombat(attacker, attackerTokenId, defender, defenderTokenId, target)
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static onMsgEncaisser(msg) { static onMsgEncaisser(msg) {
let defender = canvas.tokens.get(msg.defenderTokenId).actor;
if (Misc.isOwnerPlayerOrUniqueConnectedGM()) { if (Misc.isOwnerPlayerOrUniqueConnectedGM()) {
let attackerRoll = msg.attackerRoll; let attackerRoll = msg.attackerRoll;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined; let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
let defender = canvas.tokens.get(msg.defenderToken.id).actor;
defender.encaisserDommages(attackerRoll, attacker); defender.encaisserDommages(attackerRoll, attacker, msg.attackerToken);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme); rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static onMsgDefense(msg) { static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderTokenId); let defenderToken = canvas.tokens.get(msg.defenderToken.id)
if (defenderToken && Misc.isUniqueConnectedGM()) { if (defenderToken && Misc.isFirstConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id)
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme); rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme)
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll); rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
} }
} }
@ -545,6 +558,7 @@ export class RdDCombat {
html.on("click", button, event => { html.on("click", button, event => {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender( const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value, event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-attackerTokenId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value); event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) { if (rddCombat) {
rddCombat.onEvent(button, event); rddCombat.onEvent(button, event);
@ -560,22 +574,38 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor(attacker, defender, defenderTokenId, target) { constructor(attacker, attackerTokenId, defender, defenderTokenId, target) {
this.attacker = attacker this.attacker = attacker
this.defender = defender this.defender = defender
this.target = target this.target = target
this.attackerId = this.attacker.id this.attackerId = this.attacker.id
this.defenderId = this.defender.id this.defenderId = this.defender.id
this.attackerTokenId = attackerTokenId
this.defenderTokenId = defenderTokenId 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) { async onEvent(button, event) {
const chatMessage = ChatUtility.getChatMessage(event); const chatMessage = ChatUtility.getChatMessage(event);
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll'); const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll'); const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll');
console.log('RdDCombat', attackerRoll, defenderRoll); console.log('RdDCombat', attackerRoll, defenderRoll);
const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value;
const armeParadeId = event.currentTarget.attributes['data-armeid']?.value; const armeParadeId = event.currentTarget.attributes['data-armeid']?.value;
const competence = event.currentTarget.attributes['data-competence']?.value; const competence = event.currentTarget.attributes['data-competence']?.value;
@ -585,7 +615,7 @@ export class RdDCombat {
case '#particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value); case '#particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '#parer-button': return this.parade(attackerRoll, armeParadeId); case '#parer-button': return this.parade(attackerRoll, armeParadeId);
case '#esquiver-button': return this.esquive(attackerRoll, compId, competence); case '#esquiver-button': return this.esquive(attackerRoll, compId, competence);
case '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll, defenderTokenId); case '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll);
case '#echec-total-attaque': return this._onEchecTotal(attackerRoll); case '#echec-total-attaque': return this._onEchecTotal(attackerRoll);
case '#appel-chance-attaque': return this.attacker.rollAppelChance( case '#appel-chance-attaque': return this.attacker.rollAppelChance(
@ -702,7 +732,7 @@ export class RdDCombat {
}) })
} }
else { else {
const defenderToken = canvas.tokens.get(this.defenderTokenId); const defenderToken = canvas.tokens.get(this.defenderTokenId)
const dist = this.distance(_token, defenderToken) const dist = this.distance(_token, defenderToken)
const isVisible = this.isVisible(_token, defenderToken) const isVisible = this.isVisible(_token, defenderToken)
const portee = this._ajustementPortee(dist, rollData.arme) const portee = this._ajustementPortee(dist, rollData.arme)
@ -769,7 +799,7 @@ export class RdDCombat {
} }
RdDEmpoignade.checkEmpoignadeEnCours(this.attacker) RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
let rollData = this._prepareAttaque(competence, arme); let rollData = this._prepareAttaque(competence, arme)
console.log("RdDCombat.attaque >>>", rollData); console.log("RdDCombat.attaque >>>", rollData);
if (arme) { if (arme) {
this.attacker.verifierForceMin(arme); this.attacker.verifierForceMin(arme);
@ -795,15 +825,18 @@ export class RdDCombat {
dialog.render(true); dialog.render(true);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_prepareAttaque(competence, arme) { _prepareAttaque(competence, arme) {
let rollData = { let rollData = {
alias: this.attackerToken.name,
passeArme: foundry.utils.randomID(16), passeArme: foundry.utils.randomID(16),
mortalite: arme?.system.mortalite, mortalite: arme?.system.mortalite,
competence: competence, competence: competence,
surprise: this.attacker.getSurprise(true), surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true), surpriseDefenseur: this.defender.getSurprise(true),
targetToken: Targets.extractTokenData(this.target), sourceToken: this.attackerToken,
targetToken: this.defenderToken,
essais: {} essais: {}
}; };
@ -848,9 +881,10 @@ export class RdDCombat {
alias: this.attacker.name, alias: this.attacker.name,
whisper: ChatUtility.getOwners(this.attacker), whisper: ChatUtility.getOwners(this.attacker),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html', { content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html', {
alias: this.attacker.name, alias: this.attackerToken.name,
attackerId: this.attackerId, attackerId: this.attackerId,
defenderTokenId: this.defenderTokenId, attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
isForce: isForce, isForce: isForce,
isFinesse: isFinesse, isFinesse: isFinesse,
isRapide: isRapide, isRapide: isRapide,
@ -867,7 +901,7 @@ export class RdDCombat {
attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite()); attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite());
let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} } let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
attackerRoll.show = { attackerRoll.show = {
cible: this.target ? this.defender.name : 'la cible', cible: this.defenderToken?.name ?? 'la cible',
isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge') isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
} }
await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html'); await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
@ -898,7 +932,7 @@ export class RdDCombat {
// # utilisation esquive // # utilisation esquive
const corpsACorps = this.defender.getCompetenceCorpsACorps({ onMessage: it => console.info(it, this.defender) }); const corpsACorps = this.defender.getCompetenceCorpsACorps({ onMessage: it => console.info(it, this.defender) });
const esquives = foundry.utils.duplicate(this.defender.getCompetencesEsquive()) const esquives = foundry.utils.duplicate(this.defender.getCompetencesEsquive())
esquives.forEach(e => e.system.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0); esquives.forEach(e => e.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0);
const paramChatDefense = { const paramChatDefense = {
passeArme: attackerRoll.passeArme, passeArme: attackerRoll.passeArme,
@ -908,7 +942,8 @@ export class RdDCombat {
attacker: this.attacker, attacker: this.attacker,
attackerId: this.attackerId, attackerId: this.attackerId,
esquives: esquives, esquives: esquives,
defenderTokenId: this.defenderTokenId, attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
mainsNues: attackerRoll.dmg.mortalite != 'mortel' && corpsACorps, mainsNues: attackerRoll.dmg.mortalite != 'mortel' && corpsACorps,
armes: this._filterArmesParade(this.defender, attackerRoll.competence, attackerRoll.arme), armes: this._filterArmesParade(this.defender, attackerRoll.competence, attackerRoll.arme),
diffLibre: attackerRoll.ajustements?.diffLibre?.value ?? 0, diffLibre: attackerRoll.ajustements?.diffLibre?.value ?? 0,
@ -919,7 +954,7 @@ export class RdDCombat {
dmg: attackerRoll.dmg, dmg: attackerRoll.dmg,
}; };
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
await this._chatMessageDefense(paramChatDefense, defenderRoll); await this._chatMessageDefense(paramChatDefense, defenderRoll);
} }
else { else {
@ -932,7 +967,7 @@ export class RdDCombat {
const choixDefense = await ChatMessage.create({ const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs) // message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)), speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker.name, alias: this.attackerToken.name,
whisper: ChatUtility.getOwners(this.defender), whisper: ChatUtility.getOwners(this.defender),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense), content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense),
}); });
@ -946,8 +981,9 @@ export class RdDCombat {
game.socket.emit(SYSTEM_SOCKET_ID, { game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_defense", data: { msg: "msg_defense", data: {
attackerId: this.attacker?.id, attackerId: this.attacker?.id,
attackerToken: this.attackerToken,
defenderId: this.defender?.id, defenderId: this.defender?.id,
defenderTokenId: this.defenderTokenId, defenderToken: this.defenderToken,
defenderRoll: defenderRoll, defenderRoll: defenderRoll,
paramChatDefense: paramChatDefense, paramChatDefense: paramChatDefense,
rollMode: true rollMode: true
@ -956,20 +992,21 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_filterArmesParade(defender, competence) { _filterArmesParade(defender, competence, arme) {
let items = defender.items.filter(it => RdDItemArme.isArmeUtilisable(it) || RdDItemCompetenceCreature.isCompetenceParade(it)) let defenses = 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 defenses = foundry.utils.duplicate(defenses)
defenses.forEach(armeDefense => {
// Ajout du # d'utilisation ce round
armeDefense.nbUsage = defender.getItemUse(armeDefense.id)
armeDefense.typeParade = RdDItemArme.defenseArmeParade(arme, armeDefense)
})
switch (competence.system.categorie) { switch (competence.system.categorie) {
case 'tir': case 'tir':
case 'lancer': case 'lancer':
return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers') return defenses.filter(armeDefense => RdDItemArme.getCategorieParade(armeDefense) == 'boucliers')
default: default:
// Le fléau ne peut être paré quau bouclier p115 return defenses.filter(armeDefense => armeDefense.typeParade != '')
if (competence.name == "Fléau") {
return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
}
return items.filter(item => RdDItemArme.getCategorieParade(item));
} }
} }
@ -980,7 +1017,8 @@ export class RdDCombat {
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', { content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
attackerId: this.attackerId, attackerId: this.attackerId,
attacker: this.attacker, attacker: this.attacker,
defenderTokenId: this.defenderTokenId, attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
essais: attackerRoll.essais essais: attackerRoll.essais
}) })
}); });
@ -1052,8 +1090,11 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
_prepareParade(attackerRoll, armeParade, competenceParade) { _prepareParade(attackerRoll, armeParade, competenceParade) {
let defenderRoll = { let defenderRoll = {
alias: this.defenderToken?.name,
passeArme: attackerRoll.passeArme, passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre, diffLibre: attackerRoll.diffLibre,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
attackerRoll: attackerRoll, attackerRoll: attackerRoll,
competence: this.defender.getCompetence(competenceParade), competence: this.defender.getCompetence(competenceParade),
arme: armeParade, arme: armeParade,
@ -1133,8 +1174,11 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
_prepareEsquive(attackerRoll, competence) { _prepareEsquive(attackerRoll, competence) {
let rollData = { let rollData = {
alias: this.defenderToken?.name,
passeArme: attackerRoll.passeArme, passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre, diffLibre: attackerRoll.diffLibre,
attackerToken: this.attackerToken,
defenderToken: this.defenderToken,
attackerRoll: attackerRoll, attackerRoll: attackerRoll,
competence: competence, competence: competence,
surprise: this.defender.getSurprise(true), surprise: this.defender.getSurprise(true),
@ -1276,9 +1320,8 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async encaisser(attackerRoll, defenderRoll, defenderTokenId) { async encaisser(attackerRoll, defenderRoll) {
defenderTokenId = defenderTokenId || this.defenderTokenId; console.log("RdDCombat.encaisser >>>", attackerRoll, defenderRoll);
console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) { if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) {
this._onEchecTotal(defenderRoll); this._onEchecTotal(defenderRoll);
@ -1286,18 +1329,19 @@ export class RdDCombat {
if (Misc.isOwnerPlayerOrUniqueConnectedGM(this.defender)) { if (Misc.isOwnerPlayerOrUniqueConnectedGM(this.defender)) {
attackerRoll.attackerId = this.attackerId; attackerRoll.attackerId = this.attackerId;
attackerRoll.defenderTokenId = defenderTokenId; attackerRoll.defenderTokenId = this.defenderToken.id;
await this.computeRecul(defenderRoll); await this.computeRecul(defenderRoll);
await this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show); await this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show, this.attackerToken, this.defenderToken);
} }
else { // envoi à un GM: les joueurs n'ont pas le droit de modifier les personnages qu'ils ne possèdent pas 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, { game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_encaisser", msg: "msg_encaisser",
data: { data: {
attackerId: this.attackerId, attackerId: this.attackerId,
defenderTokenId: defenderTokenId, attackerRoll: attackerRoll,
attackerRoll: attackerRoll attackerToken: this.attackerToken,
defenderToken: this.defenderToken
} }
}); });
} }
@ -1305,28 +1349,31 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async displayActorCombatStatus(combat, actor, tokenId) { static async displayActorCombatStatus(combat, actor, token) {
if (!actor?.isActorCombat()) {
return
}
let formData = { let formData = {
combatId: combat._id, combatId: combat._id,
alias: actor.name, alias: token.name ?? actor.name,
etatGeneral: actor.getEtatGeneral(), etatGeneral: actor.getEtatGeneral(),
isSonne: actor.getSonne(), isSonne: actor.isSonne(),
blessuresStatus: actor.computeResumeBlessure(), blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(), SConst: actor.getSConst(),
actorId: actor.id, actorId: actor.id,
actor: actor, actor: actor,
tokenId: tokenId, tokenId: token.id,
isGrave: actor.countBlessures(it => it.isGrave()) > 0, isGrave: actor.countBlessures(it => it.isGrave()) > 0,
isCritique: actor.countBlessures(it => it.isCritique()) > 0 isCritique: actor.countBlessures(it => it.isCritique()) > 0
} }
await ChatMessage.create({ await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs`, formData), content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs`, formData),
alias: actor.name alias: token.name ?? actor.name
}) })
await ChatMessage.create({ await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData), content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData),
whisper: ChatUtility.getOwners(actor), whisper: ChatUtility.getOwners(actor),
alias: actor.name alias: token.name ?? actor.name
}); });
} }
} }

View File

@ -68,6 +68,7 @@ import { OptionsAvancees } from "./settings/options-avancees.js"
import { ExportScriptarium } from "./actor/export-scriptarium/export-scriptarium.js" import { ExportScriptarium } from "./actor/export-scriptarium/export-scriptarium.js"
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js" import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js"
import { RdDActorExportSheet } from "./actor/export-scriptarium/actor-encart-sheet.js" import { RdDActorExportSheet } from "./actor/export-scriptarium/actor-encart-sheet.js"
import { RdDStatBlockParser } from "./apps/rdd-import-stats.js"
/** /**
* RdD system * RdD system
@ -87,6 +88,7 @@ export class SystemReveDeDragon {
this.config = RDD_CONFIG this.config = RDD_CONFIG
this.RdDUtility = RdDUtility this.RdDUtility = RdDUtility
this.RdDHotbar = RdDHotbar this.RdDHotbar = RdDHotbar
this.RdDStatBlockParser = RdDStatBlockParser
this.itemClasses = { this.itemClasses = {
armure: RdDItemArmure, armure: RdDItemArmure,
blessure: RdDItemBlessure, blessure: RdDItemBlessure,
@ -278,7 +280,7 @@ export class SystemReveDeDragon {
/* Foundry VTT Initialization */ /* Foundry VTT Initialization */
/* -------------------------------------------- */ /* -------------------------------------------- */
game.system.rdd.calendrier = new RdDCalendrier() game.system.rdd.calendrier = new RdDCalendrier()
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
new Migrations().migrate() new Migrations().migrate()
this.messageDeBienvenue() this.messageDeBienvenue()
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => { import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
@ -292,6 +294,8 @@ export class SystemReveDeDragon {
StatusEffects.onReady() StatusEffects.onReady()
RdDHerbes.onReady() RdDHerbes.onReady()
RdDDice.onReady() RdDDice.onReady()
RdDStatBlockParser.parseStatBlock()
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Affiche/Init le calendrier */ /* Affiche/Init le calendrier */
game.system.rdd.calendrier.display() game.system.rdd.calendrier.display()

View File

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

View File

@ -37,7 +37,7 @@ export class RdDTokenHud {
// initiative // initiative
await RdDTokenHud.addExtensionHudInit(html, combatant, actions); await RdDTokenHud.addExtensionHudInit(html, combatant, actions);
// combat // combat
await RdDTokenHud.addExtensionHudCombat(html, combatant, actions); await RdDTokenHud.addExtensionHudCombat(html, combatant, token, actions);
} }
@ -68,8 +68,8 @@ export class RdDTokenHud {
}); });
} }
static async addExtensionHudCombat(html, combatant, actions) { static async addExtensionHudCombat(html, combatant, token, actions) {
const hudData = { combatant, actions, commandes: [] }; const hudData = { combatant, token, actions, commandes: [] };
const controlIconTarget = html.find('.control-icon[data-action=target]'); const controlIconTarget = html.find('.control-icon[data-action=target]');
await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData, await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData,
(event) => { (event) => {
@ -80,7 +80,7 @@ export class RdDTokenHud {
combatant.actor.conjurerPossession(possession); combatant.actor.conjurerPossession(possession);
} }
else { else {
combatant.actor.rollArme(action); combatant.actor.rollArme(action, 'competence', token)
} }
}); });
} }

View File

@ -563,14 +563,14 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async jetEncaissement(rollData, armure, options = { showDice: HIDE_DICE }) { static async jetEncaissement(actor, rollData, armure, options = { showDice: HIDE_DICE }) {
const diff = Math.abs(rollData.diffLibre); const diff = Math.abs(rollData.diffLibre);
let formula = RdDUtility.formuleEncaissement(diff, options) let formula = RdDUtility.formuleEncaissement(diff, options)
const roll = await RdDDice.roll(formula, options); const roll = await RdDDice.roll(formula, options);
RdDUtility.remplaceDeMinParDifficulte(roll, diff, options); RdDUtility.remplaceDeMinParDifficulte(roll, diff, options);
return await RdDUtility.prepareEncaissement(rollData, roll, armure); return await RdDUtility.prepareEncaissement(actor, rollData, roll, armure);
} }
static remplaceDeMinParDifficulte(roll, diff, options) { static remplaceDeMinParDifficulte(roll, diff, options) {
@ -602,15 +602,20 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async prepareEncaissement(rollData, roll, armure) { static async prepareEncaissement(actor, rollData, roll, armure) {
// La difficulté d'ataque s'ajoute aux dégâts // 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 bonusDegatsDiffLibre = ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(rollData.diffLibre ?? 0) : 0
const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre
const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite); const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite);
const over20 = Math.max(jetTotal - 20, 0); const over20 = Math.max(jetTotal - 20, 0);
encaissement.dmg = rollData.dmg; encaissement.dmg = rollData.dmg
encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(this.type); if (ReglesOptionnelles.isUsing('localisation-aleatoire')) {
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'; 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.bonusDegatsDiffLibre = bonusDegatsDiffLibre encaissement.dmg.bonusDegatsDiffLibre = bonusDegatsDiffLibre
encaissement.roll = roll; encaissement.roll = roll;
encaissement.armure = armure; encaissement.armure = armure;
@ -741,6 +746,15 @@ export class RdDUtility {
return undefined; 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) { static getSelectedActor(msgPlayer = undefined) {
if (canvas.tokens.controlled.length == 1) { if (canvas.tokens.controlled.length == 1) {
let token = canvas.tokens.controlled[0]; let token = canvas.tokens.controlled[0];
@ -867,10 +881,10 @@ export class RdDUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static afficherHeuresChanceMalchance(heureNaissance) { static afficherHeuresChanceMalchance(heureNaissance) {
if (game.user.isGM) { if (game.user.isGM) {
const heure = RdDTimestamp.findHeure(heureNaissance - 1); const heure = RdDTimestamp.findHeure(heureNaissance)
if (heureNaissance && heure) { if (heureNaissance && heure) {
let ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance); const ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance)
const current = game.system.rdd.calendrier.heureCourante(); const current = game.system.rdd.calendrier.heureCourante()
ChatMessage.create({ 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>.`, 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: ChatUtility.getGMs()

View File

@ -11,6 +11,7 @@ const listeReglesOptionnelles = [
{ group: 'Récupération', name: 'recuperation-moral', descr: "Le moral revient vers 0 durant Château Dormant"}, { 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: '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: '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" }, { group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },

View File

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

View File

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

View File

@ -48,7 +48,7 @@ export class RdDCalendrier extends Application {
constructor() { constructor() {
super(); super();
this.timestamp = RdDTimestamp.getWorldTime(); this.timestamp = RdDTimestamp.getWorldTime();
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM if (Misc.isFirstConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp); RdDTimestamp.setWorldTime(this.timestamp);
this.rebuildNombresAstraux(); // Ensure always up-to-date this.rebuildNombresAstraux(); // Ensure always up-to-date
} }
@ -258,7 +258,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */ /* -------------------------------------------- */
async rebuildNombresAstraux() { async rebuildNombresAstraux() {
if (Misc.isUniqueConnectedGM()) { if (Misc.isFirstConnectedGM()) {
const nombresAstraux = this.getNombresAstraux() const nombresAstraux = this.getNombresAstraux()
let newNombresAstraux = []; let newNombresAstraux = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) { for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
@ -337,7 +337,7 @@ export class RdDCalendrier extends Application {
/* -------------------------------------------- */ /* -------------------------------------------- */
async requestNombreAstral(request) { async requestNombreAstral(request) {
const actor = game.actors.get(request.id); const actor = game.actors.get(request.id);
if (Misc.isUniqueConnectedGM()) { // Only once if (Misc.isFirstConnectedGM()) { // Only once
console.log(request); console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date); let jourDiff = this.getLectureAstrologieDifficulte(request.date);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat); let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);

View File

@ -160,15 +160,21 @@ export class RdDTimestamp {
} }
static findHeure(heure) { static findHeure(heure) {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure); let filtered
let parHeureOuLabel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure || it.heure == Misc.modulo(parseInt(heure), RDD_HEURES_PAR_JOUR)); if (Number.isInteger(Number(heure))) {
if (parHeureOuLabel.length == 1) { filtered = DEFINITION_HEURES.filter(it => it.heure == Misc.modulo(Number(heure) - 1, RDD_HEURES_PAR_JOUR))
return parHeureOuLabel[0];
} }
let parLabelPartiel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure)); else {
if (parLabelPartiel.length > 0) { heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
parLabelPartiel.sort(Misc.ascending(h => h.label.length)); filtered = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure || it.heure == Misc.modulo(parseInt(heure), RDD_HEURES_PAR_JOUR));
return parLabelPartiel[0]; }
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]
} }
return undefined; return undefined;
} }

View File

@ -65,6 +65,7 @@
{"_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":"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":"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":"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":"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":"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":"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":"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"}} {"_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"}}
{"_id":"OYWzXiQUFsjU5AF2","name":"Perles de Bjwal","type":"potion","img":"systems/foundryvtt-reve-de-dragon/icons/objets/perles_bjwal.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.OYWzXiQUFsjU5AF2"}},"system":{"description":"<p>Petits granul&eacute;s translucides.</p>","descriptionmj":"","encombrement":0.1,"quantite":1,"qualite":0,"cout":1,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Rarissime","frequence":2},{"milieu":"Villes","rarete":"Rare","frequence":6}],"temporel":{"debut":{"indexDate":-1,"indexMinute":0},"fin":{"indexDate":-1,"indexMinute":0}},"rarete":"","categorie":"Remede","herbe":"","herbebrins":0,"herbebonus":0,"reposalchimique":false,"pr":0,"prpermanent":false,"prdate":0,"soinherbe":"","soinherbebonus":0},"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":"OYWzXiQUFsjU5AF2","name":"Perles de Bjwal","type":"potion","img":"systems/foundryvtt-reve-de-dragon/icons/objets/perles_bjwal.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.foundryvtt-reve-de-dragon.equipement.OYWzXiQUFsjU5AF2"}},"system":{"description":"<p>Petits granul&eacute;s translucides.</p>","descriptionmj":"","encombrement":0.1,"quantite":1,"qualite":0,"cout":1,"milieu":"","environnement":[{"milieu":"Villages","rarete":"Rarissime","frequence":2},{"milieu":"Villes","rarete":"Rare","frequence":6}],"temporel":{"debut":{"indexDate":-1,"indexMinute":0},"fin":{"indexDate":-1,"indexMinute":0}},"rarete":"","categorie":"Remede","herbe":"","herbebrins":0,"herbebonus":0,"reposalchimique":false,"pr":0,"prpermanent":false,"prdate":0,"soinherbe":"","soinherbebonus":0},"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,4 +11,3 @@
{"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":"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":"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":"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"}}

View File

@ -1561,6 +1561,11 @@ div.control-icon.token-hud-icon {
height: 100%; height: 100%;
object-fit: contain; object-fit: contain;
} }
.chat-inline-icon {
border: 0;
padding: 1px;
vertical-align: text-top;
}
#sidebar-tabs { #sidebar-tabs {
flex: 0 0 28px; flex: 0 0 28px;

View File

@ -1,8 +1,8 @@
{ {
"id": "foundryvtt-reve-de-dragon", "id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon", "title": "Rêve de Dragon",
"version": "12.0.15", "version": "12.0.18",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-12.0.15.zip", "download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-12.0.18.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json", "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", "changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"compatibility": { "compatibility": {

View File

@ -25,7 +25,7 @@
<span class="competence-value">{{plusMoins arme.system.niveau}}</span> <span class="competence-value">{{plusMoins arme.system.niveau}}</span>
<span class="competence-value">{{plusMoins arme.system.dommagesReels}}</span> <span class="competence-value">{{plusMoins arme.system.dommagesReels}}</span>
<span class="competence-value"></span> <span class="competence-value"></span>
<span class="initiative-value arme-initiative"><a data-tooltip="{{arme.name}}: initiative {{arme.system.initiative}}">{{arme.system.initiative}}</a></span> <span class="initiative-value"><a class="roll-init-arme" data-tooltip="{{arme.name}}: initiative {{arme.system.initiative}}">{{arme.system.initiative}}</a></span>
</li> </li>
{{/each}} {{/each}}
{{#each esquives as |esq key|}} {{#each esquives as |esq key|}}

View File

@ -1,11 +1,17 @@
<form class="{{cssClass}}" autocomplete="off" > <form class="{{cssClass}}" autocomplete="off" >
<section class="sheet-header"> <section class="sheet-header flexrow">
<div class="flexrow"> <div class="flex-grow-0-5">
<div class="flex-group-left flex-grow-0-5"> <img class="profile-img" src="{{img}}" data-edit="img" data-tooltip="{{name}}" />
<h1 class="charname">{{name}}
</h1>
</div> </div>
<div class="flex-group-right flex-grow-3"> <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}} {{#if system.attributs.hautrevant.value}}
<a class="button-tmr" data-tooltip="Montée dans les Terres M&eacute;dianes !" {{#if hautreve.isDemiReve}}disabled{{/if}}> <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"/> <img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-normal.svg"/>
@ -17,10 +23,6 @@
<img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-view.svg"/> <img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/icon-tmr-view.svg"/>
</a> </a>
{{/if}} {{/if}}
<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 @root.options.isGM}} {{#if @root.options.isGM}}
<a class="button-remise-a-neuf"><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a> <a class="button-remise-a-neuf"><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a>
{{/if}} {{/if}}

View File

@ -10,7 +10,11 @@
<div class="flexrow"> <div class="flexrow">
<div>{{niveau}}</div> <div>{{niveau}}</div>
{{#if init}} {{#if init}}
{{#if name}}
<div><a class="roll-init-arme">{{init}}</a></div>
{{else}}
<div>{{init}}</div> <div>{{init}}</div>
{{/if}}
<div>{{dommages}}</div> <div>{{dommages}}</div>
{{else}} {{else}}
<div></div> <div></div>

View File

@ -11,8 +11,6 @@
<a class="{{button-name}}-plus"><i class="fa-regular fa-square-plus"></i></a> <a class="{{button-name}}-plus"><i class="fa-regular fa-square-plus"></i></a>
</div> </div>
<div class="flex-group-right"> <div class="flex-group-right">
{{log carac.colName carac.value (eq carac.value actuel.value) actuel.value}}
{{log carac.colName carac.value (eq 1 1) actuel.value}}
<a class="{{actuel.rollClass}}" data-carac-name="{{actuel.column}}">{{carac.value}} {{#unless (eq carac.value actuel.value)}}(Actuel : {{actuel.value}}){{/unless}}</a> <a class="{{actuel.rollClass}}" data-carac-name="{{actuel.column}}">{{carac.value}} {{#unless (eq carac.value actuel.value)}}(Actuel : {{actuel.value}}){{/unless}}</a>
</div> </div>
</div> </div>

View File

@ -2,7 +2,11 @@
<div> <div>
{{upperFirst carac.colName}} {{upperFirst carac.colName}}
<a class="{{carac.column}}-moins"><i class="fa-regular fa-square-minus"></i></a> <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> <a class="{{carac.column}}-plus"><i class="fa-regular fa-square-plus"></i></a>
{{/if}}
</div> </div>
<div class="flex-group-right"> <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> <a class="{{actuel.rollClass}}" data-carac-name="{{actuel.column}}">{{carac.value}} {{#unless (eq carac.value actuel.value)}}(Actuel : {{actuel.value}}){{/unless}}</a>

View File

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

View File

@ -3,7 +3,11 @@
{{#if etat.fatigue.malus}} {{#if etat.fatigue.malus}}
({{etat.fatigue.malus}}) ({{etat.fatigue.malus}})
{{/if}} {{/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> <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> <a class="fatigue-plus"><i class="fa-regular fa-square-plus"></i></a>
</div> </div>
<div class="flex-group-right"> <div class="flex-group-right">

View File

@ -3,20 +3,22 @@
<br> <br>
{{#if (eq attacker.type 'personnage')}} {{#if (eq attacker.type 'personnage')}}
{{#unless essais.attaqueChance}} {{#unless essais.attaqueChance}}
<a class='chat-card-button' id='appel-chance-attaque' data-attackerId='{{attackerId}}' <a class='chat-card-button' id='appel-chance-attaque'
data-defenderTokenId='{{defenderTokenId}}'>Faire appel à la chance</a> data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Faire appel à la chance</a>
</a> </a>
<br> <br>
{{/unless}} {{/unless}}
{{#if (gt attacker.system.compteurs.destinee.value 0)}} {{#if (gt attacker.system.compteurs.destinee.value 0)}}
<a class='chat-card-button' id='appel-destinee-attaque' data-attackerId='{{attackerId}}' <a class='chat-card-button' id='appel-destinee-attaque'
data-defenderTokenId='{{defenderTokenId}}'>Utiliser la destinée</a> data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Utiliser la destinée</a>
</a> </a>
<br> <br>
{{/if}} {{/if}}
{{/if}} {{/if}}
<a class='chat-card-button' id='echec-total-attaque' data-attackerId='{{attackerId}}' <a class='chat-card-button' id='echec-total-attaque'
data-defenderTokenId='{{defenderTokenId}}'> data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Tirer la maladresse ! Tirer la maladresse !
</a> </a>
</div> </div>

View File

@ -1,17 +1,17 @@
<div data-passearme="{{passeArme}}"> <div data-passearme="{{passeArme}}">
{{#if (eq surprise 'totale')}} {{#if (eq surprise 'totale')}}
<span><strong>{{defender.name}}</strong> est totalement surpris</span> <span><strong>{{defenderToken.name}}</strong> est totalement surpris</span>
{{else if essais.defense}} {{else if essais.defense}}
<span><strong>{{defender.name}}</strong> doit :</span> <span><strong>{{defenderToken.name}}</strong> doit :</span>
{{else}} {{else}}
<span><strong>{{defender.name}}</strong> doit se défendre <span><strong>{{defenderToken.name}}</strong> doit se défendre
{{~#if (eq surprise 'demi')}} avec une significative {{/if}} d'une attaque {{~#if (eq surprise 'demi')}} avec une significative {{/if}} d'une attaque
{{~#if attaqueParticuliere}} <strong>particulière en {{~#if attaqueParticuliere}} <strong>particulière en
{{~#if (eq attaqueParticuliere 'finesse')}} finesse {{~#if (eq attaqueParticuliere 'finesse')}} finesse
{{else if (eq attaqueParticuliere 'force')}} force {{else if (eq attaqueParticuliere 'force')}} force
{{else if (eq attaqueParticuliere 'rapidite')}} rapidité {{else if (eq attaqueParticuliere 'rapidite')}} rapidité
{{/if~}}</strong> {{/if~}}</strong>
{{/if}} de {{attacker.name}} ({{attaqueArme.name}}): {{/if}} de {{attackerToken.name}} ({{attaqueArme.name}}):
</span> </span>
{{/if}} {{/if}}
<span class='chat-card-button-area'> <span class='chat-card-button-area'>
@ -20,49 +20,64 @@
{{#if essais.defense}} {{#if essais.defense}}
{{#unless essais.defenseChance}} {{#unless essais.defenseChance}}
{{#if (eq defender.type 'personnage')}} {{#if (eq defender.type 'personnage')}}
<a class='chat-card-button' id='appel-chance-defense' data-attackerId='{{attackerId}}' <a class='chat-card-button' id='appel-chance-defense'
data-defenderTokenId='{{defenderTokenId}}'>Faire appel à la chance</a> data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Faire appel à la chance</a>
</a> </a>
<br> <br>
{{/if}} {{/if}}
{{#if (eq defender.type 'personnage')}} {{#if (and (eq defender.type 'personnage') (gt defender.system.compteurs.destinee.value 0))}}
{{#if (gt defender.system.compteurs.destinee.value 0)}} <a class='chat-card-button' id='appel-destinee-defense'
<a class='chat-card-button' id='appel-destinee-defense' data-attackerId='{{attackerId}}' data-attackerId='{{attackerId}}' data-attackerTokenId='{{attackerToken.id}}' data-defenderTokenId='{{defenderToken.id}}'>
data-defenderTokenId='{{defenderTokenId}}'>Utiliser la destinée</a> Utiliser la destinée</a>
</a> </a>
<br> <br>
{{/if}} {{/if}}
{{/if}}
{{/unless}} {{/unless}}
{{else}} {{else}}
{{#each armes as |arme key|}} {{#each armes as |arme key|}}
<a class='chat-card-button' id='parer-button' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderTokenId}}' <a class='chat-card-button' id='parer-button'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
data-armeid='{{arme._id}}'> data-armeid='{{arme._id}}'>
Parer avec {{arme.name}} à {{../diffLibre }}{{#if arme.system.nbUsage}} (Utilisations : {{arme.system.nbUsage}}){{/if}} Parer avec {{arme.name}}
{{#if (or (eq ../attaqueCategorie 'tir') (eq ../attaqueCategorie 'lancer'))}}
(difficulté à déterminer)
{{else}}à {{../diffLibre }}
{{/if}}
{{#if (eq arme.typeParade 'sign')}}
<span class="rdd-diviseur">&times;&frac12;</span>
{{/if}}
{{#if arme.nbUsage}}(Utilisations : {{arme.nbUsage}}){{/if}}
</a> </a>
<br> <br>
{{/each}} {{/each}}
{{#if mainsNues}} {{#if mainsNues}}
<a class='chat-card-button' id='parer-button' data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderTokenId}}' <a class='chat-card-button' id='parer-button'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'
data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'> data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
Parer à mains nues à {{diffLibre}}{{#if arme.system.nbUsage}} (Utilisations : {{arme.system.nbUsage}}){{/if}} Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}}
</a> </a>
<br> <br>
{{/if}} {{/if}}
{{#if (ne attaqueCategorie 'tir')}} {{#if (ne attaqueCategorie 'tir')}}
{{#each esquives as |esquive key|}} {{#each esquives as |esquive key|}}
<a class='chat-card-button' id='esquiver-button' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderTokenId}}' <a class='chat-card-button' id='esquiver-button'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
data-compid='{{esquive._id}}' data-competence='{{esquive.name}}'> data-compid='{{esquive._id}}' data-competence='{{esquive.name}}'>
{{log 'esquive' esquive}} {{esquive.name}}
{{esquive.name}} à {{../diffLibre}} {{#if esquive.system.nbUsage}} (Utilisations : {{esquive.system.nbUsage}}){{/if}} {{#if (or (eq ../attaqueCategorie 'tir') (eq ../attaqueCategorie 'lancer'))}}
(difficulté à déterminer)
{{else}}à {{../diffLibre }}
{{/if}}
{{#if esquive.nbUsage}}(Utilisations : {{esquive.nbUsage}}){{/if}}
</a> </a>
<br> <br>
{{/each}} {{/each}}
{{/if}} {{/if}}
{{/if}} {{/if}}
{{/unless}} {{/unless}}
<a class='chat-card-button' id='encaisser-button' data-attackerId='{{attackerId}}' <a class='chat-card-button' id='encaisser-button'
data-defenderTokenId='{{defenderTokenId}}'> data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Encaisser à {{plusMoins dmg.total}} Encaisser à {{plusMoins dmg.total}}
{{#if (eq dmg.mortalite 'non-mortel')~}} {{#if (eq dmg.mortalite 'non-mortel')~}}
(non-mortel) ! (non-mortel) !

View File

@ -1,5 +1,4 @@
{{#if isGM}} {{#if isGM}}
{{log this}}
<span> <span>
{{#if (gt endurance 0)}} {{#if (gt endurance 0)}}
De plus, {{alias}} a perdu {{endurance}} points d'endurance De plus, {{alias}} a perdu {{endurance}} points d'endurance
@ -18,18 +17,18 @@
</h4> </h4>
<div> <div>
Jet d'encaissement de {{roll.total}} Jet d'encaissement de {{roll.total}}
{{#unless (eq armure 0)}}, l'armure a protègé de {{armure}} {{~#unless (eq armure 0)}}, l'armure a protègé de {{armure}}
{{~#unless (eq penetration 0)}} (pénétration de {{penetration}}){{/unless}} {{~#unless (eq penetration 0)}} (pénétration de {{penetration}}){{/unless}}
{{~/unless}}, total: <span class="rdd-roll-echec">{{total}}</span> {{~/unless~}}, total: <span class="rdd-roll-echec">{{total}}</span>
<br> <br>
{{alias}} {{alias}}
{{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup {{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup
{{else if mort}}vient de mourir {{else if mort}}vient de mourir
{{else if blessure}} {{else if blessure}}
{{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}} {{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}}
{{else}}subit une contusion {{~else~}}subit une contusion
{{~/if~}} {{~/if~}}
{{else}}s'en sort sans une égratignure {{~else~}}s'en sort sans une égratignure
{{~/if~}} {{~/if~}}
{{~#unless (eq dmg.mortalite 'entiteincarnee')}} {{~#unless (eq dmg.mortalite 'entiteincarnee')}}
@ -39,11 +38,11 @@
{{/unless~}} {{/unless~}}
{{~#if (gt endurance 0)}} {{~#if (gt endurance 0)}}
{{~#if hasPlayerOwner}}, a perdu {{endurance}} points d'endurance {{~#if hasPlayerOwner}}, a perdu {{endurance}} points d'endurance
{{#if (ne vie 0)}}, <span class="rdd-roll-echec">{{vie}} points de vie</span>{{/if}} {{~#if (ne vie 0)}}, <span class="rdd-roll-echec">{{vie}} points de vie</span>{{/if~}}
{{/if}} {{/if}}
{{#if (ne dmg.mortalite 'entiteincarnee')}} {{#if (ne dmg.mortalite 'entiteincarnee')}}
{{#if (gt endurance 1)}}et {{#if (gt endurance 1)}}et
{{#if sonne}}est <strong>sonné</strong><img class="chat-icon" src="icons/svg/stoned.svg" data-tooltip="charge" height="16" width="16" /> jusqu'à la fin du prochain round{{else}}n'est pas sonné{{/if}}! {{#if sonne}}est <strong>sonné</strong><img class="chat-inline-icon" src="icons/svg/stoned.svg" data-tooltip="charge" height="16" width="16" /> jusqu'à la fin du prochain round{{else}}n'est pas sonné{{/if}}!
{{#if hasPlayerOwner}}Jet d'endurance : {{jetEndurance}} / {{resteEndurance}}{{/if}} {{#if hasPlayerOwner}}Jet d'endurance : {{jetEndurance}} / {{resteEndurance}}{{/if}}
{{/if}} {{/if}}
{{/if}} {{/if}}