forked from public/foundryvtt-reve-de-dragon
		
	Compare commits
	
		
			84 Commits
		
	
	
		
			be132a9ff1
			...
			12.0.24
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 37c281b300 | |||
| 9b1a179a71 | |||
| 8be4c3343f | |||
| 6af5a85dc1 | |||
| c6d64e09d5 | |||
| 56c99c5044 | |||
| a7862a25e5 | |||
| f4b5a4a4d2 | |||
| 6a17586a6c | |||
| 40f7ef48c9 | |||
| 62dfa8a580 | |||
| e71088fe2f | |||
| b7cb0f0752 | |||
| 9b31f25640 | |||
| 2291eaf135 | |||
| a91264cd82 | |||
| 864194e3b4 | |||
| 2c836f17e7 | |||
| 444e52cb49 | |||
| 82b0537d43 | |||
| 1eea07c8a8 | |||
| de70ba8a3c | |||
| f0e02e2205 | |||
| 7d9ccfaf28 | |||
| 830cd1c200 | |||
| b9458c717e | |||
| 22f25b45ce | |||
| dca9505925 | |||
| 45e9e94646 | |||
| a0c7b87996 | |||
| a214256d69 | |||
| d2de8aa972 | |||
| 00ec86632f | |||
| 010234e001 | |||
| 39604afa32 | |||
| aad256bfee | |||
| 6ebd2ee576 | |||
| ac6e8b4689 | |||
| 387713e6f8 | |||
| c69cb3c13a | |||
| bda2e39e35 | |||
| cb2d462cfe | |||
| 5f8b5b104d | |||
| 9fe265165b | |||
| b92003ac1f | |||
| a1e4bfb529 | |||
| b4f0be5d64 | |||
| 5ff31d462f | |||
| be1d109def | |||
| 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 | |||
| a7e1ca0b07 | |||
| e2c4d93413 | |||
| 3888efc6aa | |||
| 5004774a15 | |||
| 226afc1680 | |||
| 582df7e290 | |||
| 501f1f2e4f | |||
| 90d46c6a78 | |||
| 174ef4256a | 
							
								
								
									
										63
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| name: Release Creation | ||||
|  | ||||
| on:  | ||||
|   release: | ||||
|     types: [published] | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - run: echo "💡 The ${{ gitea.repository }} repository will cloned to the runner." | ||||
|      | ||||
|     #- uses: actions/checkout@v3 | ||||
|     - uses: RouxAntoine/checkout@v3.5.4 | ||||
|  | ||||
|     # get part of the tag after the `v` | ||||
|     - name: Extract tag version number | ||||
|       id: get_version | ||||
|       uses: battila7/get-version-action@v2 | ||||
|  | ||||
|     # Substitute the Manifest and Download URLs in the module.json | ||||
|     - name: Substitute Manifest and Download Links For Versioned Ones | ||||
|       id: sub_manifest_link_version | ||||
|       uses: microsoft/variable-substitution@v1 | ||||
|       with: | ||||
|         files: 'system.json' | ||||
|       env: | ||||
|         version: ${{steps.get_version.outputs.version-without-v}} | ||||
|         url: https://www.uberwald.me/gitea/${{gitea.repository}} | ||||
|         manifest: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download//${{github.event.release.tag_name}}/system.json | ||||
|         download: https://www.uberwald.me/gitea/${{gitea.repository}}/releases/download/${{github.event.release.tag_name}}/rddsystem.zip | ||||
|      | ||||
|     - name: Set up Node.js | ||||
|       uses: actions/setup-node@v2 | ||||
|       with: | ||||
|         node-version: '18'  # Use the node version your project requires | ||||
|  | ||||
|     - name: Install dependencies | ||||
|       run: npm install | ||||
|        | ||||
|     - name: Build the compendiums | ||||
|       run: node ./tools/packCompendiumsToDist.mjs | ||||
|  | ||||
|     # Create a zip file with all files required by the module to add to the release | ||||
|     - run: | | ||||
|          apt update -y | ||||
|          apt install -y zip | ||||
|  | ||||
|     - run: zip -r ./rddsystem.zip system.json template.json README.md LICENSE.txt assets/ fonts/ icons lang/ module/ packs/ pic/ sounds/ styles/ templates/  | ||||
|      | ||||
|     - name: setup go | ||||
|       uses: actions/setup-go@v3 | ||||
|       with: | ||||
|         go-version: '>=1.20.1' | ||||
|      | ||||
|     - name: Use Go Action   | ||||
|       id: use-go-action | ||||
|       uses: https://gitea.com/actions/release-action@main | ||||
|       with: | ||||
|         files: |- | ||||
|           ./rddsystem.zip   | ||||
|           system.json | ||||
|         api_key: '${{secrets.ALLOW_PUSH_RELEASE}}' | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/scenes/YSpVuLeMCX9tAmgn-thumb.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/scenes/YSpVuLeMCX9tAmgn-thumb.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 7.3 KiB | 
							
								
								
									
										65
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,4 +1,69 @@ | ||||
| # 12.0 | ||||
| ## 12.0.24 - Les ajustements d'Astrobazzarh | ||||
| - amélioration | ||||
|   - meilleure gestion des noms des voies de draconic | ||||
|   - affichage du détail des sorts avec le nom de voie, 'court', la difficulté, le coût | ||||
| - corrections | ||||
|   - les tas dans les conteneurs peuvent être désempilés sans rendre le conteneur inutilisable | ||||
|   - les conteneurs ne peuvent plus être empilés (pour éviter que le contenu de Schroedinger quand on les sépare) | ||||
|   - on peut maintenant saisir et supprimer les bonus de cases de manière intuitive | ||||
|    | ||||
| ## 12.0.23 - La bibliothèque d'Astrobazzarh | ||||
| - corrections mineures | ||||
|   - meilleure gestion de la parade des armes naturelles | ||||
|   - cas de "User lacks permission to update" pour les blessures et les StatusEffects | ||||
|   - risque de message d'encaissement non affiché | ||||
|   - support de sorts à voies multiples | ||||
| - correction de compendiums | ||||
|   - résistance des armes mise à jour | ||||
|   - voies multiples pour las sorts de Lecture d'aura, Détection d'aura et Annulation de magie | ||||
| - améliorations "Scriptarium" | ||||
|   - recherche des compétences sans accents pour permettre les noms accentués (standard Scriptarium) | ||||
|   - affichage r1+ des sorts à coût variable dans la feuille simplifiée | ||||
|   - affichage de Corps à corps pour le combat à mains nues dans la feuille simplifiée | ||||
|   - dans les compendiums, les compétences Écriture et Épée ont une majuscule accentuée. Les Épée dans le compendium d'équipements référence le nom de compétence accentué. | ||||
|  | ||||
| ## 12.0.21 - La nomination d'Astrobazzarh | ||||
| - Les noms pour les messages dans le tchat sont maintenant ceux des tokens plutôt que ceux des acteurs | ||||
| - Fix: le choix des effets dans les options s'affiche correctement | ||||
|  | ||||
| ## 12.0.20 - Le tableau d'Astrobazzarh | ||||
| - Ecran d'accueil officiel Scriptarium | ||||
|  | ||||
| ## 12.0.19 - La témérité d'Astrobazzarh | ||||
| - Fix | ||||
|   - les défenses des créatures sont correctement filtrées | ||||
|   - le lancer d'initiative pour tous les personnages/PNJs fonctionne correctement | ||||
|   - les lieux et commerces n'ont pas d'initiative | ||||
|  | ||||
| ## 12.0.18 - A la barbe d'Astrobazzarh | ||||
| - Améliorations sur la feuille de PNJ simplifiée | ||||
|   - Ajout du portrait | ||||
|   - Ajout du corps à corps | ||||
|   - Affichage du niveau d'esquive | ||||
|   - Un clic sur l'initiative permet de lancer l'initiative | ||||
|   - les boutons +/- pour la vie, l'endurance et la fatigue changent si on est à la valeur normale | ||||
|   - un clic sur l'endurance effectue un jet d'endurance | ||||
| - Fix | ||||
|   - les achats des commerces sont de nouveau possibles | ||||
|   - la commande /astro fonctionne de nouveau | ||||
|   - le nombre d'utilisations d'items est réinitialisé à chaque round et fin de combat | ||||
|   - la difficulté de parade pour les armes à distances n'est plus indiquée | ||||
|   - les propositions d'armes de parade sont corrigées | ||||
| - Ajout d'un indicateur pour les armes de parade nécessitant une significative | ||||
|  | ||||
| ## 12.0.16 - Le secret d'Astrobazzarh | ||||
| - Fix: les jets envoyés messages uniquement au MJ ne sont plus envoyés à tous les autres joueurs (et dupliqués) | ||||
| - Les noms affichés dans les automatisations de combat sont maintenant ceux des tokens plutôt que ceux des acteurs | ||||
| - Ajout d'une option pour la localisation des blessures | ||||
|  | ||||
| ## 12.0.15 - Le messager d'Astrobazzarh | ||||
| - Correction des faces de dés personalisés dice-so-nice | ||||
| - Les messages de maladies ne sont plus publics | ||||
| - Les messages privés dans les TMR sont aussi envoyés au GM | ||||
| - Les informations de compétences pouvant augmenter s'affichent comme tooltips | ||||
| - Amélioration du rendu des tables de compendiums (commande /table) | ||||
|  | ||||
| ## 12.0.14 - Les légions d'Astrobazzarh | ||||
| - Feuille de PNJ: | ||||
|   - boutons standard (encaissement, ...) | ||||
|   | ||||
							
								
								
									
										61
									
								
								dev-notes.md
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								dev-notes.md
									
									
									
									
									
								
							| @@ -1,61 +0,0 @@ | ||||
