Compare commits
	
		
			26 Commits
		
	
	
		
			foundryvtt
			...
			d91bee0f42
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d91bee0f42 | |||
| ed222c7b6d | |||
| 70198a5727 | |||
| bfe70c6634 | |||
| 09365eb744 | |||
| 894d4f3941 | |||
| 6e234411ca | |||
| 85acb5a255 | |||
| 6106e2a19e | |||
| f896f1da6e | |||
| 9bb45c2349 | |||
| 5cab219e0e | |||
| 2de9ea49c8 | |||
| 1760d26014 | |||
| 92185d4a5b | |||
| 210b129934 | |||
| b892068b38 | |||
| 3906cb0a7b | |||
| 5c58932a0d | |||
| 3b06bd382b | |||
| b4a725ff12 | |||
| 05df6a68cc | |||
| 025c3483a9 | |||
| 97138b25c7 | |||
| c0066f79c1 | |||
| 4db8bf95f9 | 
							
								
								
									
										21
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,4 +1,25 @@
 | 
			
		||||
# 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
 | 
			
		||||
- Correction des faces de dés personalisés dice-so-nice
 | 
			
		||||
- Les messages de maladies ne sont plus publics
 | 
			
		||||
 
 | 
			
		||||
@@ -220,18 +220,18 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
 | 
			
		||||
    // Points de reve actuel
 | 
			
		||||
    this.html.find('.roll-reve-actuel').click(async event => this.actor.rollCarac('reve-actuel', true))
 | 
			
		||||
    this.html.find('.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
 | 
			
		||||
    this.html.find('.arme-initiative a').click(async event => {
 | 
			
		||||
      let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id);
 | 
			
		||||
    this.html.find('.roll-init-arme').click(async event => {
 | 
			
		||||
      let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id)
 | 
			
		||||
      if (combatant) {
 | 
			
		||||
        let action = this._getEventArmeCombat(event);
 | 
			
		||||
        RdDCombatManager.rollInitiativeAction(combatant._id, action);
 | 
			
		||||
        RdDCombatManager.rollInitiativeAction(combatant._id, this._getEventArmeCombat(event));
 | 
			
		||||
      } else {
 | 
			
		||||
        ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    })
 | 
			
		||||
    // Display TMR
 | 
			
		||||
 | 
			
		||||
    this.html.find('.button-tmr').click(async event => this.actor.displayTMR("normal"))
 | 
			
		||||
 
 | 
			
		||||
@@ -1548,7 +1548,7 @@ export class RdDActor extends RdDBaseActorSang {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async appliquerAjoutExperience(rollData, hideChatMessage = 'show') {
 | 
			
		||||
    if (!Misc.firstConnectedGM()){
 | 
			
		||||
    if (!Misc.isFirstConnectedGM()){
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    hideChatMessage = hideChatMessage == 'hide' || (Misc.isRollModeHiddenToPlayer() && !game.user.isGM)
 | 
			
		||||
@@ -2615,7 +2615,7 @@ export class RdDActor extends RdDBaseActorSang {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  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) {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      let draconique = Draconique.all().find(it => it.match(item));
 | 
			
		||||
      if (draconique) {
 | 
			
		||||
        await draconique.onActorCreateOwned(this, item)
 | 
			
		||||
@@ -3023,7 +3023,7 @@ export class RdDActor extends RdDBaseActorSang {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async onDeleteOwnedDraconique(item, options, id) {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      let draconique = Draconique.all().find(it => it.match(item));
 | 
			
		||||
      if (draconique) {
 | 
			
		||||
        await draconique.onActorDeleteOwned(this, item)
 | 
			
		||||
@@ -3033,7 +3033,7 @@ export class RdDActor extends RdDBaseActorSang {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async onDeleteOwnedCaseTmr(item, options, id) {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      let draconique = Draconique.all().find(it => it.isCase(item));
 | 
			
		||||
      if (draconique) {
 | 
			
		||||
        await draconique.onActorDeleteCaseTmr(this, item)
 | 
			
		||||
 
 | 
			
		||||
@@ -78,8 +78,10 @@ export class RdDBaseActorReve extends RdDBaseActor {
 | 
			
		||||
  }
 | 
			
		||||
  async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } }
 | 
			
		||||
  isDead() { return false }
 | 
			
		||||
  isSonne() { return false }
 | 
			
		||||
  blessuresASoigner() { return [] }
 | 
			
		||||
  getEtatGeneral(options = { ethylisme: false }) { return 0 }
 | 
			
		||||
  isActorCombat() { return true }
 | 
			
		||||
 | 
			
		||||
  async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
 | 
			
		||||
  async remiseANeuf() { }
 | 
			
		||||
@@ -327,14 +329,15 @@ export class RdDBaseActorReve extends RdDBaseActor {
 | 
			
		||||
    const competence = this.getCompetence(idOrName);
 | 
			
		||||
    let rollData = { carac: this.system.carac, competence: competence, arme: options.arme }
 | 
			
		||||
    if (competence.type == ITEM_TYPES.competencecreature) {
 | 
			
		||||
      const token = RdDUtility.getSelectedToken(this)
 | 
			
		||||
      const arme = RdDItemCompetenceCreature.armeCreature(competence)
 | 
			
		||||
      if (arme && options.tryTarget && Targets.hasTargets()) {
 | 
			
		||||
        Targets.selectOneToken(target => {
 | 
			
		||||
        Targets.selectOneTargetToken(target => {
 | 
			
		||||
          if (arme.action == "possession") {
 | 
			
		||||
            RdDPossession.onAttaquePossession(target, this, competence)
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
 | 
			
		||||
            RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        return;
 | 
			
		||||
@@ -364,7 +367,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
 | 
			
		||||
   * @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
 | 
			
		||||
   * @returns 
 | 
			
		||||
   */
 | 
			
		||||
  rollArme(arme, categorieArme = "competence") {
 | 
			
		||||
  rollArme(arme, categorieArme, token) {
 | 
			
		||||
    token = token ?? RdDUtility.getSelectedToken(this)
 | 
			
		||||
    const compToUse = this.$getCompetenceArme(arme, categorieArme)
 | 
			
		||||
    if (!RdDItemArme.isArmeUtilisable(arme)) {
 | 
			
		||||
      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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Targets.selectOneToken(target => {
 | 
			
		||||
    Targets.selectOneTargetToken(target => {
 | 
			
		||||
      if (Targets.isTargetEntite(target)) {
 | 
			
		||||
        ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
 | 
			
		||||
        return
 | 
			
		||||
@@ -395,7 +399,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
 | 
			
		||||
      if (competence.isCompetencePossession()) {
 | 
			
		||||
        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)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  verifierForceMin(item) {
 | 
			
		||||
  }
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async resetItemUse() { }
 | 
			
		||||
  async incDecItemUse(itemId, inc = 1) { }
 | 
			
		||||
  getItemUse(itemId) { return 0; }
 | 
			
		||||
  verifierForceMin(item) { }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  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')) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const armure = await this.computeArmure(rollData);
 | 
			
		||||
    if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
 | 
			
		||||
      await this.encaisserDommagesValidationGR(rollData, armure, attacker?.id, show);
 | 
			
		||||
      await this.encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      const jet = await RdDUtility.jetEncaissement(rollData, armure, { showDice: SHOW_DICE });
 | 
			
		||||
      await this.$onEncaissement(jet, show, attacker);
 | 
			
		||||
      const jet = await RdDUtility.jetEncaissement(this, rollData, armure, { showDice: SHOW_DICE });
 | 
			
		||||
      await this.$onEncaissement(jet, show, attackerToken, defenderToken)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async encaisserDommagesValidationGR(rollData, armure, attackerId, show) {
 | 
			
		||||
  async encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken) {
 | 
			
		||||
    if (!game.user.isGM) {
 | 
			
		||||
      RdDBaseActor.remoteActorCall({
 | 
			
		||||
        tokenId: this.token?.id,
 | 
			
		||||
        actorId: this.id,
 | 
			
		||||
        method: 'encaisserDommagesValidationGR',
 | 
			
		||||
        args: [rollData, armure, attackerId, show]
 | 
			
		||||
      });
 | 
			
		||||
        args: [rollData, armure, show, attackerToken, defenderToken]
 | 
			
		||||
      })
 | 
			
		||||
    } else {
 | 
			
		||||
      const attacker = game.actors.get(attackerId);
 | 
			
		||||
      DialogValidationEncaissement.validerEncaissement(this, rollData, armure,
 | 
			
		||||
        jet => this.$onEncaissement(jet, show, attacker));
 | 
			
		||||
        jet => this.$onEncaissement(jet, show, attackerToken, defenderToken));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async $onEncaissement(jet, show, attacker) {
 | 
			
		||||
    await this.onAppliquerJetEncaissement(jet, attacker);
 | 
			
		||||
    await this.$afficherEncaissement(jet, show);
 | 
			
		||||
  async $onEncaissement(jet, show, attackerToken, defenderToken) {
 | 
			
		||||
    await this.onAppliquerJetEncaissement(jet, attackerToken);
 | 
			
		||||
    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, {
 | 
			
		||||
      alias: this.name,
 | 
			
		||||
      alias: defenderToken?.name ?? this.name,
 | 
			
		||||
      hasPlayerOwner: this.hasPlayerOwner,
 | 
			
		||||
      show: show ?? {}
 | 
			
		||||
    });
 | 
			
		||||
    }, {overwrite: false});
 | 
			
		||||
 | 
			
		||||
    await ChatUtility.createChatWithRollMode(
 | 
			
		||||
      {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 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 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) {
 | 
			
		||||
      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 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()) {
 | 
			
		||||
      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é`);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    await this.setEffect(STATUSES.StatusStunned, sonne);
 | 
			
		||||
    await this.setEffect(STATUSES.StatusStunned, sonne)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getSonne() {
 | 
			
		||||
    return this.getEffect(STATUSES.StatusStunned);
 | 
			
		||||
  isSonne() {
 | 
			
		||||
    return this.getEffect(STATUSES.StatusStunned)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isEffectAllowed(effectId) { return true }
 | 
			
		||||
 
 | 
			
		||||
@@ -205,11 +205,8 @@ export class RdDBaseActor extends Actor {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async onPreUpdateItem(item, change, options, id) { }
 | 
			
		||||
 | 
			
		||||
  async onCreateItem(item, options, id) { }
 | 
			
		||||
 | 
			
		||||
  async onDeleteItem(item, options, id) { }
 | 
			
		||||
 | 
			
		||||
  async onUpdateActor(update, options, actorId) { }
 | 
			
		||||
 | 
			
		||||
  async onTimeChanging(oldTimestamp, newTimestamp) {
 | 
			
		||||
@@ -218,7 +215,7 @@ export class RdDBaseActor extends Actor {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async creerObjetParMJ(object) {
 | 
			
		||||
    if (!Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (!Misc.isFirstConnectedGM()) {
 | 
			
		||||
      RdDBaseActor.remoteActorCall({
 | 
			
		||||
        tokenId: this.token?.id,
 | 
			
		||||
        actorId: this.id,
 | 
			
		||||
@@ -317,7 +314,7 @@ export class RdDBaseActor extends Actor {
 | 
			
		||||
 | 
			
		||||
      RdDAudio.PlayContextAudio("argent"); // Petit son
 | 
			
		||||
      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.`
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
@@ -335,7 +332,7 @@ export class RdDBaseActor extends Actor {
 | 
			
		||||
      ui.notifications.info("Inutile de se vendre à soi-même");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (!Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (!Misc.isFirstConnectedGM()) {
 | 
			
		||||
      RdDBaseActor.remoteActorCall({
 | 
			
		||||
        actorId: achat.vendeurId ?? achat.acheteurId,
 | 
			
		||||
        method: 'achatVente',
 | 
			
		||||
@@ -708,5 +705,9 @@ export class RdDBaseActor extends Actor {
 | 
			
		||||
    }
 | 
			
		||||
    return undefined
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async resetItemUse() { }
 | 
			
		||||
  async incDecItemUse(itemId, inc = 1) { }
 | 
			
		||||
  getItemUse(itemId) { return 0; }
 | 
			
		||||
  async finDeRound(options = { terminer: false }) { }
 | 
			
		||||
  isActorCombat() { return false }
 | 
			
		||||
}
 | 
			
		||||
@@ -74,7 +74,7 @@ export class RdDEntite extends RdDBaseActorReve {
 | 
			
		||||
    return [STATUSES.StatusComma].includes(effectId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async onAppliquerJetEncaissement(encaissement, attacker) {
 | 
			
		||||
  async onAppliquerJetEncaissement(encaissement, attackerToken) {
 | 
			
		||||
    const perteEndurance = await this.santeIncDec("endurance", -encaissement.endurance);
 | 
			
		||||
    foundry.utils.mergeObject(encaissement, {
 | 
			
		||||
      resteEndurance: perteEndurance.newValue,
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,7 @@ const MAPPING_BASE = [
 | 
			
		||||
  { column: "malus_armure", getter: (actor, context) => Mapping.getMalusArmure(actor, context) },
 | 
			
		||||
  { column: "reve_actuel", rollClass: 'roll-reve-actuel', colName: 'Rêve actuel', getter: (actor, context) => actor.system.reve.reve.value },
 | 
			
		||||
  { column: "vie_actuel", rollClass: 'jet-vie', getter: (actor, context) => actor.system.sante.vie.value },
 | 
			
		||||
  { column: "endurance_actuel", rollClass: 'jet-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_armure", getter: (actor, context) => Mapping.getEsquiveArmure(context) },
 | 
			
		||||
  { column: "competences", getter: (actor, context) => Mapping.getCompetences(actor, CATEGORIES_COMPETENCES) },
 | 
			
		||||
@@ -140,16 +140,15 @@ export class Mapping {
 | 
			
		||||
 | 
			
		||||
  static prepareArmes(actor) {
 | 
			
		||||
    const armes = actor.items.filter(it => it.type == ITEM_TYPES.arme)
 | 
			
		||||
    return armes.map(arme =>
 | 
			
		||||
      [
 | 
			
		||||
        arme.system.unemain ? Mapping.prepareArme(actor, arme, 'unemain') : undefined,
 | 
			
		||||
        arme.system.deuxmains ? Mapping.prepareArme(actor, arme, 'deuxmains') : undefined,
 | 
			
		||||
        !(arme.system.unemain || arme.system.deuxmains) ? Mapping.prepareArme(actor, arme, 'competence') : undefined,
 | 
			
		||||
        arme.system.lancer != "" ? Mapping.prepareArme(actor, arme, 'lancer') : undefined,
 | 
			
		||||
        arme.system.tir != "" ? Mapping.prepareArme(actor, arme, 'tir') : undefined
 | 
			
		||||
      ]
 | 
			
		||||
        .filter(it => it != undefined)
 | 
			
		||||
    ).reduce((a, b) => a.concat(b), [])
 | 
			
		||||
    RdDItemArme.ajoutCorpsACorps(armes, actor)
 | 
			
		||||
    return armes.map(arme => [
 | 
			
		||||
      arme.system.unemain ? Mapping.prepareArme(actor, arme, 'unemain') : undefined,
 | 
			
		||||
      arme.system.deuxmains ? Mapping.prepareArme(actor, arme, 'deuxmains') : undefined,
 | 
			
		||||
      !(arme.system.unemain || arme.system.deuxmains) ? Mapping.prepareArme(actor, arme, 'competence') : undefined,
 | 
			
		||||
      arme.system.lancer != "" ? Mapping.prepareArme(actor, arme, 'lancer') : undefined,
 | 
			
		||||
      arme.system.tir != "" ? Mapping.prepareArme(actor, arme, 'tir') : undefined]
 | 
			
		||||
      .filter(it => it != undefined))
 | 
			
		||||
      .reduce((a, b) => a.concat(b), [])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static prepareArme(actor, arme, maniement) {
 | 
			
		||||
@@ -158,27 +157,35 @@ export class Mapping {
 | 
			
		||||
    if (RdDItemCompetence.isNiveauBase(competence)) {
 | 
			
		||||
      return undefined
 | 
			
		||||
    }
 | 
			
		||||
    const dmgArme = RdDItemArme.dommagesReels(arme, maniement)
 | 
			
		||||
    const dommages = dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme)
 | 
			
		||||
    const categorie = Mapping.complementCategorie(arme, maniement)
 | 
			
		||||
    const dommages = Mapping.dommagesArme(actor, arme, maniement)
 | 
			
		||||
    return {
 | 
			
		||||
      name: arme.name + categorie,
 | 
			
		||||
      niveau: Misc.toSignedString(competence.system.niveau),
 | 
			
		||||
      init: Mapping.calculBaseInit(actor, competence.system.categorie) + competence.system.niveau,
 | 
			
		||||
      dommages: Misc.toSignedString(dommages),
 | 
			
		||||
      dommages: dommages,
 | 
			
		||||
      competence: competence,
 | 
			
		||||
      arme: arme
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  static dommagesArme(actor, arme, maniement){
 | 
			
		||||
    const dmgArme = RdDItemArme.dommagesReels(arme, maniement)
 | 
			
		||||
    const dommages = Misc.toSignedString(dmgArme + RdDBonus.bonusDmg(actor, maniement, dmgArme))
 | 
			
		||||
    switch(arme.system.mortalite) {
 | 
			
		||||
      case 'non-mortel': return `(${dommages})`
 | 
			
		||||
      case 'empoignade': return '-'
 | 
			
		||||
    }
 | 
			
		||||
    return dommages
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static complementCategorie(arme, maniement) {
 | 
			
		||||
    switch (maniement) {
 | 
			
		||||
      case 'unemain': return (arme.system.deuxmains) ? ' 1 main' : (arme.system.lancer||arme.system.tir) ? ' mêlée': '' 
 | 
			
		||||
      case 'deuxmains': return (arme.system.unemain) ? ' 2 mains' : (arme.system.lancer||arme.system.tir) ? ' mêlée': '' 
 | 
			
		||||
      case 'unemain': return (arme.system.deuxmains) ? ' 1 main' : (arme.system.lancer || arme.system.tir) ? ' mêlée' : ''
 | 
			
		||||
      case 'deuxmains': return (arme.system.unemain) ? ' 2 mains' : (arme.system.lancer || arme.system.tir) ? ' mêlée' : ''
 | 
			
		||||
      case 'lancer': return (arme.system.unemain || arme.system.deuxmains || arme.system.tir) ? ' jet' : ''
 | 
			
		||||
      case 'tir': return (arme.system.unemain || arme.system.deuxmains || arme.system.lancer) ? ' tir' : ''
 | 
			
		||||
    }
 | 
			
		||||
    return '' 
 | 
			
		||||
    return ''
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static calculBaseInit(actor, categorie) {
 | 
			
		||||
@@ -327,7 +334,8 @@ export class Mapping {
 | 
			
		||||
    const txtByCategories = Object.values(byCategories)
 | 
			
		||||
      .map(it => it.competencesParNiveau)
 | 
			
		||||
      .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) {
 | 
			
		||||
          return ''
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										433
									
								
								module/apps/rdd-import-stats.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										433
									
								
								module/apps/rdd-import-stats.js
									
									
									
									
									
										Normal 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é à l’heure 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
 | 
			
		||||
 l’heure 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
 | 
			
		||||
 l’heure 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 n’aboient 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
									
								
							
							
						
						
									
										8225
									
								
								module/apps/xregexp-all.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -49,7 +49,7 @@ export class ChatUtility {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static onRemoveMessages(socketData) {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      if (socketData.part) {
 | 
			
		||||
        const toDelete = game.messages.filter(it => it.content.includes(socketData.part));
 | 
			
		||||
        toDelete.forEach(it => it.delete());
 | 
			
		||||
@@ -63,7 +63,7 @@ export class ChatUtility {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
  static removeMessages(socketData) {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      ChatUtility.onRemoveMessages(socketData);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
@@ -161,7 +161,7 @@ export class ChatUtility {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static handleGMChatMessage(socketData) {
 | 
			
		||||
    console.log("blindMessageToGM", socketData);
 | 
			
		||||
    if (Misc.firstConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      ChatMessage.create({
 | 
			
		||||
        user: game.user.id,
 | 
			
		||||
        whisper: ChatUtility.getGMs(),
 | 
			
		||||
 
 | 
			
		||||
@@ -8,14 +8,13 @@ import { RdDUtility } from "./rdd-utility.js";
 | 
			
		||||
export class DialogValidationEncaissement extends Dialog {
 | 
			
		||||
 | 
			
		||||
  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', {
 | 
			
		||||
      actor: actor,
 | 
			
		||||
      rollData: rollData,
 | 
			
		||||
      encaissement: encaissement
 | 
			
		||||
    });
 | 
			
		||||
    const dialog = new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser);
 | 
			
		||||
    dialog.render(true);
 | 
			
		||||
    new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser).render(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
@@ -56,14 +55,14 @@ export class DialogValidationEncaissement extends Dialog {
 | 
			
		||||
    this.html = html;
 | 
			
		||||
    this.html.find('input.encaissement-roll-result').keyup(async event => {
 | 
			
		||||
      this.forceDiceResult.total = event.currentTarget.value;
 | 
			
		||||
      this.encaissement = await RdDUtility.jetEncaissement(this.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-blessure').text(this.encaissement.blessures)
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -81,37 +81,59 @@ export class RdDItemArme extends Item {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static getCategorieParade(armeData) {
 | 
			
		||||
    if (armeData.system.categorie_parade) {
 | 
			
		||||
      return armeData.system.categorie_parade;
 | 
			
		||||
      return armeData.system.categorie_parade
 | 
			
		||||
    }
 | 
			
		||||
    // pour compatibilité avec des personnages existants
 | 
			
		||||
    if (armeData.type == ITEM_TYPES.competencecreature || armeData.system.categorie == 'creature') {
 | 
			
		||||
      return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '');
 | 
			
		||||
      return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '')
 | 
			
		||||
    }
 | 
			
		||||
    if (!armeData.type.match(/arme|competencecreature/)) {
 | 
			
		||||
      return '';
 | 
			
		||||
      return ''
 | 
			
		||||
    }
 | 
			
		||||
    if (armeData.system.competence == undefined) {
 | 
			
		||||
      return ITEM_TYPES.competencecreature;
 | 
			
		||||
    }
 | 
			
		||||
    let compname = armeData.system.competence.toLowerCase();
 | 
			
		||||
    if (compname.match(/^(dague de jet|javelot|fouet|arc|arbalête|fronde|hache de jet|fléau)$/)) return '';
 | 
			
		||||
    if (compname.match(/^(dague de jet|javelot|fouet|arc|arbalête|fronde|hache de jet|fléau)$/)) {
 | 
			
		||||
      return ''
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (compname.match('hache')) return 'haches';
 | 
			
		||||
    if (compname.match('hast')) return 'hast';
 | 
			
		||||
    if (compname.match('lance')) return 'lances';
 | 
			
		||||
    if (compname.match('bouclier')) return 'boucliers';
 | 
			
		||||
    if (compname.match('masse')) return 'masses';
 | 
			
		||||
    if (compname.match('hache')) return 'haches'
 | 
			
		||||
    if (compname.match('hast')) return 'hast'
 | 
			
		||||
    if (compname.match('lance')) return 'lances'
 | 
			
		||||
    if (compname.match('bouclier')) return 'boucliers'
 | 
			
		||||
    if (compname.match('masse')) return 'masses'
 | 
			
		||||
    if (compname.match('epée') || compname.match('épée')) {
 | 
			
		||||
      if (armeData.name.toLowerCase().match(/(gnome)/))
 | 
			
		||||
        return 'epees-courtes';
 | 
			
		||||
        return 'epees-courtes'
 | 
			
		||||
      if (armeData.name.toLowerCase().match(/((e|é)pée dragone|esparlongue|demi-dragonne)/))
 | 
			
		||||
        return 'epees-longues';
 | 
			
		||||
      return 'epees-lourdes';
 | 
			
		||||
        return 'epees-longues'
 | 
			
		||||
      return 'epees-lourdes'
 | 
			
		||||
    }
 | 
			
		||||
    if (compname.match('dague')) {
 | 
			
		||||
      return 'dagues';
 | 
			
		||||
      return '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;
 | 
			
		||||
    }
 | 
			
		||||
    // categories d'armes à la parade (cf. page 115 )
 | 
			
		||||
    let attCategory = RdDItemArme.getCategorieParade(armeAttaque);
 | 
			
		||||
    let defCategory = RdDItemArme.getCategorieParade(armeParade);
 | 
			
		||||
    const attCategory = RdDItemArme.getCategorieParade(armeAttaque)
 | 
			
		||||
    const defCategory = RdDItemArme.getCategorieParade(armeParade)
 | 
			
		||||
    // bouclier et mêmes catégorie: peuvent se parer sans difficulté
 | 
			
		||||
    if (defCategory == 'boucliers') {
 | 
			
		||||
      return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -190,7 +190,7 @@ export class RdDItemCompetence extends 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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
 
 | 
			
		||||
@@ -267,10 +267,17 @@ export class RdDItemSheet extends ItemSheet {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  /** @override */
 | 
			
		||||
  _updateObject(event, formData) {
 | 
			
		||||
    if (this.item.type == 'sort') {
 | 
			
		||||
      // Données de bonus de cases ?
 | 
			
		||||
      formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue);
 | 
			
		||||
    switch (this.item.type) {
 | 
			
		||||
      case ITEM_TYPES.sort:
 | 
			
		||||
        // Données de bonus de cases ?
 | 
			
		||||
        formData['system.bonuscase'] = RdDItemSort.buildBonuscaseFromArrays(formData.bonusValue, formData.caseValue)
 | 
			
		||||
        break
 | 
			
		||||
      case ITEM_TYPES.competence:
 | 
			
		||||
        if (formData['system.niveau'] == undefined) {
 | 
			
		||||
          formData['system.niveau'] = formData['system.base']
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.item.update(formData);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 blessure = {
 | 
			
		||||
      name: definition.label,
 | 
			
		||||
@@ -77,7 +77,7 @@ export class RdDItemBlessure extends RdDItem {
 | 
			
		||||
        gravite: gravite,
 | 
			
		||||
        difficulte: - gravite,
 | 
			
		||||
        localisation: localisation,
 | 
			
		||||
        origine: attacker?.name ?? ""
 | 
			
		||||
        origine: attackerToken?.name ?? ""
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    const blessures = await actor.createEmbeddedDocuments('Item', [blessure])
 | 
			
		||||
 
 | 
			
		||||
@@ -139,7 +139,7 @@ export class Misc {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static join(params, separator = '') {
 | 
			
		||||
    return params?.reduce(Misc.joining(separator)) ?? '';
 | 
			
		||||
    return (!params || params.length == 0) ? '' : params.reduce(Misc.joining(separator))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static joining(separator = '') {
 | 
			
		||||
@@ -195,7 +195,7 @@ export class Misc {
 | 
			
		||||
        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 undefined
 | 
			
		||||
@@ -206,14 +206,14 @@ export class Misc {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
   */
 | 
			
		||||
  static isUniqueConnectedGM() {
 | 
			
		||||
    return game.user.id == Misc.firstConnectedGMId();
 | 
			
		||||
  static isFirstConnectedGM() {
 | 
			
		||||
    return game.user == Misc.firstConnectedGM();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static firstConnectedGMId() {
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,8 @@ export class RdDCombatManager extends Combat {
 | 
			
		||||
    /* -------------------------------------------- */
 | 
			
		||||
    Hooks.on("getCombatTrackerEntryContext", (html, options) => { RdDCombatManager.pushInitiativeOptions(html, options); });
 | 
			
		||||
    Hooks.on("updateCombat", (combat, change, options, userId) => { RdDCombat.onUpdateCombat(combat, change, options, userId) });
 | 
			
		||||
    Hooks.on("preDeleteCombat", (combat, html, id) => { combat.onPreDeleteCombat() });
 | 
			
		||||
    Hooks.on("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() {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      await this.finDeRound({ terminer: true })
 | 
			
		||||
      ChatUtility.removeChatMessageContaining(`<div data-combatid="${this.id}" data-combatmessage="actor-turn-summary">`)
 | 
			
		||||
      game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined)
 | 
			
		||||
@@ -61,20 +62,31 @@ export class RdDCombatManager extends Combat {
 | 
			
		||||
      RdDEmpoignade.deleteAllEmpoignades()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async finDeRound(options = { terminer: false }) {
 | 
			
		||||
    this.turns.forEach(turn => turn.actor.resetItemUse());
 | 
			
		||||
 | 
			
		||||
    for (let combatant of this.combatants) {
 | 
			
		||||
      if (combatant.actor) {
 | 
			
		||||
        await combatant.actor.finDeRound(options);
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur!`)
 | 
			
		||||
  async onDeleteCombat() {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      if (game.combats.size <= 1) {
 | 
			
		||||
        game.actors.forEach(actor => actor.resetItemUse())
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async finDeRound(options = { terminer: false }) {
 | 
			
		||||
 | 
			
		||||
    for (let combatant of this.combatants) {
 | 
			
		||||
      if (!combatant.actor) {
 | 
			
		||||
        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) {
 | 
			
		||||
    const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0
 | 
			
		||||
    const etatGeneral = actor.getEtatGeneral() ?? 0
 | 
			
		||||
@@ -125,7 +137,6 @@ export class RdDCombatManager extends Combat {
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      //console.log("Combatat", c);
 | 
			
		||||
      const roll = combatant.getInitiativeRoll(rollFormula);
 | 
			
		||||
      if (!roll.total) {
 | 
			
		||||
        await roll.evaluate();
 | 
			
		||||
@@ -236,29 +247,12 @@ export class RdDCombatManager extends Combat {
 | 
			
		||||
    return attaque;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static listActionsCreature(competences) {
 | 
			
		||||
    return competences
 | 
			
		||||
      .filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
 | 
			
		||||
      .map(it => RdDItemCompetenceCreature.armeCreature(it))
 | 
			
		||||
      .filter(it => it != undefined);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static listActionsPossessions(actor) {
 | 
			
		||||
    return RdDCombatManager._indexActions(actor.getPossessions().map(p => {
 | 
			
		||||
      return {
 | 
			
		||||
        name: p.name,
 | 
			
		||||
        action: 'possession',
 | 
			
		||||
        system: {
 | 
			
		||||
          competence: p.name,
 | 
			
		||||
          possessionid: p.system.possessionid,
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static listActionsCombat(combatant) {
 | 
			
		||||
    const actor = combatant.actor;
 | 
			
		||||
    if (!actor.isActorCombat()) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    let actions = RdDCombatManager.listActionsPossessions(actor);
 | 
			
		||||
    if (actions.length > 0) {
 | 
			
		||||
      return actions;
 | 
			
		||||
@@ -281,6 +275,26 @@ export class RdDCombatManager extends Combat {
 | 
			
		||||
    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) {
 | 
			
		||||
    for (let index = 0; index < actions.length; index++) {
 | 
			
		||||
      actions[index].index = index;
 | 
			
		||||
@@ -291,7 +305,7 @@ export class RdDCombatManager extends Combat {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static processPremierRoundInit() {
 | 
			
		||||
    // 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);
 | 
			
		||||
      if (!initMissing) { // Premier round !
 | 
			
		||||
        for (let combatant of game.combat.combatants) {
 | 
			
		||||
@@ -454,11 +468,11 @@ export class RdDCombat {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static combatNouveauTour(combat) {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId);
 | 
			
		||||
      if (turn?.actor) {
 | 
			
		||||
        RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token.id);
 | 
			
		||||
        // TODO Playaudio for player??
 | 
			
		||||
        RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -469,52 +483,51 @@ export class RdDCombat {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static rddCombatTarget(target, attacker) {
 | 
			
		||||
    const defender = target?.actor;
 | 
			
		||||
    const defenderTokenId = target?.id;
 | 
			
		||||
    return new RdDCombat(attacker, defender, defenderTokenId, target)
 | 
			
		||||
  static rddCombatTarget(target, attacker, attackerToken) {
 | 
			
		||||
    return new RdDCombat(attacker, attackerToken?.id, target?.actor, target?.id, target)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) {
 | 
			
		||||
    const attacker = game.actors.get(attackerId);
 | 
			
		||||
    let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined;
 | 
			
		||||
  static rddCombatForAttackerAndDefender(attackerId, attackerTokenId, defenderTokenId) {
 | 
			
		||||
    const attacker = game.actors.get(attackerId)
 | 
			
		||||
    const defenderToken = defenderTokenId ? canvas.tokens.get(defenderTokenId) : undefined
 | 
			
		||||
    let defender = defenderToken?.actor;
 | 
			
		||||
    let target = undefined
 | 
			
		||||
    if (!defenderTokenId || !defender) {
 | 
			
		||||
      console.warn(`RdDCombat.rddCombatForAttackerAndDefender: appel avec defenderTokenId ${defenderTokenId} incorrect, ou pas de defender correspondant`);
 | 
			
		||||
      target = Targets.getTarget()
 | 
			
		||||
      if (!target) {
 | 
			
		||||
        return;
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      defenderTokenId = target.id;
 | 
			
		||||
      defender = target.actor;
 | 
			
		||||
      if (!defenderTokenId || !defender) {
 | 
			
		||||
        return;
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return new RdDCombat(attacker, defender, defenderTokenId, target)
 | 
			
		||||
    return new RdDCombat(attacker, attackerTokenId, defender, defenderTokenId, target)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static onMsgEncaisser(msg) {
 | 
			
		||||
    let defender = canvas.tokens.get(msg.defenderTokenId).actor;
 | 
			
		||||
    if (Misc.isOwnerPlayerOrUniqueConnectedGM()) {
 | 
			
		||||
      let attackerRoll = msg.attackerRoll;
 | 
			
		||||
      let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
 | 
			
		||||
      let defender = canvas.tokens.get(msg.defenderToken.id).actor;
 | 
			
		||||
 | 
			
		||||
      defender.encaisserDommages(attackerRoll, attacker);
 | 
			
		||||
      const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
 | 
			
		||||
      defender.encaisserDommages(attackerRoll, attacker, msg.attackerToken);
 | 
			
		||||
      const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id);
 | 
			
		||||
      rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static onMsgDefense(msg) {
 | 
			
		||||
    let defenderToken = canvas.tokens.get(msg.defenderTokenId);
 | 
			
		||||
    if (defenderToken && Misc.isUniqueConnectedGM()) {
 | 
			
		||||
      const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
 | 
			
		||||
      rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme);
 | 
			
		||||
      rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
 | 
			
		||||
    let defenderToken = canvas.tokens.get(msg.defenderToken.id)
 | 
			
		||||
    if (defenderToken && Misc.isFirstConnectedGM()) {
 | 
			
		||||
      const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id)
 | 
			
		||||
      rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme)
 | 
			
		||||
      rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -545,6 +558,7 @@ export class RdDCombat {
 | 
			
		||||
      html.on("click", button, event => {
 | 
			
		||||
        const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
 | 
			
		||||
          event.currentTarget.attributes['data-attackerId']?.value,
 | 
			
		||||
          event.currentTarget.attributes['data-attackerTokenId']?.value,
 | 
			
		||||
          event.currentTarget.attributes['data-defenderTokenId']?.value);
 | 
			
		||||
        if (rddCombat) {
 | 
			
		||||
          rddCombat.onEvent(button, event);
 | 
			
		||||
@@ -560,22 +574,38 @@ export class RdDCombat {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  constructor(attacker, defender, defenderTokenId, target) {
 | 
			
		||||
  constructor(attacker, attackerTokenId, defender, defenderTokenId, target) {
 | 
			
		||||
    this.attacker = attacker
 | 
			
		||||
    this.defender = defender
 | 
			
		||||
    this.target = target
 | 
			
		||||
    this.attackerId = this.attacker.id
 | 
			
		||||
    this.defenderId = this.defender.id
 | 
			
		||||
    this.attackerTokenId = attackerTokenId
 | 
			
		||||
    this.defenderTokenId = defenderTokenId
 | 
			
		||||
    this.attackerToken = RdDCombat.$extractAttackerTokenData(attacker, attackerTokenId)
 | 
			
		||||
    this.defenderToken = RdDCombat.$extractDefenderTokenData(defender, defenderTokenId, target)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static $extractAttackerTokenData(attacker, attackerTokenId) {
 | 
			
		||||
    const token = canvas.tokens.get(attackerTokenId);
 | 
			
		||||
    return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(attackerTokenId, attacker)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static $extractDefenderTokenData(defender, defenderTokenId, target) {
 | 
			
		||||
    if (target) {
 | 
			
		||||
      return Targets.extractTokenData(target)
 | 
			
		||||
    }
 | 
			
		||||
    const token = canvas.tokens.get(defenderTokenId);
 | 
			
		||||
    return token ? Targets.extractTokenData(token) : Targets.buildActorTokenData(defenderTokenId, defender)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async onEvent(button, event) {
 | 
			
		||||
    const chatMessage = ChatUtility.getChatMessage(event);
 | 
			
		||||
    const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
 | 
			
		||||
    const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll');
 | 
			
		||||
    console.log('RdDCombat', attackerRoll, defenderRoll);
 | 
			
		||||
    const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value;
 | 
			
		||||
 | 
			
		||||
    const armeParadeId = event.currentTarget.attributes['data-armeid']?.value;
 | 
			
		||||
    const competence = event.currentTarget.attributes['data-competence']?.value;
 | 
			
		||||
@@ -585,7 +615,7 @@ export class RdDCombat {
 | 
			
		||||
      case '#particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
 | 
			
		||||
      case '#parer-button': return this.parade(attackerRoll, armeParadeId);
 | 
			
		||||
      case '#esquiver-button': return this.esquive(attackerRoll, compId, competence);
 | 
			
		||||
      case '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll, defenderTokenId);
 | 
			
		||||
      case '#encaisser-button': return this.encaisser(attackerRoll, defenderRoll);
 | 
			
		||||
      case '#echec-total-attaque': return this._onEchecTotal(attackerRoll);
 | 
			
		||||
 | 
			
		||||
      case '#appel-chance-attaque': return this.attacker.rollAppelChance(
 | 
			
		||||
@@ -702,7 +732,7 @@ export class RdDCombat {
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        const defenderToken = canvas.tokens.get(this.defenderTokenId);
 | 
			
		||||
        const defenderToken = canvas.tokens.get(this.defenderTokenId)
 | 
			
		||||
        const dist = this.distance(_token, defenderToken)
 | 
			
		||||
        const isVisible = this.isVisible(_token, defenderToken)
 | 
			
		||||
        const portee = this._ajustementPortee(dist, rollData.arme)
 | 
			
		||||
@@ -769,7 +799,7 @@ export class RdDCombat {
 | 
			
		||||
    }
 | 
			
		||||
    RdDEmpoignade.checkEmpoignadeEnCours(this.attacker)
 | 
			
		||||
 | 
			
		||||
    let rollData = this._prepareAttaque(competence, arme);
 | 
			
		||||
    let rollData = this._prepareAttaque(competence, arme)
 | 
			
		||||
    console.log("RdDCombat.attaque >>>", rollData);
 | 
			
		||||
    if (arme) {
 | 
			
		||||
      this.attacker.verifierForceMin(arme);
 | 
			
		||||
@@ -795,15 +825,18 @@ export class RdDCombat {
 | 
			
		||||
    dialog.render(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  _prepareAttaque(competence, arme) {
 | 
			
		||||
    let rollData = {
 | 
			
		||||
      alias: this.attackerToken.name,
 | 
			
		||||
      passeArme: foundry.utils.randomID(16),
 | 
			
		||||
      mortalite: arme?.system.mortalite,
 | 
			
		||||
      competence: competence,
 | 
			
		||||
      surprise: this.attacker.getSurprise(true),
 | 
			
		||||
      surpriseDefenseur: this.defender.getSurprise(true),
 | 
			
		||||
      targetToken: Targets.extractTokenData(this.target),
 | 
			
		||||
      sourceToken: this.attackerToken,
 | 
			
		||||
      targetToken: this.defenderToken,
 | 
			
		||||
      essais: {}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -848,9 +881,10 @@ export class RdDCombat {
 | 
			
		||||
      alias: this.attacker.name,
 | 
			
		||||
      whisper: ChatUtility.getOwners(this.attacker),
 | 
			
		||||
      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,
 | 
			
		||||
        defenderTokenId: this.defenderTokenId,
 | 
			
		||||
        attackerToken: this.attackerToken,
 | 
			
		||||
        defenderToken: this.defenderToken,
 | 
			
		||||
        isForce: isForce,
 | 
			
		||||
        isFinesse: isFinesse,
 | 
			
		||||
        isRapide: isRapide,
 | 
			
		||||
@@ -867,7 +901,7 @@ export class RdDCombat {
 | 
			
		||||
    attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker, this.defender.isEntite());
 | 
			
		||||
    let defenderRoll = { attackerRoll: attackerRoll, passeArme: attackerRoll.passeArme, show: {} }
 | 
			
		||||
    attackerRoll.show = {
 | 
			
		||||
      cible: this.target ? this.defender.name : 'la cible',
 | 
			
		||||
      cible: this.defenderToken?.name ?? 'la cible',
 | 
			
		||||
      isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge')
 | 
			
		||||
    }
 | 
			
		||||
    await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
 | 
			
		||||
@@ -898,7 +932,7 @@ export class RdDCombat {
 | 
			
		||||
    // # utilisation esquive
 | 
			
		||||
    const corpsACorps = this.defender.getCompetenceCorpsACorps({ onMessage: it => console.info(it, this.defender) });
 | 
			
		||||
    const esquives = foundry.utils.duplicate(this.defender.getCompetencesEsquive())
 | 
			
		||||
    esquives.forEach(e => e.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 = {
 | 
			
		||||
      passeArme: attackerRoll.passeArme,
 | 
			
		||||
@@ -908,7 +942,8 @@ export class RdDCombat {
 | 
			
		||||
      attacker: this.attacker,
 | 
			
		||||
      attackerId: this.attackerId,
 | 
			
		||||
      esquives: esquives,
 | 
			
		||||
      defenderTokenId: this.defenderTokenId,
 | 
			
		||||
      attackerToken: this.attackerToken,
 | 
			
		||||
      defenderToken: this.defenderToken,
 | 
			
		||||
      mainsNues: attackerRoll.dmg.mortalite != 'mortel' && corpsACorps,
 | 
			
		||||
      armes: this._filterArmesParade(this.defender, attackerRoll.competence, attackerRoll.arme),
 | 
			
		||||
      diffLibre: attackerRoll.ajustements?.diffLibre?.value ?? 0,
 | 
			
		||||
@@ -919,7 +954,7 @@ export class RdDCombat {
 | 
			
		||||
      dmg: attackerRoll.dmg,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      await this._chatMessageDefense(paramChatDefense, defenderRoll);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
@@ -932,7 +967,7 @@ export class RdDCombat {
 | 
			
		||||
    const choixDefense = await ChatMessage.create({
 | 
			
		||||
      // message privé: du défenseur à lui même (et aux GMs)
 | 
			
		||||
      speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
 | 
			
		||||
      alias: this.attacker.name,
 | 
			
		||||
      alias: this.attackerToken.name,
 | 
			
		||||
      whisper: ChatUtility.getOwners(this.defender),
 | 
			
		||||
      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, {
 | 
			
		||||
      msg: "msg_defense", data: {
 | 
			
		||||
        attackerId: this.attacker?.id,
 | 
			
		||||
        attackerToken: this.attackerToken,
 | 
			
		||||
        defenderId: this.defender?.id,
 | 
			
		||||
        defenderTokenId: this.defenderTokenId,
 | 
			
		||||
        defenderToken: this.defenderToken,
 | 
			
		||||
        defenderRoll: defenderRoll,
 | 
			
		||||
        paramChatDefense: paramChatDefense,
 | 
			
		||||
        rollMode: true
 | 
			
		||||
@@ -956,20 +992,21 @@ export class RdDCombat {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  _filterArmesParade(defender, competence) {
 | 
			
		||||
    let items = defender.items.filter(it => RdDItemArme.isArmeUtilisable(it) || RdDItemCompetenceCreature.isCompetenceParade(it))
 | 
			
		||||
    items.forEach(item => item.system.nbUsage = defender.getItemUse(item.id)); // Ajout du # d'utilisation ce round  
 | 
			
		||||
  _filterArmesParade(defender, competence, arme) {
 | 
			
		||||
    let defenses = defender.items.filter(it => RdDItemArme.isArmeUtilisable(it) || RdDItemCompetenceCreature.isCompetenceParade(it))
 | 
			
		||||
    defenses = foundry.utils.duplicate(defenses)
 | 
			
		||||
    defenses.forEach(armeDefense => {
 | 
			
		||||
      // Ajout du # d'utilisation ce round  
 | 
			
		||||
      armeDefense.nbUsage = defender.getItemUse(armeDefense.id)
 | 
			
		||||
      armeDefense.typeParade = RdDItemArme.defenseArmeParade(arme, armeDefense)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    switch (competence.system.categorie) {
 | 
			
		||||
      case 'tir':
 | 
			
		||||
      case 'lancer':
 | 
			
		||||
        return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
 | 
			
		||||
        return defenses.filter(armeDefense => RdDItemArme.getCategorieParade(armeDefense) == 'boucliers')
 | 
			
		||||
      default:
 | 
			
		||||
        // Le fléau ne peut être paré qu’au bouclier p115
 | 
			
		||||
        if (competence.name == "Fléau") {
 | 
			
		||||
          return items.filter(item => RdDItemArme.getCategorieParade(item) == 'boucliers')
 | 
			
		||||
        }
 | 
			
		||||
        return items.filter(item => RdDItemArme.getCategorieParade(item));
 | 
			
		||||
        return defenses.filter(armeDefense => armeDefense.typeParade != '')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -980,7 +1017,8 @@ export class RdDCombat {
 | 
			
		||||
      content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', {
 | 
			
		||||
        attackerId: this.attackerId,
 | 
			
		||||
        attacker: this.attacker,
 | 
			
		||||
        defenderTokenId: this.defenderTokenId,
 | 
			
		||||
        attackerToken: this.attackerToken,
 | 
			
		||||
        defenderToken: this.defenderToken,
 | 
			
		||||
        essais: attackerRoll.essais
 | 
			
		||||
      })
 | 
			
		||||
    });
 | 
			
		||||
@@ -1052,8 +1090,11 @@ export class RdDCombat {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  _prepareParade(attackerRoll, armeParade, competenceParade) {
 | 
			
		||||
    let defenderRoll = {
 | 
			
		||||
      alias: this.defenderToken?.name,
 | 
			
		||||
      passeArme: attackerRoll.passeArme,
 | 
			
		||||
      diffLibre: attackerRoll.diffLibre,
 | 
			
		||||
      attackerToken: this.attackerToken,
 | 
			
		||||
      defenderToken: this.defenderToken,
 | 
			
		||||
      attackerRoll: attackerRoll,
 | 
			
		||||
      competence: this.defender.getCompetence(competenceParade),
 | 
			
		||||
      arme: armeParade,
 | 
			
		||||
@@ -1133,8 +1174,11 @@ export class RdDCombat {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  _prepareEsquive(attackerRoll, competence) {
 | 
			
		||||
    let rollData = {
 | 
			
		||||
      alias: this.defenderToken?.name,
 | 
			
		||||
      passeArme: attackerRoll.passeArme,
 | 
			
		||||
      diffLibre: attackerRoll.diffLibre,
 | 
			
		||||
      attackerToken: this.attackerToken,
 | 
			
		||||
      defenderToken: this.defenderToken,
 | 
			
		||||
      attackerRoll: attackerRoll,
 | 
			
		||||
      competence: competence,
 | 
			
		||||
      surprise: this.defender.getSurprise(true),
 | 
			
		||||
@@ -1276,9 +1320,8 @@ export class RdDCombat {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async encaisser(attackerRoll, defenderRoll, defenderTokenId) {
 | 
			
		||||
    defenderTokenId = defenderTokenId || this.defenderTokenId;
 | 
			
		||||
    console.log("RdDCombat.encaisser >>>", attackerRoll, defenderTokenId);
 | 
			
		||||
  async encaisser(attackerRoll, defenderRoll) {
 | 
			
		||||
    console.log("RdDCombat.encaisser >>>", attackerRoll, defenderRoll);
 | 
			
		||||
 | 
			
		||||
    if (defenderRoll?.rolled && RdDCombat.isEchecTotal(defenderRoll)) {
 | 
			
		||||
      this._onEchecTotal(defenderRoll);
 | 
			
		||||
@@ -1286,18 +1329,19 @@ export class RdDCombat {
 | 
			
		||||
 | 
			
		||||
    if (Misc.isOwnerPlayerOrUniqueConnectedGM(this.defender)) {
 | 
			
		||||
      attackerRoll.attackerId = this.attackerId;
 | 
			
		||||
      attackerRoll.defenderTokenId = defenderTokenId;
 | 
			
		||||
      attackerRoll.defenderTokenId = this.defenderToken.id;
 | 
			
		||||
 | 
			
		||||
      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
 | 
			
		||||
      game.socket.emit(SYSTEM_SOCKET_ID, {
 | 
			
		||||
        msg: "msg_encaisser",
 | 
			
		||||
        data: {
 | 
			
		||||
          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 = {
 | 
			
		||||
      combatId: combat._id,
 | 
			
		||||
      alias: actor.name,
 | 
			
		||||
      alias: token.name ?? actor.name,
 | 
			
		||||
      etatGeneral: actor.getEtatGeneral(),
 | 
			
		||||
      isSonne: actor.getSonne(),
 | 
			
		||||
      isSonne: actor.isSonne(),
 | 
			
		||||
      blessuresStatus: actor.computeResumeBlessure(),
 | 
			
		||||
      SConst: actor.getSConst(),
 | 
			
		||||
      actorId: actor.id,
 | 
			
		||||
      actor: actor,
 | 
			
		||||
      tokenId: tokenId,
 | 
			
		||||
      tokenId: token.id,
 | 
			
		||||
      isGrave: actor.countBlessures(it => it.isGrave()) > 0,
 | 
			
		||||
      isCritique: actor.countBlessures(it => it.isCritique()) > 0
 | 
			
		||||
    }
 | 
			
		||||
    await ChatMessage.create({
 | 
			
		||||
      content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs`, formData),
 | 
			
		||||
      alias: actor.name
 | 
			
		||||
      alias: token.name ?? actor.name
 | 
			
		||||
    })
 | 
			
		||||
    await ChatMessage.create({
 | 
			
		||||
      content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData),
 | 
			
		||||
      whisper: ChatUtility.getOwners(actor),
 | 
			
		||||
      alias: actor.name
 | 
			
		||||
      alias: token.name ?? actor.name
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -68,6 +68,7 @@ import { OptionsAvancees } from "./settings/options-avancees.js"
 | 
			
		||||
import { ExportScriptarium } from "./actor/export-scriptarium/export-scriptarium.js"
 | 
			
		||||
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js"
 | 
			
		||||
import { RdDActorExportSheet } from "./actor/export-scriptarium/actor-encart-sheet.js"
 | 
			
		||||
import { RdDStatBlockParser } from "./apps/rdd-import-stats.js"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * RdD system
 | 
			
		||||
@@ -87,6 +88,7 @@ export class SystemReveDeDragon {
 | 
			
		||||
    this.config = RDD_CONFIG
 | 
			
		||||
    this.RdDUtility = RdDUtility
 | 
			
		||||
    this.RdDHotbar = RdDHotbar
 | 
			
		||||
    this.RdDStatBlockParser = RdDStatBlockParser
 | 
			
		||||
    this.itemClasses = {
 | 
			
		||||
      armure: RdDItemArmure,
 | 
			
		||||
      blessure: RdDItemBlessure,
 | 
			
		||||
@@ -278,7 +280,7 @@ export class SystemReveDeDragon {
 | 
			
		||||
    /*  Foundry VTT Initialization                  */
 | 
			
		||||
    /* -------------------------------------------- */
 | 
			
		||||
    game.system.rdd.calendrier = new RdDCalendrier()
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      new Migrations().migrate()
 | 
			
		||||
      this.messageDeBienvenue()
 | 
			
		||||
      import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
 | 
			
		||||
@@ -292,6 +294,8 @@ export class SystemReveDeDragon {
 | 
			
		||||
    StatusEffects.onReady()
 | 
			
		||||
    RdDHerbes.onReady()
 | 
			
		||||
    RdDDice.onReady()
 | 
			
		||||
 | 
			
		||||
    RdDStatBlockParser.parseStatBlock()
 | 
			
		||||
    /* -------------------------------------------- */
 | 
			
		||||
    /* Affiche/Init le calendrier */
 | 
			
		||||
    game.system.rdd.calendrier.display()
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,6 @@ export class RdDEncaisser extends Dialog {
 | 
			
		||||
        encaisserSpecial: this.encaisserSpecial,
 | 
			
		||||
        mortalite: mortalite
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ export class RdDTokenHud {
 | 
			
		||||
      // initiative
 | 
			
		||||
      await RdDTokenHud.addExtensionHudInit(html, combatant, actions);
 | 
			
		||||
      // 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) {
 | 
			
		||||
    const hudData = { combatant, actions, commandes: [] };
 | 
			
		||||
  static async addExtensionHudCombat(html, combatant, token, actions) {
 | 
			
		||||
    const hudData = { combatant, token, actions, commandes: [] };
 | 
			
		||||
    const controlIconTarget = html.find('.control-icon[data-action=target]');
 | 
			
		||||
    await RdDTokenHud._configureSubMenu(controlIconTarget, 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html', hudData,
 | 
			
		||||
      (event) => {
 | 
			
		||||
@@ -80,7 +80,7 @@ export class RdDTokenHud {
 | 
			
		||||
          combatant.actor.conjurerPossession(possession);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          combatant.actor.rollArme(action);
 | 
			
		||||
          combatant.actor.rollArme(action, 'competence', token)
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    let formula = RdDUtility.formuleEncaissement(diff, options)
 | 
			
		||||
    const roll = await RdDDice.roll(formula, 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) {
 | 
			
		||||
@@ -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
 | 
			
		||||
    const bonusDegatsDiffLibre = ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(rollData.diffLibre ?? 0) : 0
 | 
			
		||||
    const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre
 | 
			
		||||
    const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite);
 | 
			
		||||
    const over20 = Math.max(jetTotal - 20, 0);
 | 
			
		||||
    encaissement.dmg = rollData.dmg;
 | 
			
		||||
    encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(this.type);
 | 
			
		||||
    encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;';
 | 
			
		||||
    encaissement.dmg = rollData.dmg
 | 
			
		||||
    if (ReglesOptionnelles.isUsing('localisation-aleatoire')) {
 | 
			
		||||
      encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(actor.type)
 | 
			
		||||
      encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      encaissement.dmg.loc = { label: '' }
 | 
			
		||||
    }
 | 
			
		||||
    encaissement.dmg.bonusDegatsDiffLibre = bonusDegatsDiffLibre
 | 
			
		||||
    encaissement.roll = roll;
 | 
			
		||||
    encaissement.armure = armure;
 | 
			
		||||
@@ -741,6 +746,15 @@ export class RdDUtility {
 | 
			
		||||
    return undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getSelectedToken(actor) {
 | 
			
		||||
    if (canvas.tokens.controlled.length > 0) {
 | 
			
		||||
      const tokens = canvas.tokens.controlled
 | 
			
		||||
        .filter(it => it.actor.id == actor.id)
 | 
			
		||||
      return tokens[0]
 | 
			
		||||
    }
 | 
			
		||||
    return undefined
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getSelectedActor(msgPlayer = undefined) {
 | 
			
		||||
    if (canvas.tokens.controlled.length == 1) {
 | 
			
		||||
      let token = canvas.tokens.controlled[0];
 | 
			
		||||
@@ -867,10 +881,10 @@ export class RdDUtility {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  static afficherHeuresChanceMalchance(heureNaissance) {
 | 
			
		||||
    if (game.user.isGM) {
 | 
			
		||||
      const heure = RdDTimestamp.findHeure(heureNaissance - 1);
 | 
			
		||||
      const heure = RdDTimestamp.findHeure(heureNaissance)
 | 
			
		||||
      if (heureNaissance && heure) {
 | 
			
		||||
        let ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance);
 | 
			
		||||
        const current = game.system.rdd.calendrier.heureCourante();
 | 
			
		||||
        const ajustement = game.system.rdd.calendrier.getAjustementAstrologique(heureNaissance)
 | 
			
		||||
        const current = game.system.rdd.calendrier.heureCourante()
 | 
			
		||||
        ChatMessage.create({
 | 
			
		||||
          content: `A l'heure de <strong>${current.label}</strong>, le modificateur de Chance/Malchance est de <strong>${Misc.toSignedString(ajustement)}</strong> pour l'heure de naissance <strong>${heure.label}</strong>.`,
 | 
			
		||||
          whisper: ChatUtility.getGMs()
 | 
			
		||||
 
 | 
			
		||||
@@ -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ègles de combat', name: 'localisation-aleatoire',      descr: "Proposer une localisation aléatoire des blessures" },
 | 
			
		||||
  { group: 'Règles de combat', name: 'recul',                       descr: "Appliquer le recul en cas de particulière en force ou de charge" },
 | 
			
		||||
  { group: 'Règles de combat', name: 'resistanceArmeParade',        descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
 | 
			
		||||
  { group: 'Règles de combat', name: 'deteriorationArmure',         descr: "Tenir compte de la détérioration des armures" },
 | 
			
		||||
 
 | 
			
		||||
@@ -186,7 +186,7 @@ export class AppAstrologie extends Application {
 | 
			
		||||
      date: this.html.find('[name="joursAstrologie"]').val(),
 | 
			
		||||
      userId: game.user.id
 | 
			
		||||
    }
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      game.system.rdd.calendrier.requestNombreAstral(socketData);
 | 
			
		||||
    } else {
 | 
			
		||||
      game.socket.emit(SYSTEM_SOCKET_ID, {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,18 +13,21 @@ export class Targets {
 | 
			
		||||
  static extractTokenData(target) {
 | 
			
		||||
    return { id: target?.id, name: target?.document.name, img: target?.document.texture.src ?? target?.actor.img ?? 'icons/svg/mystery-man.svg' };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static buildActorTokenData(tokenId, actor) {
 | 
			
		||||
    return { id: tokenId, name: actor.name, img: actor.img ?? 'icons/svg/mystery-man.svg' };
 | 
			
		||||
  }
 | 
			
		||||
  static isTargetEntite(target) {
 | 
			
		||||
    return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static async selectOneToken(onSelectTarget = target => { }) {
 | 
			
		||||
    const targets = Targets.listTargets();
 | 
			
		||||
  static async selectOneTargetToken(onSelectTarget = target => { }) {
 | 
			
		||||
    const targets = Targets.listTargets()
 | 
			
		||||
    switch (targets.length) {
 | 
			
		||||
      case 0: return;
 | 
			
		||||
      case 0:
 | 
			
		||||
        return
 | 
			
		||||
      case 1:
 | 
			
		||||
        onSelectTarget(targets[0]);
 | 
			
		||||
        return;
 | 
			
		||||
        onSelectTarget(targets[0])
 | 
			
		||||
        return
 | 
			
		||||
      default:
 | 
			
		||||
        {
 | 
			
		||||
          const selectData = {
 | 
			
		||||
@@ -32,7 +35,7 @@ export class Targets {
 | 
			
		||||
            label: "Choisir une seule des cibles",
 | 
			
		||||
            list: targets.map(it => Targets.extractTokenData(it))
 | 
			
		||||
          };
 | 
			
		||||
          DialogSelect.select(selectData, onSelectTarget);
 | 
			
		||||
          DialogSelect.select(selectData, onSelectTarget)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ export class RdDCalendrier extends Application {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
    this.timestamp = RdDTimestamp.getWorldTime();
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) { // Uniquement si GM
 | 
			
		||||
      RdDTimestamp.setWorldTime(this.timestamp);
 | 
			
		||||
      this.rebuildNombresAstraux(); // Ensure always up-to-date
 | 
			
		||||
    }
 | 
			
		||||
@@ -258,7 +258,7 @@ export class RdDCalendrier extends Application {
 | 
			
		||||
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async rebuildNombresAstraux() {
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) {
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) {
 | 
			
		||||
      const nombresAstraux = this.getNombresAstraux()
 | 
			
		||||
      let newNombresAstraux = [];
 | 
			
		||||
      for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
 | 
			
		||||
@@ -337,7 +337,7 @@ export class RdDCalendrier extends Application {
 | 
			
		||||
  /* -------------------------------------------- */
 | 
			
		||||
  async requestNombreAstral(request) {
 | 
			
		||||
    const actor = game.actors.get(request.id);
 | 
			
		||||
    if (Misc.isUniqueConnectedGM()) { // Only once
 | 
			
		||||
    if (Misc.isFirstConnectedGM()) { // Only once
 | 
			
		||||
      console.log(request);
 | 
			
		||||
      let jourDiff = this.getLectureAstrologieDifficulte(request.date);
 | 
			
		||||
      let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
 | 
			
		||||
 
 | 
			
		||||
@@ -160,15 +160,21 @@ export class RdDTimestamp {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static findHeure(heure) {
 | 
			
		||||
    heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
 | 
			
		||||
    let parHeureOuLabel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure || it.heure == Misc.modulo(parseInt(heure), RDD_HEURES_PAR_JOUR));
 | 
			
		||||
    if (parHeureOuLabel.length == 1) {
 | 
			
		||||
      return parHeureOuLabel[0];
 | 
			
		||||
    let filtered
 | 
			
		||||
    if (Number.isInteger(Number(heure))) {
 | 
			
		||||
      filtered = DEFINITION_HEURES.filter(it => it.heure == Misc.modulo(Number(heure) - 1, RDD_HEURES_PAR_JOUR))
 | 
			
		||||
    }
 | 
			
		||||
    let parLabelPartiel = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
 | 
			
		||||
    if (parLabelPartiel.length > 0) {
 | 
			
		||||
      parLabelPartiel.sort(Misc.ascending(h => h.label.length));
 | 
			
		||||
      return parLabelPartiel[0];
 | 
			
		||||
    else {
 | 
			
		||||
      heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
 | 
			
		||||
      filtered = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure || it.heure == Misc.modulo(parseInt(heure), RDD_HEURES_PAR_JOUR));
 | 
			
		||||
    }
 | 
			
		||||
    if (filtered.length == 1) {
 | 
			
		||||
      return filtered[0]
 | 
			
		||||
    }
 | 
			
		||||
    filtered = DEFINITION_HEURES.filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
 | 
			
		||||
    if (filtered.length > 0) {
 | 
			
		||||
      filtered.sort(Misc.ascending(h => h.label.length));
 | 
			
		||||
      return filtered[0]
 | 
			
		||||
    }
 | 
			
		||||
    return undefined;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -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’indique, c’est une <em>bâtarde </em>de l'épée longue et de l'épée sorde, à la fois longue et large, pouvant s’utiliser à 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":"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":"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é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"}}
 | 
			
		||||
 
 | 
			
		||||
@@ -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":"Jet d'éthylisme","type":"script","scope":"global","author":"Hp9ImM4o9YRTSdfu","img":"icons/consumables/drinks/alcohol-beer-stein-wooden-metal-brown.webp","command":"const selected = game.system.rdd.RdDUtility.getSelectedActor();\nif (selected) {\n   selected.jetEthylisme();\n}\nelse {\n  ui.notifications.info('Pas de personnage sélectionné');\n}","ownership":{"default":0,"Hp9ImM4o9YRTSdfu":3},"flags":{"core":{"sourceId":"Macro.XHNbjnGKXaCiCadq"}},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"11.1.0","coreVersion":"10.291","createdTime":1671220038331,"modifiedTime":1671233646086,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"},"folder":null,"sort":0,"_id":"mvub1dRHNFmWjRr7"}
 | 
			
		||||
{"name":"Tirer le tarot","type":"chat","scope":"global","author":"Hp9ImM4o9YRTSdfu","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp","command":"/tirer tarot","ownership":{"default":0,"Hp9ImM4o9YRTSdfu":3},"flags":{"core":{"sourceId":"Macro.HBZSKR9OHCQbLcTC"}},"_stats":{"systemId":"foundryvtt-reve-de-dragon","systemVersion":"11.1.0","coreVersion":"10.291","createdTime":1669469547231,"modifiedTime":1671237401618,"lastModifiedBy":"Hp9ImM4o9YRTSdfu"},"folder":null,"sort":0,"_id":"vTfJTFYYiRu8X5LM"}
 | 
			
		||||
{"name": "Mon personnage","type": "script","author": "Hp9ImM4o9YRTSdfu","img": "systems/foundryvtt-reve-de-dragon/icons/voyageurs/token_hr_dilettante.webp","scope": "global","command": "if (game.user.isGM) {\n  ui.notifications.warn(\"En tant que gardien, vous n'avez pas de personnage attitré\")\n  return\n}\nconst actor = game.users.get(game.userId)?.character\nif (!actor) {\n  ui.notifications.warn(\"Vous n'avez pas de personnage attitré\")\n  return\n}\nactor.sheet.render(true)","folder": null,"flags": {"core": {},"exportSource": {"world": "graine","system": "foundryvtt-reve-de-dragon","coreVersion": "11.313","systemVersion": "11.1.1"}},"_stats": {"systemId": "foundryvtt-reve-de-dragon","systemVersion": "11.1.1","coreVersion": "11.313","createdTime": 1699477824379,"modifiedTime": 1699485023429,"lastModifiedBy": "Hp9ImM4o9YRTSdfu"}}
 | 
			
		||||
 
 | 
			
		||||
@@ -1561,6 +1561,11 @@ div.control-icon.token-hud-icon {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  object-fit: contain;
 | 
			
		||||
}
 | 
			
		||||
.chat-inline-icon {
 | 
			
		||||
  border: 0;
 | 
			
		||||
  padding: 1px;
 | 
			
		||||
  vertical-align: text-top;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#sidebar-tabs {
 | 
			
		||||
  flex: 0 0 28px;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "id": "foundryvtt-reve-de-dragon",
 | 
			
		||||
  "title": "Rêve de Dragon",
 | 
			
		||||
  "version": "12.0.15",
 | 
			
		||||
  "download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-12.0.15.zip",
 | 
			
		||||
  "version": "12.0.18",
 | 
			
		||||
  "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",
 | 
			
		||||
  "changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
 | 
			
		||||
  "compatibility": {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
    <span class="competence-value">{{plusMoins arme.system.niveau}}</span>
 | 
			
		||||
    <span class="competence-value">{{plusMoins arme.system.dommagesReels}}</span>
 | 
			
		||||
    <span class="competence-value"></span>
 | 
			
		||||
    <span class="initiative-value 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>
 | 
			
		||||
  {{/each}}
 | 
			
		||||
  {{#each esquives as |esq key|}}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,17 @@
 | 
			
		||||
<form class="{{cssClass}}" autocomplete="off" >
 | 
			
		||||
  <section class="sheet-header">
 | 
			
		||||
    <div class="flexrow">
 | 
			
		||||
      <div class="flex-group-left flex-grow-0-5">
 | 
			
		||||
        <h1 class="charname">{{name}}
 | 
			
		||||
        </h1>
 | 
			
		||||
  <section class="sheet-header flexrow">
 | 
			
		||||
    <div class="flex-grow-0-5">
 | 
			
		||||
      <img class="profile-img" src="{{img}}" data-edit="img" data-tooltip="{{name}}" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="flexcol flex-grow-2">
 | 
			
		||||
      <div>
 | 
			
		||||
        <h1 class="charname">{{name}}</h1>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="flex-group-right flex-grow-3">
 | 
			
		||||
      <div>
 | 
			
		||||
        <a class="button-appel-chance"><img class="button-img" src="systems/foundryvtt-reve-de-dragon/styles/img/ui/appel-chance.svg" data-tooltip="Appel à la chance"/></a>
 | 
			
		||||
        <a class="button-encaissement"><img class="button-img" src="icons/svg/bones.svg" data-tooltip="Encaisser des dommages"/></a>
 | 
			
		||||
        <a class="button-ethylisme"><img class="button-img" src="icons/svg/tankard.svg" data-tooltip="Boire"/></a>
 | 
			
		||||
        <a class="button-repos"><img class="button-img" src="icons/svg/sleep.svg" data-tooltip="Se reposer"/></a>
 | 
			
		||||
        {{#if system.attributs.hautrevant.value}}
 | 
			
		||||
        <a class="button-tmr" data-tooltip="Montée dans les Terres Médianes !" {{#if hautreve.isDemiReve}}disabled{{/if}}>
 | 
			
		||||
          <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"/>
 | 
			
		||||
        </a>
 | 
			
		||||
        {{/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}}
 | 
			
		||||
        <a class="button-remise-a-neuf"><img class="button-img" src="icons/svg/regen.svg" data-tooltip="Remise à neuf"/></a>
 | 
			
		||||
        {{/if}}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,11 @@
 | 
			
		||||
  <div class="flexrow">
 | 
			
		||||
    <div>{{niveau}}</div>
 | 
			
		||||
    {{#if init}}
 | 
			
		||||
    <div>{{init}}</div>
 | 
			
		||||
    {{#if name}}
 | 
			
		||||
      <div><a class="roll-init-arme">{{init}}</a></div>
 | 
			
		||||
    {{else}}
 | 
			
		||||
      <div>{{init}}</div>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
    <div>{{dommages}}</div>
 | 
			
		||||
    {{else}}
 | 
			
		||||
    <div></div>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,6 @@
 | 
			
		||||
    <a class="{{button-name}}-plus"><i class="fa-regular fa-square-plus"></i></a>
 | 
			
		||||
  </div>
 | 
			
		||||
  <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>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,11 @@
 | 
			
		||||
  <div>
 | 
			
		||||
    {{upperFirst carac.colName}}
 | 
			
		||||
    <a class="{{carac.column}}-moins"><i class="fa-regular fa-square-minus"></i></a>
 | 
			
		||||
    {{#if (eq carac.value actuel.value)}}
 | 
			
		||||
    <i class="fa-regular fa-square"></i>
 | 
			
		||||
    {{else}}
 | 
			
		||||
    <a class="{{carac.column}}-plus"><i class="fa-regular fa-square-plus"></i></a>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="flex-group-right">
 | 
			
		||||
    <a class="{{actuel.rollClass}}" data-carac-name="{{actuel.column}}">{{carac.value}} {{#unless (eq carac.value actuel.value)}}(Actuel : {{actuel.value}}){{/unless}}</a>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
<div class="flexrow item" data-item-id="{{competence._id}}">
 | 
			
		||||
  <a class="roll-competence" name="Esquive">{{upperFirst name}}</a>
 | 
			
		||||
  <div class="flexrow">
 | 
			
		||||
    <div>{{niveau}}</div>
 | 
			
		||||
    <div>{{numberFormat competence.system.niveau decimals=0 sign=true}}</div>
 | 
			
		||||
    <div></div>
 | 
			
		||||
    <div></div>
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,11 @@
 | 
			
		||||
    {{#if etat.fatigue.malus}}
 | 
			
		||||
    ({{etat.fatigue.malus}})
 | 
			
		||||
    {{/if}}
 | 
			
		||||
    {{#if (eq etat.fatigue.value 0)}}
 | 
			
		||||
    <i class="fa-regular fa-square"></i>
 | 
			
		||||
    {{else}}
 | 
			
		||||
    <a class="fatigue-moins"><i class="fa-regular fa-square-minus"></i></a>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
    <a class="fatigue-plus"><i class="fa-regular fa-square-plus"></i></a>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="flex-group-right">
 | 
			
		||||
 
 | 
			
		||||
@@ -3,20 +3,22 @@
 | 
			
		||||
  <br>
 | 
			
		||||
  {{#if (eq attacker.type 'personnage')}}
 | 
			
		||||
    {{#unless essais.attaqueChance}}
 | 
			
		||||
      <a class='chat-card-button' id='appel-chance-attaque' data-attackerId='{{attackerId}}'
 | 
			
		||||
        data-defenderTokenId='{{defenderTokenId}}'>Faire appel à la chance</a>
 | 
			
		||||
      <a class='chat-card-button' id='appel-chance-attaque'
 | 
			
		||||
        data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
 | 
			
		||||
        Faire appel à la chance</a>
 | 
			
		||||
      </a>
 | 
			
		||||
      <br>
 | 
			
		||||
    {{/unless}}
 | 
			
		||||
    {{#if (gt attacker.system.compteurs.destinee.value 0)}}
 | 
			
		||||
      <a class='chat-card-button' id='appel-destinee-attaque' data-attackerId='{{attackerId}}'
 | 
			
		||||
        data-defenderTokenId='{{defenderTokenId}}'>Utiliser la destinée</a>
 | 
			
		||||
      <a class='chat-card-button' id='appel-destinee-attaque'
 | 
			
		||||
        data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
 | 
			
		||||
        Utiliser la destinée</a>
 | 
			
		||||
      </a>
 | 
			
		||||
      <br>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
  {{/if}}
 | 
			
		||||
  <a class='chat-card-button' id='echec-total-attaque' data-attackerId='{{attackerId}}'
 | 
			
		||||
    data-defenderTokenId='{{defenderTokenId}}'>
 | 
			
		||||
  <a class='chat-card-button' id='echec-total-attaque'
 | 
			
		||||
    data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
 | 
			
		||||
    Tirer la maladresse !
 | 
			
		||||
  </a>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -1,17 +1,17 @@
 | 
			
		||||
<div data-passearme="{{passeArme}}">
 | 
			
		||||
  {{#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}}
 | 
			
		||||
    <span><strong>{{defender.name}}</strong> doit :</span>
 | 
			
		||||
    <span><strong>{{defenderToken.name}}</strong> doit :</span>
 | 
			
		||||
  {{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 attaqueParticuliere}} <strong>particulière en
 | 
			
		||||
        {{~#if (eq attaqueParticuliere 'finesse')}} finesse
 | 
			
		||||
        {{else if (eq attaqueParticuliere 'force')}} force
 | 
			
		||||
        {{else if (eq attaqueParticuliere 'rapidite')}} rapidité
 | 
			
		||||
        {{/if~}}</strong>
 | 
			
		||||
      {{/if}} de {{attacker.name}} ({{attaqueArme.name}}):
 | 
			
		||||
      {{/if}} de {{attackerToken.name}} ({{attaqueArme.name}}):
 | 
			
		||||
    </span>
 | 
			
		||||
  {{/if}}
 | 
			
		||||
  <span class='chat-card-button-area'>
 | 
			
		||||
@@ -20,49 +20,64 @@
 | 
			
		||||
        {{#if essais.defense}}
 | 
			
		||||
          {{#unless essais.defenseChance}}
 | 
			
		||||
            {{#if (eq defender.type 'personnage')}}
 | 
			
		||||
            <a class='chat-card-button' id='appel-chance-defense' data-attackerId='{{attackerId}}'
 | 
			
		||||
              data-defenderTokenId='{{defenderTokenId}}'>Faire appel à la chance</a>
 | 
			
		||||
            </a>
 | 
			
		||||
            <br>
 | 
			
		||||
            {{/if}}
 | 
			
		||||
            {{#if (eq defender.type 'personnage')}}
 | 
			
		||||
            {{#if (gt defender.system.compteurs.destinee.value 0)}}
 | 
			
		||||
              <a class='chat-card-button' id='appel-destinee-defense' data-attackerId='{{attackerId}}'
 | 
			
		||||
                data-defenderTokenId='{{defenderTokenId}}'>Utiliser la destinée</a>
 | 
			
		||||
              <a class='chat-card-button' id='appel-chance-defense'
 | 
			
		||||
                data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
 | 
			
		||||
                Faire appel à la chance</a>
 | 
			
		||||
              </a>
 | 
			
		||||
              <br>
 | 
			
		||||
            {{/if}}
 | 
			
		||||
            {{#if (and (eq defender.type 'personnage') (gt defender.system.compteurs.destinee.value 0))}}
 | 
			
		||||
              <a class='chat-card-button' id='appel-destinee-defense'
 | 
			
		||||
                data-attackerId='{{attackerId}}' data-attackerTokenId='{{attackerToken.id}}' data-defenderTokenId='{{defenderToken.id}}'>
 | 
			
		||||
                Utiliser la destinée</a>
 | 
			
		||||
              </a>
 | 
			
		||||
              <br>
 | 
			
		||||
            {{/if}}
 | 
			
		||||
          {{/unless}}
 | 
			
		||||
        {{else}}
 | 
			
		||||
          {{#each armes as |arme key|}}
 | 
			
		||||
            <a class='chat-card-button' id='parer-button' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderTokenId}}'
 | 
			
		||||
                data-armeid='{{arme._id}}'>
 | 
			
		||||
              Parer avec {{arme.name}} à {{../diffLibre }}{{#if arme.system.nbUsage}} (Utilisations : {{arme.system.nbUsage}}){{/if}}
 | 
			
		||||
            </a>
 | 
			
		||||
            <br>
 | 
			
		||||
          {{/each}}
 | 
			
		||||
          {{#if mainsNues}}
 | 
			
		||||
            <a class='chat-card-button' id='parer-button' data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderTokenId}}'
 | 
			
		||||
                data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
 | 
			
		||||
              Parer à mains nues à {{diffLibre}}{{#if arme.system.nbUsage}} (Utilisations : {{arme.system.nbUsage}}){{/if}}
 | 
			
		||||
            <a class='chat-card-button' id='parer-button'
 | 
			
		||||
              data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
 | 
			
		||||
              data-armeid='{{arme._id}}'>
 | 
			
		||||
              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">×½</span>
 | 
			
		||||
              {{/if}}
 | 
			
		||||
              {{#if arme.nbUsage}}(Utilisations : {{arme.nbUsage}}){{/if}}
 | 
			
		||||
            </a>
 | 
			
		||||
            <br>
 | 
			
		||||
            {{/each}}
 | 
			
		||||
            {{#if mainsNues}}
 | 
			
		||||
            <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}}'>
 | 
			
		||||
            Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}}
 | 
			
		||||
          </a>
 | 
			
		||||
          <br>
 | 
			
		||||
          {{/if}}
 | 
			
		||||
          {{#if (ne attaqueCategorie 'tir')}}
 | 
			
		||||
            {{#each esquives as |esquive key|}}
 | 
			
		||||
            <a class='chat-card-button' id='esquiver-button' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderTokenId}}'
 | 
			
		||||
                data-compid='{{esquive._id}}'  data-competence='{{esquive.name}}'>
 | 
			
		||||
                {{log 'esquive' esquive}}
 | 
			
		||||
              {{esquive.name}} à {{../diffLibre}} {{#if esquive.system.nbUsage}} (Utilisations : {{esquive.system.nbUsage}}){{/if}}
 | 
			
		||||
          {{#each esquives as |esquive key|}}
 | 
			
		||||
            <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}}'>
 | 
			
		||||
              {{esquive.name}}
 | 
			
		||||
              {{#if (or (eq ../attaqueCategorie 'tir') (eq ../attaqueCategorie 'lancer'))}}
 | 
			
		||||
              (difficulté à déterminer)
 | 
			
		||||
              {{else}}à {{../diffLibre }}
 | 
			
		||||
              {{/if}}
 | 
			
		||||
              {{#if esquive.nbUsage}}(Utilisations : {{esquive.nbUsage}}){{/if}}
 | 
			
		||||
            </a>
 | 
			
		||||
            <br>
 | 
			
		||||
            {{/each}}
 | 
			
		||||
          {{/if}}
 | 
			
		||||
        {{/if}}
 | 
			
		||||
      {{/unless}}
 | 
			
		||||
    <a class='chat-card-button' id='encaisser-button' data-attackerId='{{attackerId}}'
 | 
			
		||||
      data-defenderTokenId='{{defenderTokenId}}'>
 | 
			
		||||
    <a class='chat-card-button' id='encaisser-button' 
 | 
			
		||||
      data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
 | 
			
		||||
      Encaisser à {{plusMoins dmg.total}}
 | 
			
		||||
      {{#if (eq dmg.mortalite 'non-mortel')~}}
 | 
			
		||||
      (non-mortel) !
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
{{#if isGM}}
 | 
			
		||||
{{log this}}
 | 
			
		||||
<span>
 | 
			
		||||
  {{#if (gt endurance 0)}}
 | 
			
		||||
  De plus, {{alias}} a perdu {{endurance}} points d'endurance
 | 
			
		||||
@@ -18,18 +17,18 @@
 | 
			
		||||
</h4>
 | 
			
		||||
<div>
 | 
			
		||||
  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}}, total: <span class="rdd-roll-echec">{{total}}</span>
 | 
			
		||||
  {{~/unless~}}, total: <span class="rdd-roll-echec">{{total}}</span>
 | 
			
		||||
  <br>  
 | 
			
		||||
  {{alias}} 
 | 
			
		||||
  {{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup
 | 
			
		||||
  {{else if mort}}vient de mourir
 | 
			
		||||
  {{else if blessure}}
 | 
			
		||||
    {{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}}
 | 
			
		||||
    {{else}}subit une contusion
 | 
			
		||||
    {{~else~}}subit une contusion
 | 
			
		||||
    {{~/if~}}
 | 
			
		||||
  {{else}}s'en sort sans une égratignure
 | 
			
		||||
  {{~else~}}s'en sort sans une égratignure
 | 
			
		||||
  {{~/if~}}
 | 
			
		||||
 | 
			
		||||
  {{~#unless (eq dmg.mortalite 'entiteincarnee')}}
 | 
			
		||||
@@ -39,11 +38,11 @@
 | 
			
		||||
  {{/unless~}}
 | 
			
		||||
  {{~#if (gt endurance 0)}}
 | 
			
		||||
  {{~#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 (ne dmg.mortalite 'entiteincarnee')}}
 | 
			
		||||
  {{#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}}
 | 
			
		||||
  {{/if}}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user