Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd3c228b32 | ||
|
|
9b3eceb96b | ||
|
|
73b91285e3 | ||
|
|
1238460cc8 | ||
|
|
d590dfce7f | ||
|
|
332da735f8 | ||
|
|
4a6913a2ba | ||
|
|
3a55d8e912 | ||
|
|
7caeb0c918 | ||
|
|
188e7676fa | ||
|
|
bb56a3a809 | ||
|
|
ac2e2027a0 | ||
|
|
dca201c5e2 | ||
|
|
5db30ddeb3 | ||
|
|
3758d0ca85 | ||
|
|
d8cfd02807 | ||
|
|
9aca2c32ef | ||
|
|
882aa5fbcb | ||
|
|
1272cfdfd1 | ||
|
|
2348b2262b | ||
|
|
81cb3bb6bd | ||
|
|
e0e3d4f230 | ||
|
|
3afd111711 | ||
|
|
1c288b654a | ||
|
|
a5843888c0 | ||
|
|
7d9ebd601a | ||
|
|
25e3df1f5d | ||
|
|
84ce2f78d7 | ||
|
|
8fddbe698c | ||
|
|
d0a434fb0b | ||
|
|
27b232776c | ||
|
|
d06f1de433 | ||
|
|
54fc1e8e1a | ||
|
|
70092ffd8f | ||
|
|
36b33890db | ||
|
|
ec2bf050d4 | ||
|
|
00edfe8201 | ||
|
|
6fdb07a4e8 | ||
|
|
c4f9150fdb | ||
|
|
8400d88677 | ||
|
|
93a1478cba | ||
|
|
51b05d025a | ||
|
|
348536556a | ||
|
|
afabba430a | ||
|
|
3e63054b9d | ||
|
|
07997576cd | ||
|
|
d216e8fcf6 | ||
|
|
29cdcca1bb | ||
|
|
8c9c8f3345 | ||
|
|
8e748a1d94 | ||
|
|
7599db07ad | ||
|
|
1db733f379 | ||
|
|
07a48fb3cc | ||
|
|
f52575f413 | ||
|
|
5969009eb1 |
@@ -0,0 +1,28 @@
|
||||
# Session Summary: v13 Migration — DataModel + _id/duplicate Cleanup + template.json removal
|
||||
|
||||
## Done
|
||||
|
||||
### `duplicate()` → `deepClone()` (finding #10, 36 files)
|
||||
`foundry.utils.duplicate()` → `foundry.utils.deepClone()` across the system. `deepClone` preserves Document prototypes where `duplicate` stripped them.
|
||||
|
||||
### `_id` → `id` on Documents (finding #11, finding #7, ~25 sites)
|
||||
- 21 read accesses migrated during initial sweep
|
||||
- Review sweep caught 3 more: `rdd-combat.js:148` (combatant._id), `rdd-combat.js:898` (esquive e._id), `rdd-tmr-dialog.js:261` (casetmr._id)
|
||||
- Also fixed: `actor.js:1844` (selectedSort._id), `actor.js:2884` (herbeData._id), `base-actor.js:404` (conteneur c._id)
|
||||
- Remaining `_id` references are on POJOs (`toObject()`/migration data) — correct.
|
||||
|
||||
### `_onClose` → `super._onClose()` (finding #9)
|
||||
All dialog `_onClose` overrides now call `super._onClose()` for proper HandlebarsApplicationMixin cleanup.
|
||||
|
||||
### Actor DataModel Migration (3 phases)
|
||||
- Phase 1: 5 DataModels in `module/models/` + helpers + `CONFIG.Actor.dataModels` registration
|
||||
- Phase 2: `prepareActorData()` chain → DataModel `prepareDerivedData()` in each model
|
||||
- Phase 3: template.json deletion, dead code cleanup
|
||||
|
||||
### `template.json` elimination (like `fvtt-hawkmoon-cyd`)
|
||||
- `template.json` deleted. Foundry v13+ uses `documentTypes` in system.json.
|
||||
- Added `documentTypes.Actor` (5 types) + `documentTypes.Item` (44 types) with `htmlFields`
|
||||
- Fixed CI/CD files referencing template.json
|
||||
|
||||
## Still remaining (v14-blocking)
|
||||
V1 Application/Dialog classes (24 `extends Dialog`, 6+ V1 sheets) cannot run in v14 — findings #1–#6.
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
run: node ./tools/packCompendiumsToDist.mjs
|
||||
|
||||
- name: Create ZIP file
|
||||
run: zip -r ./rddsystem.zip system.json template.json README.md LICENSE.txt assets/ css/ fonts/ icons lang/ module/ packs/ pic/ sounds/ styles/ templates/
|
||||
run: zip -r ./rddsystem.zip system.json README.md LICENSE.txt assets/ css/ fonts/ icons lang/ module/ packs/ pic/ sounds/ styles/ templates/
|
||||
|
||||
- name: setup go
|
||||
uses: actions/setup-go@v3
|
||||
|
||||
@@ -13,3 +13,6 @@ todo.md
|
||||
/packs/*/LOG
|
||||
/packs/*/LOCK
|
||||
.github/
|
||||
*.bak
|
||||
/AGENTS.md
|
||||
/regles/
|
||||
|
||||
@@ -12,7 +12,6 @@ test:
|
||||
stage: test
|
||||
script:
|
||||
- python -mjson.tool 'system.json' > /dev/null
|
||||
- python -mjson.tool 'template.json' > /dev/null
|
||||
|
||||
build:
|
||||
stage: build
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
# TODO v14 — Compatibilité Foundry VTT
|
||||
|
||||
## ✅ Bloquant — terminé
|
||||
|
||||
### 1. ✅ Sheets acteur V1 → V2
|
||||
- V1 déplacés vers `_deprecated/module/actor/` (10 fichiers `.js`)
|
||||
- V2 dans `module/applications/sheets/` (fichiers `.mjs`)
|
||||
|
||||
### 2. ✅ Sheet journal V1 → V2
|
||||
- `module/journal/journal-sheet.mjs` → `extends JournalEntryPageProseMirrorSheet`
|
||||
- `_prepareContentContext` override → `RdDTextEditor.enrichHTML()`
|
||||
- `_onRender` → roll-text/chat-roll-text click listeners
|
||||
|
||||
### 3. ✅ 24 classes `extends Dialog` → V2
|
||||
- Toutes converties en `extends foundry.applications.api.DialogV2`
|
||||
- `module/tirage/fenetre-recherche-tirage.js` — `FenetreRechercheTirage` migré V1 App → V2 ApplicationV2
|
||||
- `module/sommeil/dialog-nouveau-repos-cd.js` — `new Dialog(...)` direct → `DialogV2.wait()`
|
||||
|
||||
### 4. ✅ 3 classes `extends Application` → V2
|
||||
- `module/tirage/fenetre-recherche-tirage.js` — FAIT (ApplicationV2)
|
||||
- `module/time/rdd-calendrier.js` (RdDCalendrier) — FAIT (ApplicationV2)
|
||||
- `module/sommeil/app-astrologie.js` (AppAstrologie) — FAIT (ApplicationV2)
|
||||
|
||||
### 5. ✅ 1 classe `extends FormApplication` → V2
|
||||
- `module/actor/random/app-personnage-aleatoire.js` — FAIT (ApplicationV2)
|
||||
|
||||
### 6. ✅ 7 appels `new Dialog({...})` directs → DialogV2
|
||||
Tous convertis. +3 autres dans `module/applications/sheets/base-actor-sheet.mjs` & `personnage-sheet.mjs`.
|
||||
|
||||
## 🟠 Haute priorité
|
||||
|
||||
### 7. ✅ `_id` dans `updateEmbeddedDocuments` → utiliser `.id` — **FAIT** (10 occ. changées, 2 déjà OK, 1 string param)
|
||||
|
||||
### 8. ✅ `bringToFront()` sur apps V1 → adapter
|
||||
Rétrocompatibilité V2 gère. Pas d'erreur constatée (les apps converties utilisent V2).
|
||||
|
||||
### 9. ✅ `_onClose()` sans `super._onClose()` → **FAIT**
|
||||
- `module/dialog-gerer-potions.js:45-49`
|
||||
- `module/dialog-gerer-ingredients.js:45-49`
|
||||
- `module/dialog-gerer-herbes.js:47-51`
|
||||
|
||||
## 🟡 Priorité moyenne
|
||||
|
||||
### 10. ✅ `foundry.utils.duplicate()` → `deepClone()` — **FAIT** (36 fichiers)
|
||||
|
||||
### 11. ✅ `._id` en lecture → `.id` — **FAIT** (21 occ. changées, 11 conservées sur objets de données)
|
||||
~45 occurrences : `item._id`, `actor._id`, `combatant._id`, `token._id`…
|
||||
|
||||
### 12. `render(true)` sur apps V2 → `render({force: true})`
|
||||
Rétrocompatibilité V2 normalise booléen → pas prioritaire.
|
||||
~60 occurrences dans module/ — nos fichiers convertis l'utilisent déjà.
|
||||
|
||||
### 13. ✅ Incohérence registration sheets
|
||||
`module/applications/sheets/common-item-sheet.mjs` — `Items.registerSheet()` → `foundry.documents.collections.Items.registerSheet()`
|
||||
|
||||
## 🟢 Basse priorité / Info
|
||||
|
||||
### 14. ✅ `canvas` sans vérification `ready`
|
||||
27 occurrences dans 11 fichiers sécurisées avec `?.`
|
||||
|
||||
### 15. ✅ `element[0]` jQuery
|
||||
`module/tirage/fenetre-recherche-tirage.js:213` — déjà résolu par refacto antérieur
|
||||
|
||||
### 16. ✅ `_warnedAppV1 = true`
|
||||
Supprimé de `rdd-calendrier.js`; restant dans `_deprecated/` ignoré
|
||||
|
||||
### 17. ✅ `system.json` — nom de langue
|
||||
`"name": "English"` → `"Français"` pour ne pas tromper sur `lang/fr.json`
|
||||
@@ -2,7 +2,7 @@ import { RdDUtility } from "./rdd-utility.js";
|
||||
import { HtmlUtility } from "./html-utility.js";
|
||||
import { RdDBonus } from "./rdd-bonus.js";
|
||||
import { Misc } from "./misc.js";
|
||||
import { RdDCombatManager } from "./rdd-combat.js";
|
||||
import { RdDCombat } from "./combat/rdd-combat.js";
|
||||
import { CARACS, RdDCarac } from "./rdd-carac.js";
|
||||
import { DialogSplitItem } from "./dialog-split-item.js";
|
||||
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
|
||||
@@ -13,7 +13,7 @@ import { ACTOR_TYPES, ITEM_TYPES } from "./constants.js";
|
||||
import { RdDItem } from "./item.js";
|
||||
import { RdDItemCompetence } from "./item-competence.js";
|
||||
import { RdDItemBlessure } from "./item/blessure.js";
|
||||
import { RdDEmpoignade } from "./rdd-empoignade.js";
|
||||
import { RdDEmpoignade } from "./combat/rdd-empoignade.js";
|
||||
import { RdDBaseActorSangSheet } from "./actor/base-actor-sang-sheet.js";
|
||||
import { RdDCoeur } from "./coeur/rdd-coeur.js";
|
||||
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js";
|
||||
@@ -126,7 +126,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
|
||||
.forEach(tache => {
|
||||
if (tache.isTacheCommune()) {
|
||||
const tacheCommune = tache.getTacheOuCommune()
|
||||
tache.system = foundry.utils.duplicate(tacheCommune.system)
|
||||
tache.system = foundry.utils.deepClone(tacheCommune.system)
|
||||
tache.system.tacheCommuneId = tache.system.tacheCommuneId
|
||||
}
|
||||
return tache
|
||||
@@ -214,7 +214,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
|
||||
this.html.find('.action-jeu').click(async event => await this.actor.rollJeu(RdDSheetUtility.getItemId(event)))
|
||||
this.html.find('.action-recettecuisine').click(async event => await this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event)))
|
||||
|
||||
this.html.find('.description-aleatoire').click(async event => new AppPersonnageAleatoire(this.actor).render(true))
|
||||
this.html.find('.description-aleatoire').click(async event => new AppPersonnageAleatoire(this.actor).render({ force: true }))
|
||||
|
||||
this.html.find('.gerer-herbes').click(async event => {
|
||||
const { DialogGererHerbes } = await import("./dialog-gerer-herbes.js")
|
||||
@@ -265,7 +265,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
|
||||
this.html.find('.roll-init-arme').click(async event => {
|
||||
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id)
|
||||
if (combatant) {
|
||||
RdDCombatManager.rollInitiativeAction(combatant._id, this._getActionCombat(event));
|
||||
RdDCombat.rollInitiativeAction(combatant.id, this._getActionCombat(event));
|
||||
} else {
|
||||
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
|
||||
}
|
||||
@@ -323,6 +323,8 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
|
||||
this.html.find('.moral-neutre').click(async event => await this.actor.jetDeMoral(MORAL.NEUTRE))
|
||||
this.html.find('.moral-heureux').click(async event => await this.actor.jetDeMoral(MORAL.HEUREUX))
|
||||
this.html.find('.button-ethylisme').click(async event => await this.actor.jetEthylisme())
|
||||
this.html.find('.btn-sust-externe').click(async event => await this.actor.mangerExterne())
|
||||
this.html.find('.btn-sust-rations').click(async event => await this.actor.mangerRations())
|
||||
|
||||
this.html.find('.ptreve-actuel-plus').click(async event => await this.actor.reveActuelIncDec(1))
|
||||
this.html.find('.ptreve-actuel-moins').click(async event => await this.actor.reveActuelIncDec(-1))
|
||||
@@ -371,18 +373,13 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
|
||||
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
|
||||
}
|
||||
content += '</select>';
|
||||
let dialog = new Dialog({
|
||||
new foundry.applications.api.DialogV2({
|
||||
title: "Créer une oeuvre",
|
||||
content: content,
|
||||
buttons: {
|
||||
create: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Créer l'oeuvre",
|
||||
callback: () => this.actor.createItem($(".item-type").val())
|
||||
}
|
||||
}
|
||||
});
|
||||
dialog.render(true);
|
||||
content,
|
||||
buttons: [
|
||||
{ label: "Créer l'oeuvre", action: "create", icon: "fa-solid fa-check", callback: () => this.actor.createItem($(".item-type").val()) }
|
||||
]
|
||||
}).render({ force: true });
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -428,7 +425,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
|
||||
async _onSplitItem(item, split) {
|
||||
if (split >= 1 && split < item.system.quantite) {
|
||||
await item.diminuerQuantite(split);
|
||||
const splitItem = foundry.utils.duplicate(item);
|
||||
const splitItem = foundry.utils.deepClone(item);
|
||||
splitItem.system.quantite = split;
|
||||
await this.actor.createEmbeddedDocuments('Item', [splitItem])
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { RdDItem } from "../item.js";
|
||||
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js";
|
||||
import { ItemAction } from "../item/item-actions.js";
|
||||
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
|
||||
import { DialogVerifierIngredients } from "../dialog-verifier-ingredients.js";
|
||||
import { DialogLancerRecette } from "../dialog-lancer-recette.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
@@ -90,6 +92,8 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
|
||||
this.html.find('.actionItem').click(async event => await ItemAction.onActionItem(event, this.actor, this.options))
|
||||
this.html.find('.item-edit').click(async event => await this.itemActionEdit(event))
|
||||
this.html.find('.item-verify-ingredients').click(async event => await this._onVerifyIngredients(event))
|
||||
this.html.find('.item-lancer-recette').click(async event => await this._onLancerRecette(event))
|
||||
this.html.find('.conteneur-name a').click(async event => {
|
||||
RdDUtility.toggleAfficheContenu(this.getItemId(event))
|
||||
this.render(true)
|
||||
@@ -137,35 +141,57 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
event.preventDefault()
|
||||
const item = this.getItem(event)
|
||||
if (!item?.isInventaire()) return
|
||||
new Dialog({
|
||||
new foundry.applications.api.DialogV2({
|
||||
title: `Quantité — ${item.name}`,
|
||||
content: `<p>Réduire la quantité de :</p>
|
||||
<select class="qty-amount" style="width:100%">
|
||||
<select class="qty-amount">
|
||||
<option value="1">1</option>
|
||||
<option value="5">5</option>
|
||||
<option value="10">10</option>
|
||||
<option value="all">Tout jeter</option>
|
||||
</select>`,
|
||||
buttons: {
|
||||
reduce: {
|
||||
icon: '<i class="fas fa-minus"></i>',
|
||||
label: 'Retirer',
|
||||
callback: html => {
|
||||
const amount = html.find('.qty-amount').val()
|
||||
if (amount === 'all') {
|
||||
item.delete()
|
||||
this.render(true)
|
||||
} else {
|
||||
item.update({ 'system.quantite': Math.max(0, item.system.quantite - parseInt(amount)) })
|
||||
}
|
||||
buttons: [
|
||||
{ action: "reduce", label: "Retirer", icon: "fa-solid fa-minus", callback: (event, button, dialog) => {
|
||||
const amount = dialog.element.querySelector('.qty-amount').value
|
||||
if (amount === 'all') {
|
||||
item.delete()
|
||||
this.render({ force: true })
|
||||
} else {
|
||||
item.update({ 'system.quantite': Math.max(0, item.system.quantite - parseInt(amount)) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}).render(true)
|
||||
}}
|
||||
]
|
||||
}).render({ force: true })
|
||||
})
|
||||
|
||||
this.html.find('.creer-un-objet').click(async event => await this.selectObjetTypeToCreate())
|
||||
this.html.find('.nettoyer-conteneurs').click(async event => await this.actor.nettoyerConteneurs())
|
||||
this.html.find('.quick-add-ration').click(async event => {
|
||||
const existante = this.actor.items.find(it => it.type === "nourritureboisson" && it.name === "Ration")
|
||||
if (existante) {
|
||||
await existante.update({ "system.quantite": Math.max(0, Number(existante.system.quantite) + 1) })
|
||||
} else {
|
||||
await this.actor.createEmbeddedDocuments('Item', [{
|
||||
name: "Ration", type: "nourritureboisson",
|
||||
img: "systems/foundryvtt-reve-de-dragon/icons/objets/provision_crue.webp",
|
||||
system: { sust: 1, encombrement: 0.1, quantite: 1, cout: 0.1, boisson: false }
|
||||
}], { renderSheet: false })
|
||||
}
|
||||
this.render(true)
|
||||
})
|
||||
this.html.find('.quick-add-eau').click(async event => {
|
||||
const existante = this.actor.items.find(it => it.type === "nourritureboisson" && it.name === "Eau")
|
||||
if (existante) {
|
||||
await existante.update({ "system.quantite": Math.max(0, Number(existante.system.quantite) + 1) })
|
||||
} else {
|
||||
await this.actor.createEmbeddedDocuments('Item', [{
|
||||
name: "Eau", type: "nourritureboisson",
|
||||
img: "systems/foundryvtt-reve-de-dragon/icons/objets/outre.webp",
|
||||
system: { desaltere: 1, encombrement: 0.1, quantite: 1, cout: 0.05, boisson: true }
|
||||
}], { renderSheet: false })
|
||||
}
|
||||
this.render(true)
|
||||
})
|
||||
|
||||
this.html.find('.vue-detaillee').click(async event => {
|
||||
this.options.vueDetaillee = !this.options.vueDetaillee
|
||||
@@ -174,8 +200,29 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
}
|
||||
|
||||
itemActionEdit(event) {
|
||||
const item = this.getItem(event);
|
||||
return item?.sheet.render(true);
|
||||
const item = this.getItem(event)
|
||||
if (item) item.sheet.render(true)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async _onVerifyIngredients(event) {
|
||||
event.preventDefault()
|
||||
const item = this.getItem(event)
|
||||
if (!item || item.type !== ITEM_TYPES.recettealchimique) return
|
||||
DialogVerifierIngredients.create(item, this.actor)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async _onLancerRecette(event) {
|
||||
event.preventDefault()
|
||||
const item = this.getItem(event)
|
||||
if (!item || item.type !== ITEM_TYPES.recettealchimique) return
|
||||
const ingredients = Misc.safeJsonParse(item.system.ingredients, [])
|
||||
if (!ingredients.length) {
|
||||
ui.notifications.warn('Cette recette n\'a pas d\'ingrédients définis.')
|
||||
return
|
||||
}
|
||||
DialogLancerRecette.create(item, this.actor)
|
||||
}
|
||||
|
||||
_rechercherKeyup(event) {
|
||||
@@ -262,18 +309,13 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
|
||||
}
|
||||
content += '</select>';
|
||||
let d = new Dialog({
|
||||
new foundry.applications.api.DialogV2({
|
||||
title: "Créer un équipement",
|
||||
content: content,
|
||||
buttons: {
|
||||
create: {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: "Créer l'objet",
|
||||
callback: () => this.actor.createItem($(".item-type").val())
|
||||
}
|
||||
}
|
||||
});
|
||||
d.render(true);
|
||||
content,
|
||||
buttons: [
|
||||
{ action: "create", label: "Créer l'objet", icon: "fa-solid fa-check", callback: () => this.actor.createItem($(".item-type").val()) }
|
||||
]
|
||||
}).render({ force: true });
|
||||
}
|
||||
|
||||
getTypesInventaire() {
|
||||
@@ -302,7 +344,7 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
async _onSplitItem(item, split) {
|
||||
if (split >= 1 && split < item.system.quantite) {
|
||||
await item.diminuerQuantite(split);
|
||||
const splitItem = foundry.utils.duplicate(item);
|
||||
const splitItem = foundry.utils.deepClone(item);
|
||||
splitItem.system.quantite = split;
|
||||
await this.actor.createEmbeddedDocuments('Item', [splitItem])
|
||||
}
|
||||
+104
@@ -1,5 +1,109 @@
|
||||
# 13.0
|
||||
|
||||
## 14.0.4 - Le sommeil de Tétragor
|
||||
|
||||
### Sommeil & Château Dormant (nouveau mode de repos)
|
||||
|
||||
- Insomnie (Queue de Dragon)
|
||||
- bannière d'avertissement dans le dialogue de repos
|
||||
- rafraîchissement du drapeau à l'ouverture du dialogue
|
||||
- vérification en direct dans la boucle de sommeil : si l'insomnie surgit en
|
||||
cours de nuit, la récupération de fatigue et de rêve est interrompue
|
||||
- drapeau remis à jour dans les deux sens (levée possible)
|
||||
- mention dans le dialogue Château Dormant
|
||||
- Château Dormant automatique : déclenché quand l'heure de fin de nuit (11) est
|
||||
réellement atteinte, au lieu de seulement après 12 heures de sommeil
|
||||
- Reprise de nuit après un Rêve de Dragon
|
||||
- la durée de sommeil est conservée
|
||||
- les heures restantes sont proposées à la réouverture du dialogue avec une
|
||||
bannière « Reprise de nuit »
|
||||
- remise à zéro après une nuit complète
|
||||
- don « double rêve » : second jet de récupération de rêve par heure (parité
|
||||
ancien mode)
|
||||
- garde anti-boucle infinie si l'insomnie apparaît pendant le sommeil
|
||||
- info-bulle sur le réveil forcé
|
||||
- perte de rêve des enchantements au Château Dormant : suppression du message en
|
||||
double (whisper dédié), texte intégré au résumé ; la gemme renvoie son
|
||||
nouveau total de points de rêve
|
||||
|
||||
### Commerces
|
||||
|
||||
- achat depuis un commerce en quantité illimitée : la quantité achetée est
|
||||
correctement utilisée au lieu de celle du commerce
|
||||
- fiche commerce : le champ Description utilise toute la largeur de la fiche
|
||||
|
||||
### Compétences
|
||||
|
||||
- ne plus créer d'item « Sans compétence » lors des migrations
|
||||
|
||||
## 14.0.3 - Les bases de Tétragor
|
||||
|
||||
- XP des compétences : les bases de catégorie peuvent être négatives
|
||||
(base stockée en entier signé, migration 14.0.3)
|
||||
- réorganisation des titres CSS
|
||||
|
||||
## 14.0.2 - Le grang ménage de Tétragor
|
||||
|
||||
### Migration Foundry V14
|
||||
|
||||
- Migration des classes restantes de V1 vers V2 (ApplicationV2)
|
||||
- OptionsAvancees, StatusEffects, SystemCompendiums, ReglesOptionnelles,
|
||||
DialogEncaissementTables
|
||||
- DialogV2: formes imbriquées, boutons, styles et positionnement corrigés
|
||||
- DialogV2 requiert au moins un bouton (boutons vides interdits en V14)
|
||||
- templates avec wrapper à racine unique
|
||||
- correction des éditeurs ProseMirror et des scrollbar sur les fiches d'acteur
|
||||
- rollMode → messageMode, Token HUD, data-message-id
|
||||
- champ age en StringField pour compat V14
|
||||
- compat V14: _onFirstRender (HTMLElement.show) pour les jets de dés,
|
||||
fermeture des dialogs TMR après lecture d'un signe draconique
|
||||
- 10 fichiers .js V1 inutilisés déplacés vers _deprecated/
|
||||
- scrollbar des applications V2 corrigées (LESS, plus de style inline)
|
||||
- migration de toutes les directives CSS inline vers LESS
|
||||
- titres des tables d'encaissement alternatives lisibles
|
||||
- le dialog ne passe plus au premier plan lors du re-render (hook)
|
||||
- refresh des dialogs herbes/potions/ingrédients après édition de fiche
|
||||
- TODO-v14.md pour le suivi de la compatibilité V14
|
||||
|
||||
### Feuilles d'items
|
||||
|
||||
- migration des feuilles d'items vers V2 (models, apps, templates, less + common/mixins)
|
||||
- ajout des sections environnement (inventaire) et temporel (blessure, maladie,
|
||||
poison, rencontre, signe draconique) manquantes dans les sheets V2
|
||||
|
||||
### Sommeil & Château Dormant
|
||||
|
||||
- nouveau mode sommeil avec sélection des heures de repos
|
||||
- dialogs Château Dormant intégrés, toolbar équipement
|
||||
- message de statut MJ + journal de suivi stress/moral/événements par jour
|
||||
- journal enrichi avec stress, perte d'enchantements, moral
|
||||
- message stress journal: perte fixe de 1, reste X, conversion Y en xp
|
||||
- boutons Repas externe et Rations harmonisés avec les cartouches de tchat
|
||||
|
||||
### Inventaire
|
||||
|
||||
- boutons +Ration/+Eau et cartouche de tchat compacte
|
||||
- consommation des rations: assez d'unités pour remplir sustentation/eau en un
|
||||
clic + alerte si vide
|
||||
- ne plus afficher « rien à manger » quand les compteurs sont déjà pleins
|
||||
|
||||
### Règles & encaissement
|
||||
|
||||
- fusion des Règles customs dans Règles optionnelles (section Options alternatives)
|
||||
- tables d'encaissement révisées optionnelles + dialog de consultation
|
||||
|
||||
### Divers
|
||||
|
||||
- regroupement des classes de combat
|
||||
- fix assign dans les constructeurs
|
||||
|
||||
### Tests automatisés
|
||||
|
||||
- plan de test (227 cas, 33 sections) + scripts TC-01 à TC-07
|
||||
- 22 scripts TC supplémentaires (TC-08 à TC-31) avec fonctions partagées
|
||||
- orchestrateur run-all-tests.sh (sélection, exécution, rapport final)
|
||||
- documentation du lancement des tests dans test/README.md
|
||||
|
||||
## 14.0.1 - Les imprécisions de Tétragor
|
||||
|
||||
- correction de l'indicateur de remplissage des conteneurs
|
||||
|
||||
+5312
-149
File diff suppressed because it is too large
Load Diff
+648
-134
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
.arme-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.armure-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.blessure-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.casetmr-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.chant-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.competence-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.competencecreature-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.conteneur-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.danse-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.empoignade-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.extraitpoetique-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.faune-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.gemme-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.herbe-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.ingredient-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.jeu-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.livre-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.maladie-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.meditation-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -3,5 +3,6 @@
|
||||
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Styles pour la feuille d'objet Monnaie
|
||||
// Styles pour la feuille d'objet Munition
|
||||
.munition-content {
|
||||
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.musique-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.nombreastral-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.nourritureboisson-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.objet-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.oeuvre-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.ombre-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.plante-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.poison-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.possession-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.potion-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.queue-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.race-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.recettealchimique-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
|
||||
.ingredient-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
padding: 2px 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.recettecuisine-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.rencontre-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.service-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.signedraconique-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.sort-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.sortreserve-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.souffle-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.tache-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// Styles pour la feuille d'objet Monnaie
|
||||
// Styles pour la feuille d'objet Tarot
|
||||
.tarot-content {
|
||||
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.tete-content {
|
||||
.item-sheet-common();
|
||||
.item-sheet-header();
|
||||
.item-sheet-scrollable-body();
|
||||
}
|
||||
+26
-4
@@ -74,9 +74,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
.item-sheet-scrollable-body() {
|
||||
max-height: 75vh;
|
||||
overflow: hidden;
|
||||
|
||||
> section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.sheet-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sheet-body {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.item-sheet-header() {
|
||||
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
.sheet-header {
|
||||
background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top;
|
||||
}
|
||||
.sheet-header-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -86,7 +108,7 @@
|
||||
font-family: CaslonAntique;
|
||||
font-size: 2.5rem;
|
||||
font-weight: normal;
|
||||
color: var(--window-header-title-color);
|
||||
color: var(--window-header-title-color, #fff);
|
||||
margin: 4px;
|
||||
height: 3rem;
|
||||
}
|
||||
@@ -100,8 +122,8 @@
|
||||
input[type="date"],
|
||||
input[type="time"]
|
||||
) {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border: 0 none;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Supprimer tous les items du monde
|
||||
// Coller ce code dans une macro Foundry (type script)
|
||||
const count = game.items.size
|
||||
if (!count) return ui.notifications.info("Aucun item dans le monde.")
|
||||
|
||||
const confirmed = await Dialog.confirm({
|
||||
title: "Supprimer tous les items",
|
||||
content: `<p>⚠️ <b>${count} items</b> vont être définitivement supprimés.</p>`,
|
||||
})
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
await Item.deleteDocuments(game.items.map(i => i.id))
|
||||
ui.notifications.info(`${count} items supprimés.`)
|
||||
@@ -12,7 +12,7 @@ export class ChatVente {
|
||||
return undefined;
|
||||
}
|
||||
const nbLots = chatMessage.getFlag(SYSTEM_RDD, NB_LOTS)
|
||||
const detail = foundry.utils.duplicate(chatMessage.getFlag(SYSTEM_RDD, DETAIL_VENTE))
|
||||
const detail = foundry.utils.deepClone(chatMessage.getFlag(SYSTEM_RDD, DETAIL_VENTE))
|
||||
if (!detail.item) {
|
||||
ui.notifications.warn("Impossible d'acheter: informations sur l'objet manquantes")
|
||||
return undefined;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { renderTemplate } from "../constants.js";
|
||||
import { Misc } from "../misc.js";
|
||||
import { RdDUtility } from "../rdd-utility.js";
|
||||
import { ChatVente } from "./chat-vente.js";
|
||||
|
||||
export class DialogItemAchat extends Dialog {
|
||||
import { renderTemplate } from "../constants.js"
|
||||
import { Misc } from "../misc.js"
|
||||
import { RdDUtility } from "../rdd-utility.js"
|
||||
import { ChatVente } from "./chat-vente.js"
|
||||
|
||||
export class DialogItemAchat extends foundry.applications.api.DialogV2 {
|
||||
static preparerAchat(chatButton) {
|
||||
return ChatVente.getDetailAchatVente(RdDUtility.findChatMessageId(chatButton))
|
||||
}
|
||||
@@ -21,57 +20,64 @@ export class DialogItemAchat extends Dialog {
|
||||
choix: { seForcer: false, supprimerSiZero: true },
|
||||
prixLot,
|
||||
isVente: prixLot > 0,
|
||||
isConsommable: item.type == 'nourritureboisson' && acheteur?.isPersonnage(),
|
||||
isConsommable: item.type == "nourritureboisson" && acheteur?.isPersonnage(),
|
||||
chatMessageIdVente
|
||||
}
|
||||
if (venteData.vendeur?.id == venteData.acheteur?.id) {
|
||||
ui.notifications.info("Inutile de se vendre à soi-même")
|
||||
return
|
||||
}
|
||||
|
||||
DialogItemAchat.changeNombreLots(venteData, 1)
|
||||
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.hbs`, venteData)
|
||||
new DialogItemAchat(html, venteData).render(true)
|
||||
const content = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.hbs", venteData)
|
||||
new DialogItemAchat(content, venteData).render({ force: true })
|
||||
}
|
||||
|
||||
static changeNombreLots(venteData, nombreLots) {
|
||||
venteData.choix.nombreLots = nombreLots;
|
||||
venteData.prixTotal = (nombreLots * venteData.prixLot).toFixed(2);
|
||||
venteData.choix.nombreLots = nombreLots
|
||||
venteData.prixTotal = (nombreLots * venteData.prixLot).toFixed(2)
|
||||
if (venteData.isConsommable) {
|
||||
const doses = nombreLots * venteData.tailleLot;
|
||||
venteData.totalSust = Misc.keepDecimals(doses * (venteData.item.system.sust ?? 0), 2);
|
||||
const doses = nombreLots * venteData.tailleLot
|
||||
venteData.totalSust = Misc.keepDecimals(doses * (venteData.item.system.sust ?? 0), 2)
|
||||
venteData.totalDesaltere = venteData.item.system.boisson
|
||||
? Misc.keepDecimals(doses * (venteData.item.system.desaltere ?? 0), 2)
|
||||
: 0;
|
||||
: 0
|
||||
}
|
||||
}
|
||||
|
||||
constructor(html, venteData) {
|
||||
let options = { classes: ["dialogachat"], width: 400, height: 'fit-content', 'z-index': 99999 };
|
||||
|
||||
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre";
|
||||
const buttons = {};
|
||||
constructor(content, venteData) {
|
||||
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre"
|
||||
const buttons = []
|
||||
if (venteData.isConsommable) {
|
||||
buttons["consommer"] = { label: venteData.item.system.boisson ? "Boire" : "Manger", callback: it => this.onAchatConsommer() }
|
||||
buttons.push({
|
||||
label: venteData.item.system.boisson ? "Boire" : "Manger",
|
||||
action: "consommer",
|
||||
callback: () => this.onAchatConsommer()
|
||||
})
|
||||
}
|
||||
buttons[actionAchat] = { label: actionAchat, callback: it => { this.onAchat(); } };
|
||||
buttons["decliner"] = { label: "Décliner", callback: it => { } };
|
||||
const acheteur = venteData.acheteur?.name ?? 'Un acheteur';
|
||||
const vendeur = venteData.vendeur?.name ?? 'Un vendeur';
|
||||
let conf = {
|
||||
buttons.push({ label: actionAchat, action: actionAchat, callback: () => this.onAchat() })
|
||||
buttons.push({ label: "Décliner", action: "decliner", callback: () => {} })
|
||||
const acheteur = venteData.acheteur?.name ?? "Un acheteur"
|
||||
const vendeur = venteData.vendeur?.name ?? "Un vendeur"
|
||||
super({
|
||||
title: `${acheteur} - ${actionAchat} à ${vendeur}`,
|
||||
content: html,
|
||||
default: actionAchat,
|
||||
buttons: buttons
|
||||
};
|
||||
content,
|
||||
classes: ["dialogachat"],
|
||||
position: { width: 400, height: 'fit-content' },
|
||||
buttons
|
||||
})
|
||||
this.venteData = venteData
|
||||
}
|
||||
|
||||
super(conf, options);
|
||||
this.venteData = venteData;
|
||||
_onRender(context) {
|
||||
super._onRender(context)
|
||||
this.element.querySelector(".nombreLots")?.addEventListener("change", event => this.setNombreLots(Number(event.currentTarget.value)))
|
||||
this.element.querySelector(".se-forcer")?.addEventListener("change", event => this.setSeForcer(event))
|
||||
}
|
||||
|
||||
async onAchat() {
|
||||
await this.html.find(".nombreLots").change();
|
||||
(this.venteData.vendeur ?? this.venteData.acheteur).achatVente({
|
||||
const el = this.element
|
||||
el.querySelector(".nombreLots")?.dispatchEvent(new Event("change"))
|
||||
;(this.venteData.vendeur ?? this.venteData.acheteur).achatVente({
|
||||
userId: game.user.id,
|
||||
vendeurId: this.venteData.vendeur?.id,
|
||||
acheteurId: this.venteData.acheteur?.id,
|
||||
@@ -79,42 +85,34 @@ export class DialogItemAchat extends Dialog {
|
||||
chatMessageIdVente: this.venteData.chatMessageIdVente,
|
||||
choix: this.venteData.choix,
|
||||
vente: this.venteData
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
async onAchatConsommer() {
|
||||
this.venteData.choix.consommer = true;
|
||||
await this.onAchat();
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
this.html = html;
|
||||
this.html.find(".nombreLots").change(event => this.setNombreLots(Number(event.currentTarget.value)));
|
||||
this.html.find(".se-forcer").change(event => this.setSeForcer(event));
|
||||
this.venteData.choix.consommer = true
|
||||
await this.onAchat()
|
||||
}
|
||||
|
||||
setSeForcer(event) {
|
||||
this.venteData.choix.seForcer = event.currentTarget.checked;
|
||||
this.venteData.choix.seForcer = event.currentTarget.checked
|
||||
}
|
||||
|
||||
setNombreLots(nbLots) {
|
||||
|
||||
if (!this.venteData.quantiteIllimite) {
|
||||
if (!this.venteData.quantiteIllimite && nbLots > this.venteData.nbLots) {
|
||||
if (nbLots > this.venteData.nbLots) {
|
||||
ui.notifications.warn(`Seulement ${this.venteData.nbLots} lots disponibles, vous ne pouvez pas en prendre ${nbLots}`)
|
||||
}
|
||||
nbLots = Math.min(nbLots, this.venteData.nbLots);
|
||||
nbLots = Math.min(nbLots, this.venteData.nbLots)
|
||||
}
|
||||
|
||||
DialogItemAchat.changeNombreLots(this.venteData, nbLots);
|
||||
|
||||
this.html.find(".nombreLots").val(nbLots);
|
||||
this.html.find(".prixTotal").text(this.venteData.prixTotal);
|
||||
this.html.find("span.total-sust").text(this.venteData.totalSust);
|
||||
this.html.find("span.total-desaltere").text(this.venteData.totalDesaltere);
|
||||
DialogItemAchat.changeNombreLots(this.venteData, nbLots)
|
||||
const el = this.element
|
||||
const nbl = el.querySelector(".nombreLots")
|
||||
if (nbl) nbl.value = nbLots
|
||||
const pt = el.querySelector(".prixTotal")
|
||||
if (pt) pt.textContent = this.venteData.prixTotal
|
||||
const sust = el.querySelector("span.total-sust")
|
||||
if (sust) sust.textContent = this.venteData.totalSust
|
||||
const des = el.querySelector("span.total-desaltere")
|
||||
if (des) des.textContent = this.venteData.totalDesaltere
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { renderTemplate } from "../constants.js";
|
||||
import { renderTemplate } from "../constants.js"
|
||||
import { HtmlUtility } from "../html-utility.js";
|
||||
import { RdDUtility } from "../rdd-utility.js";
|
||||
import { ChatVente } from "./chat-vente.js";
|
||||
|
||||
export class DialogItemVente extends Dialog {
|
||||
|
||||
export class DialogItemVente extends foundry.applications.api.DialogV2 {
|
||||
static async display({ item, quantiteMax = undefined }) {
|
||||
const quantite = quantiteMax ?? item.getQuantite() ?? 1;
|
||||
const quantite = quantiteMax ?? item.getQuantite() ?? 1
|
||||
const venteData = {
|
||||
item: item,
|
||||
item,
|
||||
alias: item.actor?.name ?? game.user.name,
|
||||
vendeurId: item.actor?.id,
|
||||
prixOrigine: item.calculerPrixCommercant(),
|
||||
@@ -19,93 +18,99 @@ export class DialogItemVente extends Dialog {
|
||||
maxLots: quantite,
|
||||
quantiteMax: quantite,
|
||||
quantiteIllimite: item.isItemCommerce() ? quantiteMax == undefined : !item.parent,
|
||||
isOwned: item.parent,
|
||||
isOwned: item.parent,
|
||||
}
|
||||
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.hbs`, venteData);
|
||||
return new DialogItemVente(venteData, html).render(true);
|
||||
const content = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.hbs", venteData)
|
||||
const dialog = new DialogItemVente(venteData, content)
|
||||
dialog.render({ force: true })
|
||||
return dialog
|
||||
}
|
||||
|
||||
constructor(venteData, html) {
|
||||
let options = { classes: ["dialogvente"], width: 400, height: 'fit-content', 'z-index': 99999 };
|
||||
|
||||
let conf = {
|
||||
constructor(venteData, content) {
|
||||
super({
|
||||
title: "Proposer",
|
||||
content: html,
|
||||
default: "proposer",
|
||||
buttons: { "proposer": { label: "Proposer", callback: it => { this.onProposer(it); } } }
|
||||
};
|
||||
|
||||
super(conf, options);
|
||||
this.venteData = venteData;
|
||||
content,
|
||||
classes: ["dialogvente"],
|
||||
position: { width: 400, height: 'fit-content' },
|
||||
buttons: [
|
||||
{ label: "Proposer", action: "proposer", callback: () => this.onProposer() }
|
||||
]
|
||||
})
|
||||
this.venteData = venteData
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
this.html = html;
|
||||
this.html.find(".tailleLot").change(event => this.setTailleLot(Number(event.currentTarget.value)));
|
||||
this.html.find(".nbLots").change(event => this.setNbLots(Number(event.currentTarget.value)));
|
||||
this.html.find(".quantiteIllimite").change(event => this.setQuantiteIllimite(event.currentTarget.checked));
|
||||
this.html.find(".prixLot").change(event => this.setPrixLot(Number(event.currentTarget.value)));
|
||||
|
||||
this.setQuantiteIllimite(this.venteData.quantiteIllimite);
|
||||
_onRender(context) {
|
||||
super._onRender(context)
|
||||
const el = this.element
|
||||
el.querySelector(".tailleLot")?.addEventListener("change", event => this.setTailleLot(Number(event.currentTarget.value)))
|
||||
el.querySelector(".nbLots")?.addEventListener("change", event => this.setNbLots(Number(event.currentTarget.value)))
|
||||
el.querySelector(".quantiteIllimite")?.addEventListener("change", event => this.setQuantiteIllimite(event.currentTarget.checked))
|
||||
el.querySelector(".prixLot")?.addEventListener("change", event => this.setPrixLot(Number(event.currentTarget.value)))
|
||||
this.setQuantiteIllimite(this.venteData.quantiteIllimite)
|
||||
}
|
||||
|
||||
async onProposer(it) {
|
||||
this.updateVente(this.getChoixVente());
|
||||
|
||||
this.venteData["properties"] = this.venteData.item.getProprietes();
|
||||
async onProposer() {
|
||||
this.updateVente(this.getChoixVente())
|
||||
this.venteData["properties"] = this.venteData.item.getProprietes()
|
||||
if (this.venteData.isOwned) {
|
||||
if (this.venteData.nbLots * this.venteData.tailleLot > this.venteData.quantiteMax) {
|
||||
ui.notifications.warn(`Vous avez ${this.venteData.quantiteMax} ${this.venteData.item.name}, ce n'est pas suffisant pour vendre ${this.venteData.nbLots} de ${this.venteData.tailleLot}`)
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
await ChatVente.displayAchatVente(this.venteData)
|
||||
}
|
||||
|
||||
updateVente(update) {
|
||||
foundry.utils.mergeObject(this.venteData, update);
|
||||
foundry.utils.mergeObject(this.venteData, update)
|
||||
}
|
||||
|
||||
getChoixVente() {
|
||||
const el = this.element
|
||||
return {
|
||||
nbLots: Number(this.html.find(".nbLots").val()),
|
||||
tailleLot: Number(this.html.find(".tailleLot").val()),
|
||||
quantiteIllimite: this.html.find(".quantiteIllimite").is(':checked'),
|
||||
prixLot: Number(this.html.find(".prixLot").val())
|
||||
};
|
||||
nbLots: Number(el.querySelector(".nbLots")?.value ?? 0),
|
||||
tailleLot: Number(el.querySelector(".tailleLot")?.value ?? 0),
|
||||
quantiteIllimite: el.querySelector(".quantiteIllimite")?.checked ?? false,
|
||||
prixLot: Number(el.querySelector(".prixLot")?.value ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
setPrixLot(prixLot) {
|
||||
this.venteData.prixLot = prixLot;
|
||||
this.venteData.prixLot = prixLot
|
||||
}
|
||||
|
||||
setTailleLot(tailleLot) {
|
||||
const maxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
|
||||
const maxLots = Math.floor(this.venteData.quantiteMax / tailleLot)
|
||||
this.updateVente({
|
||||
tailleLot,
|
||||
nbLots: Math.min(maxLots, this.venteData.nbLots),
|
||||
maxLots: maxLots,
|
||||
maxLots,
|
||||
prixLot: (tailleLot * this.venteData.prixOrigine).toFixed(2)
|
||||
});
|
||||
|
||||
this.html.find(".prixLot").val(this.venteData.prixLot);
|
||||
this.html.find(".nbLots").val(this.venteData.nbLots);
|
||||
this.html.find(".nbLots").attr("max", this.venteData.maxLots)
|
||||
})
|
||||
const el = this.element
|
||||
const prix = el.querySelector(".prixLot")
|
||||
if (prix) prix.value = this.venteData.prixLot
|
||||
const nb = el.querySelector(".nbLots")
|
||||
if (nb) {
|
||||
nb.value = this.venteData.nbLots
|
||||
nb.setAttribute("max", this.venteData.maxLots)
|
||||
}
|
||||
}
|
||||
|
||||
setNbLots(nbLots) {
|
||||
this.updateVente({
|
||||
nbLots: this.venteData.isOwned ? Math.max(0, Math.min(nbLots, this.venteData.maxLots)) : nbLots
|
||||
})
|
||||
this.html.find(".nbLots").val(this.venteData.nbLots);
|
||||
const nb = this.element.querySelector(".nbLots")
|
||||
if (nb) nb.value = this.venteData.nbLots
|
||||
}
|
||||
|
||||
setQuantiteIllimite(checked) {
|
||||
this.updateVente({ quantiteIllimite: checked })
|
||||
this.html.find(".label-quantiteIllimite").text(this.venteData.quantiteIllimite ? "Illimités" : "disponibles");
|
||||
HtmlUtility.showControlWhen(this.html.find(".nbLots"), !this.venteData.quantiteIllimite)
|
||||
const el = this.element
|
||||
const label = el.querySelector(".label-quantiteIllimite")
|
||||
if (label) label.textContent = checked ? "Illimités" : "disponibles"
|
||||
const nb = el.querySelector(".nbLots")
|
||||
if (nb) HtmlUtility.showControlWhen(nb, !checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+169
-88
@@ -18,14 +18,16 @@ import { CARACS, LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js";
|
||||
import { DialogConsommer } from "./dialog-item-consommer.js";
|
||||
import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js";
|
||||
import { RollDataAjustements } from "./rolldata-ajustements-v1.js";
|
||||
import { RdDPossession } from "./rdd-possession.js";
|
||||
import { RdDPossession } from "./combat/rdd-possession.js";
|
||||
import { RdDPossessionV2 } from "./combat/rdd-possession-v2.mjs";
|
||||
import { ACTOR_TYPES, renderTemplate, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
|
||||
import { RdDConfirm } from "./rdd-confirm.js";
|
||||
import { DialogRepos } from "./sommeil/dialog-repos.js";
|
||||
import { DialogNouveauRepos } from "./sommeil/dialog-nouveau-repos.js";
|
||||
import { RdDBaseActor } from "./actor/base-actor.js";
|
||||
import { RdDTimestamp } from "./time/rdd-timestamp.js";
|
||||
import { AppAstrologie } from "./sommeil/app-astrologie.js";
|
||||
import { RdDEmpoignade } from "./rdd-empoignade.js";
|
||||
import { RdDEmpoignade } from "./combat/rdd-empoignade.js";
|
||||
import { ExperienceLog, XP_TOPIC } from "./actor/experience-log.js";
|
||||
import { ITEM_TYPES } from "./constants.js";
|
||||
import { RdDBaseActorSang } from "./actor/base-actor-sang.js";
|
||||
@@ -34,7 +36,7 @@ import { DialogChoixXpCarac } from "./dialog-choix-xp-carac.js";
|
||||
|
||||
import { ATTAQUE_TYPE, RdDItemArme } from "./item/arme.js";
|
||||
import { RdDItemBlessure } from "./item/blessure.js";
|
||||
import { RdDItemTete } from "./item/tete.js";
|
||||
import { RdDItemTete } from "./documents/_module.mjs";
|
||||
import { RdDItemSort } from "./item-sort.js";
|
||||
import { RdDItemRace } from "./item/race.js";
|
||||
import { RdDItemCompetence } from "./item-competence.js";
|
||||
@@ -44,11 +46,10 @@ import { DialogSelect } from "./dialog-select.js";
|
||||
import { CATEGORIES_COMPETENCES, PAS_DE_DRACONIC, SANS_DRACONIC } from "./item/base-items.js";
|
||||
|
||||
import { RdDRollResult } from "./rdd-roll-result.js";
|
||||
import { RdDInitiative } from "./initiative.mjs";
|
||||
import { RdDInitiative } from "./combat/initiative.mjs";
|
||||
import RollDialog from "./roll/roll-dialog.mjs";
|
||||
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js";
|
||||
import { ROLL_TYPE_COMP, ROLL_TYPE_CUISINE, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_APPEL_CHANCE, ROLL_TYPE_TACHE } from "./roll/roll-constants.mjs";
|
||||
import { RdDPossessionV2 } from "./rdd-possession-v2.mjs";
|
||||
import { Apprecier, MORAL, SITUATION_MORAL } from "./moral/apprecier.mjs";
|
||||
import { Distance } from "./combat/distance.mjs";
|
||||
|
||||
@@ -62,36 +63,6 @@ export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
|
||||
*/
|
||||
export class RdDActor extends RdDBaseActorSang {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Prepare Character type specific data
|
||||
*/
|
||||
prepareActorData() {
|
||||
|
||||
RdDItemRace.applyRacialLimits(this)
|
||||
|
||||
this.system.carac.melee.value = Math.floor((this.getForce() + parseInt(this.system.carac.agilite.value)) / 2);
|
||||
this.system.carac.tir.value = Math.floor((parseInt(this.system.carac.vue.value) + parseInt(this.system.carac.dexterite.value)) / 2);
|
||||
this.system.carac.lancer.value = Math.floor((parseInt(this.system.carac.tir.value) + parseInt(this.system.carac.force.value)) / 2);
|
||||
this.system.carac.derobee.value = Math.floor(parseInt(((21 - this.system.carac.taille.value)) + parseInt(this.system.carac.agilite.value)) / 2);
|
||||
|
||||
super.prepareActorData()
|
||||
|
||||
this.system.attributs.sconst.value = RdDCarac.calculSConst(this.getConstitution())
|
||||
this.system.attributs.sust.value = RdDCarac.getCaracDerivee(this.getTaille()).sust
|
||||
|
||||
this.system.sante.fatigue.max = this.getFatigueMax()
|
||||
this.system.sante.fatigue.value = Math.min(this.system.sante.fatigue.value, this.system.sante.fatigue.max);
|
||||
|
||||
//Compteurs
|
||||
this.system.reve.reve.max = 3 * this.getReve()
|
||||
this.system.compteurs.chance.max = this.getChance()
|
||||
|
||||
this.system.attributs.hautrevant.value = this.itemTypes[ITEM_TYPES.tete].find(it => RdDItemTete.isDonDeHautReve(it))
|
||||
? "Haut rêvant"
|
||||
: "";
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
canReceive(item) {
|
||||
return ![ITEM_TYPES.competencecreature, ITEM_TYPES.tarot, ITEM_TYPES.service].includes(item.type)
|
||||
@@ -243,19 +214,26 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async $perteReveEnchantementsChateauDormants() {
|
||||
async $perteReveEnchantementsChateauDormants(message = undefined) {
|
||||
const toUpdate = this.items.filter(it => [ITEM_TYPES.potion, ITEM_TYPES.gemme].includes(it.type))
|
||||
.map(it => it.perteReveChateauDormant())
|
||||
.filter(it => it != undefined)
|
||||
|
||||
if (toUpdate.length > 0) {
|
||||
console.log('perte de rêve des enchantements', toUpdate)
|
||||
const messageUpdates = await Promise.all(
|
||||
toUpdate.map(async it => await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-pertereve-enchantement-chateaudormant.hbs`, it)))
|
||||
await ChatMessage.create({
|
||||
whisper: ChatUtility.getOwners(this),
|
||||
content: messageUpdates.reduce(Misc.joining('<br>'))
|
||||
})
|
||||
if (message) {
|
||||
message.content += toUpdate
|
||||
.map(it => {
|
||||
const type = it.item.type === 'potion' ? 'potion' : 'gemme'
|
||||
const nouveaupr = it.nouveaupr ?? it.item.system.pr
|
||||
let texte = `La ${type} enchantée ${it.item.name} de ${it.alias} a perdu un Point de Rêve (nouveau total : ${nouveaupr}).`
|
||||
if (type === 'potion' && nouveaupr === 0) {
|
||||
texte += ` Sa potion étant désormais à 0 Point de Rêve, elle est inutilisable et peut être jetée.`
|
||||
}
|
||||
return texte
|
||||
})
|
||||
.reduce(Misc.joining(' ')) + ' '
|
||||
}
|
||||
await this.updateEmbeddedDocuments('Item', toUpdate.map(it => it.update));
|
||||
}
|
||||
}
|
||||
@@ -296,7 +274,11 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
|
||||
async repos() {
|
||||
await DialogRepos.create(this);
|
||||
if (ReglesOptionnelles.isUsing("nouveau-mode-repos")) {
|
||||
await DialogNouveauRepos.create(this);
|
||||
} else {
|
||||
await DialogRepos.create(this);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -360,15 +342,16 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
whisper: ChatUtility.getOwners(this),
|
||||
content: ""
|
||||
}
|
||||
const stressAvant = this.system.compteurs.stress.value
|
||||
|
||||
await this._recuperationSante(message)
|
||||
await this._recupereMoralChateauDormant(message)
|
||||
await this._recupereChance()
|
||||
await this.transformerStress()
|
||||
await this.transformerStress(message)
|
||||
await this.retourSeuilDeReve(message)
|
||||
await this.setBonusPotionSoin(0)
|
||||
await this.retourSust(message)
|
||||
await this.$perteReveEnchantementsChateauDormants()
|
||||
await this.$perteReveEnchantementsChateauDormants(message)
|
||||
await this.$suppressionLancementsSort()
|
||||
await RdDCoeur.applyCoeurChateauDormant(this, message)
|
||||
if (message.content != "") {
|
||||
@@ -376,6 +359,35 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
await ChatMessage.create(message)
|
||||
}
|
||||
await this.resetInfoSommeil()
|
||||
|
||||
const stressApres = this.system.compteurs.stress.value
|
||||
const date = game.system.rdd.calendrier.dateCourante()
|
||||
const recapMJ = `**${this.name}** — Château Dormant (${date})<br>Stress : ${stressAvant} → ${stressApres}`
|
||||
await ChatUtility.tellToGM(message.content ? recapMJ + '<br>' + message.content : recapMJ)
|
||||
|
||||
const journalId = game.settings.get(SYSTEM_RDD, 'cd-suivi-journal')
|
||||
if (journalId) {
|
||||
const journalEntry = game.journal.get(journalId)
|
||||
if (journalEntry?.canUserModify(game.user, 'update')) {
|
||||
const timestamp = game.system.rdd.calendrier.getTimestamp()
|
||||
const jour = timestamp.jour + 1
|
||||
const mois = RdDTimestamp.definition(timestamp.mois).label
|
||||
const annee = timestamp.annee
|
||||
const section = `${jour} ${mois} ${annee}`
|
||||
const log = `<p><b>${this.name}</b> — Stress ${stressAvant} → ${stressApres}</p>` + message.content
|
||||
let page = journalEntry.pages.find(p => p.type == 'text' && Grammar.equalsInsensitive(p.name, section))
|
||||
if (page) {
|
||||
await page.update({ 'text.content': page.text.content + '\n' + log })
|
||||
} else {
|
||||
await journalEntry.createEmbeddedDocuments('JournalEntryPage', [new JournalEntryPage({
|
||||
name: section,
|
||||
type: 'text',
|
||||
title: { show: true, level: 1 },
|
||||
text: { content: log, format: 1 }
|
||||
})])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async resetInfoSommeil() {
|
||||
@@ -391,9 +403,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
|
||||
async setInfoSommeilInsomnie() {
|
||||
if (EffetsDraconiques.isSujetInsomnie(this)) {
|
||||
await this.update({ 'system.sommeil.insomnie': true });
|
||||
}
|
||||
await this.update({ 'system.sommeil.insomnie': EffetsDraconiques.isSujetInsomnie(this) });
|
||||
}
|
||||
|
||||
async setInfoSommeilMoral(situationMoral) {
|
||||
@@ -657,6 +667,69 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
return cumul;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async mangerExterne() {
|
||||
const besoin = this.system.attributs.sust.value
|
||||
await this.update({
|
||||
'system.compteurs.sust.value': besoin,
|
||||
'system.compteurs.eau.value': besoin
|
||||
})
|
||||
ChatUtility.tellToUserAndGM(`${this.name} a mangé et bu pour la journée (repas externe)`)
|
||||
}
|
||||
|
||||
async mangerRations() {
|
||||
const besoin = this.system.attributs.sust.value
|
||||
let restantSust = besoin - this.system.compteurs.sust.value
|
||||
let restantEau = besoin - this.system.compteurs.eau.value
|
||||
let totalSust = 0, totalEau = 0
|
||||
let foundAny = false
|
||||
|
||||
if (restantSust <= 0 && restantEau <= 0) return
|
||||
|
||||
for (const item of this.items) {
|
||||
if (item.type !== 'nourritureboisson') continue
|
||||
|
||||
const qty = item.system.quantite
|
||||
if (!qty || qty <= 0) continue
|
||||
const sustUnit = item.system.sust || 0
|
||||
const eauUnit = item.system.desaltere || 0
|
||||
if (sustUnit <= 0 && eauUnit <= 0) continue
|
||||
|
||||
foundAny = true
|
||||
let unitNeeded = 0
|
||||
if (restantSust > 0 && sustUnit > 0) {
|
||||
unitNeeded = Math.max(unitNeeded, Math.ceil(restantSust / sustUnit))
|
||||
}
|
||||
if (restantEau > 0 && eauUnit > 0) {
|
||||
unitNeeded = Math.max(unitNeeded, Math.ceil(restantEau / eauUnit))
|
||||
}
|
||||
const take = Math.min(unitNeeded, qty)
|
||||
|
||||
totalSust += Math.min(sustUnit * take, restantSust)
|
||||
totalEau += Math.min(eauUnit * take, restantEau)
|
||||
restantSust -= Math.min(sustUnit * take, restantSust)
|
||||
restantEau -= Math.min(eauUnit * take, restantEau)
|
||||
|
||||
await item.diminuerQuantite(take)
|
||||
}
|
||||
|
||||
if (!foundAny) {
|
||||
ChatUtility.tellToUserAndGM(`${this.name} n'a rien à manger dans ses rations !`)
|
||||
return
|
||||
}
|
||||
|
||||
if (totalSust > 0) {
|
||||
await this.update({ 'system.compteurs.sust.value': this.system.compteurs.sust.value + totalSust })
|
||||
}
|
||||
if (totalEau > 0) {
|
||||
await this.update({ 'system.compteurs.eau.value': this.system.compteurs.eau.value + totalEau })
|
||||
}
|
||||
|
||||
ChatUtility.tellToUserAndGM(
|
||||
`${this.name} a mangé ${totalSust}/${besoin} sust, bu ${totalEau}/${besoin} eau (rations)`
|
||||
)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async retourSeuilDeReve(message) {
|
||||
const seuil = this.system.reve.seuil.value;
|
||||
@@ -776,7 +849,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
let carac = this.findCaracByName(caracName);
|
||||
if (carac) {
|
||||
carac = foundry.utils.duplicate(carac);
|
||||
carac = foundry.utils.deepClone(carac);
|
||||
const fromXp = Number(carac.xp);
|
||||
const fromValue = Number(carac.value);
|
||||
let toXp = fromXp;
|
||||
@@ -916,7 +989,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
|
||||
async deleteExperienceLog(from, count) {
|
||||
if (from >= 0 && count > 0) {
|
||||
let expLog = foundry.utils.duplicate(this.system.experiencelog)
|
||||
let expLog = foundry.utils.deepClone(this.system.experiencelog)
|
||||
expLog.splice(from, count)
|
||||
await this.update({ [`system.experiencelog`]: expLog })
|
||||
}
|
||||
@@ -1046,7 +1119,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
notifyRefreshTMR() {
|
||||
game.socket.emit(SYSTEM_SOCKET_ID, {
|
||||
msg: "msg_tmr_move", data: {
|
||||
actorId: this._id,
|
||||
actorId: this.id,
|
||||
tmrPos: this.system.reve.tmrpos
|
||||
}
|
||||
});
|
||||
@@ -1256,7 +1329,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
|
||||
async mangerNourriture(item) {
|
||||
return (await DialogConsommer.create(this, item)).render(true);
|
||||
return await DialogConsommer.create(this, item);
|
||||
}
|
||||
|
||||
async actionLire(item) {
|
||||
@@ -1383,7 +1456,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async saouler(forceAlcool, alcool = undefined) {
|
||||
let ethylisme = foundry.utils.duplicate(this.system.compteurs.ethylisme);
|
||||
let ethylisme = foundry.utils.deepClone(this.system.compteurs.ethylisme);
|
||||
|
||||
const etat = this.getEtatGeneral({ ethylisme: true });
|
||||
const nbDoses = Number(this.system.compteurs.ethylisme.nb_doses || 0);
|
||||
@@ -1457,7 +1530,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async transformerStress() {
|
||||
async transformerStress(message = undefined) {
|
||||
if (!ReglesOptionnelles.isUsing("transformation-stress")) { return }
|
||||
const fromStress = Number(this.system.compteurs.stress.value);
|
||||
if (this.system.sommeil?.insomnie || fromStress <= 0) {
|
||||
@@ -1491,6 +1564,11 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-transformer-stress.hbs`, stressRollData)
|
||||
});
|
||||
|
||||
if (message) {
|
||||
const reste = Math.max(fromStress - stressRollData.perte - 1, 0)
|
||||
message.content += `Transformation de ${stressRollData.stress} points de stress : perte fixe de 1, reste ${reste}, conversion de ${stressRollData.convertis} en expérience. `
|
||||
}
|
||||
|
||||
const toStress = Math.max(fromStress - stressRollData.perte - 1, 0);
|
||||
const fromXpSress = Number(this.system.compteurs.experience.value);
|
||||
const toXpStress = fromXpSress + Number(stressRollData.xp);
|
||||
@@ -1543,7 +1621,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
const niveauSuivant = Number(carac.value) + 1;
|
||||
let xpNeeded = RdDCarac.getCaracNextXp(niveauSuivant);
|
||||
if (carac.xp >= xpNeeded) {
|
||||
carac = foundry.utils.duplicate(carac);
|
||||
carac = foundry.utils.deepClone(carac);
|
||||
carac.value = niveauSuivant;
|
||||
|
||||
let checkXp = {
|
||||
@@ -1570,10 +1648,10 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async ajoutExperience(rollData, hideChatMessage = 'show') {
|
||||
const rollMode = rollData.current?.rollmode?.key ?? game.settings.get("core", "rollMode")
|
||||
const rollMode = rollData.current?.rollmode?.key ?? game.settings.get("core", "messageMode")
|
||||
if (!rollData.rolled.isPart ||
|
||||
rollData.finalLevel >= 0 ||
|
||||
rollMode == 'selfroll' ||
|
||||
rollMode == 'self' ||
|
||||
!Misc.hasConnectedGM()) {
|
||||
return
|
||||
}
|
||||
@@ -1599,7 +1677,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async appliquerAppelMoral(rollData) {
|
||||
if (!rollData.use.moral || game.settings.get("core", "rollMode") == 'selfroll') {
|
||||
if (!rollData.use.moral || game.settings.get("core", "messageMode") == 'self') {
|
||||
return
|
||||
}
|
||||
if (rollData.rolled.isEchec ||
|
||||
@@ -1641,14 +1719,14 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
return await this.rollUnSortV2();
|
||||
}
|
||||
// Duplication car les pts de reve sont modifiés dans le sort!
|
||||
let sorts = foundry.utils.duplicate(this.itemTypes[ITEM_TYPES.sort].filter(it => RdDItemSort.isSortOnCoord(it, coord)))
|
||||
let sorts = foundry.utils.deepClone(this.itemTypes[ITEM_TYPES.sort].filter(it => RdDItemSort.isSortOnCoord(it, coord)))
|
||||
if (sorts.length == 0) {
|
||||
ui.notifications.info(`Aucun sort disponible en ${TMRUtility.getTMR(coord).label} !`);
|
||||
return
|
||||
}
|
||||
|
||||
const draconicList = this.computeDraconicAndSortIndex(sorts)
|
||||
const reve = foundry.utils.duplicate(this.system.carac.reve)
|
||||
const reve = foundry.utils.deepClone(this.system.carac.reve)
|
||||
|
||||
const dialog = await this.openRollDialog({
|
||||
name: 'lancer-un-sort',
|
||||
@@ -1765,7 +1843,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
console.log('lancement de sort', rollData.selectedSort)
|
||||
const lancements = RdDItemSort.prepareSortAddLancement(rollData.selectedSort, rollData.selectedSort.system.ptreve_reel)
|
||||
await this.updateEmbeddedDocuments('Item',
|
||||
[{ _id: rollData.selectedSort._id, 'system.lancements': lancements }]
|
||||
[{ _id: rollData.selectedSort.id, 'system.lancements': lancements }]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1903,7 +1981,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
diffConditions: 0,
|
||||
use: { libre: false, conditions: true },
|
||||
carac: {
|
||||
[tache.system.carac]: foundry.utils.duplicate(this.system.carac[tache.system.carac])
|
||||
[tache.system.carac]: foundry.utils.deepClone(this.system.carac[tache.system.carac])
|
||||
}
|
||||
},
|
||||
callbacks: [{ action: r => this._tacheResult(r, options) }]
|
||||
@@ -1965,7 +2043,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
const artData = {
|
||||
art: 'jeu', verbe: 'Jeu',
|
||||
use: { libre: true, conditions: true, },
|
||||
competence: foundry.utils.duplicate(this.getCompetence('jeu')),
|
||||
competence: foundry.utils.deepClone(this.getCompetence('jeu')),
|
||||
forceCarac: {}
|
||||
};
|
||||
listCarac.forEach(c => artData.forceCarac[c] = this.system.carac[c]);
|
||||
@@ -1977,7 +2055,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async rollMeditation(id) {
|
||||
const meditation = foundry.utils.duplicate(this.getMeditation(id));
|
||||
const meditation = foundry.utils.deepClone(this.getMeditation(id));
|
||||
|
||||
if (meditation && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
|
||||
const rollData = {
|
||||
@@ -1988,7 +2066,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
return await RollDialog.create(rollData)
|
||||
}
|
||||
|
||||
const competence = foundry.utils.duplicate(this.getCompetence(meditation.system.competence));
|
||||
const competence = foundry.utils.deepClone(this.getCompetence(meditation.system.competence));
|
||||
competence.system.defaut_carac = "intellect"; // Meditation = toujours avec intellect
|
||||
let meditationData = {
|
||||
competence: competence,
|
||||
@@ -2018,7 +2096,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
await this.createEmbeddedDocuments("Item", [RdDItemSigneDraconique.prepareSigneDraconiqueMeditation(meditationRoll.meditation, meditationRoll.rolled)]);
|
||||
}
|
||||
if (meditationRoll.rolled.isEPart) {
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: meditationRoll.meditation._id, 'system.malus': meditationRoll.meditation.system.malus - 1 }]);
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: meditationRoll.meditation.id, 'system.malus': meditationRoll.meditation.system.malus - 1 }]);
|
||||
}
|
||||
await this.santeIncDec("fatigue", 2);
|
||||
await RdDRollResult.displayRollData(meditationRoll, this.name, 'chat-resultat-meditation.hbs');
|
||||
@@ -2050,7 +2128,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
let draconicList = this.getDraconics()
|
||||
.filter(it => it.system.niveau > -11)
|
||||
.map(draconic => {
|
||||
let draconicLecture = foundry.utils.duplicate(draconic)
|
||||
let draconicLecture = foundry.utils.deepClone(draconic)
|
||||
draconicLecture.system.defaut_carac = "intellect"
|
||||
return draconicLecture
|
||||
});
|
||||
@@ -2100,12 +2178,15 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
if (rollData.xpSort > 0) {
|
||||
const fromXp = Number(compData.system.xp_sort);
|
||||
const toXp = fromXp + rollData.xpSort;
|
||||
await this.updateEmbeddedDocuments("Item", [{ _id: compData._id, 'system.xp_sort': toXp }]);
|
||||
await this.updateEmbeddedDocuments("Item", [{ _id: compData.id, 'system.xp_sort': toXp }]);
|
||||
await ExperienceLog.add(this, XP_TOPIC.XPSORT, fromXp, toXp, `${rollData.competence.name} : signe draconique`);
|
||||
}
|
||||
await this.deleteEmbeddedDocuments("Item", [rollData.signe._id]);
|
||||
await this.deleteEmbeddedDocuments("Item", [rollData.signe.id]);
|
||||
await RdDRollResult.displayRollData(rollData, this.name, 'chat-resultat-lecture-signedraconique.hbs');
|
||||
this.tmrApp.close();
|
||||
if (this.tmrApp) {
|
||||
this.tmrApp.subdialog = undefined; // débloque $forceClose qui refuse de fermer tant qu'un subdialog est actif
|
||||
await this.tmrApp.close();
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -2205,7 +2286,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
if (xpData.competence) {
|
||||
const from = Number(xpData.competence.system.xp);
|
||||
const to = from + xpData.xpCompetence;
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: xpData.competence._id, 'system.xp': to }]);
|
||||
await this.updateEmbeddedDocuments('Item', [{ _id: xpData.competence.id, 'system.xp': to }]);
|
||||
xpData.checkComp = await this.checkCompetenceXP(xpData.competence.name, undefined, false)
|
||||
await ExperienceLog.add(this, XP_TOPIC.XP, from, to, xpData.competence.name);
|
||||
return [xpData]
|
||||
@@ -2216,7 +2297,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
/* -------------------------------------------- */
|
||||
async _xpCarac(xpData) {
|
||||
if (xpData.xpCarac > 0) {
|
||||
const carac = foundry.utils.duplicate(this.system.carac)
|
||||
const carac = foundry.utils.deepClone(this.system.carac)
|
||||
const code = RdDBaseActor._findCaracNode(carac, xpData.caracName)
|
||||
const selectedCarac = carac[code]
|
||||
if (this.isCaracMax(code)) {
|
||||
@@ -2269,7 +2350,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async deleteNombresAstraux() {
|
||||
const deletions = this.itemTypes['nombreastral'].map(it => it._id);
|
||||
const deletions = this.itemTypes['nombreastral'].map(it => it.id);
|
||||
await this.deleteEmbeddedDocuments("Item", deletions);
|
||||
}
|
||||
|
||||
@@ -2294,7 +2375,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
if (calendrier) {
|
||||
const toDelete = this.itemTypes['nombreastral']
|
||||
.filter(it => calendrier.isAfterIndexDate(it.system.jourindex))
|
||||
.map(it => it._id);
|
||||
.map(it => it.id);
|
||||
await this.deleteEmbeddedDocuments("Item", toDelete);
|
||||
}
|
||||
}
|
||||
@@ -2513,7 +2594,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
async incDecItemUse(itemId, shouldIncrease = true) {
|
||||
if (shouldIncrease) {
|
||||
const currentItemUse = this.getFlag(SYSTEM_RDD, 'itemUse');
|
||||
let itemUse = currentItemUse ? foundry.utils.duplicate(currentItemUse) : {};
|
||||
let itemUse = currentItemUse ? foundry.utils.deepClone(currentItemUse) : {};
|
||||
itemUse[itemId] = (itemUse[itemId] ?? 0) + 1;
|
||||
await this.setFlag(SYSTEM_RDD, 'itemUse', itemUse);
|
||||
}
|
||||
@@ -2610,7 +2691,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
|
||||
async setPointsCoeur(subActorId, coeurs, options = { immediat: false }) {
|
||||
const newSuivants = foundry.utils.duplicate(this.system.subacteurs.suivants)
|
||||
const newSuivants = foundry.utils.deepClone(this.system.subacteurs.suivants)
|
||||
const amoureux = newSuivants.find(it => it.id == subActorId)
|
||||
if (amoureux) {
|
||||
amoureux[options.immediat ? 'coeur' : 'prochainCoeur'] = coeurs
|
||||
@@ -2654,7 +2735,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
if (!this.isAddSubActeurAllowed(subActor)) {
|
||||
return
|
||||
}
|
||||
const subActorOnlyId = { id: subActor._id };
|
||||
const subActorOnlyId = { id: subActor.id };
|
||||
if (subActor.type == 'vehicule') {
|
||||
this.pushSubActeur(subActorOnlyId, this.system.subacteurs.vehicules, `system.subacteurs.vehicules`, `Le véhicule ${subActor.name}`)
|
||||
} else if (subActor.type == 'creature') {
|
||||
@@ -2805,7 +2886,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
}
|
||||
await this.createEmbeddedDocuments('Item', [newPotion])
|
||||
await this.diminuerQuantiteObjet(herbeData._id, herbeData.nbBrins)
|
||||
await this.diminuerQuantiteObjet(herbeData.id, herbeData.nbBrins)
|
||||
|
||||
await ChatMessage.create({
|
||||
whisper: ChatUtility.getOwners(this),
|
||||
@@ -2980,8 +3061,8 @@ 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,
|
||||
carac: foundry.utils.deepClone(this.system.carac),
|
||||
heure: RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "self", showDice: SHOW_DICE })).key,
|
||||
age: 18,
|
||||
biographie: '',
|
||||
notes: '',
|
||||
@@ -3061,7 +3142,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
else {
|
||||
const artData = { art: 'oeuvre', verbe: 'Interpréter' }
|
||||
const oeuvre = foundry.utils.duplicate(this.findItemLike(id, artData.art))
|
||||
const oeuvre = foundry.utils.deepClone(this.findItemLike(id, artData.art))
|
||||
await this._rollArtV1(artData, oeuvre.system.default_carac, oeuvre)
|
||||
}
|
||||
}
|
||||
@@ -3072,7 +3153,7 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
else {
|
||||
const artData = { art: 'chant', verbe: 'Chanter' }
|
||||
const oeuvre = foundry.utils.duplicate(this.getChant(id))
|
||||
const oeuvre = foundry.utils.deepClone(this.getChant(id))
|
||||
await this._rollArtV1(artData, "ouie", oeuvre)
|
||||
}
|
||||
}
|
||||
@@ -3083,14 +3164,14 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
}
|
||||
else {
|
||||
const artData = { art: 'danse', verbe: 'Danser', forceCarac: {} }
|
||||
const oeuvre = foundry.utils.duplicate(this.findItemLike(id, artData.art))
|
||||
const oeuvre = foundry.utils.deepClone(this.findItemLike(id, artData.art))
|
||||
let selectedCarac = this.getCompetence(oeuvre.system.competence)?.system.defaut_carac
|
||||
if (oeuvre.system.apparence) {
|
||||
artData.forceCarac['apparence'] = foundry.utils.duplicate(this.system.carac.apparence)
|
||||
artData.forceCarac['apparence'] = foundry.utils.deepClone(this.system.carac.apparence)
|
||||
selectedCarac = "apparence"
|
||||
}
|
||||
if (oeuvre.system.agilite) {
|
||||
artData.forceCarac['agilite'] = foundry.utils.duplicate(this.system.carac.agilite)
|
||||
artData.forceCarac['agilite'] = foundry.utils.deepClone(this.system.carac.agilite)
|
||||
selectedCarac = "agilite"
|
||||
}
|
||||
await this._rollArtV1(artData, selectedCarac, oeuvre)
|
||||
@@ -3115,17 +3196,17 @@ export class RdDActor extends RdDBaseActorSang {
|
||||
{
|
||||
oeuvre: oeuvre,
|
||||
art: oeuvre.type,
|
||||
competence: foundry.utils.duplicate(this.getCompetence(artData.compName ?? oeuvre.system.competence ?? artData.art)),
|
||||
competence: foundry.utils.deepClone(this.getCompetence(artData.compName ?? oeuvre.system.competence ?? artData.art)),
|
||||
diffLibre: - oeuvre.system.niveau,
|
||||
diffConditions: 0,
|
||||
use: { libre: false, conditions: true, surenc: false },
|
||||
selectedCarac: foundry.utils.duplicate(this.system.carac[selected])
|
||||
selectedCarac: foundry.utils.deepClone(this.system.carac[selected])
|
||||
},
|
||||
{ overwrite: false });
|
||||
artData.competence.system.defaut_carac = selected;
|
||||
if (!artData.forceCarac) {
|
||||
artData.forceCarac = {};
|
||||
artData.forceCarac[selected] = foundry.utils.duplicate(this.system.carac[selected]);
|
||||
artData.forceCarac[selected] = foundry.utils.deepClone(this.system.carac[selected]);
|
||||
}
|
||||
|
||||
await this.openRollDialog({
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
|
||||
import { RdDBaseActor } from "./base-actor.js";
|
||||
import { ITEM_TYPES } from "../constants.js";
|
||||
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
|
||||
import { Targets } from "../targets.js";
|
||||
import { Targets } from "../combat/targets.js";
|
||||
import { RdDConfirm } from "../rdd-confirm.js";
|
||||
import { CARACS, RdDCarac } from "../rdd-carac.js";
|
||||
import { RdDRollResult } from "../rdd-roll-result.js";
|
||||
@@ -19,18 +19,19 @@ import { RdDItemCompetence } from "../item-competence.js";
|
||||
|
||||
import { ChatUtility } from "../chat-utility.js";
|
||||
import { DialogValidationEncaissement } from "../dialog-validation-encaissement.js";
|
||||
import { RdDCombat } from "../rdd-combat.js";
|
||||
import { RdDEmpoignade } from "../rdd-empoignade.js";
|
||||
import { RdDPossession } from "../rdd-possession.js";
|
||||
import { PasseArme } from "../combat/passe-arme.mjs";
|
||||
import { RdDEmpoignade } from "../combat/rdd-empoignade.js";
|
||||
import { RdDPossession } from "../combat/rdd-possession.js";
|
||||
import { RdDPossessionV2 } from "../combat/rdd-possession-v2.mjs";
|
||||
|
||||
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES_CREATURES, SANS_DRACONIC } from "../item/base-items.js";
|
||||
import { RollDataAjustements } from "../rolldata-ajustements-v1.js";
|
||||
import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
|
||||
import RollDialog from "../roll/roll-dialog.mjs";
|
||||
import { DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP, ROLL_TYPE_REVE_RESISTANCE } from "../roll/roll-constants.mjs";
|
||||
import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
|
||||
import { RdDInitiative } from "../initiative.mjs";
|
||||
import { RdDInitiative } from "../combat/initiative.mjs";
|
||||
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
|
||||
import { RdDPossessionV2 } from "../rdd-possession-v2.mjs";
|
||||
|
||||
/**
|
||||
* Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
|
||||
@@ -39,13 +40,6 @@ import { RdDPossessionV2 } from "../rdd-possession-v2.mjs";
|
||||
*/
|
||||
export class RdDBaseActorReve extends RdDBaseActor {
|
||||
|
||||
prepareActorData() {
|
||||
super.prepareActorData()
|
||||
this.system.attributs.plusdom.value = this.getBonusDegat()
|
||||
this.system.sante.endurance.max = this.getEnduranceMax()
|
||||
this.system.sante.endurance.value = Math.min(this.system.sante.endurance.value, this.system.sante.endurance.max)
|
||||
}
|
||||
|
||||
getCarac() {
|
||||
const carac = super.getCarac()
|
||||
foundry.utils.mergeObject(carac, this.getCaracReveActuel())
|
||||
@@ -369,7 +363,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
|
||||
selectedCarac: carac,
|
||||
competence: competence,
|
||||
diffLibre: diff ?? 0,
|
||||
current: { rollmode: { key: game.settings.get("core", "rollMode") } },
|
||||
current: { rollmode: { key: game.settings.get("core", "messageMode") } },
|
||||
show: { title: options?.title ?? '' }
|
||||
}
|
||||
RollDataAjustements.calcul(rollData, this);
|
||||
@@ -531,7 +525,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
|
||||
RdDPossession.onAttaquePossession(target, this, competence)
|
||||
}
|
||||
else {
|
||||
RdDCombat.rddCombatTarget(target, this, token).attaque(competence, arme)
|
||||
PasseArme.rddCombatTarget(target, this, token).attaque(competence, arme)
|
||||
}
|
||||
});
|
||||
return
|
||||
@@ -566,7 +560,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
|
||||
return
|
||||
}
|
||||
|
||||
RdDCombat.rddCombatTarget(target, this, token).attaqueV2();
|
||||
PasseArme.rddCombatTarget(target, this, token).attaqueV2();
|
||||
})
|
||||
}
|
||||
else {
|
||||
@@ -630,7 +624,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
|
||||
// TODO: vérifier si c'est possible, sinon simplifier
|
||||
return RdDPossession.onAttaquePossession(target, this, comp);
|
||||
}
|
||||
RdDCombat.rddCombatTarget(target, this, token).attaque(comp, arme, maniement)
|
||||
PasseArme.rddCombatTarget(target, this, token).attaque(comp, arme, maniement)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -696,7 +690,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
|
||||
{ actor: this }))
|
||||
|
||||
if (!encaissement.hasPlayerOwner && encaissement.endurance != 0) {
|
||||
encaissement = foundry.utils.duplicate(encaissement)
|
||||
encaissement = foundry.utils.deepClone(encaissement)
|
||||
encaissement.isGM = true
|
||||
await ChatMessage.create({
|
||||
whisper: ChatUtility.getGMs(),
|
||||
|
||||
@@ -25,13 +25,6 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
|
||||
return super._preUpdate(changed, options, user)
|
||||
}
|
||||
|
||||
prepareActorData() {
|
||||
this.system.sante.vie.max = Math.ceil((this.getTaille() + this.getConstitution()) / 2)
|
||||
this.system.sante.vie.value = Math.min(this.system.sante.vie.value, this.system.sante.vie.max)
|
||||
super.prepareActorData()
|
||||
this.system.attributs.encombrement.value = this.getEncombrementMax()
|
||||
}
|
||||
|
||||
getCarac() {
|
||||
const carac = super.getCarac()
|
||||
foundry.utils.mergeObject(carac, this.getCaracChanceActuelle())
|
||||
@@ -109,7 +102,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async onAppliquerJetEncaissement(encaissement, attackerToken) {
|
||||
const santeOrig = foundry.utils.duplicate(this.system.sante);
|
||||
const santeOrig = foundry.utils.deepClone(this.system.sante);
|
||||
const blessure = this.nouvelleBlessure(encaissement.gravite, {
|
||||
localisation: encaissement.dmg?.loc.label ?? '',
|
||||
origine: attackerToken?.name ?? ''
|
||||
@@ -124,7 +117,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
|
||||
await rollPerteEndurance.evaluate()
|
||||
blessure.system.endurance = rollPerteEndurance.total
|
||||
}
|
||||
const isCritique = blessure.system.gravite >= 6;
|
||||
const isCritique = blessure.system.gravite >= 6 || encaissement._reduireEndAZero;
|
||||
if (isCritique) {
|
||||
blessure.system.endurance = this.getEnduranceActuelle()
|
||||
}
|
||||
@@ -156,7 +149,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
|
||||
if (!this.system.sante[name]) {
|
||||
return
|
||||
}
|
||||
const sante = foundry.utils.duplicate(this.system.sante)
|
||||
const sante = foundry.utils.deepClone(this.system.sante)
|
||||
const compteur = sante[name]
|
||||
const result = { sonne: false }
|
||||
let perteEndurance = 0
|
||||
|
||||
+18
-18
@@ -91,7 +91,7 @@ export class RdDBaseActor extends Actor {
|
||||
}
|
||||
|
||||
static getRealActor(actorId, tokenId) {
|
||||
const actor = tokenId ? canvas.tokens.get(tokenId)?.actor : undefined
|
||||
const actor = tokenId ? canvas?.tokens?.get(tokenId)?.actor : undefined
|
||||
return actor ?? game.actors.get(actorId)
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ export class RdDBaseActor extends Actor {
|
||||
}
|
||||
|
||||
getCarac() {
|
||||
return foundry.utils.duplicate(this.system.carac)
|
||||
return foundry.utils.deepClone(this.system.carac)
|
||||
}
|
||||
|
||||
findCaracByName(name) {
|
||||
@@ -220,16 +220,16 @@ export class RdDBaseActor extends Actor {
|
||||
await this.delayedRenderSheet('_preUpdate', changed, options)
|
||||
return super._preUpdate(changed, options, user)
|
||||
}
|
||||
|
||||
|
||||
_onUpdate(changed, options, userId) {
|
||||
super._onUpdate(changed, options, userId)
|
||||
if (userId == game.user.id){
|
||||
if (userId == game.user.id) {
|
||||
this.delayedRenderSheet('_onUpdate', changed, options)
|
||||
}
|
||||
}
|
||||
|
||||
async delayedRenderSheet(caller, data, options) {
|
||||
this.refreshDelayCounter = (this.refreshDelayCounter ?? 0)+1
|
||||
this.refreshDelayCounter = (this.refreshDelayCounter ?? 0) + 1
|
||||
if (this.refreshDelayCounter == 1) {
|
||||
this.renderAfterDelay(this.refreshDelayCounter)
|
||||
}
|
||||
@@ -250,14 +250,11 @@ export class RdDBaseActor extends Actor {
|
||||
/* -------------------------------------------- */
|
||||
prepareData() {
|
||||
super.prepareData()
|
||||
this.prepareActorData()
|
||||
this.cleanupConteneurs()
|
||||
this.computeEtatGeneral()
|
||||
this.computeEncTotal()
|
||||
}
|
||||
|
||||
prepareActorData() { }
|
||||
|
||||
async computeEtatGeneral() { }
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -383,8 +380,8 @@ export class RdDBaseActor extends Actor {
|
||||
}
|
||||
|
||||
async onTimeChanging(oldTimestamp, newTimestamp) {
|
||||
this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
|
||||
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
|
||||
await Promise.all(this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
|
||||
.map(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp)))
|
||||
}
|
||||
|
||||
async creerObjetParMJ(object) {
|
||||
@@ -404,7 +401,7 @@ export class RdDBaseActor extends Actor {
|
||||
if (Misc.isOwnerPlayer(this)) {
|
||||
let updates = this.itemTypes[ITEM_TYPES.conteneur]
|
||||
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
|
||||
.map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
|
||||
.map(c => { return { _id: c.id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
|
||||
if (updates.length > 0) {
|
||||
await this.updateEmbeddedDocuments("Item", updates)
|
||||
}
|
||||
@@ -545,7 +542,7 @@ export class RdDBaseActor extends Actor {
|
||||
if (cout > 0) {
|
||||
RdDAudio.PlayContextAudio("argent");
|
||||
}
|
||||
const chatAchatItem = foundry.utils.duplicate(achat.vente);
|
||||
const chatAchatItem = foundry.utils.deepClone(achat.vente);
|
||||
chatAchatItem.quantiteTotal = quantite;
|
||||
await ChatMessage.create({
|
||||
user: achat.userId,
|
||||
@@ -631,17 +628,20 @@ export class RdDBaseActor extends Actor {
|
||||
return
|
||||
}
|
||||
|
||||
const isItemEmpilable = "quantite" in item.system;
|
||||
const baseItem = {
|
||||
id: undefined,
|
||||
type: item.type,
|
||||
img: item.img,
|
||||
name: item.name,
|
||||
system: foundry.utils.mergeObject(item.system, { quantite: isItemEmpilable ? quantite : undefined }, { inplace: false })
|
||||
system: foundry.utils.duplicate(item.system)
|
||||
}
|
||||
const createItemArray = "quantite" in item.system
|
||||
? (it, qt) => { it.system.quantite = qt; return [it] }
|
||||
: (it, qt) => Array.from({ length: qt }, (_, i) => it)
|
||||
|
||||
const newItems = isItemEmpilable ? [baseItem] : Array.from({ length: quantite }, (_, i) => baseItem);
|
||||
const items = await this.createEmbeddedDocuments("Item", newItems);
|
||||
return newItems.length > 0 ? items[0].id : undefined;
|
||||
const newItems = createItemArray(baseItem, quantite)
|
||||
const items = await this.createEmbeddedDocuments("Item", newItems)
|
||||
return items.length > 0 ? items[0].id : undefined;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -851,7 +851,7 @@ export class RdDBaseActor extends Actor {
|
||||
sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list
|
||||
|
||||
const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id))
|
||||
.map(it => foundry.utils.duplicate(it))
|
||||
.map(it => foundry.utils.deepClone(it))
|
||||
.map(it => { it.system.contenu = []; return it; });
|
||||
let newItems = await this.createEmbeddedDocuments('Item', itemsDataToCreate);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ async function randomPoidsKg(taille) {
|
||||
}
|
||||
|
||||
async function randomHeure() {
|
||||
return RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "selfroll", showDice: SHOW_DICE })).key
|
||||
return RdDTimestamp.defHeure(await RdDDice.rollHeure({ rollMode: "self", showDice: SHOW_DICE })).key
|
||||
}
|
||||
|
||||
const CONTROL_UNKNOWN = { name: 'unknown', path: '', getter: (act) => { undefined }, random: async act => undefined }
|
||||
@@ -79,7 +79,24 @@ const CONTROLS = [
|
||||
{ name: 'yeux', path: 'system.yeux', getter: act => act.system.yeux, random: async act => await randomFrom(TABLE_COULEURS_YEUX) },
|
||||
]
|
||||
|
||||
export class AppPersonnageAleatoire extends FormApplication {
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
export class AppPersonnageAleatoire extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
|
||||
static PARTS = {
|
||||
main: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/random/app-personnage-aleatoire.hbs" }
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['app-personnage-aleatoire'],
|
||||
window: {
|
||||
title: "Génération aléatoire",
|
||||
controls: []
|
||||
},
|
||||
position: {
|
||||
width: 600,
|
||||
height: 600
|
||||
}
|
||||
}
|
||||
|
||||
static preloadHandlebars() {
|
||||
foundry.applications.handlebars.loadTemplates([
|
||||
'systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs',
|
||||
@@ -87,18 +104,6 @@ export class AppPersonnageAleatoire extends FormApplication {
|
||||
])
|
||||
}
|
||||
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/actor/random/app-personnage-aleatoire.hbs",
|
||||
title: "Génération aléatoire",
|
||||
width: 'fit-content',
|
||||
height: 'fit-content',
|
||||
classes: ['app-personnage-aleatoire'],
|
||||
popOut: true,
|
||||
resizable: true
|
||||
}, { inplace: false })
|
||||
}
|
||||
|
||||
constructor(actor) {
|
||||
super({})
|
||||
this.actor = actor
|
||||
@@ -106,18 +111,18 @@ export class AppPersonnageAleatoire extends FormApplication {
|
||||
this.checked = Object.fromEntries(CONTROLS.map(it => [it.name, [0, '', undefined].includes(this.current[it.name])]))
|
||||
}
|
||||
|
||||
async getData(options) {
|
||||
return foundry.utils.mergeObject(await super.getData(options), {
|
||||
_prepareContext() {
|
||||
return {
|
||||
actor: this.actor,
|
||||
current: this.current,
|
||||
checked: this.checked,
|
||||
options: { isGM: game.user.isGM }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html)
|
||||
this.html = html
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
this.html = $(this.element)
|
||||
this.html.find("input.current-value").change(async event => await this.onChange(event))
|
||||
this.html.find("a.random").click(async event => await this.onRandom(event))
|
||||
this.html.find("a.reset").click(async event => await this.onReset(event))
|
||||
@@ -132,8 +137,6 @@ export class AppPersonnageAleatoire extends FormApplication {
|
||||
this.html.find("button.button-apply").click(async event => await this.onApply())
|
||||
}
|
||||
|
||||
async _updateObject(event, formData) { }
|
||||
|
||||
async onApply() {
|
||||
const updates = Object.fromEntries(
|
||||
CONTROLS.filter(control => game.user.isGM || control.name != 'name')
|
||||
@@ -154,14 +157,14 @@ export class AppPersonnageAleatoire extends FormApplication {
|
||||
|
||||
async onSexe(sexe) {
|
||||
this.current['sexe'] = sexe
|
||||
this.render()
|
||||
this.render({ force: true })
|
||||
}
|
||||
|
||||
async onChange(event) {
|
||||
const name = this.getName(event.currentTarget)
|
||||
const control = this.getControl(name)
|
||||
this.current[control.name] = event.currentTarget.value
|
||||
this.render()
|
||||
this.render({ force: true })
|
||||
}
|
||||
|
||||
|
||||
@@ -169,20 +172,20 @@ export class AppPersonnageAleatoire extends FormApplication {
|
||||
const name = this.getName(event.currentTarget)
|
||||
const control = this.getControl(name)
|
||||
await this.randomControl(control)
|
||||
this.render()
|
||||
this.render({ force: true })
|
||||
}
|
||||
|
||||
async onReset(event) {
|
||||
const name = this.getName(event.currentTarget)
|
||||
const control = this.getControl(name)
|
||||
this.current[control.name] = control.getter(this.actor)
|
||||
await this.render()
|
||||
this.render({ force: true })
|
||||
}
|
||||
|
||||
async onCheckForRandom(event) {
|
||||
const name = this.getName(event.currentTarget)
|
||||
this.checked[name] = event.currentTarget.checked
|
||||
this.render()
|
||||
this.render({ force: true })
|
||||
}
|
||||
|
||||
async onRandomizeSelected() {
|
||||
@@ -191,11 +194,10 @@ export class AppPersonnageAleatoire extends FormApplication {
|
||||
.toArray()
|
||||
.map(it => this.getControl(it.attributes['data-field-name'].value))
|
||||
await Promise.all(controls.map(it => this.randomControl(it)))
|
||||
this.render()
|
||||
this.render({ force: true })
|
||||
}
|
||||
|
||||
async randomControl(control) {
|
||||
this.current[control.name] = await control.random(this.current)
|
||||
this.current[control.name] = await control.random(this.actor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
export { default as RdDItemBaseSheet} from "./common-item-sheet.mjs"
|
||||
export { default as RdDMonnaieSheet } from "./monnaie-sheet.mjs"
|
||||
export { default as RdDMunitionSheet } from "./munition-sheet.mjs"
|
||||
export { default as RdDTarotSheet } from "./tarot-sheet.mjs"
|
||||
|
||||
export { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs"
|
||||
export { RdDCreatureSheetV2 } from "./creature-sheet.mjs"
|
||||
export { RdDActorVehiculeSheetV2 } from "./vehicule-sheet.mjs"
|
||||
export { RdDCommerceSheetV2 } from "./commerce-sheet.mjs"
|
||||
export { RdDActorEntiteSheetV2 } from "./entite-sheet.mjs"
|
||||
export { RdDActorSheetV2 } from "./personnage-sheet.mjs"
|
||||
export { RdDActorExportSheetV2 } from "./export-encart-sheet.mjs"
|
||||
export { default as RdDItemBaseSheet} from "./common-item-sheet.mjs"
|
||||
export { default as RdDMonnaieSheet } from "./monnaie-sheet.mjs"
|
||||
export { default as RdDMunitionSheet } from "./munition-sheet.mjs"
|
||||
export { default as RdDTarotSheet } from "./tarot-sheet.mjs"
|
||||
export { default as RdDTeteSheet } from "./tete-sheet.mjs"
|
||||
export { default as RdDQueueSheet } from "./queue-sheet.mjs"
|
||||
export { default as RdDSouffleSheet } from "./souffle-sheet.mjs"
|
||||
export { default as RdDOmbreSheet } from "./ombre-sheet.mjs"
|
||||
export { default as RdDChantSheet } from "./chant-sheet.mjs"
|
||||
export { default as RdDMusiqueSheet } from "./musique-sheet.mjs"
|
||||
export { default as RdDDanseSheet } from "./danse-sheet.mjs"
|
||||
export { default as RdDJeuSheet } from "./jeu-sheet.mjs"
|
||||
export { default as RdDRecetteAlchimiqueSheet } from "./recettealchimique-sheet.mjs"
|
||||
export { default as RdDRecetteCuisineSheet } from "./recettecuisine-sheet.mjs"
|
||||
export { default as RdDArmeSheet } from "./arme-sheet.mjs"
|
||||
export { default as RdDArmureSheet } from "./armure-sheet.mjs"
|
||||
export { default as RdDObjetSheet } from "./objet-sheet.mjs"
|
||||
export { default as RdDConteneurSheet } from "./conteneur-sheet.mjs"
|
||||
export { default as RdDNourritureBoissonSheet } from "./nourritureboisson-sheet.mjs"
|
||||
export { default as RdDGemmeSheet } from "./gemme-sheet.mjs"
|
||||
export { default as RdDServiceSheet } from "./service-sheet.mjs"
|
||||
export { default as RdDTacheSheet } from "./tache-sheet.mjs"
|
||||
export { default as RdDBlessureSheet } from "./blessure-sheet.mjs"
|
||||
export { default as RdDMaladieSheet } from "./maladie-sheet.mjs"
|
||||
export { default as RdDPoisonSheet } from "./poison-sheet.mjs"
|
||||
export { default as RdDCompetenceSheet } from "./competence-sheet.mjs"
|
||||
export { default as RdDCompetenceCreatureSheet } from "./competencecreature-sheet.mjs"
|
||||
export { default as RdDPotionSheet } from "./potion-sheet.mjs"
|
||||
export { default as RdDSigneDraconiqueSheet } from "./signedraconique-sheet.mjs"
|
||||
export { default as RdDSortSheet } from "./sort-sheet.mjs"
|
||||
export { default as RdDRaceSheet } from "./race-sheet.mjs"
|
||||
export { default as RdDRencontreSheet } from "./rencontre-sheet.mjs"
|
||||
export { default as RdDEmpoignadeSheet } from "./empoignade-sheet.mjs"
|
||||
export { default as RdDPossessionSheet } from "./possession-sheet.mjs"
|
||||
export { default as RdDLivreSheet } from "./livre-sheet.mjs"
|
||||
export { default as RdDFauneSheet } from "./faune-sheet.mjs"
|
||||
export { default as RdDHerbeSheet } from "./herbe-sheet.mjs"
|
||||
export { default as RdDPlanteSheet } from "./plante-sheet.mjs"
|
||||
export { default as RdDIngredientSheet } from "./ingredient-sheet.mjs"
|
||||
export { default as RdDExtraitPoetiqueSheet } from "./extraitpoetique-sheet.mjs"
|
||||
export { default as RdDNombreAstralSheet } from "./nombreastral-sheet.mjs"
|
||||
export { default as RdDCaseTMRSheet } from "./casetmr-sheet.mjs"
|
||||
export { default as RdDSortReserveSheet } from "./sortreserve-sheet.mjs"
|
||||
export { default as RdDMeditationSheet } from "./meditation-sheet.mjs"
|
||||
export { default as RdDOeuvreSheet } from "./oeuvre-sheet.mjs"
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES, ACTOR_TYPES } from "../../constants.js";
|
||||
import { SystemCompendiums } from "../../settings/system-compendiums.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDArmeSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.arme; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE]; }
|
||||
|
||||
static MORTALITE_CHOICES = {
|
||||
mortel: "Mortel",
|
||||
"non-mortel": "Non mortel",
|
||||
empoignade: "Empoignade",
|
||||
}
|
||||
|
||||
static CATEGORIE_PARADE_CHOICES = {
|
||||
"sans-armes": "Sans arme",
|
||||
"armes-naturelles": "Armes naturelles",
|
||||
hast: "Armes d'hast",
|
||||
batons: "Bâtons",
|
||||
boucliers: "Boucliers",
|
||||
dagues: "Dagues",
|
||||
"epees-courtes": "Epées courtes",
|
||||
"epees-longues": "Epées longues",
|
||||
"epees-lourdes": "Epées lourdes",
|
||||
haches: "Haches",
|
||||
lances: "Lances",
|
||||
masses: "Masses",
|
||||
}
|
||||
|
||||
static INIT_CHOICES = {
|
||||
hast: "Armes Hast",
|
||||
lance: "Lance",
|
||||
baton: "Bâton",
|
||||
doubledragonne: "Double Dragonne",
|
||||
esparlongue: "Esparlongue",
|
||||
epeedragonne: "Epée Dragonne",
|
||||
epeebatarde: "Epée Bâtarde",
|
||||
epeecyane: "Epée Cyane",
|
||||
epeesorde: "Epée Sorde",
|
||||
grandehache: "Grande Hache",
|
||||
hachebataille: "Hache de Bataille",
|
||||
epeegnome: "Epée Gnome",
|
||||
masse: "Masse",
|
||||
gourdin: "Gourdin",
|
||||
fleau: "Fléaux",
|
||||
dague: "Dague",
|
||||
armenaturelle: "Armes Naturelles",
|
||||
autre: "Autres",
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "arme"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["arme-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/arme.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
async _prepareContext() {
|
||||
const competences = (await SystemCompendiums.getCompetences(ACTOR_TYPES.personnage))
|
||||
.filter(it => it.isCompetenceArme())
|
||||
const competenceMeleeChoices = {}
|
||||
const competenceLancerChoices = {}
|
||||
const competenceTirChoices = {}
|
||||
competences.forEach(c => {
|
||||
const label = c.name
|
||||
switch (c.system.categorie) {
|
||||
case 'melee': competenceMeleeChoices[label] = label; break
|
||||
case 'lancer': competenceLancerChoices[label] = label; break
|
||||
case 'tir': competenceTirChoices[label] = label; break
|
||||
}
|
||||
})
|
||||
return Object.assign(
|
||||
await super._prepareContext(),
|
||||
{
|
||||
competenceMeleeChoices,
|
||||
competenceLancerChoices,
|
||||
competenceTirChoices,
|
||||
mortaliteChoices: RdDArmeSheet.MORTALITE_CHOICES,
|
||||
categorieParadeChoices: RdDArmeSheet.CATEGORIE_PARADE_CHOICES,
|
||||
initChoices: RdDArmeSheet.INIT_CHOICES,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDArmureSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.armure; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "armure"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["armure-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/armure.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
import { RdDUtility } from "../../rdd-utility.js"
|
||||
import { Misc } from "../../misc.js"
|
||||
import { DialogSplitItem } from "../../dialog-split-item.js"
|
||||
import { RdDSheetUtility } from "../../rdd-sheet-utility.js"
|
||||
import { Monnaie } from "../../item-monnaie.js"
|
||||
import { ITEM_TYPES } from "../../constants.js"
|
||||
import { RdDItem } from "../../item.js"
|
||||
import { RdDTextEditor } from "../../apps/rdd-text-roll-editor.js"
|
||||
import { ItemAction } from "../../item/item-actions.js"
|
||||
import { DialogVerifierIngredients } from "../../dialog-verifier-ingredients.js"
|
||||
import { DialogLancerRecette } from "../../dialog-lancer-recette.js"
|
||||
import { ChatUtility } from "../../chat-utility.js"
|
||||
import { RdDItemBlessure } from "../../item/blessure.js"
|
||||
import { Grammar } from "../../grammar.js"
|
||||
import { SYSTEM_RDD } from "../../constants.js"
|
||||
|
||||
export class RdDBaseActorSheetV2 extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
|
||||
get actor() { return this.document }
|
||||
|
||||
// Was this.options.* — custom state
|
||||
vueDetaillee = false
|
||||
triEquipement = "name"
|
||||
recherche = undefined
|
||||
|
||||
static TYPE = undefined
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["rdd", "sheet", "actor"],
|
||||
position: { width: 640 },
|
||||
form: { submitOnChange: true, closeOnSubmit: false },
|
||||
window: { resizable: true, controls: [] },
|
||||
actions: {
|
||||
postToChat: RdDBaseActorSheetV2._onPostToChat,
|
||||
},
|
||||
}
|
||||
|
||||
//#region Registration
|
||||
|
||||
static register() {
|
||||
foundry.documents.collections.Actors.registerSheet(SYSTEM_RDD, this, {
|
||||
types: [this.TYPE],
|
||||
makeDefault: true,
|
||||
})
|
||||
}
|
||||
|
||||
static async registerAll(...sheetClasses) {
|
||||
const handlebars = []
|
||||
sheetClasses.forEach(sheetClass => {
|
||||
if (sheetClass.PARTS) {
|
||||
Object.values(sheetClass.PARTS).forEach(part => {
|
||||
if (part.template) handlebars.push(part.template)
|
||||
})
|
||||
}
|
||||
sheetClass.register()
|
||||
})
|
||||
await foundry.applications.handlebars.loadTemplates(Misc.distinct(handlebars))
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Data
|
||||
|
||||
async _prepareContext() {
|
||||
Monnaie.validerMonnaies(this.actor)
|
||||
this.actor.computeEtatGeneral()
|
||||
|
||||
this._sheetOptions = foundry.utils.mergeObject(
|
||||
RdDSheetUtility.mergeDocumentRights({ ...this.options }, this.actor, this.isEditable),
|
||||
{ vueDetaillee: this.vueDetaillee, triEquipement: this.triEquipement, recherche: this.recherche },
|
||||
{ inplace: false }
|
||||
)
|
||||
const ctx = {
|
||||
title: this.title,
|
||||
id: this.actor.id,
|
||||
type: this.actor.type,
|
||||
img: this.actor.img,
|
||||
name: this.actor.name,
|
||||
system: this.actor.system,
|
||||
systemFields: this.actor.system.schema.fields,
|
||||
description: await RdDTextEditor.enrichHTML(this.actor.system.description, this.actor),
|
||||
notesmj: await RdDTextEditor.enrichHTML(this.actor.system.notesmj, this.actor),
|
||||
options: this._sheetOptions,
|
||||
effects: this.actor.effects,
|
||||
editable: this.isEditable,
|
||||
cssClass: this.isEditable ? "editable" : "locked",
|
||||
limited: this.actor.limited,
|
||||
owner: this.actor.isOwner,
|
||||
}
|
||||
|
||||
RdDUtility.filterItemsPerTypeForSheet(ctx, this.actor.itemTypes, this.triEquipement)
|
||||
ctx.calc = {
|
||||
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
|
||||
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
|
||||
encTotal: this.actor.getEncTotal(),
|
||||
}
|
||||
|
||||
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(ctx.conteneurs, ctx.inventaires)
|
||||
this._appliquerRechercheObjets(ctx.conteneurs, ctx.inventaires)
|
||||
ctx.conteneurs = RdDUtility.conteneursRacine(ctx.conteneurs)
|
||||
ctx.competences.filter(it => it.type == ITEM_TYPES.competencecreature)
|
||||
.forEach(it => it.isdommages = it.isDommages())
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
_appliquerRechercheObjets(conteneurs, inventaires) {
|
||||
if (this.recherche?.text) {
|
||||
const recherche = this.recherche
|
||||
const allVisible = inventaires.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id)
|
||||
let addVisible = conteneurs.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id)
|
||||
do {
|
||||
allVisible.push(...addVisible)
|
||||
const parentsIds = conteneurs.filter(it => it.system.contenu.find(id => allVisible.includes(id))).map(it => it.id)
|
||||
addVisible = parentsIds.filter(id => !allVisible.includes(id))
|
||||
}
|
||||
while (addVisible.length > 0)
|
||||
inventaires.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
|
||||
conteneurs.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
|
||||
}
|
||||
else {
|
||||
inventaires.forEach(it => it.system.isHidden = false)
|
||||
conteneurs.forEach(it => it.system.isHidden = false)
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Rendering
|
||||
|
||||
async _renderFrame(options) {
|
||||
const frame = await super._renderFrame(options)
|
||||
return frame
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options)
|
||||
this.html = $(this.element)
|
||||
|
||||
const nav = this.element.querySelector(".sheet-tabs")
|
||||
if (nav) {
|
||||
const tab = this.tabGroups?.primary || nav.querySelector("[data-tab]")?.dataset.tab
|
||||
if (tab) this.changeTab(tab, "primary", { force: true })
|
||||
}
|
||||
|
||||
this.html.find(".actionItem").click(async event => await ItemAction.onActionItem(event, this.actor, this._sheetOptions))
|
||||
this.html.find(".item-edit").click(async event => await this._onItemEdit(event))
|
||||
this.html.find(".item-verify-ingredients").click(async event => await this._onVerifyIngredients(event))
|
||||
this.html.find(".item-lancer-recette").click(async event => await this._onLancerRecette(event))
|
||||
this.html.find(".conteneur-name a").click(async event => {
|
||||
RdDUtility.toggleAfficheContenu(this._getItemId(event))
|
||||
this.render({ force: true })
|
||||
})
|
||||
|
||||
this.html.find(".actor-montrer").click(async event => await this.actor.postActorToChat())
|
||||
|
||||
this.html.find(".recherche")
|
||||
.each((_index, field) => this._rechercheSelectArea(field))
|
||||
.keyup(async event => this._rechercherKeyup(event))
|
||||
.change(async event => this._rechercherKeyup(event))
|
||||
|
||||
this.html.find(".recherche").prop("disabled", false)
|
||||
|
||||
this.html.find(".item-list .item").dblclick(async event => {
|
||||
const item = this._getItem(event)
|
||||
if (item) item.sheet.render({ force: true })
|
||||
})
|
||||
|
||||
this.html.find(".tri-equipement").change(event => {
|
||||
this.triEquipement = event.currentTarget.value
|
||||
this.render({ force: true })
|
||||
})
|
||||
|
||||
if (!this.isEditable) return
|
||||
|
||||
this.html.find(".item-equip-armure").click(async event => await this.actor.equiperObjet(this._getItem(event)))
|
||||
this.html.find(".item-delete").click(async event => await RdDUtility.confirmActorItemDelete(this._getItem(event), this.actor))
|
||||
this.html.find(".item-quantite-plus").click(async event => await this.actor.itemQuantiteIncDec(this._getItemId(event), 1))
|
||||
this.html.find(".item-quantite-moins").click(async event => await this.actor.itemQuantiteIncDec(this._getItemId(event), -1))
|
||||
this.html.find(".item-quantite").change(async event => {
|
||||
const input = event.currentTarget
|
||||
const item = this.actor.items.get(input.dataset.itemId)
|
||||
if (item) {
|
||||
const newQty = Math.max(0, parseInt(input.value) || 0)
|
||||
await item.update({ "system.quantite": newQty })
|
||||
if (newQty === 0) this.render({ force: true })
|
||||
}
|
||||
})
|
||||
this.html.find(".item-quantite").on("contextmenu", async event => {
|
||||
event.preventDefault()
|
||||
const item = this._getItem(event)
|
||||
if (!item?.isInventaire()) return
|
||||
new foundry.applications.api.DialogV2({
|
||||
title: `Quantité — ${item.name}`,
|
||||
content: `<p>Réduire la quantité de :</p>
|
||||
<select class="qty-amount">
|
||||
<option value="1">1</option>
|
||||
<option value="5">5</option>
|
||||
<option value="10">10</option>
|
||||
<option value="all">Tout jeter</option>
|
||||
</select>`,
|
||||
buttons: [
|
||||
{ action: "reduce", label: "Retirer", icon: "fa-solid fa-minus", callback: (event, button, dialog) => {
|
||||
const amount = dialog.element.querySelector(".qty-amount").value
|
||||
if (amount === "all") {
|
||||
item.delete()
|
||||
this.render({ force: true })
|
||||
} else {
|
||||
item.update({ "system.quantite": Math.max(0, item.system.quantite - parseInt(amount)) })
|
||||
}
|
||||
}},
|
||||
],
|
||||
}).render({ force: true })
|
||||
})
|
||||
|
||||
this.html.find(".creer-un-objet").click(async event => await this._selectObjetTypeToCreate())
|
||||
this.html.find(".nettoyer-conteneurs").click(async event => await this.actor.nettoyerConteneurs())
|
||||
this.html.find(".quick-add-ration").click(async event => {
|
||||
const existante = this.actor.items.find(it => it.type === "nourritureboisson" && it.name === "Ration")
|
||||
if (existante) {
|
||||
await existante.update({ "system.quantite": Math.max(0, Number(existante.system.quantite) + 1) })
|
||||
} else {
|
||||
await this.actor.createEmbeddedDocuments("Item", [{
|
||||
name: "Ration", type: "nourritureboisson",
|
||||
img: "systems/foundryvtt-reve-de-dragon/icons/objets/provision_crue.webp",
|
||||
system: { sust: 1, encombrement: 0.1, quantite: 1, cout: 0.1, boisson: false },
|
||||
}], { renderSheet: false })
|
||||
}
|
||||
this.render({ force: true })
|
||||
})
|
||||
this.html.find(".quick-add-eau").click(async event => {
|
||||
const existante = this.actor.items.find(it => it.type === "nourritureboisson" && it.name === "Eau")
|
||||
if (existante) {
|
||||
await existante.update({ "system.quantite": Math.max(0, Number(existante.system.quantite) + 1) })
|
||||
} else {
|
||||
await this.actor.createEmbeddedDocuments("Item", [{
|
||||
name: "Eau", type: "nourritureboisson",
|
||||
img: "systems/foundryvtt-reve-de-dragon/icons/objets/outre.webp",
|
||||
system: { desaltere: 1, encombrement: 0.1, quantite: 1, cout: 0.05, boisson: true },
|
||||
}], { renderSheet: false })
|
||||
}
|
||||
this.render({ force: true })
|
||||
})
|
||||
|
||||
this.html.find(".vue-detaillee").click(async event => {
|
||||
this.vueDetaillee = !this.vueDetaillee
|
||||
this.render({ force: true })
|
||||
})
|
||||
|
||||
// Revel listeners (was base-actor-reve-sheet.js)
|
||||
this.html.find(".button-encaissement").click(async event => await this.actor.encaisser())
|
||||
this.html.find(".roll-carac").click(async event => {
|
||||
await this.actor.rollCarac(Grammar.toLowerCaseNoAccent(event.currentTarget.attributes["data-carac-name"].value))
|
||||
})
|
||||
this.html.find(".roll-competence").click(async event => await this.actor.rollCompetence(this._getItemId(event)))
|
||||
this.html.find(".endurance-plus").click(async event => await this.actor.santeIncDec("endurance", 1))
|
||||
this.html.find(".endurance-moins").click(async event => await this.actor.santeIncDec("endurance", -1))
|
||||
|
||||
if (game.user.isGM) {
|
||||
this.html.find(".button-remise-a-neuf").click(async event => await this.actor.remiseANeuf())
|
||||
this.html.find(".delete-active-effect").click(async event => await this.actor.removeEffect(this.html.find(event.currentTarget).parents(".active-effect").data("effect")))
|
||||
this.html.find(".enlever-tous-effets").click(async event => await this.actor.removeEffects())
|
||||
}
|
||||
this.html.find(".competence-add").click(async event =>
|
||||
await this.actor.createEmbeddedDocuments("Item", [{
|
||||
type: ITEM_TYPES.competencecreature,
|
||||
name: "Nouvelle competence",
|
||||
img: "systems/foundryvtt-reve-de-dragon/icons/compcreature-serres.webp",
|
||||
system: { carac_value: this.actor.getForce() },
|
||||
}], { renderSheet: true })
|
||||
)
|
||||
this.html.find(".roll-text").click(async event => await RdDTextEditor.rollText(event, this.actor))
|
||||
this.html.find(".chat-roll-text").click(async event => await RdDTextEditor.chatRollText(event))
|
||||
|
||||
if (this.vueDetaillee) {
|
||||
this.html.find(".carac-value").change(async event => {
|
||||
if (event.currentTarget.name.includes("carac.")) {
|
||||
const caracName = event.currentTarget.name.replace("carac.", "")
|
||||
await this.actor.updateCarac(caracName, parseInt(event.currentTarget.value))
|
||||
}
|
||||
})
|
||||
this.html.find(".competence-value").change(async event => {
|
||||
const compName = event.currentTarget.attributes.compname.value
|
||||
await this.actor.updateCompetence(compName, parseInt(event.currentTarget.value))
|
||||
})
|
||||
}
|
||||
|
||||
// Sang listeners (was base-actor-sang-sheet.js)
|
||||
this.html.find(".creer-blessure-legere").click(async event => await RdDItemBlessure.createBlessure(this.actor, 2))
|
||||
this.html.find(".creer-blessure-grave").click(async event => await RdDItemBlessure.createBlessure(this.actor, 4))
|
||||
this.html.find(".creer-blessure-critique").click(async event => await RdDItemBlessure.createBlessure(this.actor, 6))
|
||||
this.html.find(".subir-blessure-contusion").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 0))
|
||||
this.html.find(".subir-blessure-legere").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 2))
|
||||
this.html.find(".subir-blessure-grave").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 4))
|
||||
this.html.find(".subir-blessure-critique").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 6))
|
||||
this.html.find(".jet-vie").click(async event => await this.actor.jetDeVie())
|
||||
this.html.find(".jet-endurance").click(async event => await this._jetEndurance())
|
||||
this.html.find(".vie-plus").click(async event => await this.actor.santeIncDec("vie", 1))
|
||||
this.html.find(".vie-moins").click(async event => await this.actor.santeIncDec("vie", -1))
|
||||
}
|
||||
|
||||
async _jetEndurance() {
|
||||
const endurance = this.actor.getEnduranceActuelle()
|
||||
const result = await this.actor.jetEndurance(endurance)
|
||||
await ChatMessage.create({
|
||||
content: `Jet d'Endurance : ${result.jetEndurance} / ${endurance}
|
||||
<br>${this.actor.name} a ${result.sonne ? "échoué" : "réussi"} son Jet d'Endurance ${result.sonne ? "et devient Sonné" : ""}`,
|
||||
whisper: ChatUtility.getOwners(this.actor),
|
||||
})
|
||||
}
|
||||
|
||||
_rechercherKeyup(event) {
|
||||
const currentTarget = event.currentTarget
|
||||
const nouvelleRecherche = this._optionRecherche(currentTarget)
|
||||
if (this.recherche?.text != nouvelleRecherche?.text) {
|
||||
this.recherche = nouvelleRecherche
|
||||
if (this._timerRecherche) clearTimeout(this._timerRecherche)
|
||||
this._timerRecherche = setTimeout(() => {
|
||||
this._timerRecherche = undefined
|
||||
this.render({ force: true })
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
_rechercheSelectArea(field) {
|
||||
if (this.recherche) {
|
||||
field.focus()
|
||||
field.setSelectionRange(this.recherche.start, this.recherche.end)
|
||||
}
|
||||
}
|
||||
|
||||
_optionRecherche(target) {
|
||||
if (!target.value?.length) return undefined
|
||||
return {
|
||||
text: target.value,
|
||||
start: target.selectionStart,
|
||||
end: target.selectionEnd,
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Drop
|
||||
|
||||
async _onDropItem(event, item) {
|
||||
const destItemId = this.html.find(event.target)?.closest(".item").attr("data-item-id")
|
||||
const previousItemIds = new Set(this.actor.items.keys())
|
||||
const dropParams = await RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor, { uuid: item.uuid }, this.objetVersConteneur)
|
||||
if (dropParams) {
|
||||
const callSuper = await this.actor.processDropItem(dropParams)
|
||||
if (callSuper) {
|
||||
await super._onDropItem(event, item)
|
||||
}
|
||||
if (destItemId && dropParams.sourceActorId !== this.actor.id) {
|
||||
const container = this.actor.items.get(destItemId)
|
||||
if (container?.isConteneur()) {
|
||||
const dropped = [...this.actor.items].find(i => !previousItemIds.has(i.id))
|
||||
if (dropped && !this.actor.findConteneur(dropped)) {
|
||||
await this.actor.ajouterDansConteneur(dropped, container, () => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _onDropActor(event, actor) {
|
||||
this.actor.addSubActeur(actor)
|
||||
await super._onDropActor(event, actor)
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Helpers
|
||||
|
||||
_getItemId(event) {
|
||||
return RdDSheetUtility.getItemId(event)
|
||||
}
|
||||
|
||||
_getItem(event) {
|
||||
return RdDSheetUtility.getItem(event, this.actor)
|
||||
}
|
||||
|
||||
async _onItemEdit(event) {
|
||||
const item = this._getItem(event)
|
||||
if (item) item.sheet.render({ force: true })
|
||||
}
|
||||
|
||||
async _onVerifyIngredients(event) {
|
||||
event.preventDefault()
|
||||
const item = this._getItem(event)
|
||||
if (!item || item.type !== ITEM_TYPES.recettealchimique) return
|
||||
DialogVerifierIngredients.create(item, this.actor)
|
||||
}
|
||||
|
||||
async _onLancerRecette(event) {
|
||||
event.preventDefault()
|
||||
const item = this._getItem(event)
|
||||
if (!item || item.type !== ITEM_TYPES.recettealchimique) return
|
||||
const ingredients = Misc.safeJsonParse(item.system.ingredients, [])
|
||||
if (!ingredients.length) {
|
||||
ui.notifications.warn("Cette recette n'a pas d'ingrédients définis.")
|
||||
return
|
||||
}
|
||||
DialogLancerRecette.create(item, this.actor)
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Actions
|
||||
|
||||
static async _onPostToChat(event, target) {
|
||||
await this.actor.postActorToChat()
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Item operations
|
||||
|
||||
async _selectObjetTypeToCreate() {
|
||||
const types = this._getTypesInventaire().sort(Misc.ascending(type => Misc.typeName("Item", type)))
|
||||
let content = `<span class="generic-label">Selectionnez le type d'équipement</span><select class="item-type">`
|
||||
for (const typeName of types) {
|
||||
content += `<option value="${typeName}">${Misc.typeName("Item", typeName)}</option>`
|
||||
}
|
||||
content += "</select>"
|
||||
new foundry.applications.api.DialogV2({
|
||||
title: "Créer un équipement",
|
||||
content,
|
||||
buttons: [
|
||||
{ action: "create", label: "Créer l'objet", icon: "fa-solid fa-check", callback: () => this.actor.createItem($(".item-type").val()) },
|
||||
],
|
||||
}).render({ force: true })
|
||||
}
|
||||
|
||||
_getTypesInventaire() {
|
||||
return RdDItem.getItemTypesInventaire()
|
||||
}
|
||||
|
||||
async splitItem(item) {
|
||||
const dialog = await DialogSplitItem.create(item, (it, split) => this._onSplitItem(it, split))
|
||||
dialog.render({ force: true })
|
||||
}
|
||||
|
||||
async _onSplitItem(item, split) {
|
||||
if (split >= 1 && split < item.system.quantite) {
|
||||
await item.diminuerQuantite(split)
|
||||
const splitItem = foundry.utils.deepClone(item)
|
||||
splitItem.system.quantite = split
|
||||
await this.actor.createEmbeddedDocuments("Item", [splitItem])
|
||||
}
|
||||
}
|
||||
|
||||
vendre(item) {
|
||||
item?.proposerVente(this.actor.getQuantiteDisponible(item))
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDBlessureSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.blessure; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{ classes: ["fvtt-rdd", "item", "blessure"], position: { width: 448 }, window: { contentClasses: ["blessure-content"] } });
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/blessure.hbs" },
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDCaseTMRSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.casetmr; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "casetmr"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["casetmr-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/casetmr.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
import { CommonAppreciable } from "../../common/appreciable.mjs";
|
||||
|
||||
const TEMPLATE_APPRECIABLE = new CommonAppreciable();
|
||||
|
||||
export default class RdDChantSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.chant; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_APPRECIABLE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "chant"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["chant-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/chant.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs"
|
||||
import { RdDItem } from "../../item.js"
|
||||
import { ACTOR_TYPES } from "../../constants.js"
|
||||
|
||||
export class RdDCommerceSheetV2 extends RdDBaseActorSheetV2 {
|
||||
|
||||
static TYPE = ACTOR_TYPES.commerce
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, {
|
||||
position: { width: 600, height: 720 },
|
||||
})
|
||||
|
||||
static PARTS = {
|
||||
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-actor-sheet.hbs" },
|
||||
}
|
||||
|
||||
get title() {
|
||||
if (this.actor.token && this.actor.token !== this.actor.prototypeToken) {
|
||||
return this.actor.token.name
|
||||
}
|
||||
return super.title
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const ctx = await super._prepareContext()
|
||||
if (this.actor.token && this.actor.token !== this.actor.prototypeToken) {
|
||||
foundry.utils.mergeObject(ctx, {
|
||||
title: this.actor.token.name,
|
||||
token: { img: this.actor.token.texture.src },
|
||||
}, { overwrite: true })
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options)
|
||||
if (!this.isEditable) return
|
||||
|
||||
this.html.find("input.item-quantite").change(async event => {
|
||||
const newQuantite = Math.max(0, Number.parseInt(this.html.find(event.currentTarget).val()))
|
||||
await this._getItem(event)?.update({ "system.quantite": newQuantite })
|
||||
})
|
||||
this.html.find("input.item-cout").change(async event => {
|
||||
const newCout = Math.max(0, Number(this.html.find(event.currentTarget).val()))
|
||||
await this._getItem(event)?.update({ "system.cout": newCout })
|
||||
})
|
||||
}
|
||||
|
||||
_getTypesInventaire() {
|
||||
return RdDItem.getItemTypesInventaire("all")
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export default class RdDItemBaseSheet extends HandlebarsApplicationMixin(foundry
|
||||
|
||||
static register(sheetClass) {
|
||||
const itemType = sheetClass.ITEM_TYPE
|
||||
Items.registerSheet(SYSTEM_RDD, sheetClass, {
|
||||
foundry.documents.collections.Items.registerSheet(SYSTEM_RDD, sheetClass, {
|
||||
label: Misc.typeName('Item', itemType),
|
||||
types: [itemType],
|
||||
makeDefault: true
|
||||
@@ -63,13 +63,15 @@ export default class RdDItemBaseSheet extends HandlebarsApplicationMixin(foundry
|
||||
resizable: true,
|
||||
},
|
||||
actions: {
|
||||
editImage: RdDItemBaseSheet.#onEditImage,
|
||||
editImage: RdDItemBaseSheet._onEditImage,
|
||||
postToChat: RdDItemBaseSheet._onPostToChat,
|
||||
proposerVente: RdDItemBaseSheet._onProposerVente,
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
return {
|
||||
const ctx = {
|
||||
item: this.document,
|
||||
options: RdDSheetUtility.getOptions(this.document, this.isEditable),
|
||||
fields: this.document.schema.fields,
|
||||
@@ -77,7 +79,12 @@ export default class RdDItemBaseSheet extends HandlebarsApplicationMixin(foundry
|
||||
system: this.document.system,
|
||||
source: this.document.toObject(),
|
||||
isEditable: this.isEditable,
|
||||
canVendre: this.document.isInventaire?.() && this.document.isVideOuNonConteneur?.(),
|
||||
}
|
||||
for (const tpl of this.constructor.TEMPLATES) {
|
||||
Object.assign(ctx, await tpl.prepareContext(this.document))
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
// #region Actions
|
||||
@@ -91,7 +98,7 @@ export default class RdDItemBaseSheet extends HandlebarsApplicationMixin(foundry
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
static async #onEditImage(event, target) {
|
||||
static async _onEditImage(event, target) {
|
||||
const attr = target.dataset.edit
|
||||
const current = foundry.utils.getProperty(this.document, attr)
|
||||
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
|
||||
@@ -107,5 +114,40 @@ export default class RdDItemBaseSheet extends HandlebarsApplicationMixin(foundry
|
||||
})
|
||||
return fp.browse()
|
||||
}
|
||||
|
||||
static async _onPostToChat(event, target) {
|
||||
await this.document.postItemToChat()
|
||||
}
|
||||
|
||||
static async _onProposerVente(event, target) {
|
||||
await this.document.proposerVente(1)
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _renderFrame(options) {
|
||||
const frame = await super._renderFrame(options)
|
||||
const header = frame.querySelector(".window-header")
|
||||
if (header) {
|
||||
const closeBtn = header.querySelector("button[data-action=close]")
|
||||
const chatBtn = document.createElement("button")
|
||||
chatBtn.type = "button"
|
||||
chatBtn.className = "header-control icon fa-solid fa-comment"
|
||||
chatBtn.dataset.action = "postToChat"
|
||||
chatBtn.dataset.tooltip = "Poster dans le chat"
|
||||
chatBtn.ariaLabel = "Poster dans le chat"
|
||||
header.insertBefore(chatBtn, closeBtn)
|
||||
if (this.document.isInventaire?.() && this.document.isVideOuNonConteneur?.()) {
|
||||
const sellBtn = document.createElement("button")
|
||||
sellBtn.type = "button"
|
||||
sellBtn.className = "header-control icon fa-solid fa-comments-dollar"
|
||||
sellBtn.dataset.action = "proposerVente"
|
||||
sellBtn.dataset.tooltip = "Proposer à la vente"
|
||||
sellBtn.ariaLabel = "Proposer à la vente"
|
||||
header.insertBefore(sellBtn, chatBtn)
|
||||
}
|
||||
}
|
||||
return frame
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDCompetenceSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.competence; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{ classes: ["fvtt-rdd", "item", "competence"], position: { width: 448 }, window: { contentClasses: ["competence-content"] } });
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/competence.hbs" },
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDCompetenceCreatureSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.competencecreature; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{ classes: ["fvtt-rdd", "item", "competencecreature"], position: { width: 448 }, window: { contentClasses: ["competencecreature-content"] } });
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/competencecreature.hbs" },
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDConteneurSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.conteneur; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "conteneur"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["conteneur-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/conteneur.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
async _prepareContext() {
|
||||
return Object.assign(
|
||||
await super._prepareContext(),
|
||||
{
|
||||
actorId: this.document.parent?.id,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs"
|
||||
import { ACTOR_TYPES } from "../../constants.js"
|
||||
|
||||
export class RdDCreatureSheetV2 extends RdDBaseActorSheetV2 {
|
||||
|
||||
static TYPE = ACTOR_TYPES.creature
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, {
|
||||
position: { width: 640, height: 720 },
|
||||
})
|
||||
|
||||
static PARTS = {
|
||||
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor-creature-sheet.hbs" },
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options)
|
||||
if (!this.isEditable) return
|
||||
|
||||
this.html.find(".creature-carac").change(async event => {
|
||||
const compName = event.currentTarget.attributes.compname.value
|
||||
await this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value))
|
||||
})
|
||||
this.html.find(".creature-niveau").change(async event => {
|
||||
const compName = event.currentTarget.attributes.compname.value
|
||||
await this.actor.updateCreatureCompetence(compName, "niveau", parseInt(event.target.value))
|
||||
})
|
||||
this.html.find(".creature-dommages").change(async event => {
|
||||
const compName = event.currentTarget.attributes.compname.value
|
||||
await this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
import { CommonAppreciable } from "../../common/appreciable.mjs";
|
||||
|
||||
const TEMPLATE_APPRECIABLE = new CommonAppreciable();
|
||||
|
||||
export default class RdDDanseSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.danse; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_APPRECIABLE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "danse"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["danse-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/danse.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
async _prepareContext() {
|
||||
return Object.assign(
|
||||
await super._prepareContext(),
|
||||
{
|
||||
typeChoices: { recreative: "Récreative", soliste: "Soliste" },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDEmpoignadeSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.empoignade; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{ classes: ["fvtt-rdd", "item", "empoignade"], position: { width: 448 }, window: { contentClasses: ["empoignade-content"] } });
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/empoignade.hbs" },
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs"
|
||||
import { RdDSheetUtility } from "../../rdd-sheet-utility.js"
|
||||
import { RdDUtility } from "../../rdd-utility.js"
|
||||
import { DialogSelect } from "../../dialog-select.js"
|
||||
import { ACTOR_TYPES } from "../../constants.js"
|
||||
|
||||
export class RdDActorEntiteSheetV2 extends RdDBaseActorSheetV2 {
|
||||
|
||||
static TYPE = ACTOR_TYPES.entite
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, {
|
||||
position: { width: 640, height: 720 },
|
||||
})
|
||||
|
||||
static PARTS = {
|
||||
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.hbs" },
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const ctx = await super._prepareContext()
|
||||
ctx.niveau = this.actor.getNiveau()
|
||||
const { niveau: _, ...caracRest } = ctx.system.carac
|
||||
ctx.system = { ...ctx.system, carac: caracRest }
|
||||
ctx.resonances = this.actor.system.sante.resonnance.actors
|
||||
.map(actorId => game.actors.get(actorId))
|
||||
.filter(actor => actor != undefined)
|
||||
.map(actor => ({ id: actor.id, name: actor.name, img: actor.img }))
|
||||
return ctx
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options)
|
||||
if (!this.isEditable) return
|
||||
|
||||
this.html.find(".creature-carac").change(async event => {
|
||||
const compName = event.currentTarget.attributes.compname.value
|
||||
await this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value))
|
||||
})
|
||||
this.html.find(".creature-dommages").change(async event => {
|
||||
const compName = event.currentTarget.attributes.compname.value
|
||||
await this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value))
|
||||
})
|
||||
|
||||
this.html.find(".resonance-add").click(async event =>
|
||||
await DialogSelect.select({
|
||||
label: "Choisir un acteur à accorder",
|
||||
list: game.actors.filter(it => true),
|
||||
},
|
||||
it => this._resonanceAdd(it.id))
|
||||
)
|
||||
|
||||
this.html.find(".resonance-delete").click(async event => {
|
||||
const li = RdDSheetUtility.getEventElement(event)
|
||||
const actorId = li.data("actor-id")
|
||||
if (actorId) {
|
||||
const actorResonance = game.actors.get(actorId)
|
||||
RdDUtility.confirmSubActeurDelete(this, actorResonance, li, () => {
|
||||
this._resonanceDelete(actorId)
|
||||
RdDUtility.slideOnDelete(this, li)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async _onDropActor(event, dragData) {
|
||||
const dropActor = fromUuidSync(dragData.uuid)
|
||||
await this.actor.setEntiteReveAccordee(dropActor)
|
||||
await super._onDropActor(event, dragData)
|
||||
}
|
||||
|
||||
async _resonanceAdd(actorId) {
|
||||
const newResonances = [...this.actor.system.sante.resonnance.actors, actorId]
|
||||
await this.actor.update({ "system.sante.resonnance.actors": newResonances })
|
||||
}
|
||||
|
||||
async _resonanceDelete(actorId) {
|
||||
console.log("Delete : ", actorId)
|
||||
const newResonances = this.actor.system.sante.resonnance.actors.filter(id => id !== actorId)
|
||||
await this.actor.update({ "system.sante.resonnance.actors": newResonances }, { render: false })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { RdDActorSheetV2 } from "./personnage-sheet.mjs"
|
||||
import { SYSTEM_RDD } from "../../constants.js"
|
||||
import { Misc } from "../../misc.js"
|
||||
import { EXPORT_CSV_SCRIPTARIUM, OptionsAvancees } from "../../settings/options-avancees.js"
|
||||
import { ExportScriptarium } from "../../actor/export-scriptarium/export-scriptarium.js"
|
||||
import { CATEGORIES_COMPETENCES_BASE, CATEGORIES_DRACONIC, Mapping } from "../../actor/export-scriptarium/mapping.js"
|
||||
|
||||
export class RdDActorExportSheetV2 extends RdDActorSheetV2 {
|
||||
|
||||
static TYPE = "personnage"
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({}, RdDActorSheetV2.DEFAULT_OPTIONS, {
|
||||
position: { width: 550 },
|
||||
})
|
||||
|
||||
static PARTS = {
|
||||
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/actor-encart-sheet.hbs" },
|
||||
}
|
||||
|
||||
showCompNiveauBase = false
|
||||
vueArchetype = false
|
||||
|
||||
static async init() {
|
||||
await foundry.applications.handlebars.loadTemplates([
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/arme.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/arme-titre.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessures.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-compteur.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee-compteur.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/competences.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/esquive.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/fatigue.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/protection.hbs",
|
||||
"systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs",
|
||||
])
|
||||
}
|
||||
|
||||
static register() {
|
||||
foundry.documents.collections.Actors.registerSheet(SYSTEM_RDD, this, {
|
||||
types: ["personnage"],
|
||||
makeDefault: false,
|
||||
label: "Feuille simplifiée",
|
||||
})
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
const ctx = await super._prepareContext()
|
||||
ctx.context = Mapping.prepareContext(this.actor)
|
||||
ctx.attaques = this.actor.listActionsAttaque()
|
||||
ctx.export = this._getMappingValues(ctx.context, this.actor)
|
||||
ctx.competences = this._getCompetences(CATEGORIES_COMPETENCES_BASE)
|
||||
ctx.draconic = this._getCompetences(CATEGORIES_DRACONIC)
|
||||
const legeres = this.actor.countBlessures(it => it.isLegere())
|
||||
const graves = this.actor.countBlessures(it => it.isGrave())
|
||||
const critiques = this.actor.countBlessures(it => it.isCritique())
|
||||
ctx.etat = {
|
||||
surenc: this.actor.computeMalusSurEncombrement(),
|
||||
fatigue: {
|
||||
value: this.actor.getFatigueActuelle(),
|
||||
max: this.actor.getFatigueMax(),
|
||||
malus: this.actor.malusFatigue(),
|
||||
},
|
||||
blessures: legeres + graves + critiques,
|
||||
blessure: [legeres > 0, legeres > 1, legeres > 2, legeres > 3, legeres > 4, graves > 0, graves > 1, critiques > 0],
|
||||
}
|
||||
ctx.options.exportScriptarium = OptionsAvancees.isUsing(EXPORT_CSV_SCRIPTARIUM)
|
||||
return ctx
|
||||
}
|
||||
|
||||
_getMappingValues(context, actor) {
|
||||
return Object.fromEntries(Mapping.getMapping().map(it => [it.column, {
|
||||
colName: it.colName ?? it.column,
|
||||
column: it.column,
|
||||
rollClass: it.rollClass,
|
||||
value: String(it.getter(actor, context)),
|
||||
}]))
|
||||
}
|
||||
|
||||
_getCompetences(categories) {
|
||||
const competences = Mapping.getCompetencesCategorie(this.actor, categories)
|
||||
if (competences.length == 0) return ""
|
||||
const byCategories = Mapping.competencesByCategoriesByNiveau(competences, categories)
|
||||
const listByCategories = Object.values(byCategories)
|
||||
.map(it => it.competencesParNiveau)
|
||||
.map(byNiveau => {
|
||||
const niveaux = Object.keys(byNiveau).map(it => Number(it)).sort(Misc.ascending())
|
||||
if (niveaux.length == 0) return undefined
|
||||
const listCategorieByNiveau = niveaux.map(niveau => {
|
||||
const list = byNiveau[niveau].sort(Misc.ascending(it => it.name))
|
||||
return { niveau, list }
|
||||
})
|
||||
return Misc.concat(listCategorieByNiveau)
|
||||
}).filter(it => it != undefined)
|
||||
return Misc.concat(listByCategories)
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options)
|
||||
|
||||
this.html.find(".click-blessure-remove").click(async event =>
|
||||
await this.actor.supprimerBlessure({
|
||||
gravite: this.html.find(event.currentTarget).data("gravite"),
|
||||
})
|
||||
)
|
||||
this.html.find(".click-blessure-add").click(async event => {
|
||||
const blessure = this.actor.nouvelleBlessure(this.html.find(event.currentTarget).data("gravite"))
|
||||
await this.actor.createEmbeddedDocuments("Item", [blessure])
|
||||
})
|
||||
this.html.find(".button-export").click(async event => await
|
||||
ExportScriptarium.INSTANCE.exportActors([this.actor],
|
||||
`${this.actor.uuid}-${this.actor.name}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDExtraitPoetiqueSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.extraitpoetique; }
|
||||
static get TEMPLATES() { return []; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "extraitpoetique"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["extraitpoetique-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/extraitpoetique.hbs",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE, TEMPLATE_COMESTIBLE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDFauneSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.faune; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE, TEMPLATE_COMESTIBLE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "faune"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["faune-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/faune.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import { RdDItemGemme } from "../../item/gemme.js";
|
||||
import { RdDTimestamp } from "../../time/rdd-timestamp.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDGemmeSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.gemme; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "gemme"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["gemme-content"] },
|
||||
actions: {
|
||||
enchanter: RdDGemmeSheet.#onEnchanter,
|
||||
},
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/gemme.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
async _prepareContext() {
|
||||
const item = this.document
|
||||
const enchantement = RdDTimestamp.splitIndexDate(item.system.prdate)
|
||||
return Object.assign(
|
||||
await super._prepareContext(),
|
||||
{
|
||||
gemmeTypeList: RdDItemGemme.getGemmeTypeOptionList(),
|
||||
isEnchantementPossible: item.isEnchantementPossible,
|
||||
dateActuelle: game.system.rdd.calendrier.dateCourante(),
|
||||
enchantement,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_onChangeForm(event, formData) {
|
||||
const obj = formData?.object
|
||||
if (!obj) return super._onChangeForm(event, formData)
|
||||
|
||||
if (event.target?.classList?.contains('date-enchantement')) {
|
||||
const jour = Number(this.element?.querySelector('input.date-enchantement[name="enchantement.jour"]')?.value)
|
||||
const moisEl = this.element?.querySelector('select.date-enchantement[name="enchantement.mois"]')
|
||||
if (moisEl) {
|
||||
const mois = RdDTimestamp.definition(moisEl.value)
|
||||
const indexDate = game.system.rdd.calendrier.getIndexFromDate(jour, mois.heure)
|
||||
obj['system.prdate'] = indexDate
|
||||
delete obj['enchantement.jour']
|
||||
delete obj['enchantement.mois']
|
||||
}
|
||||
}
|
||||
return super._onChangeForm(event, formData)
|
||||
}
|
||||
|
||||
static async #onEnchanter(event, target) {
|
||||
const { DialogEnchanter } = await import("../../enchantement/dialog-enchanter.js")
|
||||
DialogEnchanter.enchanter(this.document)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE, TEMPLATE_COMESTIBLE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDHerbeSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.herbe; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE, TEMPLATE_COMESTIBLE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "herbe"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["herbe-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/herbe.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
async _prepareContext() {
|
||||
const ctx = await super._prepareContext()
|
||||
ctx.isBrut = this.document.getUtilisationCuisine() === 'brut'
|
||||
return ctx
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options)
|
||||
|
||||
this.element.querySelector('.preparer-nourriture')?.addEventListener('click', async () => {
|
||||
const actor = this.document.parent
|
||||
if (actor) await actor.preparerNourriture(this.document)
|
||||
})
|
||||
|
||||
this.element.querySelector('.manger-nourriture')?.addEventListener('click', async () => {
|
||||
const actor = this.document.parent
|
||||
if (actor) await actor.mangerNourriture(this.document)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE, TEMPLATE_COMESTIBLE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDIngredientSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.ingredient; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE, TEMPLATE_COMESTIBLE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "ingredient"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["ingredient-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/ingredient.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
async _prepareContext() {
|
||||
const ctx = await super._prepareContext()
|
||||
ctx.isBrut = this.document.getUtilisationCuisine() === 'brut'
|
||||
return ctx
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options)
|
||||
|
||||
this.element.querySelector('.preparer-nourriture')?.addEventListener('click', async () => {
|
||||
const actor = this.document.parent
|
||||
if (actor) await actor.preparerNourriture(this.document)
|
||||
})
|
||||
|
||||
this.element.querySelector('.manger-nourriture')?.addEventListener('click', async () => {
|
||||
const actor = this.document.parent
|
||||
if (actor) await actor.mangerNourriture(this.document)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
import { CommonAppreciable } from "../../common/appreciable.mjs";
|
||||
|
||||
const TEMPLATE_APPRECIABLE = new CommonAppreciable();
|
||||
|
||||
export default class RdDJeuSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.jeu; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_APPRECIABLE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "jeu"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["jeu-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/jeu.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
async _prepareContext() {
|
||||
return Object.assign(
|
||||
await super._prepareContext(),
|
||||
{
|
||||
typeChoices: { adressehasard: "Adresse/Hasard", de: "Dés", carte: "Cartes", reflexion: "Réflexion" },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDLivreSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.livre; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION, TEMPLATE_INVENTAIRE]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "livre"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["livre-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/livre.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDMaladieSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.maladie; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{ classes: ["fvtt-rdd", "item", "maladie"], position: { width: 448 }, window: { contentClasses: ["maladie-content"] } });
|
||||
|
||||
static PARTS = {
|
||||
main: { template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/maladie.hbs" },
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { TEMPLATE_DESCRIPTION } from "../../common/_module.mjs";
|
||||
import { ITEM_TYPES } from "../../constants.js";
|
||||
import RdDItemBaseSheet from "./common-item-sheet.mjs";
|
||||
|
||||
export default class RdDMeditationSheet extends RdDItemBaseSheet {
|
||||
static get ITEM_TYPE() { return ITEM_TYPES.meditation; }
|
||||
static get TEMPLATES() { return [TEMPLATE_DESCRIPTION]; }
|
||||
|
||||
static DEFAULT_OPTIONS = Object.assign({},
|
||||
RdDItemBaseSheet.DEFAULT_OPTIONS,
|
||||
{
|
||||
classes: ["fvtt-rdd", "item", "meditation"],
|
||||
position: { width: 448 },
|
||||
window: { contentClasses: ["meditation-content"] },
|
||||
});
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/foundryvtt-reve-de-dragon/templates/sheets/item/meditation.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
@@ -24,12 +24,4 @@ export default class RdDMonnaieSheet extends RdDItemBaseSheet {
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
return Object.assign(
|
||||
await super._prepareContext(),
|
||||
await TEMPLATE_DESCRIPTION.prepareContext(this.document),
|
||||
await TEMPLATE_INVENTAIRE.prepareContext(this.document)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,4 @@ export default class RdDMunitionSheet extends RdDItemBaseSheet {
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
return Object.assign(
|
||||
await super._prepareContext(),
|
||||
await TEMPLATE_DESCRIPTION.prepareContext(this.document),
|
||||
await TEMPLATE_INVENTAIRE.prepareContext(this.document)
|
||||
)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user