| # Actor notes | ||||
|  | ||||
| > The Actor#getData default implementation gives you the following for use in sheet rendering: | ||||
|  | ||||
| ``` | ||||
|   actor -> the Actor instance | ||||
|   data -> a cloned copy of Actor#data | ||||
|   items -> a cloned copy of Actor#data#items | ||||
|   effects -> a cloned copy of Actor#data#effects | ||||
| ``` | ||||
|  | ||||
| > if all you need is a safe copy of `Actor#data`, you'll be much better off by simply defining your own function and avoiding all the wasted work that the parent class does which will slow down your sheet | ||||
| ```js | ||||
| getData(options) { | ||||
|   return { | ||||
|     data: foundry.utils.deepClone(this.object.data) | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| who knows, maybe you don't even need to copy your actor data, skip the copy and it's even faster: | ||||
| ```js | ||||
| getData(options) { | ||||
|   return { | ||||
|     data: this.object.data | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
|  | ||||
| Atropos19/02/2021 | ||||
| There are two recommended ways to create owned items in 0.8.0: | ||||
| ```js | ||||
| await Item.create(itemData, {parent: actor}); | ||||
| await actor.createEmbeddedDocuments("Item", itemDataArray); | ||||
| ``` | ||||
|  | ||||
|  | ||||
| You can update an embedded item in one of two ways: | ||||
| ```js | ||||
| //Method 1: | ||||
|  | ||||
| const item = actor.items.get(itemId); | ||||
| item.update(data); | ||||
|  | ||||
| //Method 2: | ||||
| actor.updateEmbeddedDocuments("Item", [{_id: itemId, ...}]); | ||||
| ``` | ||||
|  | ||||
|  | ||||
| I noticed adding an ActiveEffect to an actor in code using | ||||
|  | ||||
| ```js | ||||
| this.createEmbeddedDocuments('ActiveEffect', [effet], options); | ||||
| this.applyActiveEffects(); | ||||
| ``` | ||||
|  | ||||
| Atropos — Aujourd’hui à 14:42 | ||||
| Two notes on this: | ||||
| 1. You don't actually need to call this.applyActiveEffects() because this will happen automatically whenever an effect is created/updated/deleted | ||||
| 2. If you want to suppress the automatic display of the sheet for the newly created document, you can pass options.renderSheet = false as part of your options object | ||||
| @@ -80,7 +80,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet { | ||||
|       const actor = this.actor; | ||||
|       formData.combat = foundry.utils.duplicate(formData.armes); | ||||
|       RdDItemArme.computeNiveauArmes(formData.combat, formData.competences); | ||||
|       formData.combat.push(RdDItemArme.mainsNues(actor)); | ||||
|       formData.combat.push(RdDItemArme.corpsACorps(actor)); | ||||
|       formData.combat.push(RdDItemArme.empoignade(actor)); | ||||
|  | ||||
|       formData.esquives = this.actor.getCompetences("Esquive"); | ||||
| @@ -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")) | ||||
|   | ||||
							
								
								
									
										108
									
								
								module/actor.js
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								module/actor.js
									
									
									
									
									
								
							| @@ -36,6 +36,8 @@ import { ITEM_TYPES } from "./item.js"; | ||||
| import { RdDBaseActorSang } from "./actor/base-actor-sang.js"; | ||||
| import { RdDCoeur } from "./coeur/rdd-coeur.js"; | ||||
| import { DialogChoixXpCarac } from "./dialog-choix-xp-carac.js"; | ||||
| import { RdDItemArme } from "./item-arme.js"; | ||||
| import { RdDCombatManager } from "./rdd-combat.js"; | ||||
|  | ||||
| export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre'] | ||||
|  | ||||
| @@ -130,6 +132,23 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       .reduce(Misc.sum(), 0); | ||||
|   } | ||||
|  | ||||
|   listActionsCombat() { | ||||
|     // Recupération des armes | ||||
|     const actions = RdDCombatManager.listActionsArmes( | ||||
|       this.itemTypes[ITEM_TYPES.arme] | ||||
|         .filter(it => RdDItemArme.isAttaque(it)) | ||||
|         .concat(RdDItemArme.corpsACorps(this)) | ||||
|         .concat(RdDItemArme.empoignade(this)) | ||||
|       , | ||||
|       this.itemTypes[ITEM_TYPES.competence], | ||||
|       this.system.carac) | ||||
|  | ||||
|     if (this.system.attributs.hautrevant.value) { | ||||
|       actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } }); | ||||
|     } | ||||
|     return actions | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   getTache(id) { return this.findItemLike(id, 'tache') } | ||||
|   getMeditation(id) { return this.findItemLike(id, 'meditation') } | ||||
| @@ -159,10 +178,10 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     const potionUpdates = await Promise.all(potions.map(async it => { | ||||
|       const nouveauReve = Math.max(it.system.pr - 1, 0) | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|         whisper: ChatUtility.getOwners(this), | ||||
|         content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html`, { | ||||
|           pr: nouveauReve, | ||||
|           alias: this.name, | ||||
|           alias: this.getAlias(), | ||||
|           potionName: it.name, | ||||
|           potionImg: it.img | ||||
|         }) | ||||
| @@ -210,7 +229,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|   /* -------------------------------------------- */ | ||||
|   async grisReve(nbJours) { | ||||
|     let message = { | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: `${nbJours} jours de gris rêve sont passés. ` | ||||
|     }; | ||||
|     for (let i = 0; i < nbJours; i++) { | ||||
| @@ -264,7 +283,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|   async dormirChateauDormant() { | ||||
|     if (!ReglesOptionnelles.isUsing("chateau-dormant-gardien") || !this.system.sommeil || this.system.sommeil.nouveaujour) { | ||||
|       const message = { | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|         whisper: ChatUtility.getOwners(this), | ||||
|         content: "" | ||||
|       }; | ||||
|  | ||||
| @@ -400,7 +419,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|   /* -------------------------------------------- */ | ||||
|   async remiseANeuf() { | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: 'Remise à neuf de ' + this.name | ||||
|     }); | ||||
|     await this.supprimerBlessures(it => true); | ||||
| @@ -417,7 +436,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|   /* -------------------------------------------- */ | ||||
|   async dormir(heures, options = { grisReve: false, chateauDormant: false }) { | ||||
|     const message = { | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: this.name + ': ' | ||||
|     }; | ||||
|     const insomnie = this.system.sommeil?.insomnie || heures == 0; | ||||
| @@ -488,7 +507,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       else { | ||||
|         if (!ReglesOptionnelles.isUsing("recuperation-reve")) { | ||||
|           ChatMessage.create({ | ||||
|             whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|             whisper: ChatUtility.getOwners(this), | ||||
|             content: `Pas de récupération de rêve (${reve} points ignorés)` | ||||
|           }); | ||||
|           jetsReve.push(0); | ||||
| @@ -919,7 +938,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     await this.createEmbeddedDocuments('Item', [souffle]); | ||||
|     if (options.chat) { | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|         whisper: ChatUtility.getOwners(this), | ||||
|         content: this.name + " subit un Souffle de Dragon  : " + souffle.name | ||||
|       }); | ||||
|     } | ||||
| @@ -939,7 +958,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     await this.createEmbeddedDocuments('Item', [queue]); | ||||
|     if (options.chat) { | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|         whisper: ChatUtility.getOwners(this), | ||||
|         content: this.name + " subit une Queue de Dragon : " + queue.name | ||||
|       }); | ||||
|     } | ||||
| @@ -977,7 +996,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     let tmr = await TMRUtility.getTMRAleatoire(tmr => accessible(tmr) && !innaccessible.includes(tmr.coord)); | ||||
|     ChatMessage.create({ | ||||
|       content: `${raison} : ré-insertion aléatoire.`, | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name) | ||||
|       whisper: ChatUtility.getOwners(this) | ||||
|     }); | ||||
|     await this.forcerPositionTMRInconnue(tmr); | ||||
|     return tmr; | ||||
| @@ -1082,7 +1101,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     const jetMoral = await this._jetDeMoral(situation); | ||||
|     const finMessage = (jetMoral.succes ? messageReussi : messageManque) ?? (jetMoral.ajustement == 0 ? "Vous gardez votre moral" : jetMoral.ajustement > 0 ? "Vous gagnez du moral" : "Vous perdez du moral"); | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: `${finMessage} - jet ${jetMoral.succes ? "réussi" : "manqué"} en situation ${situation} (${jetMoral.jet}/${jetMoral.difficulte}).` | ||||
|     }); | ||||
|     return jetMoral.ajustement; | ||||
| @@ -1322,7 +1341,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     const etat = this.getEtatGeneral({ ethylisme: true }); | ||||
|     const nbDoses = Number(this.system.compteurs.ethylisme.nb_doses || 0); | ||||
|     const ethylismeData = { | ||||
|       alias: this.name, | ||||
|       alias: this.getAlias(), | ||||
|       actor: this, | ||||
|       vie: this.system.sante.vie.max, | ||||
|       alcool: alcool, | ||||
| @@ -1407,7 +1426,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     const perteDissolution = Math.max(0, Math.min(dissolution, conversion)); | ||||
|  | ||||
|     let stressRollData = { | ||||
|       alias: this.name, | ||||
|       alias: this.getAlias(), | ||||
|       selectedCarac: this.system.carac.reve, | ||||
|       rolled: stressRoll, | ||||
|       stress: fromStress, | ||||
| @@ -1419,7 +1438,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     }; | ||||
|  | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-transformer-stress.html`, stressRollData) | ||||
|     }); | ||||
|  | ||||
| @@ -1498,14 +1517,14 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|         carac.value = niveauSuivant; | ||||
|  | ||||
|         let checkXp = { | ||||
|           alias: this.name, | ||||
|           alias: this.getAlias(), | ||||
|           carac: caracName, | ||||
|           value: niveauSuivant, | ||||
|           xp: carac.xp | ||||
|         } | ||||
|         if (display) { | ||||
|           ChatMessage.create({ | ||||
|             whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|             whisper: ChatUtility.getOwners(this), | ||||
|             content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html`, checkXp) | ||||
|           }); | ||||
|         } | ||||
| @@ -1528,7 +1547,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|         newCompData.system.niveau += 1; | ||||
|         newCompData.system.xp = newXP; | ||||
|         let checkXp = { | ||||
|           alias: this.name, | ||||
|           alias: this.getAlias(), | ||||
|           competence: newCompData.name, | ||||
|           niveau: newCompData.system.niveau, | ||||
|           xp: newCompData.system.xp, | ||||
| @@ -1537,7 +1556,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|         } | ||||
|         if (display) { | ||||
|           ChatMessage.create({ | ||||
|             whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|             whisper: ChatUtility.getOwners(this), | ||||
|             content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html`, checkXp) | ||||
|           }); | ||||
|         } | ||||
| @@ -1548,7 +1567,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) | ||||
| @@ -1563,7 +1582,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       } | ||||
|       else { | ||||
|         ChatMessage.create({ | ||||
|           whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|           whisper: ChatUtility.getOwners(this), | ||||
|           content: content | ||||
|         }); | ||||
|       } | ||||
| @@ -1666,7 +1685,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       } | ||||
|       ChatMessage.create({ | ||||
|         content: "Vous êtes sous le coup d'une Mauvaise Rencontre en Persective." + addMsg, | ||||
|         whisper: ChatMessage.getWhisperRecipients(this.name) | ||||
|         whisper: ChatUtility.getOwners(this) | ||||
|       }); | ||||
|     } | ||||
|     return rencSpecial; | ||||
| @@ -1678,7 +1697,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     if (countInertieDraconique > 0) { | ||||
|       ChatMessage.create({ | ||||
|         content: `Vous êtes sous le coup d'Inertie Draconique : vous perdrez ${countInertieDraconique + 1} cases de Fatigue par déplacement au lieu d'une.`, | ||||
|         whisper: ChatMessage.getWhisperRecipients(this.name) | ||||
|         whisper: ChatUtility.getOwners(this) | ||||
|       }); | ||||
|     } | ||||
|     return countInertieDraconique + 1; | ||||
| @@ -1690,7 +1709,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       await this.reveActuelIncDec(-1); | ||||
|       ChatMessage.create({ | ||||
|         content: "Vous êtes sous le coup d'un Péage : l'entrée sur cette case vous a coûté 1 Point de Rêve (déduit automatiquement).", | ||||
|         whisper: ChatMessage.getWhisperRecipients(this.name) | ||||
|         whisper: ChatUtility.getOwners(this) | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| @@ -1765,7 +1784,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     } | ||||
|     const competence = this.getCompetence(compName); | ||||
|     let rollData = { | ||||
|       alias: this.name, | ||||
|       alias: this.getAlias(), | ||||
|       caracValue: Number(carac.value), | ||||
|       selectedCarac: carac, | ||||
|       competence: competence, | ||||
| @@ -1838,7 +1857,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       label: 'Jet ' + Grammar.apostrophe('de', competence.name), | ||||
|       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html', | ||||
|       rollData: { | ||||
|         alias: this.name, | ||||
|         alias: this.getAlias(), | ||||
|         carac: this.system.carac, | ||||
|         selectedCarac: this.getCaracByName(caracName), | ||||
|         selectedCaracName: caracName, | ||||
| @@ -2270,7 +2289,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       // Cas de désir lancinant, pas d'expérience sur particulière | ||||
|       ChatMessage.create({ | ||||
|         content: `Vous souffrez au moins d'un Désir Lancinant, vous ne pouvez pas gagner d'expérience sur une Particulière tant que le désir n'est pas assouvi`, | ||||
|         whisper: ChatMessage.getWhisperRecipients(this.name) | ||||
|         whisper: ChatUtility.getOwners(this) | ||||
|       }); | ||||
|       return [] | ||||
|     } | ||||
| @@ -2405,7 +2424,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     if (countMonteeLaborieuse > 0) { | ||||
|       ChatMessage.create({ | ||||
|         content: `Vous êtes sous le coup d'une Montée Laborieuse : vos montées en TMR coûtent ${countMonteeLaborieuse} Point de Rêve de plus.`, | ||||
|         whisper: ChatMessage.getWhisperRecipients(this.name) | ||||
|         whisper: ChatUtility.getOwners(this) | ||||
|       }); | ||||
|     } | ||||
|     return countMonteeLaborieuse; | ||||
| @@ -2453,7 +2472,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       if (this.getReveActuel() < minReveValue) { | ||||
|         ChatMessage.create({ | ||||
|           content: `Vous n'avez les ${minReveValue} Points de Reve nécessaires pour monter dans les Terres Médianes`, | ||||
|           whisper: ChatMessage.getWhisperRecipients(this.name) | ||||
|           whisper: ChatUtility.getOwners(this) | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
| @@ -2615,7 +2634,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async resetItemUse() { | ||||
|     await this.setFlag(SYSTEM_RDD, 'itemUse', {}); | ||||
|     await this.unsetFlag(SYSTEM_RDD, 'itemUse'); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -2850,7 +2869,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       await this.setBonusPotionSoin(potionData.system.herbebonus); | ||||
|     } | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-soin.html`, potionData) | ||||
|     }); | ||||
|   } | ||||
| @@ -2887,7 +2906,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       this.bonusRepos = potionData.system.herbebonus; | ||||
|     } | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-repos.html`, potionData) | ||||
|     }); | ||||
|   } | ||||
| @@ -2911,7 +2930,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|  | ||||
|     let newQuantite = herbeData.system.quantite - herbeData.nbBrins; | ||||
|     let messageData = { | ||||
|       alias: this.name, | ||||
|       alias: this.getAlias(), | ||||
|       nbBrinsReste: newQuantite, | ||||
|       potion: newPotion, | ||||
|       herbe: herbeData | ||||
| @@ -2919,7 +2938,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     this.diminuerQuantiteObjet(herbeData._id, herbeData.nbBrins); | ||||
|  | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-fabriquer-potion-base.html`, messageData) | ||||
|     }); | ||||
|   } | ||||
| @@ -2944,7 +2963,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       } | ||||
|     } | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-consommer-potion-generique.html`, potionData) | ||||
|     }); | ||||
|   } | ||||
| @@ -2998,20 +3017,21 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|       case 'queue': | ||||
|       case 'ombre': | ||||
|       case 'souffle': | ||||
|         await this.onDeleteOwnedDraconique(item, options, id); | ||||
|         break; | ||||
|         await this.onDeleteOwnedDraconique(item, options, id) | ||||
|         break | ||||
|       case 'casetmr': | ||||
|         await this.onDeleteOwnedCaseTmr(item, options, id); | ||||
|         break; | ||||
|         await this.onDeleteOwnedCaseTmr(item, options, id) | ||||
|         break | ||||
|       case 'empoignade': | ||||
|         await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item) | ||||
|         break; | ||||
|         break | ||||
|     } | ||||
|     super.onDeleteItem(item, options, id) | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   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 +3043,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 +3053,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) | ||||
| @@ -3044,7 +3064,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|   /* -------------------------------------------- */ | ||||
|   notifyGestionTeteSouffleQueue(item, manualMessage = true) { | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: `${this.name} a reçu un/une ${item.type}: ${item.name}, qui ${manualMessage ? "n'est pas" : "est"} géré(e) automatiquement. ${manualMessage ? manualMessage : ''}` | ||||
|     }); | ||||
|   } | ||||
| @@ -3067,7 +3087,7 @@ export class RdDActor extends RdDBaseActorSang { | ||||
|     incarnation.name = 'Réincarnation de ' + incarnation.name | ||||
|     incarnation.system = { | ||||
|       carac: foundry.utils.duplicate(this.system.carac), | ||||
|       heure: RdDTimestamp.defHeure(await RdDDice.rollHeure( { rollMode: "selfroll", showDice: SHOW_DICE })).key, | ||||
|       heure: RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "selfroll", showDice: SHOW_DICE })).key, | ||||
|       age: 18, | ||||
|       biographie: '', | ||||
|       notes: '', | ||||
|   | ||||
| @@ -15,7 +15,7 @@ import { StatusEffects } from "../settings/status-effects.js"; | ||||
| import { ITEM_TYPES } from "../item.js"; | ||||
| import { Targets } from "../targets.js"; | ||||
| import { RdDPossession } from "../rdd-possession.js"; | ||||
| import { RdDCombat } from "../rdd-combat.js"; | ||||
| import { RdDCombat, RdDCombatManager } from "../rdd-combat.js"; | ||||
| import { RdDConfirm } from "../rdd-confirm.js"; | ||||
| import { ENTITE_INCARNE, SHOW_DICE, SYSTEM_RDD } from "../constants.js"; | ||||
| import { RdDItemArme } from "../item-arme.js"; | ||||
| @@ -78,8 +78,27 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|   } | ||||
|   async jetEndurance(resteEndurance = undefined) { return { jetEndurance: 0, sonne: false } } | ||||
|   isDead() { return false } | ||||
|   isSonne() { return false } | ||||
|   blessuresASoigner() { return [] } | ||||
|   getEtatGeneral(options = { ethylisme: false }) { return 0 } | ||||
|   isActorCombat() { return true } | ||||
|  | ||||
|   getCaracInit(competence) { | ||||
|     if (!competence){ | ||||
|       return 0 | ||||
|     } | ||||
|     if (competence.type == ITEM_TYPES.competencecreature) { | ||||
|       return competence.system.carac_value | ||||
|     } | ||||
|     return this.system.carac[competence.system.defaut_carac].value; | ||||
|   } | ||||
|   listActionsCombat() { | ||||
|     return this.itemTypes[ITEM_TYPES.competencecreature] | ||||
|       .filter(it => RdDItemCompetenceCreature.isAttaque(it)) | ||||
|       .map(it => RdDItemCompetenceCreature.armeCreature(it)) | ||||
|       .filter(it => it != undefined); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   async computeArmure(attackerRoll) { return this.getProtectionNaturelle() } | ||||
|   async remiseANeuf() { } | ||||
| @@ -98,7 +117,7 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|     for (let effect of this.getEffects()) { | ||||
|       if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) { | ||||
|         await effect.delete(); | ||||
|         ChatMessage.create({ content: `${this.name} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` }); | ||||
|         ChatMessage.create({ content: `${this.getAlias()} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -153,9 +172,6 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|   getPossession(possessionId) { | ||||
|     return this.itemTypes[ITEM_TYPES.possession].find(it => it.system.possessionid == possessionId); | ||||
|   } | ||||
|   getPossessions() { | ||||
|     return this.itemTypes[ITEM_TYPES.possession]; | ||||
|   } | ||||
|   getEmpoignades() { | ||||
|     return this.itemTypes[ITEM_TYPES.empoignade]; | ||||
|   } | ||||
| @@ -189,7 +205,7 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|   getEffect(effectId) { | ||||
|     return this.getEmbeddedCollection("ActiveEffect").find(it => it.statuses?.has(effectId)); | ||||
|   } | ||||
|    | ||||
|  | ||||
|   async setEffect(effectId, status) { | ||||
|     if (this.isEffectAllowed(effectId)) { | ||||
|       const effect = this.getEffect(effectId); | ||||
| @@ -201,7 +217,7 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
|  | ||||
|   async removeEffect(id) { | ||||
|     const effect = this.getEmbeddedCollection("ActiveEffect").find(it => it.id == id); | ||||
|     if (effect) { | ||||
| @@ -272,7 +288,7 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|  | ||||
|     await this.openRollDialog({ | ||||
|       name: `jet-${this.id}`, | ||||
|       label: `Jet de ${this.name}`, | ||||
|       label: `Jet de ${this.getAlias()}`, | ||||
|       template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll.html', | ||||
|       rollData: { | ||||
|         carac: carac, | ||||
| @@ -296,7 +312,7 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async rollCarac(caracName, jetResistance = undefined) { | ||||
|     if (Grammar.equalsInsensitive(caracName, 'taille')){ | ||||
|     if (Grammar.equalsInsensitive(caracName, 'taille')) { | ||||
|       return | ||||
|     } | ||||
|     RdDEmpoignade.checkEmpoignadeEnCours(this) | ||||
| @@ -327,14 +343,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,9 +381,10 @@ 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)) { | ||||
|     if (!RdDItemArme.isUtilisable(arme)) { | ||||
|       ui.notifications.warn(`Arme inutilisable: ${arme.name} a une résistance de 0 ou moins`) | ||||
|       return | ||||
|     } | ||||
| @@ -385,7 +403,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 +413,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,69 +421,66 @@ 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); | ||||
|     }  | ||||
|     if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) { | ||||
|       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.getAlias(), | ||||
|       hasPlayerOwner: this.hasPlayerOwner, | ||||
|       show: show ?? {} | ||||
|     }); | ||||
|     }, { overwrite: false }); | ||||
|  | ||||
|     await ChatUtility.createChatWithRollMode(this.name, { | ||||
|       roll: encaissement.roll, | ||||
|       content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement) | ||||
|     }); | ||||
|     await ChatUtility.createChatWithRollMode( | ||||
|       { | ||||
|         roll: encaissement.roll, | ||||
|         content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement) | ||||
|       }, | ||||
|       this | ||||
|     ) | ||||
|  | ||||
|     if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) { | ||||
|       encaissement = foundry.utils.duplicate(encaissement); | ||||
|       encaissement.isGM = true; | ||||
|       encaissement = foundry.utils.duplicate(encaissement) | ||||
|       encaissement.isGM = true | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatMessage.getWhisperRecipients("GM"), | ||||
|         whisper: ChatUtility.getGMs(), | ||||
|         content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-encaissement.html', encaissement) | ||||
|       }); | ||||
|     } | ||||
| @@ -481,7 +496,7 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|     } | ||||
|     const rolled = await RdDResolutionTable.roll(this.getReveActuel(), - Number(entite.system.carac.niveau.value)); | ||||
|     const rollData = { | ||||
|       alias: this.name, | ||||
|       alias: this.getAlias(), | ||||
|       rolled: rolled, | ||||
|       entite: entite.name, | ||||
|       selectedCarac: this.system.carac.reve | ||||
| @@ -501,7 +516,7 @@ export class RdDBaseActorReve extends RdDBaseActor { | ||||
|   isEntiteAccordee(attacker) { return true } | ||||
|  | ||||
|   async setEntiteReveAccordee(actor) { | ||||
|     ui.notifications.error("Impossible de s'accorder à " + this.name + ": ce n'est pas une entité incarnée"); | ||||
|     ui.notifications.error("Impossible de s'accorder à " + this.getAlias() + ": ce n'est pas une entité incarnée"); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -39,8 +39,8 @@ export class RdDBaseActorSangSheet extends RdDBaseActorReveSheet { | ||||
|     ChatMessage.create({ | ||||
|       content: `Jet d'Endurance : ${result.jetEndurance} / ${endurance} | ||||
|         <br>${this.actor.name} a ${result.sonne ? 'échoué' : 'réussi'} son Jet d'Endurance ${result.sonne ? 'et devient Sonné' : ''}`, | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.actor.name) | ||||
|     }); | ||||
|       whisper: ChatUtility.getOwners(this.actor) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import { ITEM_TYPES } from "../item.js"; | ||||
| import { RdDBaseActorReve } from "./base-actor-reve.js"; | ||||
| import { RdDDice } from "../rdd-dice.js"; | ||||
| import { RdDItemBlessure } from "../item/blessure.js"; | ||||
| import { ChatUtility } from "../chat-utility.js"; | ||||
|  | ||||
| /** | ||||
|  * Classe de base pour les acteurs qui peuvent subir des blessures | ||||
| @@ -88,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()); | ||||
|  | ||||
| @@ -168,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) { | ||||
| @@ -180,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; | ||||
|     } | ||||
| @@ -190,15 +191,15 @@ export class RdDBaseActorSang extends RdDBaseActorReve { | ||||
|       encaissement.mort = true; | ||||
|       ChatMessage.create({ | ||||
|         content: `<img class="chat-icon" src="icons/svg/skull.svg" data-tooltip="charge" /> | ||||
|         <strong>${this.name} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>` | ||||
|         <strong>${this.getAlias()} vient de succomber à une seconde blessure critique ! Que les Dragons gardent son Archétype en paix !</strong>` | ||||
|       }); | ||||
|     } | ||||
|     return blessure; | ||||
|   } | ||||
|  | ||||
|   async supprimerBlessure({gravite}) { | ||||
|   async supprimerBlessure({ gravite }) { | ||||
|     const toDelete = this.itemTypes[ITEM_TYPES.blessure].find(it => it.system.gravite == gravite)?.id | ||||
|     if (toDelete){ | ||||
|     if (toDelete) { | ||||
|       await this.deleteEmbeddedDocuments('Item', [toDelete]); | ||||
|     } | ||||
|   } | ||||
| @@ -216,7 +217,10 @@ export class RdDBaseActorSang extends RdDBaseActorReve { | ||||
|   /* -------------------------------------------- */ | ||||
|   async jetDeVie() { | ||||
|     if (this.isDead()) { | ||||
|       ChatMessage.create({ content: `Jet de Vie: ${this.name} est déjà mort, ce n'est pas la peine d'en rajouter !!!!!`, whisper: ChatMessage.getWhisperRecipients(this.name) }); | ||||
|       ChatMessage.create({ | ||||
|         content: `Jet de Vie: ${this.getAlias()} est déjà mort, ce n'est pas la peine d'en rajouter !!!!!`, | ||||
|         whisper: ChatUtility.getOwners(this) | ||||
|       }) | ||||
|       return | ||||
|     } | ||||
|     const jetDeVie = await RdDDice.roll("1d20"); | ||||
| @@ -238,12 +242,15 @@ export class RdDBaseActorSang extends RdDBaseActorReve { | ||||
|       await this.santeIncDec("vie", -perte); | ||||
|     } | ||||
|     if (this.isDead()) { | ||||
|       msgText += `<br><strong>${this.name} est mort !!!!</strong>`; | ||||
|       msgText += `<br><strong>${this.getAlias()} est mort !!!!</strong>`; | ||||
|     } | ||||
|     else if (prochainJet > 0) { | ||||
|       msgText += `<br>Prochain jet de vie dans ${prochainJet} ${isCritique ? 'round' : 'minute'}${prochainJet > 1 ? 's' : ''} ${isCritique ? '(état critique)' : '(état grave)'}` | ||||
|     } | ||||
|     ChatMessage.create({ content: msgText, whisper: ChatMessage.getWhisperRecipients(this.name) }); | ||||
|     ChatMessage.create({ | ||||
|       content: msgText, | ||||
|       whisper: ChatUtility.getOwners(this) | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -266,14 +273,14 @@ export class RdDBaseActorSang extends RdDBaseActorReve { | ||||
|  | ||||
|   async setSonne(sonne = true) { | ||||
|     if (!game.combat && sonne) { | ||||
|       ui.notifications.info(`${this.name} est hors combat, il ne reste donc pas sonné`); | ||||
|       ui.notifications.info(`${this.getAlias()} 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 } | ||||
|   | ||||
| @@ -78,6 +78,13 @@ export class RdDBaseActor extends Actor { | ||||
|     return game.actors.get(actorId) | ||||
|   } | ||||
|  | ||||
|   getAlias() { | ||||
|     if (this.token?.name != null && this.token != this.prototypeToken) { | ||||
|       return this.token.name | ||||
|     } | ||||
|     return this.name | ||||
|   } | ||||
|  | ||||
|   isPersonnageJoueur() { return false } | ||||
|  | ||||
|   static extractActorMin = (actor) => { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img }; }; | ||||
| @@ -205,12 +212,21 @@ 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 onDeleteItem(item, options, id) { | ||||
|     if (item.isInventaire()) { | ||||
|       this._removeItemFromConteneur(item) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   _removeItemFromConteneur(item) { | ||||
|     this.items.filter(it => it.isConteneur() && it.system.contenu.includes(item.id)) | ||||
|       .forEach(conteneur => { | ||||
|         const nouveauContenu = conteneur.system.contenu.filter(id => id != item.id); | ||||
|         conteneur.update({ 'system.contenu': nouveauContenu }); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   async onTimeChanging(oldTimestamp, newTimestamp) { | ||||
|     this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp)) | ||||
| @@ -218,7 +234,7 @@ export class RdDBaseActor extends Actor { | ||||
|   } | ||||
|  | ||||
|   async creerObjetParMJ(object) { | ||||
|     if (!Misc.isUniqueConnectedGM()) { | ||||
|     if (!Misc.isFirstConnectedGM()) { | ||||
|       RdDBaseActor.remoteActorCall({ | ||||
|         tokenId: this.token?.id, | ||||
|         actorId: this.id, | ||||
| @@ -270,6 +286,7 @@ export class RdDBaseActor extends Actor { | ||||
|     } | ||||
|     let fortune = this.getFortune(); | ||||
|     console.log("payer", game.user.character, depense, fortune); | ||||
|     // TODO: passer en handlebars | ||||
|     let msg = ""; | ||||
|     if (fortune >= depense) { | ||||
|       await Monnaie.optimiserFortune(this, fortune - depense); | ||||
| @@ -279,11 +296,10 @@ export class RdDBaseActor extends Actor { | ||||
|       msg = "Vous n'avez pas assez d'argent pour payer cette somme !"; | ||||
|     } | ||||
|  | ||||
|     let message = { | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: msg | ||||
|     }; | ||||
|     ChatMessage.create(message); | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   async depenserSols(sols) { | ||||
| @@ -317,7 +333,7 @@ export class RdDBaseActor extends Actor { | ||||
|  | ||||
|       RdDAudio.PlayContextAudio("argent"); // Petit son | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(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 +351,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', | ||||
| @@ -370,8 +386,8 @@ export class RdDBaseActor extends Actor { | ||||
|     chatAchatItem.quantiteTotal = quantite; | ||||
|     ChatMessage.create({ | ||||
|       user: achat.userId, | ||||
|       speaker: { alias: (acheteur ?? vendeur).name }, | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       speaker: { alias: (acheteur ?? vendeur).getAlias() }, | ||||
|       whisper: ChatUtility.getOwners(this), | ||||
|       content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem) | ||||
|     }); | ||||
|  | ||||
| @@ -686,7 +702,7 @@ export class RdDBaseActor extends Actor { | ||||
|       type: this.type, | ||||
|       img: this.img, | ||||
|       pack: this.pack, | ||||
|       name: this.name, | ||||
|       name: this.getAlias(), | ||||
|       system: { description: this.system.description } | ||||
|     } | ||||
|     renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.html', chatData) | ||||
| @@ -694,10 +710,10 @@ export class RdDBaseActor extends Actor { | ||||
|   } | ||||
|  | ||||
|   actionImpossible(action) { | ||||
|     ui.notifications.info(`${this.name} ne peut pas faire cette action: ${action}`) | ||||
|     ui.notifications.info(`${this.getAlias()} ne peut pas faire cette action: ${action}`) | ||||
|  | ||||
|   } | ||||
|   async roll() { this.actionImpossible("jet de caractéristiques") } | ||||
|  | ||||
|   async jetEthylisme() { this.actionImpossible("jet d'éthylisme") } | ||||
|   async rollAppelChance() { this.actionImpossible("appel à la chance") } | ||||
|   async jetDeMoral() { this.actionImpossible("jet de moral") } | ||||
| @@ -708,5 +724,24 @@ 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 } | ||||
|   getCaracInit(competence) { return 0 } | ||||
|   listActionsCombat() { return [] } | ||||
|   listActionsPossessions() { | ||||
|     return this.itemTypes[ITEM_TYPES.possession] | ||||
|       .map(p => { | ||||
|         return { | ||||
|           name: p.name, | ||||
|           action: 'possession', | ||||
|           system: { | ||||
|             competence: p.name, | ||||
|             possessionid: p.system.possessionid, | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|   } | ||||
| } | ||||
| @@ -73,7 +73,7 @@ export class RdDCommerceSheet extends RdDBaseActorSheet { | ||||
|     } | ||||
|     const disponible = this.actor.getQuantiteDisponible(item) | ||||
|     if (disponible == 0) { | ||||
|       ui.notifications.warn(`${this.name} n'a plus de ${item.name} en vente`); | ||||
|       ui.notifications.warn(`${this.getAlias()} n'a plus de ${item.name} en vente`); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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,45 +140,53 @@ 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), []) | ||||
|     armes.push(RdDItemArme.corpsACorps(actor)); | ||||
|     armes.push(RdDItemArme.empoignade(actor)); | ||||
|     return armes.map(arme => [ | ||||
|       arme.system.unemain ? Mapping.prepareArme(actor, arme, 'unemain') : undefined, | ||||
|       arme.system.deuxmains ? Mapping.prepareArme(actor, arme, 'deuxmains') : undefined, | ||||
|       !(arme.system.unemain || arme.system.deuxmains) ? Mapping.prepareArme(actor, arme, 'competence') : undefined, | ||||
|       arme.system.lancer != "" ? Mapping.prepareArme(actor, arme, 'lancer') : undefined, | ||||
|       arme.system.tir != "" ? Mapping.prepareArme(actor, arme, 'tir') : undefined] | ||||
|       .filter(it => it != undefined)) | ||||
|       .reduce((a, b) => a.concat(b), []) | ||||
|   } | ||||
|  | ||||
|   static prepareArme(actor, arme, maniement) { | ||||
|     const nameCompArme = RdDItemArme.getCompetenceArme(arme, maniement) | ||||
|     const competence = actor.getCompetence(nameCompArme) | ||||
|     const nameCompetenceArme = RdDItemArme.getCompetenceArme(arme, maniement) | ||||
|     const competence = actor.getCompetence(nameCompetenceArme) | ||||
|     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) { | ||||
| @@ -230,7 +238,7 @@ export class Mapping { | ||||
|  | ||||
|   static prepareSorts(actor) { | ||||
|     const codeVoies = Mapping.getCompetencesCategorie(actor, CATEGORIES_DRACONIC) | ||||
|       .map(it => RdDItemSort.getVoieCode(it)) | ||||
|       .map(it => RdDItemSort.getCodeVoie(it.name)) | ||||
|  | ||||
|     return actor.itemTypes[ITEM_TYPES.sort].map(it => Mapping.prepareSort(it, codeVoies)) | ||||
|       .sort(Misc.ascending(it => `${it.voie} : ${it.description}`)) | ||||
| @@ -238,7 +246,7 @@ export class Mapping { | ||||
|  | ||||
|   static prepareSort(sort, voies) { | ||||
|     return { | ||||
|       voie: RdDItemSort.getCodeDraconic(sort, voies), | ||||
|       voie: RdDItemSort.getCode(sort, voies), | ||||
|       description: Mapping.descriptionSort(sort), | ||||
|       bonus: Mapping.bonusCase(sort) | ||||
|     } | ||||
| @@ -247,21 +255,13 @@ export class Mapping { | ||||
|   static descriptionSort(sort) { | ||||
|     const ptSeuil = Array(sort.system.coutseuil).map(it => '*') | ||||
|     const caseTMR = sort.system.caseTMRspeciale.length > 0 ? Mapping.toVar(sort.system.caseTMRspeciale) : Misc.upperFirst(TMRType[sort.system.caseTMR].name) | ||||
|     const ptreve = Mapping.addSpaceToNonNumeric(sort.system.ptreve) | ||||
|     const diff = Mapping.addSpaceToNonNumeric(sort.system.difficulte) | ||||
|     return `${sort.name}${ptSeuil} (${caseTMR}) R${diff} r${ptreve}` | ||||
|   } | ||||
|  | ||||
|   static addSpaceToNonNumeric(value) { | ||||
|     return Number.isNumeric(value) ? value : ' ' + Mapping.toVar(value) | ||||
|   } | ||||
|  | ||||
|   static toVar(value) { | ||||
|     return value.replace('variable', 'var') | ||||
|     const coutReve = 'r' + RdDItemSort.addSpaceToNonNumeric(sort.system.ptreve) | ||||
|     const diff = 'R' + RdDItemSort.addSpaceToNonNumeric(sort.system.difficulte) | ||||
|     return `${sort.name}${ptSeuil} (${caseTMR}) ${diff} ${coutReve}` | ||||
|   } | ||||
|  | ||||
|   static bonusCase(sort) { | ||||
|     const list = RdDItemSort.bonuscaseStringToList(sort.system.bonuscase).sort(Misc.descending(it => it.bonus)) | ||||
|     const list = RdDItemSort.stringToBonuscases(sort.system.bonuscase).sort(Misc.descending(it => it.bonus)) | ||||
|     if (list.length > 0) { | ||||
|       const bonus = list[0] | ||||
|       return `+${bonus.bonus}% en ${bonus.case}` | ||||
| @@ -327,7 +327,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 '' | ||||
|         } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ const PATHS = [ | ||||
|  | ||||
| const RANDOM_VALUES = { | ||||
|   'system.sexe': { 'masculin': 1, 'féminin': 1 }, | ||||
|   'system.main': { 'droitier': 51, 'gaucher': 15, 'ambidectre': 6 }, | ||||
|   'system.main': { 'droitier': 51, 'gaucher': 15, 'ambidextre': 6 }, | ||||
|   'system.cheveux': { 'noirs': 2, 'bruns': 5, 'châtains clair': 5, 'blonds': 4, 'blonds très clair': 1, 'roux carotte': 1, 'roux cuivré': 3 }, | ||||
|   'system.yeux': { 'noirs': 2, 'noisettes': 3, 'bruns vert': 4, 'verts': 3, 'bleus clair': 3, 'bleus gris': 2, 'gris': 1, 'mauves': 1, 'indigos': 1 }, | ||||
| } | ||||
|   | ||||
							
								
								
									
										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
											
										
									
								
							| @@ -8,15 +8,20 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js"; | ||||
|  */ | ||||
| export class ChatUtility { | ||||
|  | ||||
|   static async init() { | ||||
|     Hooks.on("renderChatMessage", async (app, html, msg) => await ChatUtility.onRenderChatMessage(app, html, msg)) | ||||
|     Hooks.on("createChatMessage", async (chatMessage, options, id) => await ChatUtility.onCreateChatMessage(chatMessage, options, id)) | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static onSocketMessage(sockmsg) { | ||||
|     switch (sockmsg.msg) { | ||||
|       case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.data); | ||||
|       case "msg_user_ui_notifications": return ChatUtility.onNotifyUser(sockmsg.data); | ||||
|       case "msg_gm_chat_message": return ChatUtility.handleGMChatMessage(sockmsg.data) | ||||
|       case "msg_delete_chat_message": return ChatUtility.onRemoveMessages(sockmsg.data) | ||||
|       case "msg_user_ui_notifications": return ChatUtility.onNotifyUser(sockmsg.data) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static notifyUser(userId, level = 'info', message) { | ||||
|     const socketData = { | ||||
| @@ -44,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()); | ||||
| @@ -58,7 +63,7 @@ export class ChatUtility { | ||||
|   /* -------------------------------------------- */ | ||||
|  | ||||
|   static removeMessages(socketData) { | ||||
|     if (Misc.isUniqueConnectedGM()) { | ||||
|     if (Misc.isFirstConnectedGM()) { | ||||
|       ChatUtility.onRemoveMessages(socketData); | ||||
|     } | ||||
|     else { | ||||
| @@ -78,73 +83,90 @@ export class ChatUtility { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async createChatWithRollMode(name, chatOptions) { | ||||
|     let rollMode = game.settings.get("core", "rollMode") | ||||
|     switch (rollMode) { | ||||
|   static async createChatWithRollMode(messageData, actor = undefined) { | ||||
|     switch (game.settings.get("core", "rollMode")) { | ||||
|       case "blindroll": // GM only | ||||
|         if (!game.user.isGM) { | ||||
|           ChatUtility.blindMessageToGM(chatOptions); | ||||
|  | ||||
|           chatOptions.whisper = [game.user.id]; | ||||
|           chatOptions.content = "Message envoyé en aveugle au Gardien"; | ||||
|           ChatUtility.blindMessageToGM(messageData) | ||||
|           messageData.whisper = [game.user]; | ||||
|           messageData.content = "Message envoyé en aveugle au Gardien" | ||||
|         } | ||||
|         else { | ||||
|           chatOptions.whisper = ChatUtility.getUsers(user => user.isGM); | ||||
|           messageData.whisper = ChatUtility.getGMs() | ||||
|         } | ||||
|         break; | ||||
|       default: | ||||
|         chatOptions.whisper = ChatUtility.getWhisperRecipients(rollMode, name); | ||||
|         break; | ||||
|         break | ||||
|       case "gmroll": | ||||
|         messageData.whisper = ChatUtility.getOwners(actor) | ||||
|         break | ||||
|       case "selfroll": | ||||
|         messageData.whisper = [game.user] | ||||
|         break | ||||
|     } | ||||
|     chatOptions.alias = chatOptions.alias || name; | ||||
|     return await ChatMessage.create(chatOptions); | ||||
|     messageData.alias = messageData.alias ?? actor?.name ?? game.user.name | ||||
|     return await ChatMessage.create(messageData) | ||||
|   } | ||||
|  | ||||
|   static getOwners(document) { | ||||
|     return game.users.filter(it => document.getUserLevel(it) == CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) | ||||
|   } | ||||
|  | ||||
|   static getUserAndGMs() { | ||||
|     return [game.user, ...ChatUtility.getGMs()] | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static prepareChatMessage(rollMode, name) { | ||||
|     return { | ||||
|       user: game.user.id, | ||||
|       whisper: ChatUtility.getWhisperRecipients(rollMode, name) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getWhisperRecipients(rollMode, name) { | ||||
|     switch (rollMode) { | ||||
|       case "blindroll": return ChatUtility.getUsers(user => user.isGM); | ||||
|       case "gmroll": return ChatUtility.getWhisperRecipientsAndGMs(name); | ||||
|       case "selfroll": return [game.user.id]; | ||||
|     } | ||||
|     return undefined; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getWhisperRecipientsAndGMs(...names) { | ||||
|     let recipients = [...ChatMessage.getWhisperRecipients('GM')] | ||||
|     names.forEach(name => recipients.push(...ChatMessage.getWhisperRecipients(name))) | ||||
|     return recipients | ||||
|   static getMultipleActorsOwners(...actors) { | ||||
|     return Misc.concat(actors.map(it => it == undefined ? [] : ChatUtility.getOwners(it))) | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getUsers(filter) { | ||||
|     return game.users.filter(filter).map(user => user.id); | ||||
|     return game.users.filter(filter) | ||||
|   } | ||||
|  | ||||
|   static getGMs() { | ||||
|     return game.users.filter(user => user.isGM) | ||||
|   } | ||||
|  | ||||
|   static applyRollMode(chatMessageData = {}, rollMode = game.settings.get("core", "rollMode")) { | ||||
|     switch (rollMode) { | ||||
|       case "blindroll": | ||||
|         chatMessageData.blind = true | ||||
|         chatMessageData.whisper = ChatUtility.getGMs() | ||||
|         break | ||||
|       case "gmroll": | ||||
|         chatMessageData.whisper = ChatUtility.getGMs() | ||||
|         chatMessageData.blind = false | ||||
|         break | ||||
|       case "roll": | ||||
|         chatMessageData.whisper = ChatUtility.getUsers(user => user.active) | ||||
|         chatMessageData.blind = false | ||||
|         break | ||||
|       case "selfroll": | ||||
|         chatMessageData.whisper = [game.user] | ||||
|         chatMessageData.blind = false | ||||
|         break | ||||
|     } | ||||
|     return chatMessageData | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static blindMessageToGM(chatOptions) { | ||||
|     let chatGM = foundry.utils.duplicate(chatOptions); | ||||
|     chatGM.whisper = ChatUtility.getUsers(user => user.isGM); | ||||
|     chatGM.content = "Message aveugle de " + game.user.name + "<br>" + chatOptions.content; | ||||
|     console.log("blindMessageToGM", chatGM); | ||||
|     game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_gm_chat_message", data: chatGM }); | ||||
|     const chatGM = foundry.utils.duplicate(chatOptions) | ||||
|     chatGM.content = "Message aveugle de " + game.user.name + "<br>" + chatOptions.content | ||||
|     console.log("blindMessageToGM", chatGM) | ||||
|     game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_gm_chat_message", data: chatGM }) | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static handleGMChatMessage(socketData) { | ||||
|     console.log("blindMessageToGM", socketData); | ||||
|     if (game.user.isGM) { // message privé pour GM only | ||||
|       socketData.user = game.user.id; | ||||
|       ChatMessage.create(socketData); | ||||
|     if (Misc.isFirstConnectedGM()) { | ||||
|       ChatMessage.create({ | ||||
|         user: game.user.id, | ||||
|         whisper: ChatUtility.getGMs(), | ||||
|         content: socketData.content | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -30,7 +30,8 @@ export class RdDCoeur { | ||||
|   } | ||||
|  | ||||
|   static extractInfoCoeur(event) { | ||||
|     return ChatUtility.getMessageData(ChatUtility.getChatMessage(event), INFO_COEUR) | ||||
|     const chatMesage = ChatUtility.getChatMessage(event); | ||||
|     return ChatUtility.getMessageData(chatMesage, INFO_COEUR) | ||||
|   } | ||||
|  | ||||
|   static getInfoCoeur(sourceActorId, targetActorId) { | ||||
| @@ -98,12 +99,11 @@ export class RdDCoeur { | ||||
|  | ||||
|   static async startSubActeurTendreMoment(actorId, subActeurId) { | ||||
|     const infoCoeur = RdDCoeur.getInfoCoeur(actorId, subActeurId) | ||||
|     if (infoCoeur.target?.actor.id) { | ||||
|     if (infoCoeur.target?.actor?.id) { | ||||
|       // TODO: passer par une fenêtre pour saisir sa proposition (lieu, heure, ...) | ||||
|       const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-proposer-tendre-moment.hbs`, infoCoeur) | ||||
|       const chatMessage = await ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.target?.actor.name), | ||||
|         content: chatHtml | ||||
|         whisper: ChatUtility.getOwners(infoCoeur.target.actor), | ||||
|         content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-proposer-tendre-moment.hbs`, infoCoeur) | ||||
|       }) | ||||
|       RdDCoeur.addTagsInfoCoeur(infoCoeur, chatMessage) | ||||
|     } | ||||
| @@ -127,7 +127,7 @@ export class RdDCoeur { | ||||
|     } | ||||
|     const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-accepter-tendre-moment.hbs`, infoCoeur) | ||||
|     const chatMessage = await ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.source?.actor.name, infoCoeur.target?.actor.name), | ||||
|       whisper: ChatUtility.getMultipleActorsOwners(infoCoeur.source?.actor, infoCoeur.target?.actor), | ||||
|       content: chatHtml | ||||
|     }) | ||||
|     RdDCoeur.addTagsInfoCoeur(infoCoeur, chatMessage) | ||||
| @@ -142,7 +142,7 @@ export class RdDCoeur { | ||||
|     ChatUtility.removeChatMessageId(infoCoeur.chatMessageId) | ||||
|     const chatHtml = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/coeur/chat-refuser-tendre-moment.hbs`, infoCoeur) | ||||
|     await ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(infoCoeur.source?.actor.name, infoCoeur.target?.actor.name), | ||||
|       whisper: ChatUtility.getMultipleActorsOwners(infoCoeur.source?.actor, infoCoeur.target?.actor), | ||||
|       content: chatHtml | ||||
|     }); | ||||
|   } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ const LATEST_USED_JOURNAL_ID = "chronologie-dernier-journal"; | ||||
|  | ||||
| export class DialogChronologie extends Dialog { | ||||
|  | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     game.settings.register(SYSTEM_RDD, LATEST_USED_JOURNAL_ID, { | ||||
|       name: "Dernier article de journal utilisé pour enregistrer la chronologie", | ||||
|       scope: "client", | ||||
|   | ||||
| @@ -48,10 +48,10 @@ export class DialogCreateSigneDraconique extends Dialog { | ||||
|   async _createSigneForActor(actor, signe) { | ||||
|     actor.createEmbeddedDocuments("Item", [signe]); | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name), | ||||
|       whisper: ChatUtility.getOwners(actor), | ||||
|       content: await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-actor.html", { | ||||
|         signe: signe, | ||||
|         alias: actor.name | ||||
|         alias: actor.getAlias() | ||||
|       }) | ||||
|     }); | ||||
|   } | ||||
|   | ||||
| @@ -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) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import { CompendiumTableHelpers, CompendiumTable, SystemCompendiums } from "./se | ||||
| const COMPENDIUMS_RECHERCHE = 'compendiums-recherche'; | ||||
|  | ||||
| export class Environnement { | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     game.settings.register(SYSTEM_RDD, COMPENDIUMS_RECHERCHE, { | ||||
|       name: COMPENDIUMS_RECHERCHE, | ||||
|       default: [ | ||||
|   | ||||
| @@ -24,7 +24,7 @@ export class Grammar { | ||||
|   } | ||||
|    | ||||
|   static includesLowerCaseNoAccent(value, content) { | ||||
|     return Grammar.toLowerCaseNoAccent(value).includes(Grammar.toLowerCaseNoAccent(content)); | ||||
|     return Grammar.toLowerCaseNoAccent(value)?.includes(Grammar.toLowerCaseNoAccent(content)); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| import { Grammar } from "./grammar.js"; | ||||
| import { RdDItemCompetenceCreature } from "./item-competencecreature.js" | ||||
| import { ITEM_TYPES } from "./item.js"; | ||||
| import { RdDCombatManager } from "./rdd-combat.js"; | ||||
|  | ||||
| const nomCategorieParade = { | ||||
|   "sans-armes": "Sans arme", | ||||
|   "armes-naturelles": "Sans arme", | ||||
|   "armes-naturelles": "Armes naturelles", | ||||
|   "hast": "Armes d'hast", | ||||
|   "batons": "Bâtons", | ||||
|   "boucliers": "Boucliers", | ||||
| @@ -30,7 +31,7 @@ export class RdDItemArme extends Item { | ||||
|       case ITEM_TYPES.competencecreature: | ||||
|         return RdDItemCompetenceCreature.armeCreature(arme); | ||||
|     } | ||||
|     return RdDItemArme.mainsNues(); | ||||
|     return RdDItemArme.corpsACorps(); | ||||
|   } | ||||
|  | ||||
|   static getCompetenceArme(arme, maniement) { | ||||
| @@ -56,8 +57,8 @@ export class RdDItemArme extends Item { | ||||
|   } | ||||
|  | ||||
|   static niveauCompetenceArme(arme, competences) { | ||||
|     const compArme = competences.find(it => it.name == arme.system.competence); | ||||
|     return compArme?.system.niveau ?? -8; | ||||
|     const compArme = competences.find(it => Grammar.equalsInsensitive(it.name, arme.system.competence)) | ||||
|     return compArme?.system.niveau ?? -8 | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -81,37 +82,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 +143,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; | ||||
| @@ -184,7 +207,7 @@ export class RdDItemArme extends Item { | ||||
|     return arme.system.competence.replace(" 1 main", " 2 mains"); | ||||
|   } | ||||
|  | ||||
|   static isArmeUtilisable(arme) { | ||||
|   static isUtilisable(arme) { | ||||
|     switch (arme.type) { | ||||
|       case ITEM_TYPES.arme: return arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0) | ||||
|       case ITEM_TYPES.competencecreature: return true | ||||
| @@ -192,9 +215,24 @@ export class RdDItemArme extends Item { | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   static ajoutCorpsACorps(armes, actor) { | ||||
|     armes.push(RdDItemArme.mainsNues(actor)); | ||||
|     armes.push(RdDItemArme.empoignade(actor)); | ||||
|   static isAttaque(arme) { | ||||
|     switch (arme.type) { | ||||
|       case ITEM_TYPES.arme: | ||||
|         return arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0) | ||||
|       case ITEM_TYPES.competencecreature: | ||||
|         return arme.system.iscombat && RdDItemCompetenceCreature.isAttaque(item) | ||||
|     } | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   static isParade(arme) { | ||||
|     switch (arme.type) { | ||||
|       case ITEM_TYPES.arme: | ||||
|         return arme.system.equipe && arme.system.resistance > 0 && true/* TODO: regarder la categorie d'arme?*/ | ||||
|       case ITEM_TYPES.competencecreature: | ||||
|         return arme.system.iscombat && RdDItemCompetenceCreature.isParade(arme) | ||||
|     } | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   static corpsACorps(actor) { | ||||
| @@ -215,6 +253,8 @@ export class RdDItemArme extends Item { | ||||
|         mortalite: 'non-mortel', | ||||
|         competence: 'Corps à corps', | ||||
|         resistance: 1, | ||||
|         baseInit: 4, | ||||
|         cac: 'pugilat', | ||||
|         deuxmains: true, | ||||
|         categorie_parade: 'sans-armes' | ||||
|       } | ||||
| @@ -224,8 +264,6 @@ export class RdDItemArme extends Item { | ||||
|   static mainsNues(actor) { | ||||
|     const mainsNues = RdDItemArme.corpsACorps(actor) | ||||
|     mainsNues.name = 'Mains nues' | ||||
|     mainsNues.system.cac = 'pugilat' | ||||
|     mainsNues.system.baseInit = 4 | ||||
|     return mainsNues; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   | ||||
| @@ -27,40 +27,29 @@ export class RdDItemCompetenceCreature extends Item { | ||||
|   static armeCreature(item) { | ||||
|     const categorieAttaque = RdDItemCompetenceCreature.getCategorieAttaque(item) | ||||
|     if (categorieAttaque != undefined) { | ||||
|       // si c'est un Item compétence: cloner pour ne pas modifier la compétence | ||||
|       let arme = item.clone(); | ||||
|       return foundry.utils.mergeObject(arme, { | ||||
|       // cloner pour ne pas modifier la compétence | ||||
|       return foundry.utils.mergeObject(item, { | ||||
|           action: item.isCompetencePossession() ? 'possession' : 'attaque', | ||||
|           system: { | ||||
|             competence: arme.name, | ||||
|             competence: item.name, | ||||
|             cac: categorieAttaque == "naturelle" ? "naturelle" : "", | ||||
|             niveau: item.system.niveau, | ||||
|             initiative: RdDCombatManager.calculInitiative(item.system.niveau, item.system.carac_value), | ||||
|             equipe: true, | ||||
|             resistance: 100, | ||||
|             dommagesReels: arme.system.dommages, | ||||
|             dommagesReels: item.system.dommages, | ||||
|             penetration: 0, | ||||
|             force: 0, | ||||
|             rapide: true, | ||||
|           } | ||||
|         }, { inplace: false }); | ||||
|         }, { inplace: false, }); | ||||
|     } | ||||
|     return undefined; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static isCompetenceAttaque(item) { | ||||
|     if (item.type == ITEM_TYPES.competencecreature) { | ||||
|       switch (item.system.categorie) { | ||||
|         case "melee": | ||||
|         case "tir": | ||||
|         case "lancer": | ||||
|         case "naturelle": | ||||
|         case "possession": | ||||
|           return true | ||||
|       } | ||||
|     } | ||||
|     return undefined | ||||
|   static isAttaque(item) { | ||||
|     return RdDItemCompetenceCreature.getCategorieAttaque(item) != undefined | ||||
|   } | ||||
|  | ||||
|   static getCategorieAttaque(item) { | ||||
| @@ -71,12 +60,12 @@ export class RdDItemCompetenceCreature extends Item { | ||||
|         case "lancer": | ||||
|         case "naturelle": | ||||
|         case "possession": | ||||
|         case "parade": | ||||
|           return item.system.categorie | ||||
|       } | ||||
|     } | ||||
|     return undefined | ||||
|   } | ||||
|  | ||||
|   static isDommages(item) { | ||||
|     if (item.type == ITEM_TYPES.competencecreature) { | ||||
|       switch (item.system.categorie) { | ||||
| @@ -89,20 +78,12 @@ export class RdDItemCompetenceCreature extends Item { | ||||
|     } | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   static isParade(item) { | ||||
|     if (item.type == ITEM_TYPES.competencecreature) { | ||||
|       switch (item.system.categorie) { | ||||
|         case "melee": | ||||
|         case "naturelle": | ||||
|         case "parade": | ||||
|           return true | ||||
|       } | ||||
|       return armeData.system.categorie_parade || armeData.system.isparade | ||||
|     } | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static isCompetenceParade(item) { | ||||
|     return item.type == 'competencecreature' && item.system.categorie_parade !== ""; | ||||
|   } | ||||
| }   | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import { Misc } from "./misc.js"; | ||||
| import { RdDTimestamp } from "./time/rdd-timestamp.js"; | ||||
| import { RdDItemCompetenceCreature } from "./item-competencecreature.js"; | ||||
| import { ITEM_TYPES, RdDItem } from "./item.js"; | ||||
| import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js"; | ||||
|  | ||||
| /** | ||||
|  * Extend the basic ItemSheet for RdD specific items | ||||
| @@ -100,55 +101,59 @@ export class RdDItemSheet extends ItemSheet { | ||||
|       descriptionmj: await TextEditor.enrichHTML(this.item.system.descriptionmj, { async: true }), | ||||
|       isComestible: this.item.getUtilisationCuisine(), | ||||
|       options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable), | ||||
|       competences: await SystemCompendiums.getCompetences('personnage'), | ||||
|       categories: RdDItem.getCategories(this.item.type), | ||||
|     } | ||||
|  | ||||
|     if (this.item.type == ITEM_TYPES.competencecreature) { | ||||
|       formData.isparade = RdDItemCompetenceCreature.isParade(this.item) | ||||
|       formData.isdommages = RdDItemCompetenceCreature.isDommages(this.item) | ||||
|     } | ||||
|  | ||||
|     const competences = await SystemCompendiums.getCompetences('personnage'); | ||||
|     formData.categories = RdDItem.getCategories(this.item.type) | ||||
|     if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') { | ||||
|     if (this.item.type == ITEM_TYPES.tache || | ||||
|       this.item.type == ITEM_TYPES.livre || | ||||
|       this.item.type == ITEM_TYPES.meditation || | ||||
|       this.item.type == ITEM_TYPES.oeuvre) { | ||||
|       formData.caracList = foundry.utils.duplicate(game.model.Actor.personnage.carac) | ||||
|       formData.caracList["reve-actuel"] = foundry.utils.duplicate(game.model.Actor.personnage.reve.reve) | ||||
|       formData.competences = competences; | ||||
|     } | ||||
|     if (this.item.type == 'arme') { | ||||
|       formData.competences = competences.filter(it => it.isCompetenceArme()) | ||||
|     if (this.item.type == ITEM_TYPES.arme) { | ||||
|       formData.competences = formData.competences.filter(it => it.isCompetenceArme()) | ||||
|     } | ||||
|     if (['sort', 'sortreserve'].includes(this.item.type)) { | ||||
|       formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it)); | ||||
|     } | ||||
|     if (this.item.type == 'recettecuisine') { | ||||
|     if (this.item.type == ITEM_TYPES.recettecuisine) { | ||||
|       formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, { async: true }) | ||||
|     } | ||||
|     if (this.item.type == 'extraitpoetique') { | ||||
|     if (this.item.type == ITEM_TYPES.extraitpoetique) { | ||||
|       formData.extrait = await TextEditor.enrichHTML(this.object.system.extrait, { async: true }) | ||||
|       formData.texte = await TextEditor.enrichHTML(this.object.system.texte, { async: true }) | ||||
|     } | ||||
|     if (this.item.type == 'recettealchimique') { | ||||
|       RdDAlchimie.processManipulation(this.item, this.actor && this.actor.id); | ||||
|     if (this.item.type == ITEM_TYPES.recettealchimique) { | ||||
|       RdDAlchimie.processManipulation(this.item, this.actor?.id); | ||||
|       formData.manipulation_update = await TextEditor.enrichHTML(this.object.system.manipulation_update, { async: true }) | ||||
|       formData.utilisation = await TextEditor.enrichHTML(this.object.system.utilisation, { async: true }) | ||||
|       formData.enchantement = await TextEditor.enrichHTML(this.object.system.enchantement, { async: true }) | ||||
|       formData.sureffet = await TextEditor.enrichHTML(this.object.system.sureffet, { async: true }) | ||||
|     } | ||||
|     if (this.item.type == 'gemme') { | ||||
|     if (this.item.type == ITEM_TYPES.gemme) { | ||||
|       formData.gemmeTypeList = RdDGemme.getGemmeTypeOptionList(); | ||||
|       RdDGemme.calculDataDerivees(this.item); | ||||
|       RdDGemme.calculDataDerivees(this.item) | ||||
|     } | ||||
|     if (this.item.type == 'potion') { | ||||
|       await RdDHerbes.addPotionFormData(formData); | ||||
|     if (this.item.type == ITEM_TYPES.potion) { | ||||
|       RdDHerbes.calculFormData(formData, this.item) | ||||
|     } | ||||
|     if (formData.options.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) { | ||||
|       formData.isIngredientPotionBase = true; | ||||
|     if (this.item.type == ITEM_TYPES.herbe) { | ||||
|       if (formData.options.isOwned && ['Soin', 'Repos'].includes(formData.system.categorie)) { | ||||
|         formData.isIngredientPotionBase = true; | ||||
|       } | ||||
|     } | ||||
|     if (this.item.type == 'sortreserve') { | ||||
|     if (this.item.type == ITEM_TYPES.sortreserve) { | ||||
|       const sortId = this.item.system.sortid; | ||||
|       formData.competences = formData.competences.filter(it => RdDItemCompetence.isDraconic(it)); | ||||
|       formData.sort = formData.options.isOwned ? this.item.actor.items.get(sortId) : game.items.get(sortId); | ||||
|     } | ||||
|     formData.bonusCaseList = RdDItemSort.getBonusCaseList(formData, true); | ||||
|  | ||||
|     if (this.item.type == ITEM_TYPES.sort) { | ||||
|       formData.competences = formData.competences.filter(it => RdDItemCompetence.isDraconic(it)); | ||||
|       formData.bonusCaseList = RdDItemSort.getBonusCaseList(this.item); | ||||
|     } | ||||
|     return formData; | ||||
|   } | ||||
|  | ||||
| @@ -185,6 +190,9 @@ export class RdDItemSheet extends ItemSheet { | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|     this.html.find('.delete-bonus-case').click((event) => { | ||||
|       this.supprimerBonusCase(event.currentTarget.attributes['data-deleteCoord'].value) | ||||
|     }) | ||||
|  | ||||
|     this.html.find('.date-enchantement').change((event) => { | ||||
|       const jour = Number(this.html.find('input.date-enchantement[name="enchantement.jour"]').val()); | ||||
| @@ -264,16 +272,66 @@ export class RdDItemSheet extends ItemSheet { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async supprimerBonusCase(deleteCoord){ | ||||
|     if (this.item.type == ITEM_TYPES.sort) { | ||||
|       const oldList = RdDItemSort.getBonusCaseList(this.item) | ||||
|       const newList = oldList.filter(it => it.case != deleteCoord); | ||||
|       if (newList.length != oldList.length) { | ||||
|         await this.item.update({ | ||||
|           'system.bonuscase': RdDItemSort.bonuscasesToString(newList) | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   /** @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: | ||||
|         formData['system.bonuscase'] = RdDItemSort.bonuscasesToString(RdDItemSheet._listCaseTmr( | ||||
|           formData.caseTmrCoord, | ||||
|           formData.caseTmrBonus, | ||||
|           formData.caseTmrAdd | ||||
|         )) | ||||
|         break | ||||
|       case ITEM_TYPES.competence: | ||||
|         formData['system.niveau'] = formData['system.niveau'] ?? formData['system.base'] | ||||
|         break | ||||
|     } | ||||
|  | ||||
|     return this.item.update(formData); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   /** | ||||
|    * reconstruit les bonus de cases | ||||
|    * @param {*} caseTmrCoord tableau des coordonées | ||||
|    * @param {*} caseTmrBonus tableau des bonus | ||||
|    * @param {*} caseTmrAdd case à ajouter | ||||
|    * @returns list d'objets {coord, bonus} | ||||
|    */ | ||||
|   static _listCaseTmr(caseTmrCoord, caseTmrBonus, caseTmrAdd) { | ||||
|     const listCaseTmrCoord = caseTmrCoord == undefined ? [] : Array.isArray(caseTmrCoord) ? caseTmrCoord : [caseTmrCoord] | ||||
|     const listCaseTmrBonus = caseTmrBonus == undefined ? [] : Array.isArray(caseTmrBonus) ? caseTmrBonus : [caseTmrBonus] | ||||
|     if (caseTmrAdd != undefined && caseTmrAdd != '' && TMRUtility.verifyTMRCoord(caseTmrAdd) && !listCaseTmrCoord.includes(caseTmrAdd)) { | ||||
|       listCaseTmrCoord.push(TMRUtility.getTMR(caseTmrAdd).coord) | ||||
|       listCaseTmrBonus.push(1) | ||||
|     } | ||||
|  | ||||
|     const list = []; | ||||
|     const caseChecked = {}; | ||||
|     for (let i = 0; i < listCaseTmrBonus.length && i < listCaseTmrCoord.length; i++) { | ||||
|       const coord = listCaseTmrCoord[i] == FLEUVE_COORD ? FLEUVE_COORD : (listCaseTmrCoord[i]?.toUpperCase() ?? 'A1') | ||||
|       const bonus = listCaseTmrBonus[i] ?? 0 | ||||
|       if (TMRUtility.verifyTMRCoord(coord) && bonus >= 0 && !caseChecked[coord]) { | ||||
|         caseChecked[coord] = coord | ||||
|         list.push({ case: coord, bonus: bonus }) | ||||
|       } | ||||
|     } | ||||
|     return list | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async _onDragStart(event) { | ||||
|   } | ||||
|   | ||||
| @@ -2,52 +2,69 @@ import { Grammar } from "./grammar.js"; | ||||
| import { RdDItemCompetence } from "./item-competence.js"; | ||||
| import { ITEM_TYPES } from "./item.js"; | ||||
| import { Misc } from "./misc.js"; | ||||
| import { TMRUtility } from "./tmr-utility.js"; | ||||
| import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js"; | ||||
|  | ||||
| const VOIES_DRACONIC = [ | ||||
| export const VOIES_DRACONIC = [ | ||||
|   { code: 'O', label: "Voie d'Oniros", short: 'Oniros', ordre: 'a' }, | ||||
|   { code: 'H', label: "Voie d'Hypnos", short: 'Hypnos', ordre: 'b' }, | ||||
|   { code: 'N', label: "Voie de Narcos", short: 'Narcos', ordre: 'c' }, | ||||
|   { code: 'T', label: "Voie de Thanatos", short: 'Thanatos', ordre: 'd' }, | ||||
|   { code: 'O/H/N/T', label: "Oniros/Hypnos/Narcos/Thanatos", short: 'Oniros/Hypnos/Narcos/Thanatos', ordre: 'e' }, | ||||
|   { code: 'O/H/N', label: "Oniros/Hypnos/Narcos", short: "Oniros/Hypnos/Narcos", ordre: 'f' } | ||||
|   { code: 'O/H/N/T', label: "Oniros/Hypnos/Narcos/Thanatos", short: 'O/H/N/T', ordre: 'e' }, | ||||
|   { code: 'O/H/N', label: "Oniros/Hypnos/Narcos", short: "O/H/N", ordre: 'f' } | ||||
| ] | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| export class RdDItemSort extends Item { | ||||
|   static preloadHandlebars() { | ||||
|     Handlebars.registerHelper('itemSort-spaceIfText', val => RdDItemSort.addSpaceToNonNumeric(val)) | ||||
|     Handlebars.registerHelper('itemSort-codeDraconic', voie => RdDItemSort.getCode(voie)) | ||||
|     Handlebars.registerHelper('itemSort-shortDraconic', voie => RdDItemSort.getShortVoie(voie)) | ||||
|   } | ||||
|  | ||||
|   static getDraconicsSort(draconicList, sort) { | ||||
|   static addSpaceToNonNumeric(value) { | ||||
|     return Number.isNumeric(value) || ['-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes( String(value).charAt[0]) ? value : ' ' + RdDItemSort.toVar(value) | ||||
|   } | ||||
|  | ||||
|   static toVar(value) { | ||||
|     return value ? value.replace('variable', 'var') : '' | ||||
|   } | ||||
|  | ||||
|   static getDraconicsSort(competencesDraconic, sort) { | ||||
|     // se baser sur la voie du sort? | ||||
|     switch (Grammar.toLowerCaseNoAccent(sort.name)) { | ||||
|       case "lecture d'aura": | ||||
|       case "detection d'aura": | ||||
|         return draconicList; | ||||
|         return competencesDraconic; | ||||
|       case "annulation de magie": | ||||
|         return draconicList.filter(it => !RdDItemCompetence.isThanatos(it)); | ||||
|         return competencesDraconic.filter(it => !RdDItemCompetence.isThanatos(it)); | ||||
|     } | ||||
|     return [RdDItemCompetence.getVoieDraconic(draconicList, sort.system.draconic)]; | ||||
|     const voies = sort.system.draconic.split('/') | ||||
|     return voies.map(voie => RdDItemCompetence.getVoieDraconic(competencesDraconic, voie)) | ||||
|   } | ||||
|  | ||||
|   static getOrdreCode(code) { | ||||
|     return (VOIES_DRACONIC.find(it => it.code == code)?.ordre ?? '?') | ||||
|   } | ||||
|  | ||||
|   static getVoieCode(voie) { | ||||
|     return VOIES_DRACONIC.find(it => voie.name.includes(it.short))?.code ?? '?' | ||||
|   static getCodeVoie(voie) { | ||||
|     return VOIES_DRACONIC.find(it => [it.code, it.short, it.label].includes(voie))?.code ?? '?' | ||||
|   } | ||||
|  | ||||
|   static getCodeDraconic(sort, voies = ['O', 'H', 'N', 'T']) { | ||||
|   static getShortVoie(voie) { | ||||
|     return VOIES_DRACONIC.find(it => [it.code, it.short, it.label].includes(voie))?.short ?? voie | ||||
|   } | ||||
|  | ||||
|   static getCode(sort, codeVoies = ['O', 'H', 'N', 'T']) { | ||||
|     switch (Grammar.toLowerCaseNoAccent(sort.name)) { | ||||
|       case "lecture d'aura": | ||||
|       case "detection d'aura": | ||||
|         return RdDItemSort.$voiesConnues('O/H/N/T', voies) | ||||
|         return RdDItemSort.$voiesConnues('O/H/N/T', codeVoies) | ||||
|       case "annulation de magie": | ||||
|         return RdDItemSort.$voiesConnues('O/H/N', voies) | ||||
|         return RdDItemSort.$voiesConnues('O/H/N', codeVoies) | ||||
|     } | ||||
|     const voie = VOIES_DRACONIC.find(it => it.label.includes(sort.system.draconic)) | ||||
|     return voie?.code ?? sort.system.draconic | ||||
|     return RdDItemSort.getCodeVoie(sort.system.draconic) | ||||
|   } | ||||
|  | ||||
|  | ||||
|   static $voiesConnues(voiesSort, voies) { | ||||
|     const codes = voies.filter(it => voiesSort.includes(it)) | ||||
|       .sort(Misc.ascending(it => RdDItemSort.getOrdreCode(it))) | ||||
| @@ -79,89 +96,60 @@ export class RdDItemSort extends Item { | ||||
|     return variable; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static buildBonusCaseList(bonuscase, newCase) { | ||||
|     const list = RdDItemSort.bonuscaseStringToList(bonuscase) | ||||
|     if (newCase) { | ||||
|       list.push({ case: "Nouvelle", bonus: 0 }) | ||||
|     } | ||||
|     return list; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Retourne une liste de bonus/case pour un item-sheet | ||||
|   * @param {} item  | ||||
|   */ | ||||
|   static getBonusCaseList(item, newCase = false) { | ||||
|   static getBonusCaseList(item) { | ||||
|     // Gestion spéciale case bonus | ||||
|     if (item.type == ITEM_TYPES.sort) { | ||||
|       return RdDItemSort.buildBonusCaseList(item.system.bonuscase, newCase); | ||||
|       return RdDItemSort.stringToBonuscases(item.system.bonuscase) | ||||
|     } | ||||
|     return undefined; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   /** Met à jour les données de formulaire  | ||||
|    * si static des bonus de cases sont présents  | ||||
|    * */ | ||||
|   static buildBonuscaseFromArrays(bonuses, coords) { | ||||
|     if (bonuses) { | ||||
|       const list = []; | ||||
|       const caseCheck = {}; | ||||
|       for (let i = 0; i < bonuses.length && i < coords.length; i++) { | ||||
|         const coord = coords[i] == 'Fleuve' ? 'Fleuve' : (coords[i]?.toUpperCase() ?? 'A1'); | ||||
|         const bonus = bonuses[i] || 0; | ||||
|         if (TMRUtility.verifyTMRCoord(coord) && bonus > 0 && caseCheck[coord] == undefined) { | ||||
|           caseCheck[coord] = bonus; | ||||
|           list.push({ case: coord, bonus: bonus }); | ||||
|         } | ||||
|       } | ||||
|       return RdDItemSort._bonuscaseListToString(list); | ||||
|     } | ||||
|     return undefined; | ||||
|     return []; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static incrementBonusCase(actor, sort, coord) { | ||||
|     if (TMRUtility.getTMR(coord).type == "fleuve") { | ||||
|       coord = 'Fleuve'; | ||||
|     if (TMRUtility.isFleuve(coord)) { | ||||
|       coord = FLEUVE_COORD; | ||||
|     } | ||||
|     let list = RdDItemSort.stringToBonuscases(sort.system.bonuscase); | ||||
|     const existing = list.find(it => it.case == coord) | ||||
|     const bonus = Number(existing?.bonus ?? 0) + 1 | ||||
|     if (existing) { | ||||
|       existing.bonus = bonus | ||||
|     } | ||||
|     else { | ||||
|       list.push({ case: coord, bonus: 1 }) | ||||
|     } | ||||
|     const list = RdDItemSort.buildBonusCaseList(sort.system.bonuscase, false); | ||||
|     const bonus = Number(list.find(it => it.case == coord)?.bonus ?? 0); | ||||
|     const modified = { case: coord, bonus: bonus + 1 }; | ||||
|  | ||||
|     const bonuscase = RdDItemSort._bonuscaseListToString( | ||||
|       list.filter(it => it.case != coord).concat(modified) | ||||
|     ); | ||||
|  | ||||
|     // Sauvegarde/update | ||||
|     actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': bonuscase }]); | ||||
|     actor.updateEmbeddedDocuments('Item', [{ _id: sort._id, 'system.bonuscase': RdDItemSort.bonuscasesToString(list) }]); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getCaseBonus(sort, coord) { | ||||
|     const isFleuve = TMRUtility.getTMR(coord).type == "fleuve"; | ||||
|  | ||||
|     let bc = RdDItemSort.buildBonusCaseList(sort.system.bonuscase, false) | ||||
|       .filter(it => it.case == coord || (isFleuve && it.case == 'Fleuve')) | ||||
|       .find(it => true) | ||||
|     const search = TMRUtility.isFleuve(coord) | ||||
|       ? it => it.case == 'Fleuve' | ||||
|       : it => it.case == coord; | ||||
|     const bc = RdDItemSort.stringToBonuscases(sort.system.bonuscase) | ||||
|       .find(search) | ||||
|     return Number(bc?.bonus ?? 0); | ||||
|   } | ||||
|  | ||||
|   static _bonuscaseListToString(list) { | ||||
|   static bonuscasesToString(list) { | ||||
|     return list.map(it => `${it.case}:${it.bonus}`) | ||||
|       .sort(Misc.ascending()) | ||||
|       .join(','); | ||||
|   } | ||||
|   static bonuscaseStringToList(bonuscase) { | ||||
|  | ||||
|   static stringToBonuscases(bonuscase) { | ||||
|     if (bonuscase == undefined || bonuscase == '') { | ||||
|       return [] | ||||
|     } | ||||
|     return bonuscase.split(',').map(it => { | ||||
|       const b = it.split(':'); | ||||
|       return { case: b[0], bonus: b[1] }; | ||||
|     }); | ||||
|     return bonuscase.split(',') | ||||
|       .map(it => it.split(':')) | ||||
|       .map(it => { return { case: it[0], bonus: it[1] } }); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -571,6 +571,7 @@ export class RdDItem extends Item { | ||||
|     else { | ||||
|       await this.quantiteIncDec(item.system.quantite); | ||||
|     } | ||||
|     // TODO: suppression dans les conteneurs! | ||||
|     await item.delete(); | ||||
|   } | ||||
|  | ||||
| @@ -601,6 +602,9 @@ export class RdDItem extends Item { | ||||
|     if (!other || !this.isInventaire()) { | ||||
|       return [false, undefined]; | ||||
|     } | ||||
|     if (this.isConteneur()){ | ||||
|       return [false, `Impossible de regrouper des conteneurs, ils ne sont pas empilables`]; | ||||
|     } | ||||
|     if (this.system.quantite == undefined) { | ||||
|       return [false, `Impossible de regrouper des ${this.type}, ils ne sont pas empilables`]; | ||||
|     } | ||||
|   | ||||
| @@ -62,12 +62,12 @@ export class RdDItemBlessure extends RdDItem { | ||||
|       content: `Blessure ${definition.label} appliquée à ${actor.name}`+ | ||||
|         `<br>Perte d'endurance : ${lostEndurance}`+ | ||||
|         `<br>Perte de Vie : ${lostVie}`, | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name) | ||||
|       whisper: ChatUtility.getOwners(actor) | ||||
|     }); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   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]) | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import { ChatUtility } from "../chat-utility.js"; | ||||
| import { RdDItem } from "../item.js"; | ||||
| import { Misc } from "../misc.js"; | ||||
| import { RdDTimestamp } from "../time/rdd-timestamp.js"; | ||||
| @@ -21,9 +22,12 @@ export class RdDItemMaladie extends RdDItem { | ||||
|       const souffrance = mal.system.identifie | ||||
|         ? `de ${mal.name}` | ||||
|         : `d'un mal inconnu` | ||||
|       ChatMessage.create({ content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !` }); | ||||
|       mal.postItemToChat('gmroll'); | ||||
|       await RdDItemMaladie.prolongerPeriode(mal,oldTimestamp, newTimestamp); | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getOwners(mal.actor), | ||||
|         content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !` | ||||
|       }) | ||||
|       mal.postItemToChat('gmroll') | ||||
|       await RdDItemMaladie.prolongerPeriode(mal, oldTimestamp, newTimestamp) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import { Monnaie } from "./item-monnaie.js"; | ||||
| import { RdDItem, ITEM_TYPES } from "./item.js"; | ||||
| import { RdDTimestamp } from "./time/rdd-timestamp.js"; | ||||
| import { RdDRaretes } from "./item/raretes.js"; | ||||
| import { RdDCalendrier } from "./time/rdd-calendrier.js"; | ||||
| import { VOIES_DRACONIC } from "./item-sort.js"; | ||||
|  | ||||
| class Migration { | ||||
|   get code() { return "sample"; } | ||||
| @@ -530,6 +530,32 @@ class _11_2_20_MigrationAstrologie extends Migration { | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _12_0_24_MigrationVoieSorts extends Migration { | ||||
|   get code() { return "migration-voies-sorts" } | ||||
|   get version() { return "12.0.24" } | ||||
|  | ||||
|   async migrate() { | ||||
|     await this.applyItemsUpdates(items => items | ||||
|       .filter(it => ITEM_TYPES.sort == it.type) | ||||
|       .map(it => this.migrateSort(it)) | ||||
|     ) | ||||
|   } | ||||
|   migrateSort(it) { | ||||
|     return { | ||||
|       _id: it.id, | ||||
|       'system.draconic': this.convertDraconic(it.system.draconic), | ||||
|     } | ||||
|   } | ||||
|   convertDraconic(draconic) { | ||||
|     for (let v of VOIES_DRACONIC) { | ||||
|       if ([v.label, v.short, v.code].includes(draconic)) { | ||||
|         return v.short | ||||
|       } | ||||
|     } | ||||
|     return draconic | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class Migrations { | ||||
|   static getMigrations() { | ||||
|     return [ | ||||
|   | ||||
| @@ -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 = '') { | ||||
| @@ -189,13 +189,7 @@ export class Misc { | ||||
|    *  and there is no connected GM | ||||
|    */ | ||||
|   static documentIfResponsible(document) { | ||||
|     if (foundry.utils.isNewerVersion(game.release.version, '12.0')) { | ||||
|       if (game.users.activeGM || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) | ||||
|       { | ||||
|         return document | ||||
|       } | ||||
|     } | ||||
|     else if (Misc.isUniqueConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isOwnerPlayer(document))) { | ||||
|     if (Misc.isFirstConnectedGM() || (Misc.connectedGMs().length == 0 && Misc.isFirstOwnerPlayer(document))) { | ||||
|       return document | ||||
|     } | ||||
|     return undefined | ||||
| @@ -205,15 +199,22 @@ export class Misc { | ||||
|     return document.testUserPermission && document.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) | ||||
|   } | ||||
|  | ||||
|   static isFirstOwnerPlayer(document) { | ||||
|     if (!document.testUserPermission){ | ||||
|       return false | ||||
|     } | ||||
|     return game.users.filter(u => document.testUserPermission(u, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) == game.user | ||||
|   } | ||||
|  | ||||
|   static isOwnerPlayerOrUniqueConnectedGM(actor) { | ||||
|     return Misc.isOwnerPlayer(actor) ?? Misc.isUniqueConnectedGM(); | ||||
|     return Misc.isFirstOwnerPlayer(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() { | ||||
|   | ||||
| @@ -53,9 +53,14 @@ export class RdDCarac { | ||||
|     return selectedCarac?.label?.toLowerCase()?.match(/r(e|ê)ve(( |-)actuel)?/); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * L’appel à la chance n’est possible que pour recommencer les jets d’actions physiques : | ||||
|    * tous les jets de combat, de FORCE, d’AGILITÉ, de DEXTÉRITÉ, de Dérobée, d’APPARENCE, | ||||
|    * ainsi que de Perception active et volontaire. | ||||
|    */ | ||||
|   static isActionPhysique(selectedCarac) { | ||||
|     return !selectedCarac || | ||||
|       selectedCarac?.label.match(/(Apparence|Force|Agilité|Dextérité|Vue|Ouïe|Odorat-Goût|Empathie|Dérobée|Mêlée|Tir|Lancer)/); | ||||
|     return Grammar.toLowerCaseNoAccent(selectedCarac?.label) | ||||
|       ?.match(/(apparence|force|agilite|dexterite|vue|ouie|gout|odorat|empathie|melee|tir|lancer|derobee)/) != null | ||||
|   } | ||||
|  | ||||
|   static getCaracDerivee(value) { | ||||
| @@ -90,15 +95,4 @@ export class RdDCarac { | ||||
|   static getCaracXp(targetValue) { | ||||
|     return RdDCarac.getCaracDerivee(targetValue)?.xp ?? 200; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * L’appel à la chance n’est possible que pour recommencer les jets d’actions physiques : | ||||
|    * tous les jets de combat, de FORCE, d’AGILITÉ, de DEXTÉRITÉ, de Dérobée, d’APPARENCE, | ||||
|    * ainsi que de Perception active et volontaire. | ||||
|    */ | ||||
|   static isActionPhysique(selectedCarac) { | ||||
|     return Grammar.toLowerCaseNoAccent(selectedCarac?.label)?.match(/(apparence|force|agilite|dexterite|vue|ouie|odorat|empathie|melee|tir|lancer|derobee)/); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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,28 +54,48 @@ export class RdDCombatManager extends Combat { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async onPreDeleteCombat() { | ||||
|     if (Misc.isUniqueConnectedGM()) { | ||||
|       await this.finDeRound({ terminer: true }); | ||||
|     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) | ||||
|         .forEach(it => it.delete()); | ||||
|         .forEach(it => it.delete()) | ||||
|       RdDEmpoignade.deleteAllEmpoignades() | ||||
|     } | ||||
|   } | ||||
|   async onDeleteCombat() { | ||||
|     if (Misc.isFirstConnectedGM()) { | ||||
|       if (game.combats.size <= 1) { | ||||
|         game.actors.forEach(actor => actor.resetItemUse()) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async finDeRound(options = { terminer: false }) { | ||||
|     this.turns.forEach(turn => turn.actor.resetItemUse()); | ||||
|     this.combatants.map(it => RdDCombatManager.getActorCombatant(it, { warning: false })) | ||||
|       .filter(it => it != undefined) | ||||
|       .forEach(async actor => { | ||||
|         await actor.finDeRound(options) | ||||
|         await actor.resetItemUse() | ||||
|       }) | ||||
|   } | ||||
|  | ||||
|     for (let combatant of this.combatants) { | ||||
|       if (combatant.actor) { | ||||
|         await combatant.actor.finDeRound(options); | ||||
|       } | ||||
|       else { | ||||
|   static getActorCombatant(combatant, options = { warning: true }) { | ||||
|     if (!combatant.actor) { | ||||
|       if (options.warning) { | ||||
|         ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur!`) | ||||
|       } | ||||
|       return undefined | ||||
|     } | ||||
|     else if (!combatant.actor.isActorCombat()) { | ||||
|       if (options.warning) { | ||||
|         ui.notifications.warn(`${combatant.name} ne peut pas combattre!`) | ||||
|       } | ||||
|       return undefined | ||||
|     } | ||||
|     return combatant.actor | ||||
|   } | ||||
|  | ||||
|   static calculAjustementInit(actor, arme) { | ||||
|     const efficacite = (arme?.system.magique) ? arme.system.ecaille_efficacite : 0 | ||||
|     const etatGeneral = actor.getEtatGeneral() ?? 0 | ||||
| @@ -83,57 +104,27 @@ export class RdDCombatManager extends Combat { | ||||
|   } | ||||
|  | ||||
|   /************************************************************************************/ | ||||
|   async rollInitiative(ids, formula = undefined, messageOptions = {}) { | ||||
|     console.log(`${game.system.title} | Combat.rollInitiative()`, ids, formula, messageOptions); | ||||
|   async rollInitiative(ids, messageOptions = {}) { | ||||
|     console.log(`${game.system.title} | Combat.rollInitiative()`, ids, messageOptions) | ||||
|     ids = typeof ids === "string" ? [ids] : ids | ||||
|     ids.forEach(async id => | ||||
|       await this.rollInitRdD(id, undefined, messageOptions) | ||||
|     ) | ||||
|     return this | ||||
|   } | ||||
|  | ||||
|     ids = typeof ids === "string" ? [ids] : ids; | ||||
|     // calculate initiative | ||||
|     for (let cId = 0; cId < ids.length; cId++) { | ||||
|       const combatant = this.combatants.get(ids[cId]); | ||||
|       const ajustement = RdDCombatManager.calculAjustementInit(combatant.actor, undefined); | ||||
|       let rollFormula = formula ?? RdDCombatManager.formuleInitiative(2, 10, 0, ajustement); | ||||
|  | ||||
|       if (!formula) { | ||||
|         if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') { | ||||
|           const competence = combatant.actor.items.find(it => RdDItemCompetenceCreature.isCompetenceAttaque(it)) | ||||
|           if (competence) { | ||||
|             rollFormula = RdDCombatManager.formuleInitiative(2, competence.system.carac_value, competence.system.niveau, etatGeneral); | ||||
|           } | ||||
|         } else { | ||||
|           const armeCombat = combatant.actor.itemTypes['arme'].find(it => it.system.equipe) | ||||
|           let compName = "Corps à corps" | ||||
|           if (armeCombat) { | ||||
|             if (armeCombat.system.competence != "") { | ||||
|               compName = armeCombat.system.competence | ||||
|             } | ||||
|             if (armeCombat.system.lancer != "") { | ||||
|               compName = armeCombat.system.lancer | ||||
|             } | ||||
|             if (armeCombat.system.tir != "") { | ||||
|               compName = armeCombat.system.tir | ||||
|             } | ||||
|           } | ||||
|           const competence = RdDItemCompetence.findCompetence(combatant.actor.items, compName); | ||||
|           if (competence && competence.system.defaut_carac) { | ||||
|             const carac = combatant.actor.system.carac[competence.system.defaut_carac].value; | ||||
|             const niveau = competence.system.niveau; | ||||
|  | ||||
|             const ajustement = RdDCombatManager.calculAjustementInit(combatant.actor, armeCombat) | ||||
|             rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, ajustement); | ||||
|           } else { | ||||
|             ui.notifications.warn(`Votre arme ${armeCombat.name} n'a pas de compétence renseignée`); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       //console.log("Combatat", c); | ||||
|   async rollInitRdD(id, formula, messageOptions = {}) { | ||||
|     const combatant = this.combatants.get(id); | ||||
|     const actor = RdDCombatManager.getActorCombatant(combatant) | ||||
|     if (actor) { | ||||
|       const rollFormula = formula ?? RdDCombatManager.getFirstInitRollFormula(actor) | ||||
|       const roll = combatant.getInitiativeRoll(rollFormula); | ||||
|       if (!roll.total) { | ||||
|         await roll.evaluate(); | ||||
|       } | ||||
|       const total = Math.max(roll.total, 0.00); | ||||
|       console.log("Compute init for", rollFormula, roll, total, combatant); | ||||
|       let id = combatant._id || combatant.id; | ||||
|       await this.updateEmbeddedDocuments("Combatant", [{ _id: id, initiative: total }]); | ||||
|       await this.updateEmbeddedDocuments("Combatant", [{ _id: combatant._id || combatant.id, initiative: total }]); | ||||
|  | ||||
|       // Send a chat message | ||||
|       let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode"); | ||||
| @@ -142,10 +133,10 @@ export class RdDCombatManager extends Combat { | ||||
|           scene: canvas.scene._id, | ||||
|           actor: combatant.actor?._id, | ||||
|           token: combatant.token._id, | ||||
|           alias: combatant.token.name, | ||||
|           alias: combatant.token?.name, | ||||
|           sound: CONFIG.sounds.dice, | ||||
|         }, | ||||
|         flavor: `${combatant.token.name} a fait son jet d'Initiative (${messageOptions.initInfo})<br>`, | ||||
|         flavor: `${combatant.token?.name} a fait son jet d'Initiative (${messageOptions.info})<br>` | ||||
|       }, | ||||
|         messageOptions); | ||||
|       roll.toMessage(messageData, { rollMode, create: true }); | ||||
| @@ -153,7 +144,20 @@ export class RdDCombatManager extends Combat { | ||||
|       RdDCombatManager.processPremierRoundInit(); | ||||
|     } | ||||
|     return this; | ||||
|   }; | ||||
|   } | ||||
|  | ||||
|   static getFirstInitRollFormula(actor) { | ||||
|     const actions = actor.listActionsCombat() | ||||
|     if (actions.length > 0) { | ||||
|       const action = actions[0] | ||||
|       const init = RdDCombatManager.getInitData(actor, action) | ||||
|       const ajustement = RdDCombatManager.calculAjustementInit(actor, action) | ||||
|       return RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement); | ||||
|     } | ||||
|  | ||||
|     let ajustement = RdDCombatManager.calculAjustementInit(actor, undefined); | ||||
|     return RdDCombatManager.formuleInitiative(2, 10, 0, ajustement); | ||||
|   } | ||||
|  | ||||
|   static formuleInitiative(rang, carac, niveau, bonusMalus) { | ||||
|     return `${rang} +( (${RdDCombatManager.calculInitiative(niveau, carac, bonusMalus)} )/100)`; | ||||
| @@ -222,7 +226,7 @@ export class RdDCombatManager extends Combat { | ||||
|   } | ||||
|  | ||||
|   static $prepareAttaqueArme(infoAttaque) { | ||||
|     const comp = infoAttaque.competences.find(c => c.name == infoAttaque.competence); | ||||
|     const comp = infoAttaque.competences.find(it => Grammar.equalsInsensitive(it.name, infoAttaque.competence)) | ||||
|     const arme = infoAttaque.arme; | ||||
|     const attaque = foundry.utils.duplicate(arme); | ||||
|     attaque.action = 'attaque'; | ||||
| @@ -236,62 +240,10 @@ 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; | ||||
|     let actions = RdDCombatManager.listActionsPossessions(actor); | ||||
|     if (actions.length > 0) { | ||||
|       return actions; | ||||
|     } | ||||
|     if (actor.isCreatureEntite()) { | ||||
|       actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']); | ||||
|     } else if (actor.isPersonnage()) { | ||||
|       // Recupération des items 'arme' | ||||
|       const competences = actor.itemTypes['competence']; | ||||
|       const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it)) | ||||
|         .concat(RdDItemArme.empoignade(actor)) | ||||
|         .concat(RdDItemArme.mainsNues(actor)); | ||||
|       actions = RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac); | ||||
|  | ||||
|       if (actor.system.attributs.hautrevant.value) { | ||||
|         actions.push({ name: "Draconic", action: 'haut-reve', system: { initOnly: true, competence: "Draconic" } }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return RdDCombatManager._indexActions(actions); | ||||
|   } | ||||
|  | ||||
|   static _indexActions(actions) { | ||||
|     for (let index = 0; index < actions.length; index++) { | ||||
|       actions[index].index = index; | ||||
|     } | ||||
|     return actions; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static processPremierRoundInit() { | ||||
|     // Check if we have the whole init ! | ||||
|     if (Misc.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) { | ||||
| @@ -300,7 +252,7 @@ export class RdDCombatManager extends Combat { | ||||
|           if (action && action.type == "arme") { | ||||
|             for (let initData of premierRoundInit) { | ||||
|               if (Grammar.toLowerCaseNoAccentNoSpace(action.system.initpremierround).includes(initData.pattern)) { | ||||
|                 let msg = `<h4>L'initiative de ${combatant.actor.name} a été modifiée !</h4> | ||||
|                 let msg = `<h4>L'initiative de ${combatant.actor.getAlias()} a été modifiée !</h4> | ||||
|                       <hr> | ||||
|                       <div> | ||||
|                         Etant donné son ${action.name}, son initative pour ce premier round est désormais de ${initData.init}. | ||||
| @@ -325,13 +277,13 @@ export class RdDCombatManager extends Combat { | ||||
|   /* -------------------------------------------- */ | ||||
|   static pushInitiativeOptions(html, options) { | ||||
|     for (let i = 0; i < options.length; i++) { | ||||
|       let option = options[i]; | ||||
|       let option = options[i] | ||||
|       if (option.name == 'COMBAT.CombatantReroll') { // Replace ! | ||||
|         option.name = "Sélectionner l'initiative..."; | ||||
|         option.condition = true; | ||||
|         option.icon = '<i class="far fa-question-circle"></i>'; | ||||
|         option.name = "Sélectionner l'initiative..." | ||||
|         option.condition = true | ||||
|         option.icon = '<i class="far fa-question-circle"></i>' | ||||
|         option.callback = target => { | ||||
|           RdDCombatManager.displayInitiativeMenu(html, target.data('combatant-id')); | ||||
|           RdDCombatManager.displayInitiativeMenu(html, target.data('combatant-id')) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| @@ -342,96 +294,83 @@ export class RdDCombatManager extends Combat { | ||||
|   } | ||||
|   /* -------------------------------------------- */ | ||||
|   static rollInitiativeAction(combatantId, action) { | ||||
|     const combatant = game.combat.combatants.get(combatantId); | ||||
|     if (combatant.actor == undefined) { | ||||
|       ui.notifications.warn(`Le combatant ${combatant.name} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`) | ||||
|       return []; | ||||
|     } | ||||
|     let initInfo = ""; | ||||
|     let initOffset = 0; | ||||
|     let caracForInit = 0; | ||||
|     let compNiveau = 0; | ||||
|     let compData = { name: "Aucune" }; | ||||
|     if (combatant.actor.getSurprise() == "totale") { | ||||
|       initOffset = -1; // To force 0 | ||||
|       initInfo = "Surprise Totale" | ||||
|     } else if (combatant.actor.getSurprise() == "demi") { | ||||
|       initOffset = 0; | ||||
|       initInfo = "Demi Surprise" | ||||
|     } else if (action.action == 'possession') { | ||||
|       initOffset = 10; | ||||
|       caracForInit = combatant.actor.getReveActuel(); | ||||
|       initInfo = "Possession" | ||||
|     } else if (action.action == 'autre') { | ||||
|       initOffset = 2; | ||||
|       initInfo = "Autre Action" | ||||
|     } else if (action.action == 'haut-reve') { | ||||
|       initOffset = 9; | ||||
|       initInfo = "Draconic" | ||||
|     } else { | ||||
|       compData = RdDItemCompetence.findCompetence(combatant.actor.items, action.system.competence); | ||||
|       compNiveau = compData.system.niveau; | ||||
|       initInfo = action.name + " / " + action.system.competence; | ||||
|     const combatant = game.combat.combatants.get(combatantId) | ||||
|     const actor = RdDCombatManager.getActorCombatant(combatant) | ||||
|     if (actor == undefined) { return [] } | ||||
|  | ||||
|       if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') { | ||||
|         caracForInit = compData.system.carac_value; | ||||
|       } else { | ||||
|         caracForInit = combatant.actor.system.carac[compData.system.defaut_carac].value; | ||||
|       } | ||||
|       initOffset = RdDCombatManager._baseInitOffset(compData.system.categorie, action); | ||||
|     } | ||||
|  | ||||
|     // Cas des créatures et entités vs personnages | ||||
|     const ajustement = RdDCombatManager.calculAjustementInit(combatant.actor, action) | ||||
|     let rollFormula = RdDCombatManager.formuleInitiative(initOffset, caracForInit, compNiveau, ajustement); | ||||
|     // Garder la trace de l'arme/compétence utilisée pour l'iniative | ||||
|     combatant.initiativeData = { arme: action } // pour reclasser l'init au round 0 | ||||
|     game.combat.rollInitiative(combatantId, rollFormula, { initInfo: initInfo }); | ||||
|  | ||||
|     const init = RdDCombatManager.getInitData(actor, action) | ||||
|     const ajustement = RdDCombatManager.calculAjustementInit(actor, action) | ||||
|     const rollFormula = RdDCombatManager.formuleInitiative(init.offset, init.carac, init.niveau, ajustement); | ||||
|  | ||||
|     game.combat.rollInitRdD(combatantId, rollFormula, init); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static _baseInitOffset(categorie, arme) { | ||||
|     if (categorie == "tir") { // Offset de principe pour les armes de jet | ||||
|       return 8; | ||||
|   static getInitData(actor, action) { | ||||
|     if (actor.getSurprise() == "totale") { return { offset: -1, info: "Surprise Totale", carac: 0, niveau: 0 } } | ||||
|     if (actor.getSurprise() == "demi") { return { offset: 0, info: "Demi Surprise", carac: 0, niveau: 0 } } | ||||
|     if (action.action == 'autre') { return { offset: 2, info: "Autre Action", carac: 0, niveau: 0 } } | ||||
|     if (action.action == 'possession') { return { offset: 10, info: "Possession", carac: actor.getReveActuel(), niveau: 0 } } | ||||
|     if (action.action == 'haut-reve') { return { offset: 9, info: "Draconic", carac: actor.getReveActuel(), niveau: 0 } } | ||||
|  | ||||
|     const comp = RdDItemCompetence.findCompetence(actor.items, action.system.competence); | ||||
|     return { | ||||
|       offset: RdDCombatManager.initOffset(comp?.system.categorie, action), | ||||
|       info: action.name + " / " + action.system.competence, | ||||
|       carac: actor.getCaracInit(comp), | ||||
|       niveau: comp?.system.niveau ?? -8 | ||||
|     } | ||||
|     if (categorie == "lancer") { // Offset de principe pour les armes de jet | ||||
|       return 7; | ||||
|   } | ||||
|  | ||||
|   static initOffset(categorie, arme) { | ||||
|     switch (categorie) { | ||||
|       case "tir": return 8 | ||||
|       case "lancer": return 7 | ||||
|       default: | ||||
|         switch (arme.system.cac) { | ||||
|           case "empoignade": return 3 | ||||
|           case "pugilat": return 4 | ||||
|           case "naturelle": return 4 | ||||
|           default: return 5 | ||||
|         } | ||||
|     } | ||||
|     switch (arme.system.cac) { | ||||
|       case "empoignade": | ||||
|         return 3; | ||||
|       case "pugilat": | ||||
|       case "naturelle": | ||||
|         return 4; | ||||
|     } | ||||
|     return 5; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static displayInitiativeMenu(html, combatantId) { | ||||
|     console.log("Combatant ; ", combatantId); | ||||
|     const combatant = game.combat.combatants.get(combatantId); | ||||
|     if (!(combatant?.actor)) { | ||||
|       ui.notifications.warn(`Le combatant ${combatant.name ?? combatantId} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`) | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     let actions = RdDCombatManager.listActionsCombat(combatant); | ||||
|  | ||||
|     // Build the relevant submenu | ||||
|     if (actions) { | ||||
|       let menuItems = []; | ||||
|       for (let action of actions) { | ||||
|         menuItems.push({ | ||||
|     const combatant = game.combat.combatants.get(combatantId) | ||||
|     const actor = RdDCombatManager.getActorCombatant(combatant, { warning: false }) | ||||
|     if (actor) { | ||||
|       const actions = RdDCombatManager.listActionsActorCombatant(actor) | ||||
|       // Build the relevant submenu | ||||
|       const menuItems = actions.map(action => { | ||||
|         return { | ||||
|           name: action.system.competence, | ||||
|           icon: "<i class='fas fa-dice-d6'></i>", | ||||
|           callback: target => { RdDCombatManager.rollInitiativeAction(combatantId, action) } | ||||
|         }); | ||||
|         } | ||||
|       }) | ||||
|       if (menuItems.length > 0) { | ||||
|         new ContextMenu(html, ".directory-list", menuItems).render(); | ||||
|       } | ||||
|       new ContextMenu(html, ".directory-list", menuItems).render(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static listActionsActorCombatant( actor) { | ||||
|     const possessions = actor.listActionsPossessions() | ||||
|     const actions = possessions.length > 0 | ||||
|       ? possessions | ||||
|       : actor.listActionsCombat() | ||||
|  | ||||
|     for (let index = 0; index < actions.length; index++) { | ||||
|       actions[index].index = index | ||||
|     } | ||||
|     return actions | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| @@ -454,11 +393,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 +408,50 @@ 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 defender = canvas.tokens.get(msg.defenderToken.id).actor; | ||||
|     if (Misc.isOwnerPlayerOrUniqueConnectedGM(defender)) { | ||||
|       let attackerRoll = msg.attackerRoll; | ||||
|       let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined; | ||||
|  | ||||
|       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 +482,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 +498,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 +539,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( | ||||
| @@ -698,11 +652,11 @@ export class RdDCombat { | ||||
|       if (this.defender.isEntite([ENTITE_BLURETTE])) { | ||||
|         ChatMessage.create({ | ||||
|           content: `<strong>La cible est une blurette, l'arme à distance sera perdue dans le blurêve`, | ||||
|           whisper: ChatMessage.getWhisperRecipients("GM") | ||||
|           whisper: ChatUtility.getGMs() | ||||
|         }) | ||||
|       } | ||||
|       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) | ||||
| @@ -721,7 +675,7 @@ export class RdDCombat { | ||||
|             activite: activite, | ||||
|             total: total | ||||
|           }), | ||||
|           whisper: ChatMessage.getWhisperRecipients("GM") | ||||
|           whisper: ChatUtility.getGMs() | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
| @@ -769,7 +723,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 +749,18 @@ export class RdDCombat { | ||||
|     dialog.render(true); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   _prepareAttaque(competence, arme) { | ||||
|     let rollData = { | ||||
|       alias: this.attacker?.getAlias(), | ||||
|       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: {} | ||||
|     }; | ||||
|  | ||||
| @@ -816,9 +773,9 @@ export class RdDCombat { | ||||
|     } | ||||
|     else { | ||||
|       // sans armes: à mains nues | ||||
|       const niveau = competence.system.niveau; | ||||
|       const init = RdDCombatManager.calculInitiative(niveau, this.attacker.system.carac['melee'].value); | ||||
|       rollData.arme = RdDItemArme.mainsNues({ niveau: niveau, initiative: init }); | ||||
|       rollData.arme = RdDItemArme.corpsACorps(this.attacker) | ||||
|       rollData.arme.system.niveau = competence.system.niveau | ||||
|       rollData.arme.system.initiative = RdDCombatManager.calculInitiative(competence.system.niveau, this.attacker.system.carac['melee'].value); | ||||
|     } | ||||
|     return rollData; | ||||
|   } | ||||
| @@ -845,12 +802,13 @@ export class RdDCombat { | ||||
|     } | ||||
|  | ||||
|     const choixParticuliere = await ChatMessage.create({ | ||||
|       alias: this.attacker.name, | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name), | ||||
|       alias: this.attacker.getAlias(), | ||||
|       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.attacker.getAlias(), | ||||
|         attackerId: this.attackerId, | ||||
|         defenderTokenId: this.defenderTokenId, | ||||
|         attackerToken: this.attackerToken, | ||||
|         defenderToken: this.defenderToken, | ||||
|         isForce: isForce, | ||||
|         isFinesse: isFinesse, | ||||
|         isRapide: isRapide, | ||||
| @@ -867,7 +825,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.defender?.getAlias() ?? 'la cible', | ||||
|       isRecul: (attackerRoll.particuliere == 'force' || attackerRoll.tactique == 'charge') | ||||
|     } | ||||
|     await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html'); | ||||
| @@ -898,7 +856,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 +866,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 +878,7 @@ export class RdDCombat { | ||||
|       dmg: attackerRoll.dmg, | ||||
|     }; | ||||
|  | ||||
|     if (Misc.isUniqueConnectedGM()) { | ||||
|     if (Misc.isFirstConnectedGM()) { | ||||
|       await this._chatMessageDefense(paramChatDefense, defenderRoll); | ||||
|     } | ||||
|     else { | ||||
| @@ -932,8 +891,8 @@ export class RdDCombat { | ||||
|     const choixDefense = await ChatMessage.create({ | ||||
|       // message privé: du défenseur à lui même (et aux GMs) | ||||
|       speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)), | ||||
|       alias: this.attacker.name, | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.defender.name), | ||||
|       alias: this.attacker?.getAlias(), | ||||
|       whisper: ChatUtility.getOwners(this.defender), | ||||
|       content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html', paramDemandeDefense), | ||||
|     }); | ||||
|     // flag pour garder les jets d'attaque/defense | ||||
| @@ -946,8 +905,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,31 +916,33 @@ 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, armeAttaque) { | ||||
|     let defenses = defender.items.filter(it => RdDItemArme.isParade(it)) | ||||
|     defenses = foundry.utils.duplicate(defenses) | ||||
|     defenses.forEach(armeDefense => { | ||||
|       // Ajout du # d'utilisation ce round   | ||||
|       armeDefense.nbUsage = defender.getItemUse(armeDefense.id) | ||||
|       armeDefense.typeParade = RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) | ||||
|     }) | ||||
|  | ||||
|     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 != '') | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async _onAttaqueEchecTotal(attackerRoll) { | ||||
|     const choixEchecTotal = await ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name), | ||||
|       whisper: ChatUtility.getOwners(this.attacker), | ||||
|       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 | ||||
|       }) | ||||
|     }); | ||||
| @@ -994,9 +956,9 @@ export class RdDCombat { | ||||
|     const arme = rollData.arme; | ||||
|     const avecArme = !['', 'sans-armes', 'armes-naturelles'].includes(arme?.system.categorie_parade ?? ''); | ||||
|     const action = (rollData.attackerRoll ? (arme ? "la parade" : "l'esquive") : "l'attaque"); | ||||
|     ChatUtility.createChatWithRollMode(this.defender.name, { | ||||
|       content: `<strong>Maladresse à ${action}!</strong> ` + await RdDRollTables.getMaladresse({ arme: avecArme }) | ||||
|     }); | ||||
|     ChatUtility.createChatWithRollMode( | ||||
|       { content: `<strong>Maladresse à ${action}!</strong> ` + await RdDRollTables.getMaladresse({ arme: avecArme }) }, | ||||
|       this.defender) | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -1052,8 +1014,11 @@ export class RdDCombat { | ||||
|   /* -------------------------------------------- */ | ||||
|   _prepareParade(attackerRoll, armeParade, competenceParade) { | ||||
|     let defenderRoll = { | ||||
|       alias: this.defender?.getAlias(), | ||||
|       passeArme: attackerRoll.passeArme, | ||||
|       diffLibre: attackerRoll.diffLibre, | ||||
|       attackerToken: this.attackerToken, | ||||
|       defenderToken: this.defenderToken, | ||||
|       attackerRoll: attackerRoll, | ||||
|       competence: this.defender.getCompetence(competenceParade), | ||||
|       arme: armeParade, | ||||
| @@ -1076,9 +1041,9 @@ export class RdDCombat { | ||||
|     console.log("RdDCombat._onParadeParticuliere >>>", defenderRoll); | ||||
|     if (!defenderRoll.attackerRoll.isPart) { | ||||
|       // TODO: attaquant doit jouer résistance et peut être désarmé p132 | ||||
|       ChatUtility.createChatWithRollMode(this.defender.name, { | ||||
|         content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` | ||||
|       }); | ||||
|       ChatUtility.createChatWithRollMode( | ||||
|         { content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` }, | ||||
|         this.defender) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -1106,7 +1071,7 @@ export class RdDCombat { | ||||
|   async esquive(attackerRoll, compId, compName) { | ||||
|     const esquive = this.defender.getCompetence(compId) ?? this.defender.getCompetence(compName) | ||||
|     if (esquive == undefined) { | ||||
|       ui.notifications.error(this.defender.name + " n'a pas de compétence " + compName); | ||||
|       ui.notifications.error(this.defender.getAlias() + " n'a pas de compétence " + compName); | ||||
|       return; | ||||
|     } | ||||
|     console.log("RdDCombat.esquive >>>", attackerRoll, esquive); | ||||
| @@ -1133,8 +1098,11 @@ export class RdDCombat { | ||||
|   /* -------------------------------------------- */ | ||||
|   _prepareEsquive(attackerRoll, competence) { | ||||
|     let rollData = { | ||||
|       alias: this.defender.getAlias(), | ||||
|       passeArme: attackerRoll.passeArme, | ||||
|       diffLibre: attackerRoll.diffLibre, | ||||
|       attackerToken: this.attackerToken, | ||||
|       defenderToken: this.defenderToken, | ||||
|       attackerRoll: attackerRoll, | ||||
|       competence: competence, | ||||
|       surprise: this.defender.getSurprise(true), | ||||
| @@ -1152,9 +1120,9 @@ export class RdDCombat { | ||||
|   /* -------------------------------------------- */ | ||||
|   _onEsquiveParticuliere(rollData) { | ||||
|     console.log("RdDCombat._onEsquiveParticuliere >>>", rollData); | ||||
|     ChatUtility.createChatWithRollMode(this.defender.name, { | ||||
|       content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" | ||||
|     }); | ||||
|     ChatUtility.createChatWithRollMode( | ||||
|       { content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" }, | ||||
|       this.defender); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -1276,9 +1244,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 +1253,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 +1273,32 @@ export class RdDCombat { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async displayActorCombatStatus(combat, actor, tokenId) { | ||||
|     let formData = { | ||||
|   static async displayActorCombatStatus(combat, actor, token) { | ||||
|     if (!actor?.isActorCombat()) { | ||||
|       return | ||||
|     } | ||||
|     const alias = token?.name ?? actor.getAlias(); | ||||
|     const formData = { | ||||
|       combatId: combat._id, | ||||
|       alias: actor.name, | ||||
|       alias: alias, | ||||
|       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: alias | ||||
|     }) | ||||
|     await ChatMessage.create({ | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData), | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name), | ||||
|       alias: actor.name | ||||
|     }); | ||||
|       whisper: ChatUtility.getOwners(actor), | ||||
|       alias: alias | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| @@ -17,6 +17,7 @@ import { RdDUtility } from "./rdd-utility.js"; | ||||
| import { FenetreRechercheTirage } from "./tirage/fenetre-recherche-tirage.js"; | ||||
| import { TMRUtility } from "./tmr-utility.js"; | ||||
| import { DialogFatigueVoyage } from "./voyage/dialog-fatigue-voyage.js"; | ||||
| import { ChatUtility } from "./chat-utility.js"; | ||||
|  | ||||
| const rddRollNumeric = /^(\d+)\s*([\+\-]?\d+)?\s*(s)?/; | ||||
|  | ||||
| @@ -206,26 +207,20 @@ export class RdDCommands { | ||||
|   /* Manage chat commands */ | ||||
|   processChatCommand(commandLine, content = '', msg = {}) { | ||||
|     // Setup new message's visibility | ||||
|     let rollMode = game.settings.get("core", "rollMode"); | ||||
|     if (["gmroll", "blindroll"].includes(rollMode)) { | ||||
|       msg["whisper"] = ChatMessage.getWhisperRecipients("GM"); | ||||
|     } | ||||
|     if (rollMode === "blindroll") { | ||||
|       msg["blind"] = true; | ||||
|     } | ||||
|     msg["type"] = 0; | ||||
|     ChatUtility.applyRollMode(msg) | ||||
|     msg.type = 0; | ||||
|  | ||||
|     if (!this.commandsTable) { | ||||
|       this._registerCommands(); | ||||
|       this._registerCommands() | ||||
|     } | ||||
|  | ||||
|     let command = commandLine[0].toLowerCase(); | ||||
|     if (this._isCommandHandled(command)) { | ||||
|       let params = commandLine.slice(1); | ||||
|       this._processCommand(this.commandsTable, command, params, content, msg); | ||||
|       return true; | ||||
|       this._processCommand(this.commandsTable, command, params, content, msg) | ||||
|       return true | ||||
|     } | ||||
|     return false; | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   _isCommandHandled(command) { | ||||
| @@ -300,7 +295,7 @@ export class RdDCommands { | ||||
|   async getRencontreTMR(params) { | ||||
|     if (params.length == 1 || params.length == 2) { | ||||
|       return game.system.rdd.rencontresTMR.rollRencontre(params[0], params[1]) | ||||
|     }     | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
| @@ -365,13 +360,14 @@ export class RdDCommands { | ||||
|  | ||||
|   async getTMRAleatoire(msg, params) { | ||||
|     if (params.length < 2) { | ||||
|       let type = params[0]; | ||||
|       const tmr = await TMRUtility.getTMRAleatoire(type ? (it => it.type == type) : (it => true)); | ||||
|       return RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`); | ||||
|     } | ||||
|     else { | ||||
|       return false; | ||||
|       let type = params[0] | ||||
|       const solvedTerrain = TMRUtility.findTMRLike(type)?.type | ||||
|       if (solvedTerrain){ | ||||
|         const tmr = await TMRUtility.getTMRAleatoire(type ? (it => it.type == solvedTerrain) : (it => true)) | ||||
|         return RdDCommands._chatAnswer(msg, `Case aléatoire: ${tmr.coord} - ${tmr.label}`) | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   async findTMR(msg, params) { | ||||
| @@ -389,11 +385,11 @@ export class RdDCommands { | ||||
|   async tableRencontres(msg, params) { | ||||
|     if (params && params.length > 0) { | ||||
|       const search = Misc.join(params, ' '); | ||||
|       const solvedTerrain = TMRUtility.findTMRLike(search); | ||||
|       const solvedTerrain = TMRUtility.findTMRLike(search) | ||||
|       if (solvedTerrain == undefined) { | ||||
|         return RdDCommands._chatAnswer(msg, 'Aucune TMR correspondant à ' + search); | ||||
|       } | ||||
|       return await game.system.rdd.rencontresTMR.chatTable(solvedTerrain); | ||||
|       return await game.system.rdd.rencontresTMR.chatTable(solvedTMRType.name) | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|   | ||||
| @@ -2,19 +2,14 @@ import { ChatUtility } from "./chat-utility.js"; | ||||
| import { HIDE_DICE, SHOW_DICE } from "./constants.js"; | ||||
| import { Misc } from "./misc.js"; | ||||
|  | ||||
| function img(src) { | ||||
|   return `<img src="${src}" class="dice-img" />` | ||||
| } | ||||
|  | ||||
| function iconHeure(heure) { | ||||
| const imgHeures = [1, 2, 3, 4, 5, 6, 7, 9, 9, 10, 11, 12].map(heure => { | ||||
|   if (heure < 10) { | ||||
|     heure = '0' + heure; | ||||
|   } | ||||
|   return `systems/foundryvtt-reve-de-dragon/icons/heures/hd${heure}.webp` | ||||
| } | ||||
| const imagesHeures = [1, 2, 3, 4, 5, 6, 7, 9, 9, 10, 11, 12].map(it => iconHeure(it)); | ||||
|   return `<img src="systems/foundryvtt-reve-de-dragon/icons/heures/hd${heure}.webp" class="dice-img" />` | ||||
| }) | ||||
|  | ||||
| const imgSigneDragon = img(imagesHeures[4]); | ||||
| const imgSigneDragon = imgHeures[4] | ||||
|  | ||||
| /** De pour les jets de rencontre */ | ||||
| export class DeTMR extends Die { | ||||
| @@ -25,7 +20,7 @@ export class DeTMR extends Die { | ||||
|     return { | ||||
|       type: "dt", | ||||
|       font: "HeuresDraconiques", | ||||
|       fontScale: 0.7, | ||||
|       fontScale: 0.8, | ||||
|       labels: ['1', '2', '3', '4', '5', '6', 'd', '0'], | ||||
|       system: system | ||||
|     } | ||||
| @@ -37,13 +32,13 @@ export class DeTMR extends Die { | ||||
|   } | ||||
|  | ||||
|   async evaluate(options) { | ||||
|     await super.evaluate(options); | ||||
|     this.explode("x=8"); | ||||
|     await super.evaluate(options) | ||||
|     await this.reroll('r=8', { recursive: true }) | ||||
|     return this; | ||||
|   } | ||||
|  | ||||
|   get total() { | ||||
|     return this.values.filter(it => it != 8).reduce(Misc.sum(), 0); | ||||
|     return this.values.map(it => Misc.modulo(it, 8)).reduce(Misc.sum(), 0); | ||||
|   } | ||||
|  | ||||
|   getResultLabel(diceTerm) { | ||||
| @@ -56,13 +51,14 @@ export class DeTMR extends Die { | ||||
|  | ||||
| /** DeDraconique pour le D8 sans limite avec 8=>0 */ | ||||
| export class DeDraconique extends Die { | ||||
|   /** @override */ | ||||
|   static DENOMINATION = "r"; | ||||
|  | ||||
|   static diceSoNiceData(system) { | ||||
|     return { | ||||
|       type: "dr", | ||||
|       font: "HeuresDraconiques", | ||||
|       fontScale: 0.7, | ||||
|       fontScale: 0.8, | ||||
|       labels: ['1', '2', '3', '4', '5', '6', 'd', '0'], | ||||
|       system: system | ||||
|     } | ||||
| @@ -75,7 +71,7 @@ export class DeDraconique extends Die { | ||||
|  | ||||
|   async evaluate(options) { | ||||
|     await super.evaluate(options); | ||||
|     this.explode("x=7"); | ||||
|     await this.explode("x=7"); | ||||
|     return this; | ||||
|   } | ||||
|  | ||||
| @@ -85,7 +81,7 @@ export class DeDraconique extends Die { | ||||
|  | ||||
|   getResultLabel(diceTerm) { | ||||
|     switch (diceTerm.result) { | ||||
|       case 7: return imgSigneDragon; | ||||
|       case 7: return imgSigneDragon | ||||
|       case 8: return '0'; | ||||
|     } | ||||
|     return diceTerm.result.toString(); | ||||
| @@ -102,6 +98,7 @@ export class DeHeure extends Die { | ||||
|     return { | ||||
|       type: "dh", | ||||
|       font: "HeuresDraconiques", | ||||
|       fontScale: 1.2, | ||||
|       labels: ['v', 'i', 'f', 'o', 'd', 'e', 'l', 's', 'p', 'a', 'r', 'c'], | ||||
|       system: system | ||||
|     } | ||||
| @@ -113,15 +110,15 @@ export class DeHeure extends Die { | ||||
|   } | ||||
|  | ||||
|   getResultLabel(diceTerm) { | ||||
|     return img(imagesHeures[diceTerm.result - 1]); | ||||
|     return imgHeures[diceTerm.result - 1] | ||||
|   } | ||||
| } | ||||
|  | ||||
| export class RdDDice { | ||||
|   static init() { | ||||
|     CONFIG.Dice.terms[DeTMR.DENOMINATION] = DeTMR; | ||||
|     CONFIG.Dice.terms[DeDraconique.DENOMINATION] = DeDraconique; | ||||
|     CONFIG.Dice.terms[DeHeure.DENOMINATION] = DeHeure; | ||||
|     CONFIG.Dice.terms[DeTMR.DENOMINATION] = DeTMR | ||||
|     CONFIG.Dice.terms[DeDraconique.DENOMINATION] = DeDraconique | ||||
|     CONFIG.Dice.terms[DeHeure.DENOMINATION] = DeHeure | ||||
|   } | ||||
|  | ||||
|   static onReady() { | ||||
| @@ -132,6 +129,14 @@ export class RdDDice { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static diceSoNiceReady(dice3d) { | ||||
|     dice3d.DiceFactory.systems.keys().forEach(system => { | ||||
|       dice3d.addDicePreset(DeTMR.diceSoNiceData(system)); | ||||
|       dice3d.addDicePreset(DeDraconique.diceSoNiceData(system)); | ||||
|       dice3d.addDicePreset(DeHeure.diceSoNiceData(system)); | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   static async rollHeure(options = { showDice: HIDE_DICE }) { | ||||
|     return await RdDDice.rollTotal("1dh", options) - 1 | ||||
|   } | ||||
| @@ -155,21 +160,13 @@ export class RdDDice { | ||||
|     return array[roll - 1]; | ||||
|   } | ||||
|  | ||||
|   static diceSoNiceReady(dice3d) { | ||||
|     for (const system of Object.keys(dice3d.DiceFactory.systems)) { | ||||
|       dice3d.addDicePreset(DeTMR.diceSoNiceData(system)); | ||||
|       dice3d.addDicePreset(DeDraconique.diceSoNiceData(system)); | ||||
|       dice3d.addDicePreset(DeHeure.diceSoNiceData(system)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async showDiceSoNice(roll, options) { | ||||
|     if (options.showDice == HIDE_DICE || !game.modules.get("dice-so-nice")?.active || !game.dice3d) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     let { whisper, blind } = RdDDice._getWhisperBlind(options); | ||||
|     let { whisper, blind } = ChatUtility.applyRollMode({}, options?.rollMode); | ||||
|     if (options.forceDiceResult?.total) { | ||||
|       let terms = await RdDDice._getForcedTerms(options); | ||||
|       if (terms) { | ||||
| @@ -223,24 +220,4 @@ export class RdDDice { | ||||
|     await roll.evaluate(); | ||||
|     return roll.total; | ||||
|   } | ||||
|  | ||||
|   static _getWhisperBlind(options) { | ||||
|     let whisper = undefined; | ||||
|     let blind = false; | ||||
|     let rollMode = options.rollMode ?? game.settings.get("core", "rollMode"); | ||||
|     switch (rollMode) { | ||||
|       case "blindroll": //GM only | ||||
|         blind = true; | ||||
|       case "gmroll": //GM + rolling player | ||||
|         whisper = ChatUtility.getUsers(user => user.isGM); | ||||
|         break; | ||||
|       case "roll": //everybody | ||||
|         whisper = ChatUtility.getUsers(user => user.active); | ||||
|         break; | ||||
|       case "selfroll": | ||||
|         whisper = [game.user.id]; | ||||
|         break; | ||||
|     } | ||||
|     return { whisper, blind }; | ||||
|   } | ||||
| } | ||||
| @@ -158,9 +158,12 @@ export class RdDEmpoignade { | ||||
|     empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender)) | ||||
|     //console.log("W.", empoignade, defender.hasArmeeMeleeEquipee()) | ||||
|     if ((isNouvelle || empoignade.system.pointsemp == 0) && defender.hasArmeeMeleeEquipee()) { | ||||
|       ChatUtility.createChatWithRollMode(attacker.name, { | ||||
|         content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.html`, { attacker: attacker, defender: defender }) | ||||
|       }) | ||||
|       ChatUtility.createChatWithRollMode( | ||||
|         { | ||||
|           content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.html`, { attacker: attacker, defender: defender }) | ||||
|         }, | ||||
|         attacker | ||||
|       ) | ||||
|     } else { | ||||
|       await this.onAttaqueEmpoignadeValidee(attacker, defender) | ||||
|     } | ||||
| @@ -213,7 +216,7 @@ export class RdDEmpoignade { | ||||
|       competence: attacker.getCompetenceCorpsACorps() | ||||
|     } | ||||
|     const msg = await ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(attacker.name), | ||||
|       whisper: ChatUtility.getOwners(attacker), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.html`, rollData) | ||||
|     }) | ||||
|     RdDEmpoignade.$storeRollEmpoignade(msg, rollData); | ||||
| @@ -300,7 +303,7 @@ export class RdDEmpoignade { | ||||
|   /* -------------------------------------------- */ | ||||
|   static async $onRollContrerLiberer(rollData) { | ||||
|     let empoignade = rollData.empoignade | ||||
|      | ||||
|  | ||||
|     if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) { | ||||
|       empoignade.system.pointsemp++ | ||||
|       RdDEmpoignade.$updateEtatEmpoignade(empoignade) | ||||
| @@ -309,7 +312,7 @@ export class RdDEmpoignade { | ||||
|       empoignade.system.pointsemp-- | ||||
|       RdDEmpoignade.$updateEtatEmpoignade(empoignade) | ||||
|     } | ||||
|      | ||||
|  | ||||
|     await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html') | ||||
|     if (empoignade.system.pointsemp >= 2) { | ||||
|       let msg = await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-empoignade-entrainer.html'); | ||||
|   | ||||
| @@ -28,9 +28,9 @@ export class RdDHerbes extends Item { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async addPotionFormData(formData) { | ||||
|     formData.isSoins = formData.system.categorie.includes('Soin'); | ||||
|     formData.isRepos = formData.system.categorie.includes('Repos'); | ||||
|   static calculFormData(formData, item) { | ||||
|     formData.isSoins = item.system.categorie.includes('Soin'); | ||||
|     formData.isRepos = item.system.categorie.includes('Repos'); | ||||
|     if (formData.isSoins) { | ||||
|       RdDHerbes.calculBonusHerbe(formData, this.herbesSoins, 12); | ||||
|     } | ||||
| @@ -40,7 +40,7 @@ export class RdDHerbes extends Item { | ||||
|     formData.herbesSoins = RdDHerbes.buildHerbesList(this.herbesSoins, 12); | ||||
|     formData.herbesRepos = RdDHerbes.buildHerbesList(this.herbesRepos, 7); | ||||
|     formData.dateActuelle = game.system.rdd.calendrier.dateCourante(); | ||||
|     formData.enchantement = RdDTimestamp.splitIndexDate(formData.system.prdate); | ||||
|     formData.enchantement = RdDTimestamp.splitIndexDate(item.system.prdate); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   | ||||
| @@ -79,8 +79,7 @@ export class RdDHotbar { | ||||
|    * Actor     - open actor sheet | ||||
|    * Journal   - open journal sheet | ||||
|    */ | ||||
|   static init() { | ||||
|  | ||||
|   static initHooks() { | ||||
|     Hooks.on('hotbarDrop', (bar, documentData, slot) => { | ||||
|  | ||||
|       // Create item macro if rollable item - weapon, spell, prayer, trait, or skill | ||||
| @@ -122,7 +121,7 @@ export class RdDHotbar { | ||||
|         if (item.isCorpsACorps()) { | ||||
|           switch (categorieArme) { | ||||
|             case 'pugilat': | ||||
|               return actor.rollArme(RdDItemArme.mainsNues(actor), 'competence'); | ||||
|               return actor.rollArme(RdDItemArme.corpsACorps(actor), 'competence'); | ||||
|             case 'empoignade': | ||||
|               return actor.rollArme(RdDItemArme.empoignade(actor), 'competence'); | ||||
|           } | ||||
|   | ||||
| @@ -61,13 +61,15 @@ import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js" | ||||
| import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js" | ||||
| import { AppAstrologie } from "./sommeil/app-astrologie.js" | ||||
| import { RdDItemArmure } from "./item/armure.js" | ||||
| import { AutoAdjustDarkness as AutoAdjustDarkness } from "./time/auto-adjust-darkness.js" | ||||
| import { AutoAdjustDarkness } from "./time/auto-adjust-darkness.js" | ||||
| import { RdDCreature } from "./actor/creature.js" | ||||
| import { RdDTMRDialog } from "./rdd-tmr-dialog.js" | ||||
| import { OptionsAvancees } from "./settings/options-avancees.js" | ||||
| import { ExportScriptarium } from "./actor/export-scriptarium/export-scriptarium.js" | ||||
| import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js" | ||||
| import { RdDActorExportSheet } from "./actor/export-scriptarium/actor-encart-sheet.js" | ||||
| import { RdDStatBlockParser } from "./apps/rdd-import-stats.js" | ||||
| import { RdDItemSort } from "./item-sort.js" | ||||
|  | ||||
| /** | ||||
|  * RdD system | ||||
| @@ -80,13 +82,14 @@ export class SystemReveDeDragon { | ||||
|     const system = new SystemReveDeDragon() | ||||
|     Hooks.once('init', async () => await system.onInit()) | ||||
|     Hooks.once('diceSoNiceReady', (dice3d) => RdDDice.diceSoNiceReady(dice3d)) | ||||
|     Hooks.once('ready', () => system.onReady()) | ||||
|     Hooks.once('ready', async () => await system.onReady()) | ||||
|   } | ||||
|  | ||||
|   constructor() { | ||||
|     this.config = RDD_CONFIG | ||||
|     this.RdDUtility = RdDUtility | ||||
|     this.RdDHotbar = RdDHotbar | ||||
|     this.RdDStatBlockParser = RdDStatBlockParser | ||||
|     this.itemClasses = { | ||||
|       armure: RdDItemArmure, | ||||
|       blessure: RdDItemBlessure, | ||||
| @@ -115,24 +118,32 @@ export class SystemReveDeDragon { | ||||
|     game.system.rdd = this | ||||
|     this.AppAstrologie = AppAstrologie | ||||
|  | ||||
|  | ||||
|     console.log(`Initializing Reve de Dragon System`) | ||||
|     console.log(`Initializing Reve de Dragon System Settings`) | ||||
|  | ||||
|     // preload handlebars templates | ||||
|     RdDUtility.preloadHandlebarsTemplates() | ||||
|     AppPersonnageAleatoire.preloadHandlebars() | ||||
|     RdDItemSort.preloadHandlebars() | ||||
|  | ||||
|     /* -------------------------------------------- */ | ||||
|     this.initSystemSettings() | ||||
|     ReglesOptionnelles.initSettings() | ||||
|     OptionsAvancees.initSettings() | ||||
|     AutoAdjustDarkness.initSettings() | ||||
|     RdDTimestamp.initSettings() | ||||
|     RdDCalendrier.initSettings() | ||||
|     SystemCompendiums.initSettings() | ||||
|     DialogChronologie.initSettings() | ||||
|     RdDTMRDialog.initSettings() | ||||
|     Environnement.initSettings() | ||||
|  | ||||
|     this.initSettings() | ||||
|  | ||||
|     /* -------------------------------------------- */ | ||||
|     // Set an initiative formula for the system  | ||||
|     CONFIG.Combat.initiative = { | ||||
|       formula: "1+(1d6/10)", | ||||
|       decimals: 2 | ||||
|     } | ||||
|     CONFIG.Combat.initiative = { formula: "1+(1d6/10)", decimals: 2 } | ||||
|  | ||||
|     /* -------------------------------------------- */ | ||||
|     console.log(`Initializing Reve de Dragon Socket handlers`) | ||||
|     game.socket.on(SYSTEM_SOCKET_ID, async (sockmsg) => { | ||||
|       console.log(">>>>> MSG RECV", sockmsg) | ||||
|       try { | ||||
| @@ -147,6 +158,7 @@ export class SystemReveDeDragon { | ||||
|  | ||||
|     /* -------------------------------------------- */ | ||||
|     // Define custom Entity classes | ||||
|     console.log(`Initializing Reve de Dragon Documents`) | ||||
|     CONFIG.Actor.documentClass = RdDBaseActor | ||||
|     CONFIG.Item.documentClass = RdDItem | ||||
|     CONFIG.RDD = { | ||||
| @@ -192,17 +204,12 @@ export class SystemReveDeDragon { | ||||
|         "tarot", "extraitpoetique", "empoignade" | ||||
|       ], makeDefault: true | ||||
|     }) | ||||
|     CONFIG.Combat.documentClass = RdDCombatManager | ||||
|  | ||||
|     // préparation des différents modules | ||||
|     AutoAdjustDarkness.init() | ||||
|     RdDTimestamp.init() | ||||
|     RdDCalendrier.init() | ||||
|     SystemCompendiums.init() | ||||
|     DialogChronologie.init() | ||||
|     ReglesOptionnelles.init() | ||||
|     OptionsAvancees.init() | ||||
|     RdDUtility.init() | ||||
|     console.log(`Initializing Reve de Dragon Hooks and handlers`) | ||||
|     CONFIG.Combat.documentClass = RdDCombatManager | ||||
|     ChatUtility.init() | ||||
|     RdDUtility.initHooks() | ||||
|     RdDDice.init() | ||||
|     RdDCommands.init() | ||||
|     RdDCombatManager.init() | ||||
| @@ -211,15 +218,14 @@ export class SystemReveDeDragon { | ||||
|     RdDCompendiumOrganiser.init() | ||||
|     EffetsDraconiques.init() | ||||
|     TMRUtility.init() | ||||
|     await RdDTMRDialog.init() | ||||
|     RdDHotbar.init() | ||||
|     RdDHotbar.initHooks() | ||||
|     RdDPossession.init() | ||||
|     TMRRencontres.init() | ||||
|     Environnement.init() | ||||
|     ExportScriptarium.init() | ||||
|   } | ||||
|  | ||||
|   initSystemSettings() { | ||||
|   initSettings() { | ||||
|     // TODO: déplacer vers les modules correspondants | ||||
|     game.settings.register(SYSTEM_RDD, "accorder-entite-cauchemar", { | ||||
|       name: "Accorder le rêve aux entités", | ||||
|       hint: "A quel moment les personnages doivent accorder leur rêve aux entités de cauchemar", | ||||
| @@ -270,13 +276,23 @@ export class SystemReveDeDragon { | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   static async setupAccueil() { | ||||
|     let exists = game.scenes.find(j => j.name == "Accueil RdD"); | ||||
|     if (!exists) { | ||||
|       const scenes = await SystemCompendiums.loadCompendium("foundryvtt-reve-de-dragon.scenes-rdd") | ||||
|       let newDocuments = scenes.filter(i => i.name == "Accueil RdD"); | ||||
|       await game.scenes.documentClass.create(newDocuments); | ||||
|       game.scenes.find(i => i.name == "Accueil RdD").activate(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async onReady() { | ||||
|  | ||||
|     /* -------------------------------------------- */ | ||||
|     /*  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 => { | ||||
| @@ -290,6 +306,8 @@ export class SystemReveDeDragon { | ||||
|     StatusEffects.onReady() | ||||
|     RdDHerbes.onReady() | ||||
|     RdDDice.onReady() | ||||
|  | ||||
|     RdDStatBlockParser.parseStatBlock() | ||||
|     /* -------------------------------------------- */ | ||||
|     /* Affiche/Init le calendrier */ | ||||
|     game.system.rdd.calendrier.display() | ||||
| @@ -301,6 +319,8 @@ export class SystemReveDeDragon { | ||||
|         user: game.user.id | ||||
|       }) | ||||
|     } | ||||
|  | ||||
|     SystemReveDeDragon.setupAccueil() | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -318,4 +338,3 @@ export class SystemReveDeDragon { | ||||
| } | ||||
|  | ||||
| SystemReveDeDragon.start() | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import { ChatUtility } from "./chat-utility.js" | ||||
|  | ||||
| const vents = [ | ||||
|   { min: 0, max: 0, valeur: 'Calme' }, | ||||
| @@ -117,7 +118,7 @@ export class RdDMeteo { | ||||
|  | ||||
|     ChatMessage.create({ | ||||
|       content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-resultat-meteo.html', meteo), | ||||
|       whisper: ChatMessage.getWhisperRecipients('GM') | ||||
|       whisper: ChatUtility.getGMs() | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { RdDBaseActor } from "./actor/base-actor.js"; | ||||
| import { ChatUtility } from "./chat-utility.js"; | ||||
| import { Misc } from "./misc.js"; | ||||
| import { RdDDice } from "./rdd-dice.js"; | ||||
|  | ||||
| @@ -15,7 +16,7 @@ export class RdDNameGen { | ||||
|     const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-command-nom.html`, { | ||||
|       nom: await RdDNameGen.generate() | ||||
|     }); | ||||
|     ChatMessage.create({ content: html, whisper: ChatMessage.getWhisperRecipients("GM") }); | ||||
|     ChatMessage.create({ content: html, whisper: ChatUtility.getGMs() }); | ||||
|   } | ||||
|  | ||||
|   static async generate() { | ||||
|   | ||||
| @@ -91,13 +91,14 @@ export class RdDResolutionTable { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') { | ||||
|     return await ChatUtility.createChatWithRollMode(RdDResolutionTable.actorChatName(actor), { | ||||
|       content: await RdDResolutionTable.buildRollDataHtml(rollData, template) | ||||
|     }); | ||||
|     return await ChatUtility.createChatWithRollMode( | ||||
|       { content: await RdDResolutionTable.buildRollDataHtml(rollData, template) }, | ||||
|       actor | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   static actorChatName(actor) { | ||||
|     return actor?.name ?? game.user.name; | ||||
|     return actor ?? game.user.name; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -138,14 +139,14 @@ export class RdDResolutionTable { | ||||
|     if (carac == 0) { | ||||
|       return NaN; | ||||
|     } | ||||
|     if (rolled >= carac){ | ||||
|       const upper = Math.ceil(rolled/carac); | ||||
|       return 2*upper -10 | ||||
|     if (rolled >= carac) { | ||||
|       const upper = Math.ceil(rolled / carac); | ||||
|       return 2 * upper - 10 | ||||
|     } | ||||
|     if (rolled > Math.floor(carac/2)) { | ||||
|     if (rolled > Math.floor(carac / 2)) { | ||||
|       return -8 | ||||
|     } | ||||
|     if (rolled > Math.floor(carac/4)) { | ||||
|     if (rolled > Math.floor(carac / 4)) { | ||||
|       return -9 | ||||
|     } | ||||
|     if (rolled > 1) { | ||||
| @@ -265,7 +266,7 @@ export class RdDResolutionTable { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static subTable(carac, level, delta = { carac: 2, level: 5}) { | ||||
|   static subTable(carac, level, delta = { carac: 2, level: 5 }) { | ||||
|     return { | ||||
|       carac, | ||||
|       level, | ||||
| @@ -287,8 +288,8 @@ export class RdDResolutionTable { | ||||
|       carac: carac, | ||||
|       difficulte: level, | ||||
|       min: minLevel, | ||||
|       rows: Misc.intArray(minCarac, maxCarac+1), | ||||
|       cols: Misc.intArray(minLevel, maxLevel+1) | ||||
|       rows: Misc.intArray(minCarac, maxCarac + 1), | ||||
|       cols: Misc.intArray(minLevel, maxLevel + 1) | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -72,6 +72,6 @@ export class RdDEncaisser extends Dialog { | ||||
|         encaisserSpecial: this.encaisserSpecial, | ||||
|         mortalite: mortalite | ||||
|       } | ||||
|     }); | ||||
|     }) | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import { RdDBonus } from "./rdd-bonus.js"; | ||||
| import { RdDCarac } from "./rdd-carac.js"; | ||||
| import { RdDResolutionTable } from "./rdd-resolution-table.js"; | ||||
| import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"; | ||||
| import { Grammar } from "./grammar.js"; | ||||
|  | ||||
| /** | ||||
|  * Extend the base Dialog entity to select roll parameters | ||||
| @@ -32,7 +33,7 @@ export class RdDRoll extends Dialog { | ||||
|   /* -------------------------------------------- */ | ||||
|   static _setDefaultOptions(actor, rollData) { | ||||
|     let defaultRollData = { | ||||
|       alias: actor.name, | ||||
|       alias: actor.getAlias(), | ||||
|       ajustementsConditions: CONFIG.RDD.ajustementsConditions, | ||||
|       difficultesLibres: CONFIG.RDD.difficultesLibres, | ||||
|       etat: actor.getEtatGeneral(), | ||||
| @@ -173,8 +174,8 @@ export class RdDRoll extends Dialog { | ||||
|       this.html.find("[name='diffLibre']").val(this.rollData.diffLibre); | ||||
|     }); | ||||
|     this.html.find('.roll-carac-competence').change((event) => { | ||||
|       const competence = event.currentTarget.value; | ||||
|       this.rollData.competence = this.rollData.competences.find(it => it.name == competence); | ||||
|       const competence = event.currentTarget.value | ||||
|       this.rollData.competence = this.rollData.competences.find(it => Grammar.equalsInsensitive(it.name, competence)) | ||||
|       this.updateRollResult(html); | ||||
|     }); | ||||
|     this.html.find('.select-suivant-coeur').change((event) => { | ||||
| @@ -345,24 +346,25 @@ export class RdDRoll extends Dialog { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   _getTitle(rollData) { | ||||
|     const alias = rollData.alias | ||||
|     const carac = rollData.selectedCarac.label; | ||||
|     if (!rollData.competence) { | ||||
|       return carac; | ||||
|       return `${alias}: ${carac}` | ||||
|     } | ||||
|     const compName = rollData.competence.name; | ||||
|     const niveau = Misc.toSignedString(rollData.competence.system.niveau) | ||||
|     if (compName == carac) { | ||||
|       // cas des créatures | ||||
|       return `${carac} Niveau ${niveau}` | ||||
|       return `${alias}: ${carac} Niveau ${niveau}` | ||||
|     } | ||||
|     if (rollData.draconicList && rollData.selectedSort) { | ||||
|       // cas de lancer de sort | ||||
|       return `${rollData.competence.name} Niveau ${niveau} ${rollData.selectedSort.name}` | ||||
|       return `${alias}: ${rollData.competence.name} Niveau ${niveau} ${rollData.selectedSort.name}` | ||||
|     } | ||||
|     if (rollData.arme && rollData.arme.name != compName) { | ||||
|       // ajouter l'arme au titre si son nom n'est pas la compétence | ||||
|       return `${carac} /  ${compName} (${rollData.arme.name}) Niveau ${niveau}` | ||||
|       return `${alias}: ${carac} /  ${compName} (${rollData.arme.name}) Niveau ${niveau}` | ||||
|     } | ||||
|     return `${carac} / ${compName} Niveau ${niveau}` | ||||
|     return `${alias}: ${carac} / ${compName} Niveau ${niveau}` | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import { Grammar } from "./grammar.js"; | ||||
| import { CompendiumTable, CompendiumTableHelpers, SystemCompendiums } from "./settings/system-compendiums.js"; | ||||
|  | ||||
| export class RdDRollTables { | ||||
|   | ||||
| @@ -34,7 +34,7 @@ const TMR_DISPLAY_SIZE = { | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| export class RdDTMRDialog extends Dialog { | ||||
|   static async init() { | ||||
|   static initSettings() { | ||||
|     game.settings.register(SYSTEM_RDD, TMR_DISPLAY_SIZE.code, { | ||||
|       name: 'Taille des cases des TMR', | ||||
|       hint: "Taille en pixel des cases des TMR (réglable directement dans la fenêtre des TMR)", | ||||
| @@ -50,7 +50,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     await PixiTMR.init() | ||||
|     let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', tmrData); | ||||
|     if (tmrData.mode != 'visu' && !game.user.isGM) { | ||||
|       ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatMessage.getWhisperRecipients("GM") }); | ||||
|       ChatMessage.create({ content: actor.name + " est monté dans les TMR en mode : " + tmrData.mode, whisper: ChatUtility.getGMs() }); | ||||
|     } | ||||
|     return new RdDTMRDialog(html, actor, tmrData) | ||||
|   } | ||||
| @@ -82,7 +82,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     this.rencontreState = 'aucune'; | ||||
|     this.subdialog = undefined | ||||
|     this.displaySize = undefined | ||||
|     if (!this.viewOnly) { | ||||
|     if (!this.viewOnly && !game.user.isGM) { | ||||
|       this._tellToGM(this.actor.name + " monte dans les terres médianes (" + tmrData.mode + ")"); | ||||
|     } | ||||
|     this.callbacksOnAnimate = []; | ||||
| @@ -460,7 +460,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|  | ||||
|     let rencontreData = { | ||||
|       actor: this.actor, | ||||
|       alias: this.actor.name, | ||||
|       alias: this.actor.getAlias(), | ||||
|       reveDepart: this.actor.getReveActuel(), | ||||
|       competence: this.actor.getBestDraconic(), | ||||
|       rencontre: this.currentRencontre, | ||||
| @@ -496,7 +496,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     rencData.message = this.formatMessageRencontre(rencData, result.message); | ||||
|  | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), | ||||
|       whisper: ChatUtility.getOwners(this.actor), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-rencontre-tmr.html`, rencData) | ||||
|     }); | ||||
|  | ||||
| @@ -571,12 +571,20 @@ export class RdDTMRDialog extends Dialog { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   _tellToGM(message) { | ||||
|     ChatMessage.create({ content: message, user: game.user.id, whisper: ChatMessage.getWhisperRecipients("GM") }); | ||||
|     ChatMessage.create({ | ||||
|       user: game.user.id, | ||||
|       content: message, | ||||
|       whisper: ChatUtility.getGMs() | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   _tellToUserAndGM(message) { | ||||
|     ChatMessage.create({ content: message, user: game.user.id, whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients("GM")) }); | ||||
|     ChatMessage.create({ | ||||
|       user: game.user.id, | ||||
|       content: message, | ||||
|       whisper: ChatUtility.getUserAndGMs() | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
| @@ -633,7 +641,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     // simuler une rencontre | ||||
|     let rencontreData = { | ||||
|       actor: this.actor, | ||||
|       alias: this.actor.name, | ||||
|       alias: this.actor.getAlias(), | ||||
|       reveDepart: this.actor.getReveActuel(), | ||||
|       competence: this.actor.getBestDraconic(), | ||||
|       rencontre: this.currentRencontre, | ||||
| @@ -715,7 +723,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     } | ||||
|     rollData.poesie = await Poetique.getExtrait(); | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), | ||||
|       whisper: ChatUtility.getOwners(this.actor), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData) | ||||
|     }); | ||||
|     if (rollData.rolled.isEchec) { | ||||
| @@ -739,7 +747,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     if (this.isCaseMaitrisee(tmr.coord)) { | ||||
|       ChatMessage.create({ | ||||
|         content: tmr.label + ": cette case humide est déja maitrisée grâce à votre Tête <strong>Quête des Eaux</strong>", | ||||
|         whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|         whisper: ChatUtility.getOwners(this.actor) | ||||
|       }); | ||||
|       return false; | ||||
|     } | ||||
| @@ -751,14 +759,14 @@ export class RdDTMRDialog extends Dialog { | ||||
|     if (tmr.type == 'pont' && EffetsDraconiques.isPontImpraticable(this.actor)) { | ||||
|       ChatMessage.create({ | ||||
|         content: tmr.label + ": Vous êtes sous le coup d'une Impraticabilité des Ponts : ce pont doit être maîtrisé comme une case humide.", | ||||
|         whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|         whisper: ChatUtility.getOwners(this.actor) | ||||
|       }); | ||||
|       return true; | ||||
|     } | ||||
|     if (this.isCaseInondee(tmr.coord)) { | ||||
|       ChatMessage.create({ | ||||
|         content: tmr.label + ": cette case est inondée, elle doit être maîtrisée comme une case humide.", | ||||
|         whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|         whisper: ChatUtility.getOwners(this.actor) | ||||
|       }); | ||||
|       return true; | ||||
|     } | ||||
| @@ -832,7 +840,7 @@ export class RdDTMRDialog extends Dialog { | ||||
|     } | ||||
|     rollData.poesie = await Poetique.getExtrait(); | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), | ||||
|       whisper: ChatUtility.getOwners(this.actor), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData) | ||||
|     }); | ||||
|     if (rollData.rolled.isEchec) { | ||||
| @@ -882,17 +890,16 @@ export class RdDTMRDialog extends Dialog { | ||||
|       const reserveSecurite = EffetsDraconiques.isReserveEnSecurite(this.actor); | ||||
|       const reserveExtensible = this.isReserveExtensible(coord); | ||||
|       if (!EffetsDraconiques.isUrgenceDraconique(this.actor) && (reserveSecurite || reserveExtensible)) { | ||||
|         const msg = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-demande-declencher-sort.hbs`, { | ||||
|           actor: this.actor, | ||||
|           sorts: sorts, | ||||
|           coord: coord, | ||||
|           tete: { reserveSecurite: reserveSecurite, reserveExtensible: reserveExtensible } | ||||
|         }) | ||||
|         ChatMessage.create({ | ||||
|           content: msg, | ||||
|           whisper: ChatMessage.getWhisperRecipients(game.user.name) | ||||
|         }); | ||||
|         return; | ||||
|           content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-demande-declencher-sort.hbs`, { | ||||
|             actor: this.actor, | ||||
|             sorts: sorts, | ||||
|             coord: coord, | ||||
|             tete: { reserveSecurite: reserveSecurite, reserveExtensible: reserveExtensible } | ||||
|           }), | ||||
|           whisper: ChatUtility.getOwners(this.actor) | ||||
|         }) | ||||
|         return | ||||
|       } | ||||
|       await this.processSortReserve(sorts[0]); | ||||
|     } | ||||
| @@ -906,9 +913,8 @@ export class RdDTMRDialog extends Dialog { | ||||
|       this.processSortReserve(sort); | ||||
|     } else { | ||||
|       ChatMessage.create({ | ||||
|         content: | ||||
|           "Une erreur est survenue : impossible de récupérer le sort en réserve demandé.", | ||||
|         whisper: ChatMessage.getWhisperRecipients(game.user.name), | ||||
|         content: "Une erreur est survenue : impossible de récupérer le sort en réserve demandé.", | ||||
|         whisper: ChatUtility.getOwners(this.actor) | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| @@ -1091,8 +1097,11 @@ export class RdDTMRDialog extends Dialog { | ||||
|   async notifierResonanceSigneDraconique(coord) { | ||||
|     if (!this.viewOnly && this.actor.isResonanceSigneDraconique(coord)) { | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), | ||||
|         content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-resonance.html`, { alias: this.actor.name, typeTMR: TMRUtility.getTMRType(coord) }) | ||||
|         whisper: ChatUtility.getOwners(this.actor), | ||||
|         content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-signe-draconique-resonance.html`, { | ||||
|           alias: this.actor.getAlias(), | ||||
|           typeTMR: TMRUtility.getTMRType(coord) | ||||
|         }) | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -28,16 +28,15 @@ export class RdDTokenHud { | ||||
|     await RdDTokenHud.addExtensionHudSoins(html, actor); | ||||
|  | ||||
|     if (isCombat) { | ||||
|       let combatant = game.combat.combatants.find(c => c.tokenId == tokenId); | ||||
|       if (!(combatant?.actor)) { | ||||
|         ui.notifications.warn(`Le combatant ${token.name} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`) | ||||
|         return; | ||||
|       const combatant = game.combat.combatants.find(c => c.tokenId == tokenId) | ||||
|       const actor = RdDCombatManager.getActorCombatant(combatant, { warning: false }) | ||||
|       if (actor) { | ||||
|         let actions = RdDCombatManager.listActionsActorCombatant(actor) | ||||
|         // initiative | ||||
|         await RdDTokenHud.addExtensionHudInit(html, combatant, actions) | ||||
|         // combat | ||||
|         await RdDTokenHud.addExtensionHudCombat(html, combatant, token, actions) | ||||
|       } | ||||
|       let actions = RdDCombatManager.listActionsCombat(combatant); | ||||
|       // initiative | ||||
|       await RdDTokenHud.addExtensionHudInit(html, combatant, actions); | ||||
|       // combat | ||||
|       await RdDTokenHud.addExtensionHudCombat(html, combatant, actions); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -68,8 +67,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 +79,7 @@ export class RdDTokenHud { | ||||
|           combatant.actor.conjurerPossession(possession); | ||||
|         } | ||||
|         else { | ||||
|           combatant.actor.rollArme(action); | ||||
|           combatant.actor.rollArme(action, 'competence', token) | ||||
|         } | ||||
|       }); | ||||
|   } | ||||
|   | ||||
| @@ -99,9 +99,7 @@ export class RdDUtility { | ||||
|   // persistent handling of conteneur show/hide | ||||
|   static afficheContenu = {} | ||||
|   /* -------------------------------------------- */ | ||||
|   static async init() { | ||||
|     Hooks.on("renderChatMessage", async (app, html, msg) => await ChatUtility.onRenderChatMessage(app, html, msg)) | ||||
|     Hooks.on("createChatMessage", async (chatMessage, options, id) => await ChatUtility.onCreateChatMessage(chatMessage, options, id)) | ||||
|   static async initHooks() { | ||||
|     Hooks.on('renderChatLog', (log, html, chatLog) => RdDUtility.chatListeners(html)) | ||||
|   } | ||||
|  | ||||
| @@ -292,7 +290,7 @@ export class RdDUtility { | ||||
|  | ||||
|     Handlebars.registerHelper('array-includes', (array, value) => array.includes(value)); | ||||
|     Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1))); | ||||
|     Handlebars.registerHelper('isLastIndex', (index, list) => index+1 >= list.length); | ||||
|     Handlebars.registerHelper('isLastIndex', (index, list) => index + 1 >= list.length); | ||||
|     Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option)); | ||||
|     Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name))); | ||||
|     Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences)); | ||||
| @@ -565,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) { | ||||
| @@ -604,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; | ||||
| @@ -645,18 +648,16 @@ export class RdDUtility { | ||||
|   /* -------------------------------------------- */ | ||||
|   static onSocketMessage(sockmsg) { | ||||
|     switch (sockmsg.msg) { | ||||
|       case "msg_gm_chat_message": | ||||
|         return ChatUtility.handleGMChatMessage(sockmsg.data); | ||||
|       case "msg_app_astrologie_refresh": | ||||
|         return Hooks.callAll(APP_ASTROLOGIE_REFRESH); | ||||
|         return Hooks.callAll(APP_ASTROLOGIE_REFRESH) | ||||
|       case "msg_request_nombre_astral": | ||||
|         return game.system.rdd.calendrier.requestNombreAstral(sockmsg.data); | ||||
|         return game.system.rdd.calendrier.requestNombreAstral(sockmsg.data) | ||||
|       case "msg_tmr_move": | ||||
|         let actor = game.actors.get(sockmsg.data.actorId); | ||||
|         if (actor.isOwner || game.user.isGM) { | ||||
|           actor.refreshTMRView(); | ||||
|           actor.refreshTMRView() | ||||
|         } | ||||
|         break; | ||||
|         break | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -745,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]; | ||||
| @@ -808,17 +818,13 @@ export class RdDUtility { | ||||
|       user: game.user.id, | ||||
|       rollMode: modeOverride || game.settings.get("core", "rollMode"), | ||||
|       content: content | ||||
|     }; | ||||
|  | ||||
|     if (["gmroll", "blindroll"].includes(chatData.rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id); | ||||
|     if (chatData.rollMode === "blindroll") chatData["blind"] = true; | ||||
|     else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user]; | ||||
|     } | ||||
|     ChatUtility.applyRollMode(chatData) | ||||
|  | ||||
|     if (forceWhisper) { // Final force ! | ||||
|       chatData["speaker"] = ChatMessage.getSpeaker(); | ||||
|       chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper); | ||||
|       chatData.speaker = ChatMessage.getSpeaker(); | ||||
|       chatData.whisper = ChatMessage.getWhisperRecipients(forceWhisper); | ||||
|     } | ||||
|  | ||||
|     return chatData; | ||||
|   } | ||||
|  | ||||
| @@ -875,13 +881,13 @@ 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: ChatMessage.getWhisperRecipients("GM") | ||||
|           whisper: ChatUtility.getGMs() | ||||
|         }); | ||||
|       } | ||||
|       else if (heureNaissance) { | ||||
| @@ -900,7 +906,7 @@ export class RdDUtility { | ||||
|     if (compName.includes('Thanatos')) { | ||||
|       let message = "Vous avez mis des points d'Expérience dans la Voie de Thanatos !<br>Vous devez réduire manuellement d'un même montant d'XP une autre compétence Draconique."; | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatMessage.getWhisperRecipients(game.user.name), | ||||
|         whisper: ChatUtility.getUserAndGMs(), | ||||
|         content: message | ||||
|       }); | ||||
|     } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ const OPTIONS_AVANCEES = [ | ||||
| ] | ||||
|  | ||||
| export class OptionsAvancees extends FormApplication { | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     for (const regle of OPTIONS_AVANCEES) { | ||||
|       const name = regle.name | ||||
|       const id = OptionsAvancees._getId(name) | ||||
|   | ||||
| @@ -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" }, | ||||
| @@ -46,7 +47,7 @@ const listeReglesOptionnelles = [ | ||||
| const uniquementJoueur = listeReglesOptionnelles.filter(it => it.uniquementJoueur).map(it=>it.name); | ||||
|  | ||||
| export class ReglesOptionnelles extends FormApplication { | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     for (const regle of listeReglesOptionnelles) { | ||||
|       const name = regle.name; | ||||
|       const id = ReglesOptionnelles._getIdRegle(name); | ||||
|   | ||||
| @@ -15,17 +15,17 @@ export const STATUSES = { | ||||
| } | ||||
|  | ||||
| const rddStatusEffects = [ | ||||
|   { rdd: true, id: STATUSES.StatusStunned, label: 'EFFECT.StatusStunned', icon: 'icons/svg/stoned.svg', "duration.rounds": 1 }, | ||||
|   { rdd: true, id: STATUSES.StatusBleeding, label: 'EFFECT.StatusBleeding', icon: 'icons/svg/blood.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusProne, label: 'EFFECT.StatusProne', icon: 'icons/svg/falling.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusGrappling, tint: '#33cc33', label: 'EFFECT.StatusGrappling', icon: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' }, | ||||
|   { rdd: true, id: STATUSES.StatusGrappled, tint: '#ff9900', label: 'EFFECT.StatusGrappled', icon: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' }, | ||||
|   { rdd: true, id: STATUSES.StatusRestrained, label: 'EFFECT.StatusRestrained', icon: 'icons/svg/net.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusUnconscious, label: 'EFFECT.StatusUnconscious', icon: 'icons/svg/unconscious.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusBlind, label: 'EFFECT.StatusBlind', icon: 'icons/svg/blind.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusComma, label: 'EFFECT.StatusComma', icon: 'icons/svg/skull.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusDead, label: 'EFFECT.StatusDead', icon: 'icons/svg/skull.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusDemiReve, label: 'EFFECT.StatusDemiReve', icon: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg' } | ||||
|   { rdd: true, id: STATUSES.StatusStunned, name:  'EFFECT.StatusStunned', img: 'icons/svg/stoned.svg', "duration.rounds": 1 }, | ||||
|   { rdd: true, id: STATUSES.StatusBleeding, name:  'EFFECT.StatusBleeding', img: 'icons/svg/blood.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusProne, name:  'EFFECT.StatusProne', img: 'icons/svg/falling.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusGrappling, tint: '#33cc33', name:  'EFFECT.StatusGrappling', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' }, | ||||
|   { rdd: true, id: STATUSES.StatusGrappled, tint: '#ff9900', name:  'EFFECT.StatusGrappled', img: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp' }, | ||||
|   { rdd: true, id: STATUSES.StatusRestrained, name:  'EFFECT.StatusRestrained', img: 'icons/svg/net.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusUnconscious, name:  'EFFECT.StatusUnconscious', img: 'icons/svg/unconscious.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusBlind, name:  'EFFECT.StatusBlind', img: 'icons/svg/blind.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusComma, name:  'EFFECT.StatusComma', img: 'icons/svg/skull.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusDead, name:  'EFFECT.StatusDead', img: 'icons/svg/skull.svg' }, | ||||
|   { rdd: true, id: STATUSES.StatusDemiReve, name:  'EFFECT.StatusDemiReve', img: 'systems/foundryvtt-reve-de-dragon/icons/heures/hd12.svg' } | ||||
| ]; | ||||
| const demiReveStatusEffect = rddStatusEffects.find(it => it.id == STATUSES.StatusDemiReve); | ||||
|  | ||||
| @@ -57,7 +57,7 @@ export class StatusEffects extends FormApplication { | ||||
|       restricted: true | ||||
|     }); | ||||
|  | ||||
|     CONFIG.RDD.allEffects = rddStatusEffects.concat(CONFIG.statusEffects.filter(it => !rddEffectIds.includes(it.id))); | ||||
|     CONFIG.RDD.allEffects = rddStatusEffects.concat(CONFIG.statusEffects.filter(it => !rddEffectIds.includes(it.id))) | ||||
|  | ||||
|     StatusEffects._setUseStatusEffects(StatusEffects._getUseStatusEffects()); | ||||
|     console.log('statusEffects', CONFIG.statusEffects); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ const CONFIGURABLE_COMPENDIUMS = { | ||||
|  * ======= Gestion des accès aux compendiums systèmes (ou surchargés) ======= | ||||
|  */ | ||||
| export class SystemCompendiums extends FormApplication { | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     Object.keys(CONFIGURABLE_COMPENDIUMS).forEach(compendium => { | ||||
|       const definition = CONFIGURABLE_COMPENDIUMS[compendium]; | ||||
|       foundry.utils.mergeObject(definition, { | ||||
| @@ -112,6 +112,19 @@ export class SystemCompendiums extends FormApplication { | ||||
|     return elements; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async loadCompendiumData(compendium) { | ||||
|     const pack = game.packs.get(compendium); | ||||
|     return await pack?.getDocuments() ?? []; | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async loadCompendium(compendium, filter = item => true) { | ||||
|     let compendiumData = await SystemCompendiums.loadCompendiumData(compendium); | ||||
|     return compendiumData.filter(filter); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   static async getDefaultItems(compendium) { | ||||
|     const pack = game.packs.get(SystemCompendiums._getDefaultCompendium(compendium)); | ||||
|     if (pack.metadata.type == 'Item') { | ||||
| @@ -236,12 +249,12 @@ export class CompendiumTableHelpers { | ||||
|     let max = 0; | ||||
|     const total = rows.map(it => it.frequence).reduce(Misc.sum(), 0); | ||||
|     return rows.map(row => { | ||||
|       const frequence = row.frequence; | ||||
|       row.min = max + 1; | ||||
|       row.max = max + frequence; | ||||
|       const frequence = row.frequence | ||||
|       row.min = max + 1 | ||||
|       row.max = max + frequence | ||||
|       row.total = total | ||||
|       max += frequence; | ||||
|       return row; | ||||
|       max += frequence | ||||
|       return row | ||||
|     }) | ||||
|   } | ||||
|   static async getRandom(table, type, subTypes = ['objet'], forcedRoll = undefined, localisation = undefined) { | ||||
| @@ -260,8 +273,8 @@ export class CompendiumTableHelpers { | ||||
|     } | ||||
|     const total = table[0].total; | ||||
|     const formula = `1d${total}`; | ||||
|     if (forcedRoll == undefined && (forcedRoll > total || forcedRoll <= 0)) { | ||||
|       ui.notifications.warn(`Jet de rencontre ${forcedRoll} en dehors de la table [1..${total}], le jet est relancé`); | ||||
|     if (forcedRoll != undefined && (forcedRoll > total || forcedRoll <= 0)) { | ||||
|       ui.notifications.warn(`Jet forcé ${forcedRoll} en dehors de la table [1..${total}], le jet est relancé`); | ||||
|       forcedRoll = undefined; | ||||
|     } | ||||
|     const roll = forcedRoll ? { total: forcedRoll, formula } : await RdDDice.roll(formula, { showDice: HIDE_DICE }); | ||||
| @@ -271,32 +284,29 @@ export class CompendiumTableHelpers { | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async tableRowToChatMessage(row, type = 'Item') { | ||||
|   static async tableRowToChatMessage(row, type, options = {showSource: true}) { | ||||
|     if (!row) { | ||||
|       return; | ||||
|     } | ||||
|     const percentages = (row.total == 100) ? '%' : '' | ||||
|     const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll.html', { | ||||
|     const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll.hbs', { | ||||
|       roll: row.roll, | ||||
|       document: row.document, | ||||
|       percentages, | ||||
|       typeName: Misc.typeName(type, row.document?.type ?? 'objet'), | ||||
|       isGM: game.user.isGM, | ||||
|       options | ||||
|     }); | ||||
|     const messageData = { | ||||
|       // flavor: flavorContent, | ||||
|       user: game.user.id, | ||||
|       type: CONST.CHAT_MESSAGE_TYPES.ROLL, | ||||
|       roll: row.roll, | ||||
|       rolls: [row.roll], | ||||
|       sound: CONFIG.sounds.dice, | ||||
|       content: flavorContent | ||||
|     }; | ||||
|     await ChatUtility.createChatWithRollMode(game.user.id, messageData) | ||||
|     await ChatUtility.createChatWithRollMode(messageData) | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static async tableToChatMessage(table, type, subTypes, typeName = undefined) { | ||||
|     const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table.html', { | ||||
|     const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table.hbs', { | ||||
|       img: RdDItem.getDefaultImg(subTypes[0]), | ||||
|       typeName: typeName ?? Misc.typeName(type, subTypes[0]), | ||||
|       table, | ||||
| @@ -304,10 +314,10 @@ export class CompendiumTableHelpers { | ||||
|     }); | ||||
|     const messageData = { | ||||
|       user: game.user.id, | ||||
|       whisper: game.user.id, | ||||
|       whisper: [game.user], | ||||
|       content: flavorContent | ||||
|     }; | ||||
|     await ChatUtility.createChatWithRollMode(game.user.id, messageData) | ||||
|     await ChatUtility.createChatWithRollMode(messageData) | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -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) | ||||
|         } | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ export const AUTO_ADJUST_DARKNESS = "auto-adjust-darkness"; | ||||
|  | ||||
| export class AutoAdjustDarkness { | ||||
|  | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     game.settings.register(SYSTEM_RDD, AUTO_ADJUST_DARKNESS, { | ||||
|       name: AUTO_ADJUST_DARKNESS, | ||||
|       scope: "world", | ||||
| @@ -15,7 +15,7 @@ export class AutoAdjustDarkness { | ||||
|   } | ||||
|  | ||||
|   static async adjust(darkness) { | ||||
|     if (AutoAdjustDarkness.isAuto()) { | ||||
|     if (game.user.isGM && AutoAdjustDarkness.isAuto()) { | ||||
|       const scene = game.scenes.viewed; | ||||
|       if (scene?.environment?.globalLight?.enabled && scene?.tokenVision) { | ||||
|         await scene.update({ darkness }); | ||||
|   | ||||
| @@ -15,7 +15,7 @@ const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/ca | ||||
| const INITIAL_CALENDAR_POS = { top: 200, left: 200, horlogeAnalogique: true }; | ||||
| /* -------------------------------------------- */ | ||||
| export class RdDCalendrier extends Application { | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     game.settings.register(SYSTEM_RDD, "liste-nombre-astral", { | ||||
|       name: "liste-nombre-astral", | ||||
|       scope: "world", | ||||
| @@ -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); | ||||
|   | ||||
| @@ -49,7 +49,7 @@ const FORMULES_PERIODE = [ | ||||
|  | ||||
| export class RdDTimestamp { | ||||
|  | ||||
|   static init() { | ||||
|   static initSettings() { | ||||
|     game.settings.register(SYSTEM_RDD, WORLD_TIMESTAMP_SETTING, { | ||||
|       name: WORLD_TIMESTAMP_SETTING, | ||||
|       scope: "world", | ||||
| @@ -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; | ||||
|   } | ||||
|   | ||||
| @@ -26,22 +26,19 @@ export class TMRRencontres { | ||||
|    * @param {*} forcedRoll  | ||||
|    */ | ||||
|   async rollRencontre(terrain, forcedRoll) { | ||||
|     terrain = TMRUtility.findTMRLike(terrain); | ||||
|     if (terrain == undefined) { | ||||
|     const tmrType = TMRUtility.findTMRLike(terrain)?.type | ||||
|     if (tmrType == undefined) { | ||||
|       return undefined; | ||||
|     } | ||||
|  | ||||
|     if (forcedRoll && (forcedRoll <= 0 || forcedRoll > 100)) { | ||||
|       forcedRoll = undefined; | ||||
|     } | ||||
|     const codeTerrain = Grammar.toLowerCaseNoAccent(terrain) | ||||
|     const filtreMauvaise = codeTerrain == 'mauvaise' ? it => it.system.mauvaiseRencontre : it => !it.system.mauvaiseRencontre; | ||||
|     const frequence = it => it.system.frequence[codeTerrain]; | ||||
|     const filtreMauvaise = tmrType == 'mauvaise' ? it => it.system.mauvaiseRencontre : it => !it.system.mauvaiseRencontre; | ||||
|     const frequence = it => it.system.frequence[tmrType]; | ||||
|     const row = await this.table.getRandom(frequence, filtreMauvaise, forcedRoll); | ||||
|     if (row) { | ||||
|       console.log("DORM", row); | ||||
|       //row.document.system.computedForce = new Roll(row.document.system.formula).roll({async: false}).total;  | ||||
|       await CompendiumTableHelpers.tableRowToChatMessage(row); | ||||
|       await CompendiumTableHelpers.tableRowToChatMessage(row, 'Item', {showSource: false}); | ||||
|     } | ||||
|  | ||||
|     return row?.document; | ||||
| @@ -105,7 +102,7 @@ export class TMRRencontres { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   async $chatRolledRencontre(row, rencontre, tmr) { | ||||
|     const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll-rencontre.html', | ||||
|     const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll-rencontre.hbs', | ||||
|       { | ||||
|         roll: row.roll, | ||||
|         rencontre, | ||||
|   | ||||
| @@ -2,228 +2,216 @@ import { Misc } from "./misc.js"; | ||||
| import { Grammar } from "./grammar.js"; | ||||
| import { RdDDice } from "./rdd-dice.js"; | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| const TMRMapping = { | ||||
|   A1: { type: "cite", label: "Cité Vide" }, | ||||
|   B1: { type: "plaines", label: "Plaines d’Assorh" }, | ||||
|   C1: { type: "necropole", label: "Nécropole de Kroak" }, | ||||
|   D1: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   E1: { type: "monts", label: "Monts de Kanaï" }, | ||||
|   F1: { type: "cite", label: "Cité Glauque" }, | ||||
|   G1: { type: "desolation", label: "Désolation de Jamais" }, | ||||
|   H1: { type: "lac", label: "Lac d’Anticalme" }, | ||||
|   I1: { type: "plaines", label: "Plaines Grises" }, | ||||
|   J1: { type: "monts", label: "Monts Fainéants" }, | ||||
|   K1: { type: "cite", label: "Cité d’Onkause" }, | ||||
|   L1: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   M1: { type: "cite", label: "Cité Jalouse" }, | ||||
|  | ||||
|   A2: { type: "desert", label: "Désert de Mieux" }, | ||||
|   B2: { type: "collines", label: "Collines de Dawell" }, | ||||
|   C2: { type: "marais", label: "Marais Glignants" }, | ||||
|   D2: { type: "cite", label: "Cité de Frost" }, | ||||
|   E2: { type: "plaines", label: "Plaines de Fiask" }, | ||||
|   F2: { type: "lac", label: "Lac de Misère" }, | ||||
|   G2: { type: "marais", label: "Marais Nuisants" }, | ||||
|   H2: { type: "collines", label: "Collines de Parta" }, | ||||
|   I2: { type: "foret", label: "Forêt Fade" }, | ||||
|   J2: { type: "desert", label: "Désert de Poly" }, | ||||
|   K2: { type: "foret", label: "Forêt Tamée" }, | ||||
|   L2: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   M2: { type: "necropole", label: "Nécropole de Logos" }, | ||||
|  | ||||
|   A3: { type: "desolation", label: "Désolation de Demain" }, | ||||
|   B3: { type: "plaines", label: "Plaines de Rubéga" }, | ||||
|   C3: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   D3: { type: "gouffre", label: "Gouffre d’Oki" }, | ||||
|   E3: { type: "foret", label: "Forêt d’Estoubh" }, | ||||
|   F3: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   G3: { type: "gouffre", label: "Gouffre de Sun" }, | ||||
|   H3: { type: "foret", label: "Forêt de Ganna" }, | ||||
|   I3: { type: "monts", label: "Monts Grinçants" }, | ||||
|   J3: { type: "cite", label: "Cité Venin" }, | ||||
|   K3: { type: "plaines", label: "Plaines de Dois" }, | ||||
|   L3: { type: "lac", label: "Lac Laineux" }, | ||||
|   M3: { type: "monts", label: "Monts de Vdah" }, | ||||
|  | ||||
|   A4: { type: "foret", label: "Forêt de Falconax" }, | ||||
|   B4: { type: "monts", label: "Monts Crâneurs" }, | ||||
|   C4: { type: "pont", label: "Pont de Giolii" }, | ||||
|   D4: { type: "lac", label: "Lac de Foam" }, | ||||
|   E4: { type: "plaines", label: "Plaines d’Orti" }, | ||||
|   F4: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   G4: { type: "sanctuaire", label: "Sanctuaire Blanc" }, | ||||
|   H4: { type: "plaines", label: "Plaines de Psark" }, | ||||
|   I4: { type: "plaines", label: "Plaines de Xiax" }, | ||||
|   J4: { type: "collines", label: "Collines d’Encre" }, | ||||
|   K4: { type: "pont", label: "Pont de Fah" }, | ||||
|   L4: { type: "sanctuaire", label: "Sanctuaire Mauve" }, | ||||
|   M4: { type: "gouffre", label: "Gouffre Grisant" }, | ||||
|  | ||||
|   A5: { type: "plaines", label: "Plaines de Trilkh" }, | ||||
|   B5: { type: "collines", label: "Collines de Tanegy" }, | ||||
|   C5: { type: "marais", label: "Marais Flouants" }, | ||||
|   D5: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   E5: { type: "monts", label: "Monts Brûlants" }, | ||||
|   F5: { type: "cite", label: "Cité de Panople" }, | ||||
|   G5: { type: "pont", label: "Pont d’Ik" }, | ||||
|   H5: { type: "desert", label: "Désert de Krane" }, | ||||
|   I5: { type: "desolation", label: "Désolation de Toujours" }, | ||||
|   J5: { type: "marais", label: "Marais de Jab" }, | ||||
|   K5: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   L5: { type: "collines", label: "Collines Suaves" }, | ||||
|   M5: { type: "cite", label: "Cité Rimarde" }, | ||||
|  | ||||
|   A6: { type: "necropole", label: "Nécropole de Zniak" }, | ||||
|   B6: { type: "foret", label: "Forêt de Bust" }, | ||||
|   C6: { type: "cite", label: "Cité Pavois" }, | ||||
|   D6: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   E6: { type: "sanctuaire", label: "Sanctuaire de Plaine" }, | ||||
|   F6: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   G6: { type: "marais", label: "Marais Glutants" }, | ||||
|   H6: { type: "monts", label: "Monts Gurdes" }, | ||||
|   I6: { type: "necropole", label: "Nécropole de Xotar" }, | ||||
|   J6: { type: "lac", label: "Lac d’Iaupe" }, | ||||
|   K6: { type: "desolation", label: "Désolation de Poor" }, | ||||
|   L6: { type: "foret", label: "Forêt Gueuse" }, | ||||
|   M6: { type: "desolation", label: "Désolation de Presque" }, | ||||
|  | ||||
|   A7: { type: "plaines", label: "Plaines de l’Arc" }, | ||||
|   B7: { type: "marais", label: "Marais Bluants" }, | ||||
|   C7: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   D7: { type: "plaines", label: "Plaines d’Affa" }, | ||||
|   E7: { type: "foret", label: "Forêt de Glusks" }, | ||||
|   F7: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   G7: { type: "cite", label: "Cité de Terwa" }, | ||||
|   H7: { type: "gouffre", label: "Gouffre de Kapfa" }, | ||||
|   I7: { type: "plaines", label: "Plaines de Troo" }, | ||||
|   J7: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   K7: { type: "cite", label: "Cité de Kolix" }, | ||||
|   L7: { type: "gouffre", label: "Gouffre d’Episophe" }, | ||||
|   M7: { type: "desert", label: "Désert de Lave" }, | ||||
|  | ||||
|   A8: { type: "gouffre", label: "Gouffre de Shok" }, | ||||
|   B8: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   C8: { type: "foret", label: "Forêt Turmide" }, | ||||
|   D8: { type: "cite", label: "Cité d’Olak" }, | ||||
|   E8: { type: "plaines", label: "Plaines d’Iolise" }, | ||||
|   F8: { type: "lac", label: "Lac des Chats" }, | ||||
|   G8: { type: "plaines", label: "Plaines Sans Joie" }, | ||||
|   H8: { type: "foret", label: "Forêt d’Ourf" }, | ||||
|   I8: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   J8: { type: "monts", label: "Monts Barask" }, | ||||
|   K8: { type: "desert", label: "Désert de Fumée" }, | ||||
|   L8: { type: "monts", label: "Monts Tavelés" }, | ||||
|   M8: { type: "plaines", label: "Plaines Lavées" }, | ||||
|  | ||||
|   A9: { type: "collines", label: "Collines de Korrex" }, | ||||
|   B9: { type: "lac", label: "Lac de Lucre" }, | ||||
|   C9: { type: "monts", label: "Monts Tuméfiés" }, | ||||
|   D9: { type: "pont", label: "Pont d’Orx" }, | ||||
|   E9: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   F9: { type: "plaines", label: "Plaines de Foe" }, | ||||
|   G9: { type: "desolation", label: "Désolation de Sel" }, | ||||
|   H9: { type: "collines", label: "Collines de Noirseul" }, | ||||
|   I9: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   J9: { type: "marais", label: "Marais Gronchants" }, | ||||
|   K9: { type: "sanctuaire", label: "Sanctuaire Noir" }, | ||||
|   L9: { type: "collines", label: "Collines Cornues" }, | ||||
|   M9: { type: "necropole", label: "Nécropole de Zonar" }, | ||||
|  | ||||
|   A10: { type: "sanctuaire", label: "Sanctuaire d’Olis" }, | ||||
|   B10: { type: "monts", label: "Monts Salés" }, | ||||
|   C10: { type: "marais", label: "Marais de Dom" }, | ||||
|   D10: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   E10: { type: "gouffre", label: "Gouffre de Junk" }, | ||||
|   F10: { type: "marais", label: "Marais Zultants" }, | ||||
|   G10: { type: "cite", label: "Cité de Sergal" }, | ||||
|   H10: { type: "plaines", label: "Plaines Noires" }, | ||||
|   I10: { type: "lac", label: "Lac Wanito" }, | ||||
|   J10: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   K10: { type: "plaines", label: "Plaines Jaunes" }, | ||||
|   L10: { type: "desert", label: "Désert de Nicrop" }, | ||||
|   M10: { type: "foret", label: "Forêt de Jajou" }, | ||||
|  | ||||
|   A11: { type: "desolation", label: "Désolation d’Hier" }, | ||||
|   B11: { type: "cite", label: "Cité de Brilz" }, | ||||
|   C11: { type: "pont", label: "Pont de Roï" }, | ||||
|   D11: { type: "desolation", label: "Désolation de Partout" }, | ||||
|   E11: { type: "lac", label: "Lac de Glinster" }, | ||||
|   F11: { type: "cite", label: "Cité de Noape" }, | ||||
|   G11: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   H11: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   I11: { type: "pont", label: "Pont de Yalm" }, | ||||
|   J11: { type: "plaines", label: "Plaines de Miltiar" }, | ||||
|   K11: { type: "cite", label: "Cité Tonnerre" }, | ||||
|   L11: { type: "collines", label: "Collines de Kol" }, | ||||
|   M11: { type: "cite", label: "Cité Crapaud" }, | ||||
|  | ||||
|   A12: { type: "plaines", label: "Plaines Sages" }, | ||||
|   B12: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   C12: { type: "lac", label: "Lac de Fricassa" }, | ||||
|   D12: { type: "collines", label: "Collines d’Huaï" }, | ||||
|   E12: { type: "monts", label: "Monts Ajourés" }, | ||||
|   F12: { type: "necropole", label: "Nécropole de Throat" }, | ||||
|   G12: { type: "plaines", label: "Plaines de Lufmil" }, | ||||
|   H12: { type: "collines", label: "Collines de Tooth" }, | ||||
|   I12: { type: "gouffre", label: "Gouffre Abimeux" }, | ||||
|   J12: { type: "cite", label: "Cité Folle" }, | ||||
|   K12: { type: "desolation", label: "Désolation d’Amour" }, | ||||
|   L12: { type: "plaines", label: "Plaines Venteuses" }, | ||||
|   M12: { type: "collines", label: "Collines Révulsantes" }, | ||||
|  | ||||
|   A13: { type: "fleuve", label: "Fleuve de l'Oubli" }, | ||||
|   B13: { type: "gouffre", label: "Gouffre des Litiges" }, | ||||
|   C13: { type: "desert", label: "Désert de Neige" }, | ||||
|   D13: { type: "cite", label: "Cité Sordide" }, | ||||
|   E13: { type: "plaines", label: "Plaines de Xnez" }, | ||||
|   F13: { type: "foret", label: "Forêt des Cris" }, | ||||
|   G13: { type: "plaines", label: "Plaines Calcaires" }, | ||||
|   H13: { type: "desolation", label: "Désolation de Rien" }, | ||||
|   I13: { type: "monts", label: "Monts Bigleux" }, | ||||
|   J13: { type: "gouffre", label: "Gouffre de Gromph" }, | ||||
|   K13: { type: "foret", label: "Forêt de Kluth" }, | ||||
|   L13: { type: "monts", label: "Monts Dormants" }, | ||||
|   M13: { type: "plaines", label: "Plaines d’Anjou" }, | ||||
|  | ||||
|   A14: { type: "collines", label: "Collines de Stolis" }, | ||||
|   B14: { type: "necropole", label: "Nécropole de Gorlo" }, | ||||
|   C14: { type: "foret", label: "Forêt de Bissam" }, | ||||
|   D14: { type: "sanctuaire", label: "Sanctuaire Plat" }, | ||||
|   E14: { type: "monts", label: "Monts de Quath" }, | ||||
|   F14: { type: "plaines", label: "Plaines Brisées" }, | ||||
|   G14: { type: "desert", label: "Désert de Sek" }, | ||||
|   H14: { type: "plaines", label: "Plaines Blanches" }, | ||||
|   I14: { type: "cite", label: "Cité Destituée" }, | ||||
|   J14: { type: "desert", label: "Désert de Sank" }, | ||||
|   K14: { type: "necropole", label: "Nécropole d’Antinéar" }, | ||||
|   L14: { type: "plaines", label: "Plaines de Jislith" }, | ||||
|   M14: { type: "desolation", label: "Désolation d’Après" }, | ||||
|  | ||||
|   A15: { type: "cite", label: "Cité de Mielh" }, | ||||
|   C15: { type: "plaines", label: "Plaines de Toué" }, | ||||
|   E15: { type: "foret", label: "Forêt des Furies" }, | ||||
|   G15: { type: "plaines", label: "Plaines des Soupirs" }, | ||||
|   I15: { type: "monts", label: "Monts des Dragées" }, | ||||
|   K15: { type: "collines", label: "Collines Pourpres" }, | ||||
|   M15: { type: "cite", label: "Cité de Klana" } | ||||
| export const TMRType = { | ||||
|   cite: { type: 'cite', name: "cité", genre: "f" }, | ||||
|   sanctuaire: { type: 'sanctuaire', name: "sanctuaire", genre: 'm' }, | ||||
|   plaines: { type: 'plaines', name: "plaines", genre: "fp" }, | ||||
|   pont: { type: 'pont', name: "pont", genre: "m" }, | ||||
|   collines: { type: 'collines', name: "collines", genre: "p" }, | ||||
|   foret: { type: 'foret', name: "forêt", genre: "f" }, | ||||
|   monts: { type: 'monts', name: "monts", genre: "p" }, | ||||
|   desert: { type: 'desert', name: "désert", genre: "m" }, | ||||
|   fleuve: { type: 'fleuve', name: "fleuve", genre: "m" }, | ||||
|   lac: { type: 'lac', name: "lac", genre: "m" }, | ||||
|   marais: { type: 'marais', name: "marais", genre: "m" }, | ||||
|   gouffre: { type: 'gouffre', name: "gouffre", genre: "m" }, | ||||
|   necropole: { type: 'necropole', name: "nécropole", genre: "f" }, | ||||
|   desolation: { type: 'desolation', name: "désolation", genre: "f" } | ||||
| } | ||||
|  | ||||
| export const TMRType = { | ||||
|   cite: { name: "cité", genre: "f" }, | ||||
|   sanctuaire: { name: "sanctuaire", genre: 'm' }, | ||||
|   plaines: { name: "plaines", genre: "fp" }, | ||||
|   pont: { name: "pont", genre: "m" }, | ||||
|   collines: { name: "collines", genre: "p" }, | ||||
|   foret: { name: "forêt", genre: "f" }, | ||||
|   monts: { name: "monts", genre: "p" }, | ||||
|   desert: { name: "désert", genre: "m" }, | ||||
|   fleuve: { name: "fleuve", genre: "m" }, | ||||
|   lac: { name: "lac", genre: "m" }, | ||||
|   marais: { name: "marais", genre: "m" }, | ||||
|   gouffre: { name: "gouffre", genre: "m" }, | ||||
|   necropole: { name: "nécropole", genre: "f" }, | ||||
|   desolation: { name: "désolation", genre: "f" } | ||||
| export const FLEUVE_COORD = 'Fleuve' | ||||
|  | ||||
| const TMRMapping = { | ||||
|   Fleuve: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   A1: { type: TMRType.cite.type, label: "Cité Vide" }, | ||||
|   B1: { type: TMRType.plaines.type, label: "Plaines d’Assorh" }, | ||||
|   C1: { type: TMRType.necropole.type, label: "Nécropole de Kroak" }, | ||||
|   D1: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   E1: { type: TMRType.monts.type, label: "Monts de Kanaï" }, | ||||
|   F1: { type: TMRType.cite.type, label: "Cité Glauque" }, | ||||
|   G1: { type: TMRType.desolation.type, label: "Désolation de Jamais" }, | ||||
|   H1: { type: TMRType.lac.type, label: "Lac d’Anticalme" }, | ||||
|   I1: { type: TMRType.plaines.type, label: "Plaines Grises" }, | ||||
|   J1: { type: TMRType.monts.type, label: "Monts Fainéants" }, | ||||
|   K1: { type: TMRType.cite.type, label: "Cité d’Onkause" }, | ||||
|   L1: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   M1: { type: TMRType.cite.type, label: "Cité Jalouse" }, | ||||
|   A2: { type: TMRType.desert.type, label: "Désert de Mieux" }, | ||||
|   B2: { type: TMRType.collines.type, label: "Collines de Dawell" }, | ||||
|   C2: { type: TMRType.marais.type, label: "Marais Glignants" }, | ||||
|   D2: { type: TMRType.cite.type, label: "Cité de Frost" }, | ||||
|   E2: { type: TMRType.plaines.type, label: "Plaines de Fiask" }, | ||||
|   F2: { type: TMRType.lac.type, label: "Lac de Misère" }, | ||||
|   G2: { type: TMRType.marais.type, label: "Marais Nuisants" }, | ||||
|   H2: { type: TMRType.collines.type, label: "Collines de Parta" }, | ||||
|   I2: { type: TMRType.foret.type, label: "Forêt Fade" }, | ||||
|   J2: { type: TMRType.desert.type, label: "Désert de Poly" }, | ||||
|   K2: { type: TMRType.foret.type, label: "Forêt Tamée" }, | ||||
|   L2: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   M2: { type: TMRType.necropole.type, label: "Nécropole de Logos" }, | ||||
|   A3: { type: TMRType.desolation.type, label: "Désolation de Demain" }, | ||||
|   B3: { type: TMRType.plaines.type, label: "Plaines de Rubéga" }, | ||||
|   C3: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   D3: { type: TMRType.gouffre.type, label: "Gouffre d’Oki" }, | ||||
|   E3: { type: TMRType.foret.type, label: "Forêt d’Estoubh" }, | ||||
|   F3: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   G3: { type: TMRType.gouffre.type, label: "Gouffre de Sun" }, | ||||
|   H3: { type: TMRType.foret.type, label: "Forêt de Ganna" }, | ||||
|   I3: { type: TMRType.monts.type, label: "Monts Grinçants" }, | ||||
|   J3: { type: TMRType.cite.type, label: "Cité Venin" }, | ||||
|   K3: { type: TMRType.plaines.type, label: "Plaines de Dois" }, | ||||
|   L3: { type: TMRType.lac.type, label: "Lac Laineux" }, | ||||
|   M3: { type: TMRType.monts.type, label: "Monts de Vdah" }, | ||||
|   A4: { type: TMRType.foret.type, label: "Forêt de Falconax" }, | ||||
|   B4: { type: TMRType.monts.type, label: "Monts Crâneurs" }, | ||||
|   C4: { type: TMRType.pont.type, label: "Pont de Giolii" }, | ||||
|   D4: { type: TMRType.lac.type, label: "Lac de Foam" }, | ||||
|   E4: { type: TMRType.plaines.type, label: "Plaines d’Orti" }, | ||||
|   F4: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   G4: { type: TMRType.sanctuaire.type, label: "Sanctuaire Blanc" }, | ||||
|   H4: { type: TMRType.plaines.type, label: "Plaines de Psark" }, | ||||
|   I4: { type: TMRType.plaines.type, label: "Plaines de Xiax" }, | ||||
|   J4: { type: TMRType.collines.type, label: "Collines d’Encre" }, | ||||
|   K4: { type: TMRType.pont.type, label: "Pont de Fah" }, | ||||
|   L4: { type: TMRType.sanctuaire.type, label: "Sanctuaire Mauve" }, | ||||
|   M4: { type: TMRType.gouffre.type, label: "Gouffre Grisant" }, | ||||
|   A5: { type: TMRType.plaines.type, label: "Plaines de Trilkh" }, | ||||
|   B5: { type: TMRType.collines.type, label: "Collines de Tanegy" }, | ||||
|   C5: { type: TMRType.marais.type, label: "Marais Flouants" }, | ||||
|   D5: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   E5: { type: TMRType.monts.type, label: "Monts Brûlants" }, | ||||
|   F5: { type: TMRType.cite.type, label: "Cité de Panople" }, | ||||
|   G5: { type: TMRType.pont.type, label: "Pont d’Ik" }, | ||||
|   H5: { type: TMRType.desert.type, label: "Désert de Krane" }, | ||||
|   I5: { type: TMRType.desolation.type, label: "Désolation de Toujours" }, | ||||
|   J5: { type: TMRType.marais.type, label: "Marais de Jab" }, | ||||
|   K5: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   L5: { type: TMRType.collines.type, label: "Collines Suaves" }, | ||||
|   M5: { type: TMRType.cite.type, label: "Cité Rimarde" }, | ||||
|   A6: { type: TMRType.necropole.type, label: "Nécropole de Zniak" }, | ||||
|   B6: { type: TMRType.foret.type, label: "Forêt de Bust" }, | ||||
|   C6: { type: TMRType.cite.type, label: "Cité Pavois" }, | ||||
|   D6: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   E6: { type: TMRType.sanctuaire.type, label: "Sanctuaire de Plaine" }, | ||||
|   F6: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   G6: { type: TMRType.marais.type, label: "Marais Glutants" }, | ||||
|   H6: { type: TMRType.monts.type, label: "Monts Gurdes" }, | ||||
|   I6: { type: TMRType.necropole.type, label: "Nécropole de Xotar" }, | ||||
|   J6: { type: TMRType.lac.type, label: "Lac d’Iaupe" }, | ||||
|   K6: { type: TMRType.desolation.type, label: "Désolation de Poor" }, | ||||
|   L6: { type: TMRType.foret.type, label: "Forêt Gueuse" }, | ||||
|   M6: { type: TMRType.desolation.type, label: "Désolation de Presque" }, | ||||
|   A7: { type: TMRType.plaines.type, label: "Plaines de l’Arc" }, | ||||
|   B7: { type: TMRType.marais.type, label: "Marais Bluants" }, | ||||
|   C7: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   D7: { type: TMRType.plaines.type, label: "Plaines d’Affa" }, | ||||
|   E7: { type: TMRType.foret.type, label: "Forêt de Glusks" }, | ||||
|   F7: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   G7: { type: TMRType.cite.type, label: "Cité de Terwa" }, | ||||
|   H7: { type: TMRType.gouffre.type, label: "Gouffre de Kapfa" }, | ||||
|   I7: { type: TMRType.plaines.type, label: "Plaines de Troo" }, | ||||
|   J7: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   K7: { type: TMRType.cite.type, label: "Cité de Kolix" }, | ||||
|   L7: { type: TMRType.gouffre.type, label: "Gouffre d’Episophe" }, | ||||
|   M7: { type: TMRType.desert.type, label: "Désert de Lave" }, | ||||
|   A8: { type: TMRType.gouffre.type, label: "Gouffre de Shok" }, | ||||
|   B8: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   C8: { type: TMRType.foret.type, label: "Forêt Turmide" }, | ||||
|   D8: { type: TMRType.cite.type, label: "Cité d’Olak" }, | ||||
|   E8: { type: TMRType.plaines.type, label: "Plaines d’Iolise" }, | ||||
|   F8: { type: TMRType.lac.type, label: "Lac des Chats" }, | ||||
|   G8: { type: TMRType.plaines.type, label: "Plaines Sans Joie" }, | ||||
|   H8: { type: TMRType.foret.type, label: "Forêt d’Ourf" }, | ||||
|   I8: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   J8: { type: TMRType.monts.type, label: "Monts Barask" }, | ||||
|   K8: { type: TMRType.desert.type, label: "Désert de Fumée" }, | ||||
|   L8: { type: TMRType.monts.type, label: "Monts Tavelés" }, | ||||
|   M8: { type: TMRType.plaines.type, label: "Plaines Lavées" }, | ||||
|   A9: { type: TMRType.collines.type, label: "Collines de Korrex" }, | ||||
|   B9: { type: TMRType.lac.type, label: "Lac de Lucre" }, | ||||
|   C9: { type: TMRType.monts.type, label: "Monts Tuméfiés" }, | ||||
|   D9: { type: TMRType.pont.type, label: "Pont d’Orx" }, | ||||
|   E9: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   F9: { type: TMRType.plaines.type, label: "Plaines de Foe" }, | ||||
|   G9: { type: TMRType.desolation.type, label: "Désolation de Sel" }, | ||||
|   H9: { type: TMRType.collines.type, label: "Collines de Noirseul" }, | ||||
|   I9: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   J9: { type: TMRType.marais.type, label: "Marais Gronchants" }, | ||||
|   K9: { type: TMRType.sanctuaire.type, label: "Sanctuaire Noir" }, | ||||
|   L9: { type: TMRType.collines.type, label: "Collines Cornues" }, | ||||
|   M9: { type: TMRType.necropole.type, label: "Nécropole de Zonar" }, | ||||
|   A10: { type: TMRType.sanctuaire.type, label: "Sanctuaire d’Olis" }, | ||||
|   B10: { type: TMRType.monts.type, label: "Monts Salés" }, | ||||
|   C10: { type: TMRType.marais.type, label: "Marais de Dom" }, | ||||
|   D10: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   E10: { type: TMRType.gouffre.type, label: "Gouffre de Junk" }, | ||||
|   F10: { type: TMRType.marais.type, label: "Marais Zultants" }, | ||||
|   G10: { type: TMRType.cite.type, label: "Cité de Sergal" }, | ||||
|   H10: { type: TMRType.plaines.type, label: "Plaines Noires" }, | ||||
|   I10: { type: TMRType.lac.type, label: "Lac Wanito" }, | ||||
|   J10: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   K10: { type: TMRType.plaines.type, label: "Plaines Jaunes" }, | ||||
|   L10: { type: TMRType.desert.type, label: "Désert de Nicrop" }, | ||||
|   M10: { type: TMRType.foret.type, label: "Forêt de Jajou" }, | ||||
|   A11: { type: TMRType.desolation.type, label: "Désolation d’Hier" }, | ||||
|   B11: { type: TMRType.cite.type, label: "Cité de Brilz" }, | ||||
|   C11: { type: TMRType.pont.type, label: "Pont de Roï" }, | ||||
|   D11: { type: TMRType.desolation.type, label: "Désolation de Partout" }, | ||||
|   E11: { type: TMRType.lac.type, label: "Lac de Glinster" }, | ||||
|   F11: { type: TMRType.cite.type, label: "Cité de Noape" }, | ||||
|   G11: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   H11: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   I11: { type: TMRType.pont.type, label: "Pont de Yalm" }, | ||||
|   J11: { type: TMRType.plaines.type, label: "Plaines de Miltiar" }, | ||||
|   K11: { type: TMRType.cite.type, label: "Cité Tonnerre" }, | ||||
|   L11: { type: TMRType.collines.type, label: "Collines de Kol" }, | ||||
|   M11: { type: TMRType.cite.type, label: "Cité Crapaud" }, | ||||
|   A12: { type: TMRType.plaines.type, label: "Plaines Sages" }, | ||||
|   B12: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   C12: { type: TMRType.lac.type, label: "Lac de Fricassa" }, | ||||
|   D12: { type: TMRType.collines.type, label: "Collines d’Huaï" }, | ||||
|   E12: { type: TMRType.monts.type, label: "Monts Ajourés" }, | ||||
|   F12: { type: TMRType.necropole.type, label: "Nécropole de Throat" }, | ||||
|   G12: { type: TMRType.plaines.type, label: "Plaines de Lufmil" }, | ||||
|   H12: { type: TMRType.collines.type, label: "Collines de Tooth" }, | ||||
|   I12: { type: TMRType.gouffre.type, label: "Gouffre Abimeux" }, | ||||
|   J12: { type: TMRType.cite.type, label: "Cité Folle" }, | ||||
|   K12: { type: TMRType.desolation.type, label: "Désolation d’Amour" }, | ||||
|   L12: { type: TMRType.plaines.type, label: "Plaines Venteuses" }, | ||||
|   M12: { type: TMRType.collines.type, label: "Collines Révulsantes" }, | ||||
|   A13: { type: TMRType.fleuve.type, label: "Fleuve de l'Oubli" }, | ||||
|   B13: { type: TMRType.gouffre.type, label: "Gouffre des Litiges" }, | ||||
|   C13: { type: TMRType.desert.type, label: "Désert de Neige" }, | ||||
|   D13: { type: TMRType.cite.type, label: "Cité Sordide" }, | ||||
|   E13: { type: TMRType.plaines.type, label: "Plaines de Xnez" }, | ||||
|   F13: { type: TMRType.foret.type, label: "Forêt des Cris" }, | ||||
|   G13: { type: TMRType.plaines.type, label: "Plaines Calcaires" }, | ||||
|   H13: { type: TMRType.desolation.type, label: "Désolation de Rien" }, | ||||
|   I13: { type: TMRType.monts.type, label: "Monts Bigleux" }, | ||||
|   J13: { type: TMRType.gouffre.type, label: "Gouffre de Gromph" }, | ||||
|   K13: { type: TMRType.foret.type, label: "Forêt de Kluth" }, | ||||
|   L13: { type: TMRType.monts.type, label: "Monts Dormants" }, | ||||
|   M13: { type: TMRType.plaines.type, label: "Plaines d’Anjou" }, | ||||
|   A14: { type: TMRType.collines.type, label: "Collines de Stolis" }, | ||||
|   B14: { type: TMRType.necropole.type, label: "Nécropole de Gorlo" }, | ||||
|   C14: { type: TMRType.foret.type, label: "Forêt de Bissam" }, | ||||
|   D14: { type: TMRType.sanctuaire.type, label: "Sanctuaire Plat" }, | ||||
|   E14: { type: TMRType.monts.type, label: "Monts de Quath" }, | ||||
|   F14: { type: TMRType.plaines.type, label: "Plaines Brisées" }, | ||||
|   G14: { type: TMRType.desert.type, label: "Désert de Sek" }, | ||||
|   H14: { type: TMRType.plaines.type, label: "Plaines Blanches" }, | ||||
|   I14: { type: TMRType.cite.type, label: "Cité Destituée" }, | ||||
|   J14: { type: TMRType.desert.type, label: "Désert de Sank" }, | ||||
|   K14: { type: TMRType.necropole.type, label: "Nécropole d’Antinéar" }, | ||||
|   L14: { type: TMRType.plaines.type, label: "Plaines de Jislith" }, | ||||
|   M14: { type: TMRType.desolation.type, label: "Désolation d’Après" }, | ||||
|   A15: { type: TMRType.cite.type, label: "Cité de Mielh" }, | ||||
|   C15: { type: TMRType.plaines.type, label: "Plaines de Toué" }, | ||||
|   E15: { type: TMRType.foret.type, label: "Forêt des Furies" }, | ||||
|   G15: { type: TMRType.plaines.type, label: "Plaines des Soupirs" }, | ||||
|   I15: { type: TMRType.monts.type, label: "Monts des Dragées" }, | ||||
|   K15: { type: TMRType.collines.type, label: "Collines Pourpres" }, | ||||
|   M15: { type: TMRType.cite.type, label: "Cité de Klana" } | ||||
| } | ||||
|  | ||||
| /* -------------------------------------------- */ | ||||
| @@ -251,13 +239,16 @@ const TMR_MOVE = { | ||||
|  */ | ||||
| export class TMRUtility { | ||||
|   static init() { | ||||
|  | ||||
|     for (let coord in TMRMapping) { | ||||
|       const tmr = TMRMapping[coord]; | ||||
|       tmr.coord = coord; | ||||
|       tmr.oddq = TMRUtility.coordTMRToOddq(coord); | ||||
|       tmr.genre = TMRType[tmr.type].genre; | ||||
|       const tmr = TMRMapping[coord] | ||||
|       tmr.coord = coord | ||||
|       tmr.genre = TMRType[tmr.type].genre | ||||
|       if (coord != FLEUVE_COORD) { | ||||
|         tmr.oddq = TMRUtility.coordTMRToOddq(coord) | ||||
|       } | ||||
|     } | ||||
|     let tmrByType = Misc.classify(Object.values(TMRMapping)); | ||||
|     let tmrByType = Misc.classify(Object.values(TMRMapping).filter(it => it.coord != FLEUVE_COORD)) | ||||
|     for (const [type, list] of Object.entries(tmrByType)) { | ||||
|       TMRType[type].list = list; | ||||
|     } | ||||
| @@ -265,14 +256,17 @@ export class TMRUtility { | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static verifyTMRCoord(coord) { | ||||
|     return Grammar.equalsInsensitive(coord, 'Fleuve') || TMRUtility.getTMR(coord); | ||||
|     return Grammar.equalsInsensitive(coord, FLEUVE_COORD) || TMRUtility.getTMR(coord); | ||||
|   } | ||||
|  | ||||
|   /* -------------------------------------------- */ | ||||
|   static getTMR(coord) { | ||||
|     return coord == 'Fleuve' ? TMRMapping['D1'] : TMRMapping[coord]; | ||||
|     return coord == FLEUVE_COORD ? TMRMapping['D1'] : TMRMapping[coord]; | ||||
|   } | ||||
|  | ||||
|   static isFleuve(coord) { | ||||
|     return TMRMapping[coord]?.type == TMRType.fleuve.type | ||||
|   } | ||||
|   static getTMRLabel(coord) { | ||||
|     return TMRUtility.getTMR(coord)?.label ?? (coord + ": case inconnue"); | ||||
|   } | ||||
| @@ -290,18 +284,18 @@ export class TMRUtility { | ||||
|   static findTMRLike(type, options = { inclusMauvaise: true }) { | ||||
|     const choix = [...Object.values(TMRType)] | ||||
|     if (options.inclusMauvaise) { | ||||
|       choix.push({ name: 'Mauvaise' }); | ||||
|       choix.push({ name: 'Mauvaise', type: 'mauvaise'}); | ||||
|     } | ||||
|     const selection = Misc.findAllLike(type, choix).map(it => it.name); | ||||
|     const selection = Misc.findAllLike(type, choix) | ||||
|     if (selection.length == 0) { | ||||
|       ui.notifications.warn(`Un type de TMR doit être indiqué, '${type}' n'est pas trouvé dans ${choix}`); | ||||
|       return undefined; | ||||
|       return undefined | ||||
|     } | ||||
|     if (selection.length > 1) { | ||||
|       ui.notifications.warn(`Plusieurs types de TMR pourraient correspondre à '${type}': ${selection}`); | ||||
|       ui.notifications.warn(`Plusieurs types de TMR pourraient correspondre à '${type}': ${selection.map(it => it.name)}`); | ||||
|       return undefined; | ||||
|     } | ||||
|     return selection[0]; | ||||
|     return selection[0] | ||||
|   } | ||||
|  | ||||
|   static typeTmrName(type) { | ||||
| @@ -326,7 +320,7 @@ export class TMRUtility { | ||||
|   /* -------------------------------------------- */ | ||||
|   static deplacement(coordOrig, moveName) { | ||||
|     const tmrMove = TMR_MOVE[moveName]; | ||||
|     if (! tmrMove) { | ||||
|     if (!tmrMove) { | ||||
|       ui.notifications.error(`Le déplacement dans les TMR '${moveName}' est inconnu`) | ||||
|       return coordOrig | ||||
|     } | ||||
|   | ||||
| @@ -30,7 +30,7 @@ export class EffetsRencontre { | ||||
|   static $reve_plus = async (actor, reve) => { | ||||
|     if (!ReglesOptionnelles.isUsing("recuperation-reve") && reve < 0) {  | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name), | ||||
|         whisper: ChatUtility.getOwners(actor), | ||||
|         content: `Pas de récupération de rêve (${reve} points ignorés)` | ||||
|       }); | ||||
|       return | ||||
| @@ -112,7 +112,7 @@ export class EffetsRencontre { | ||||
|       poesie: await Poetique.getExtrait() | ||||
|     }) | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(context.actor.name), | ||||
|       whisper: ChatUtility.getOwners(context.actor), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, context) | ||||
|     }); | ||||
|   } | ||||
| @@ -127,7 +127,7 @@ export class EffetsRencontre { | ||||
|     } | ||||
|  | ||||
|     ChatMessage.create({ | ||||
|       whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), | ||||
|       whisper: ChatUtility.getOwners(context.actor), | ||||
|       content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-reve-de-dragon.html`, context) | ||||
|     }); | ||||
|   } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ export class PresentCites extends Draconique { | ||||
|     let existants = actor.items.filter(it => this.isCase(it)).map(it => it.system.coord); | ||||
|     if (existants.length > 0) { | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), | ||||
|         whisper: ChatUtility.getOwners(actor), | ||||
|         content: "Vous avez encore des présents dans des cités, vous devrez tirer une autre tête pour remplacer celle ci!" | ||||
|       }) | ||||
|     } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ export class UrgenceDraconique extends Draconique { | ||||
|       // La queue se transforme en idée fixe | ||||
|       const ideeFixe = await RdDRollTables.getIdeeFixe(); | ||||
|       ChatMessage.create({ | ||||
|         whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), | ||||
|         whisper: ChatUtility.getOwners(actor), | ||||
|         content: `En l'absence de sorts en réserve, l'urgence draconique de ${actor.name} se transforme en ${ideeFixe.name}` | ||||
|       }); | ||||
|       await actor.createEmbeddedDocuments('Item', [ideeFixe]); | ||||
|   | ||||
| @@ -175,7 +175,7 @@ export class DialogFatigueVoyage extends Dialog { | ||||
|       .forEach(async it => { | ||||
|         const perteFatigue = fatigueBase + it.ajustement | ||||
|         ChatMessage.create({ | ||||
|           whisper: ChatUtility.getWhisperRecipientsAndGMs(it.actor.name), | ||||
|           whisper: ChatUtility.getOwners(it.actor), | ||||
|           content: await renderTemplate( | ||||
|             'systems/foundryvtt-reve-de-dragon/templates/voyage/chat-fatigue_voyage.hbs', | ||||
|             foundry.utils.mergeObject(it, | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								pack_ref/animaux/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/animaux/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										0
									
								
								pack_ref/animaux/000023.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/animaux/000023.log
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								pack_ref/animaux/CURRENT
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pack_ref/animaux/CURRENT
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| MANIFEST-000022 | ||||
							
								
								
									
										0
									
								
								pack_ref/animaux/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/animaux/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								pack_ref/animaux/LOG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pack_ref/animaux/LOG
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| 2024/11/10-18:39:00.183093 7fed63e006c0 Recovering log #21 | ||||
| 2024/11/10-18:39:00.195436 7fed63e006c0 Delete type=0 #21 | ||||
| 2024/11/10-18:39:00.195482 7fed63e006c0 Delete type=3 #20 | ||||
							
								
								
									
										3
									
								
								pack_ref/animaux/LOG.old
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pack_ref/animaux/LOG.old
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| 2024/11/10-18:38:41.533242 7f4c0b8006c0 Recovering log #19 | ||||
| 2024/11/10-18:38:41.547823 7f4c0b8006c0 Delete type=0 #19 | ||||
| 2024/11/10-18:38:41.547917 7f4c0b8006c0 Delete type=3 #18 | ||||
							
								
								
									
										
											BIN
										
									
								
								pack_ref/animaux/MANIFEST-000022
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/animaux/MANIFEST-000022
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pack_ref/archetypes/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/archetypes/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										0
									
								
								pack_ref/archetypes/000011.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/archetypes/000011.log
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								pack_ref/archetypes/CURRENT
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pack_ref/archetypes/CURRENT
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| MANIFEST-000010 | ||||
							
								
								
									
										0
									
								
								pack_ref/archetypes/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/archetypes/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								pack_ref/archetypes/LOG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pack_ref/archetypes/LOG
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| 2024/11/10-18:39:00.253481 7fed62a006c0 Recovering log #8 | ||||
| 2024/11/10-18:39:00.263751 7fed62a006c0 Delete type=3 #6 | ||||
| 2024/11/10-18:39:00.263866 7fed62a006c0 Delete type=0 #8 | ||||
							
								
								
									
										8
									
								
								pack_ref/archetypes/LOG.old
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pack_ref/archetypes/LOG.old
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| 2024/11/08-19:40:34.139501 7f9d7f4006c0 Recovering log #4 | ||||
| 2024/11/08-19:40:34.182207 7f9d7f4006c0 Delete type=3 #2 | ||||
| 2024/11/08-19:40:34.182311 7f9d7f4006c0 Delete type=0 #4 | ||||
| 2024/11/08-19:56:45.129455 7f9d7d6006c0 Level-0 table #9: started | ||||
| 2024/11/08-19:56:45.129487 7f9d7d6006c0 Level-0 table #9: 0 bytes OK | ||||
| 2024/11/08-19:56:45.135738 7f9d7d6006c0 Delete type=0 #7 | ||||
| 2024/11/08-19:56:45.142639 7f9d7d6006c0 Manual compaction at level-0 from '!actors!1Nng9d8r6lrPHCaJ' @ 72057594037927935 : 1 .. '!actors.items!ryUZTa17LzNv25UY.zyNYa3hYtrOcF2jA' @ 0 : 0; will stop at (end) | ||||
| 2024/11/08-19:56:45.142697 7f9d7d6006c0 Manual compaction at level-1 from '!actors!1Nng9d8r6lrPHCaJ' @ 72057594037927935 : 1 .. '!actors.items!ryUZTa17LzNv25UY.zyNYa3hYtrOcF2jA' @ 0 : 0; will stop at (end) | ||||
							
								
								
									
										
											BIN
										
									
								
								pack_ref/archetypes/MANIFEST-000010
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/archetypes/MANIFEST-000010
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pack_ref/arts-et-divertissements/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/arts-et-divertissements/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										0
									
								
								pack_ref/arts-et-divertissements/000011.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/arts-et-divertissements/000011.log
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								pack_ref/arts-et-divertissements/CURRENT
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pack_ref/arts-et-divertissements/CURRENT
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| MANIFEST-000010 | ||||
							
								
								
									
										0
									
								
								pack_ref/arts-et-divertissements/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/arts-et-divertissements/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								pack_ref/arts-et-divertissements/LOG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pack_ref/arts-et-divertissements/LOG
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| 2024/11/10-18:39:00.448675 7fed63e006c0 Recovering log #8 | ||||
| 2024/11/10-18:39:00.460061 7fed63e006c0 Delete type=3 #6 | ||||
| 2024/11/10-18:39:00.460115 7fed63e006c0 Delete type=0 #8 | ||||
							
								
								
									
										8
									
								
								pack_ref/arts-et-divertissements/LOG.old
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pack_ref/arts-et-divertissements/LOG.old
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| 2024/11/08-19:40:32.605216 7f9d7fe006c0 Recovering log #4 | ||||
| 2024/11/08-19:40:32.664872 7f9d7fe006c0 Delete type=3 #2 | ||||
| 2024/11/08-19:40:32.665020 7f9d7fe006c0 Delete type=0 #4 | ||||
| 2024/11/08-19:56:44.948429 7f9d7d6006c0 Level-0 table #9: started | ||||
| 2024/11/08-19:56:44.948495 7f9d7d6006c0 Level-0 table #9: 0 bytes OK | ||||
| 2024/11/08-19:56:44.954943 7f9d7d6006c0 Delete type=0 #7 | ||||
| 2024/11/08-19:56:44.975424 7f9d7d6006c0 Manual compaction at level-0 from '!items!240HPtZsgZQERFMF' @ 72057594037927935 : 1 .. '!items!zRQ5WnPI483CKm9Q' @ 0 : 0; will stop at (end) | ||||
| 2024/11/08-19:56:44.975473 7f9d7d6006c0 Manual compaction at level-1 from '!items!240HPtZsgZQERFMF' @ 72057594037927935 : 1 .. '!items!zRQ5WnPI483CKm9Q' @ 0 : 0; will stop at (end) | ||||
							
								
								
									
										
											BIN
										
									
								
								pack_ref/arts-et-divertissements/MANIFEST-000010
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/arts-et-divertissements/MANIFEST-000010
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pack_ref/competences-creatures/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/competences-creatures/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										0
									
								
								pack_ref/competences-creatures/000011.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/competences-creatures/000011.log
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								pack_ref/competences-creatures/CURRENT
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pack_ref/competences-creatures/CURRENT
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| MANIFEST-000010 | ||||
							
								
								
									
										0
									
								
								pack_ref/competences-creatures/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pack_ref/competences-creatures/LOCK
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								pack_ref/competences-creatures/LOG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pack_ref/competences-creatures/LOG
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| 2024/11/10-18:39:00.493027 7fed63e006c0 Recovering log #8 | ||||
| 2024/11/10-18:39:00.503119 7fed63e006c0 Delete type=3 #6 | ||||
| 2024/11/10-18:39:00.503242 7fed63e006c0 Delete type=0 #8 | ||||
							
								
								
									
										8
									
								
								pack_ref/competences-creatures/LOG.old
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pack_ref/competences-creatures/LOG.old
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| 2024/11/08-19:40:32.670029 7f9d7ea006c0 Recovering log #4 | ||||
| 2024/11/08-19:40:32.734322 7f9d7ea006c0 Delete type=3 #2 | ||||
| 2024/11/08-19:40:32.734474 7f9d7ea006c0 Delete type=0 #4 | ||||
| 2024/11/08-19:56:44.968549 7f9d7d6006c0 Level-0 table #9: started | ||||
| 2024/11/08-19:56:44.968601 7f9d7d6006c0 Level-0 table #9: 0 bytes OK | ||||
| 2024/11/08-19:56:44.975299 7f9d7d6006c0 Delete type=0 #7 | ||||
| 2024/11/08-19:56:44.975466 7f9d7d6006c0 Manual compaction at level-0 from '!items!0zRL8bOpCXNQnIR4' @ 72057594037927935 : 1 .. '!items!yDHZfK4RmwQW4YaW' @ 0 : 0; will stop at (end) | ||||
| 2024/11/08-19:56:44.975488 7f9d7d6006c0 Manual compaction at level-1 from '!items!0zRL8bOpCXNQnIR4' @ 72057594037927935 : 1 .. '!items!yDHZfK4RmwQW4YaW' @ 0 : 0; will stop at (end) | ||||
							
								
								
									
										
											BIN
										
									
								
								pack_ref/competences-creatures/MANIFEST-000010
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/competences-creatures/MANIFEST-000010
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pack_ref/competences-entites/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pack_ref/competences-entites/000005.ldb
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user