Compare commits

..
7 Commits
Author SHA1 Message Date
VincentVk 37a33691ff 14.0.1 - Les imprécisions de Tétragor
Release Creation / build (release) Failing after 14s
2026-07-21 22:45:47 +02:00
VincentVk ce7271b569 Fix: ouverture des items
Fix appel methode sur objet SANS_COMPETENCE, qui n'etait pas
un RdDItem / RdDItemCompetence
2026-07-21 22:28:06 +02:00
VincentVk 5dbd7c3784 Fix Description aléatoire 2026-07-17 03:07:30 +02:00
fabres a55904742b fix(encombrement): exclure le poids propre des conteneurs du total
- Les conteneurs n'ajoutent plus leur propre encombrement au total
  (seuls les objets qu'ils contiennent sont comptés)
- Ajout indicateur visuel quand une compétence atteint son niveau d'archétype
  (bordure dorée + icône ✓)
2026-07-14 22:08:32 +02:00
fabres ded584ccc8 feat(inventaire): tri par quantité, dialogues de gestion, duplication cachée des livres lus
Release Creation / build (release) Successful in 1m45s
- Remplace le tri 'Manuel' par un tri 'Quantité' dans l'inventaire
- Supprime _onDrop / _onSortItem (obsolètes sans tri manuel)
- Nouveau dialogue 'Gérer ses deniers' (ApplicationV2)
- Nouveau dialogue 'Gérer ses potions' (ApplicationV2)
- Boutons herbes/ingrédients/potions/deniers déplacés dans l'onglet Équipement
- Dialogues herbes/ingrédients harmonisés (alterne-list, contrôles +/-)
- Ajout titre 'Livres lus' et en-têtes alignés
- Ajout estCopieLue au schéma livre (template.json)
- creerCopieLue() extrait dans base-actor.js
- Filtre estCopieLue dans prepareInventaire/buildContenuConteneur
2026-07-14 18:56:38 +02:00
fabres ac10de85cf Gestion des livres lus et des herbes 2026-07-14 08:28:44 +02:00
VincentVk b9fec47311 Correction du but des recettes alchimiques 2026-07-14 01:21:52 +02:00
51 changed files with 1162 additions and 108 deletions
+18
View File
@@ -1,5 +1,23 @@
# 13.0
## 14.0.1 - Les imprécisions de Tétragor
- correction de l'indicateur de remplissage des conteneurs
- correction de l'ouverture des objets utilisant la liste des compétences
- correction de la description aléatoires
## 14.0.0 - Les rangements de Tétragor
- Amélioration de la gestion de l'équipement
- gestion des deniers
- gestion de herbes & plantes
- gestion des ingrédients
- gestion des potions
- indicateur de remplissage des conteneurs
- tri par nom / type / encombrement / quantité
- Le but des recettes alchimiques est affiché et éditable
## 13.0.48 - Les vagabondages d'Illisys
- Gestion des tâches à plusieurs
+127
View File
@@ -1153,6 +1153,35 @@ body {
display: flex;
min-width: max-content;
}
.system-foundryvtt-reve-de-dragon .conteneur-sature {
color: #c00 !important;
font-weight: bold;
}
.system-foundryvtt-reve-de-dragon .conteneur-capacity-bar {
width: 5rem;
height: 0.4rem;
background: rgba(0, 0, 0, 0.1);
border-radius: 0.2rem;
overflow: hidden;
margin: 0 0.3rem;
}
.system-foundryvtt-reve-de-dragon .conteneur-capacity-fill {
height: 100%;
background: #4a90d9;
border-radius: 0.2rem;
transition: width 0.3s;
}
.system-foundryvtt-reve-de-dragon .conteneur-parent-badge {
font-size: 0.7rem;
color: #666;
margin-left: 0.3rem;
}
.system-foundryvtt-reve-de-dragon .conteneur-parent-badge::before {
content: "(";
}
.system-foundryvtt-reve-de-dragon .conteneur-parent-badge::after {
content: ")";
}
.system-foundryvtt-reve-de-dragon .blessure-control {
flex-grow: 1;
flex-direction: row;
@@ -1218,6 +1247,96 @@ body {
vertical-align: baseline;
padding: 0.1rem;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table {
width: 100%;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th,
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table td {
padding: 0.2rem 0.3rem !important;
vertical-align: middle !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th {
color: var(--color-text-light-heading, #f0f0f0) !important;
font-weight: bold !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th:first-child,
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table td:first-child {
width: 36px !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th:nth-child(2),
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table td:nth-child(2) {
width: auto !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th:nth-child(3),
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table td:nth-child(3) {
width: 3rem !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th:nth-child(4),
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table td:nth-child(4) {
width: 6rem !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th:nth-child(5),
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table td:nth-child(5) {
width: 5rem !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table th:nth-child(7),
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table td:nth-child(7) {
width: 2.5rem !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table select {
max-width: 12rem !important;
min-width: 8rem !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-herbes table img {
width: 30px !important;
height: 30px !important;
object-fit: cover !important;
border: none !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table {
width: 100%;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table td {
padding: 0.2rem 0.3rem !important;
vertical-align: middle !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table td:first-child {
width: 36px !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table td:nth-child(2) {
width: auto !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table td:nth-child(3) {
width: 3rem !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table td:nth-child(4) {
width: 6rem !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table td:nth-child(5) {
width: auto !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table td:nth-child(6) {
width: 2.5rem !important;
text-align: center !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table select {
max-width: 12rem !important;
min-width: 8rem !important;
}
.system-foundryvtt-reve-de-dragon .dialog-gerer-ingredients table img {
width: 30px !important;
height: 30px !important;
object-fit: cover !important;
border: none !important;
}
.system-foundryvtt-reve-de-dragon .dice-img {
border-width: 0;
max-width: 1.5rem;
@@ -1953,6 +2072,14 @@ body {
flex-basis: 2rem;
text-align: center;
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .at-archetype {
border-left: 3px solid var(--color-archetype-atteint, #b8860b);
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .archetype-check {
color: var(--color-archetype-atteint, #b8860b);
font-size: 0.75rem;
margin-left: 0.15rem;
}
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .carac-value,
.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .competence-xp-sort {
flex-grow: 0;
+129
View File
@@ -415,6 +415,35 @@
display: flex;
min-width: max-content;
}
.conteneur-sature {
color: #c00 !important;
font-weight: bold;
}
.conteneur-capacity-bar {
width: 5rem;
height: 0.4rem;
background: rgba(0, 0, 0, 0.1);
border-radius: 0.2rem;
overflow: hidden;
margin: 0 0.3rem;
}
.conteneur-capacity-fill {
height: 100%;
background: #4a90d9;
border-radius: 0.2rem;
transition: width 0.3s;
}
.conteneur-parent-badge {
font-size: 0.7rem;
color: #666;
margin-left: 0.3rem;
&::before {
content: "(";
}
&::after {
content: ")";
}
}
.blessure-control {
flex-grow: 1;
@@ -483,6 +512,98 @@
padding: 0.1rem;
}
.dialog-gerer-herbes table {
width: 100%;
}
.dialog-gerer-herbes table th,
.dialog-gerer-herbes table td {
padding: 0.2rem 0.3rem !important;
vertical-align: middle !important;
}
.dialog-gerer-herbes table th {
color: var(--color-text-light-heading, #f0f0f0) !important;
font-weight: bold !important;
}
.dialog-gerer-herbes table th:first-child,
.dialog-gerer-herbes table td:first-child {
width: 36px !important;
text-align: center !important;
}
.dialog-gerer-herbes table th:nth-child(2),
.dialog-gerer-herbes table td:nth-child(2) {
width: auto !important;
}
.dialog-gerer-herbes table th:nth-child(3),
.dialog-gerer-herbes table td:nth-child(3) {
width: 3rem !important;
text-align: center !important;
}
.dialog-gerer-herbes table th:nth-child(4),
.dialog-gerer-herbes table td:nth-child(4) {
width: 6rem !important;
text-align: center !important;
}
.dialog-gerer-herbes table th:nth-child(5),
.dialog-gerer-herbes table td:nth-child(5) {
width: 5rem !important;
text-align: center !important;
}
.dialog-gerer-herbes table th:nth-child(7),
.dialog-gerer-herbes table td:nth-child(7) {
width: 2.5rem !important;
text-align: center !important;
}
.dialog-gerer-herbes table select {
max-width: 12rem !important;
min-width: 8rem !important;
}
.dialog-gerer-herbes table img {
width: 30px !important;
height: 30px !important;
object-fit: cover !important;
border: none !important;
}
.dialog-gerer-ingredients table {
width: 100%;
}
.dialog-gerer-ingredients table td {
padding: 0.2rem 0.3rem !important;
vertical-align: middle !important;
}
.dialog-gerer-ingredients table td:first-child {
width: 36px !important;
text-align: center !important;
}
.dialog-gerer-ingredients table td:nth-child(2) {
width: auto !important;
}
.dialog-gerer-ingredients table td:nth-child(3) {
width: 3rem !important;
text-align: center !important;
}
.dialog-gerer-ingredients table td:nth-child(4) {
width: 6rem !important;
text-align: center !important;
}
.dialog-gerer-ingredients table td:nth-child(5) {
width: auto !important;
}
.dialog-gerer-ingredients table td:nth-child(6) {
width: 2.5rem !important;
text-align: center !important;
}
.dialog-gerer-ingredients table select {
max-width: 12rem !important;
min-width: 8rem !important;
}
.dialog-gerer-ingredients table img {
width: 30px !important;
height: 30px !important;
object-fit: cover !important;
border: none !important;
}
.dice-img {
border-width: 0;
max-width: 1.5rem;
@@ -1287,6 +1408,14 @@
flex-basis: 2rem;
text-align: center;
}
.window-app.sheet .window-content .at-archetype {
border-left: 3px solid var(--color-archetype-atteint, #b8860b);
}
.window-app.sheet .window-content .archetype-check {
color: var(--color-archetype-atteint, #b8860b);
font-size: 0.75rem;
margin-left: 0.15rem;
}
.window-app.sheet .window-content .carac-value,
.window-app.sheet .window-content .competence-xp-sort {
flex-grow: 0;
+21 -1
View File
@@ -31,7 +31,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
static get defaultOptions() {
return foundry.utils.mergeObject(RdDBaseActorReveSheet.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.hbs",
width: 550,
width: 640,
showCompNiveauBase: false,
vueArchetype: false,
}, { inplace: false })
@@ -101,6 +101,9 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
cacheTMR: this.actor.isTMRCache()
}
formData.livresLus = formData.livres.filter(l => l.system.estCopieLue)
formData.livres = formData.livres.filter(l => !l.system.estCopieLue)
formData.race = actor.itemTypes[ITEM_TYPES.race].find(it => true)
formData.subacteurs = {
vehicules: this.actor.listeVehicules(),
@@ -212,6 +215,23 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
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('.gerer-herbes').click(async event => {
const { DialogGererHerbes } = await import("./dialog-gerer-herbes.js")
DialogGererHerbes.create(this.actor)
})
this.html.find('.gerer-ingredients').click(async event => {
const { DialogGererIngredients } = await import("./dialog-gerer-ingredients.js")
DialogGererIngredients.create(this.actor)
})
this.html.find('.gerer-deniers').click(async event => {
const { DialogGererDeniers } = await import("./dialog-gerer-deniers.js")
DialogGererDeniers.create(this.actor)
})
this.html.find('.gerer-potions').click(async event => {
const { DialogGererPotions } = await import("./dialog-gerer-potions.js")
DialogGererPotions.create(this.actor)
})
if (game.user.isGM) {
// experience log
this.html.find('.experiencelog-delete').click(async event => {
+1
View File
@@ -1264,6 +1264,7 @@ export class RdDActor extends RdDBaseActorSang {
if (tache) {
await this.rollTache(tache.id);
}
await this.creerCopieLue(item)
}
async fabriquerDecoctionHerbe(item) {
+1 -1
View File
@@ -14,7 +14,7 @@ export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(RdDBaseActorSheet.defaultOptions, {
width: 550
width: 640
});
}
+67 -2
View File
@@ -23,7 +23,9 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
classes: ["rdd", "sheet", "actor"],
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
vueDetaillee: false
scrollY: [".sheet-body"],
vueDetaillee: false,
triEquipement: 'name'
}, { inplace: false })
}
@@ -45,7 +47,7 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
effects: this.actor.effects
}
RdDUtility.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
RdDUtility.filterItemsPerTypeForSheet(formData, this.actor.itemTypes, this.options.triEquipement);
formData.calc = {
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
@@ -104,6 +106,16 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
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(true)
})
this.html.find('.tri-equipement').change(event => {
this.options.triEquipement = event.currentTarget.value;
this.render(true);
})
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
@@ -111,6 +123,46 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
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(true);
}
})
this.html.find('.item-quantite').on('contextmenu', async event => {
event.preventDefault()
const item = this.getItem(event)
if (!item?.isInventaire()) return
new Dialog({
title: `Quantité — ${item.name}`,
content: `<p>Réduire la quantité de :</p>
<select class="qty-amount" style="width:100%">
<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)) })
}
}
}
}
}).render(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())
@@ -180,15 +232,28 @@ export class RdDBaseActorSheet extends foundry.appv1.sheets.ActorSheet {
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
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, dragData, this.objetVersConteneur)
if (dropParams) {
const callSuper = await this.actor.processDropItem(dropParams)
if (callSuper) {
await super._onDropItem(event, dragData)
}
// For external drops onto a container row, place the new item inside
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 selectObjetTypeToCreate() {
let types = this.getTypesInventaire().sort(Misc.ascending(type => Misc.typeName('Item', type)));
+13
View File
@@ -652,6 +652,7 @@ export class RdDBaseActor extends Actor {
else {
const wasSurenc = this.isSurenc()
this.encTotal = this.items.filter(it => RdDItem.getItemTypesInventaire().includes(it.type))
.filter(it => it.type !== ITEM_TYPES.conteneur)
.map(it => it.getEncTotal()).reduce(Misc.sum(), 0)
const isSurenc = this.isSurenc()
if (isSurenc != wasSurenc) {
@@ -906,6 +907,18 @@ export class RdDBaseActor extends Actor {
async rollAppelChance() { this.actionImpossible("appel à la chance") }
async jetDeMoral() { this.actionImpossible("jet de moral") }
async creerCopieLue(item) {
if (item.type !== ITEM_TYPES.livre) return
const existante = this.items.find(i => i.type === 'livre' && i.system.estCopieLue && i.name === item.name)
if (existante) return
const copie = item.toObject()
copie.system.estCopieLue = true
copie.system.lu = true
copie.system.quantite = 0
delete copie._id
await this.createEmbeddedDocuments('Item', [copie], { renderSheet: false })
}
async resetItemUse() { }
async incDecItemUse(itemId, shouldIncrease = true) { }
getItemUse(itemId) { return 0; }
@@ -102,12 +102,8 @@ export class AppPersonnageAleatoire extends FormApplication {
constructor(actor) {
super({})
this.actor = actor
this.current = AppPersonnageAleatoire.getActorValues()
this.checked = Object.fromEntries(CONTROLS.map(it => [it.name, [0, '', undefined].includes(it.getter(this.current))]))
}
static getActorValues() {
return Object.fromEntries(CONTROLS.map(it => [it.name, it.getter(this.actor)]));
this.current = Object.fromEntries(CONTROLS.map(it => [it.name, it.getter(this.actor)]));
this.checked = Object.fromEntries(CONTROLS.map(it => [it.name, [0, '', undefined].includes(this.current[it.name])]))
}
async getData(options) {
@@ -141,7 +137,7 @@ export class AppPersonnageAleatoire extends FormApplication {
async onApply() {
const updates = Object.fromEntries(
CONTROLS.filter(control => game.user.isGM || control.name != 'name')
.map(control => [control.path, control.getter(this.current)])
.map(control => [control.path, this.current[control.name]])
)
await this.actor.update(updates)
await this.close()
+112
View File
@@ -0,0 +1,112 @@
import { ITEM_TYPES } from "./constants.js"
import { RdDSheetUtility } from "./rdd-sheet-utility.js"
import { Monnaie } from "./item-monnaie.js"
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
export class DialogGererDeniers extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
tag: "form",
classes: ["dialog-gerer-deniers"],
form: {
submitOnChange: false,
closeOnSubmit: false
},
position: {
width: 700,
height: "auto"
},
window: {
title: "Gérer ses deniers",
icon: "fa-solid fa-coins"
}
}
static PARTS = {
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-deniers.hbs" }
}
static async create(actor) {
const dialog = new DialogGererDeniers({ actor })
dialog.render(true)
}
constructor(options) {
super(options)
this.actor = options.actor
}
async _prepareContext() {
const monnaies = this.actor.itemTypes[ITEM_TYPES.monnaie] || []
const conteneurs = this.actor.itemTypes[ITEM_TYPES.conteneur] || []
const fortune = Monnaie.toSolsDeniers(this.actor.getFortune())
const monnaieData = monnaies.map(piece => ({
id: piece.id,
name: piece.name,
img: piece.img,
quantite: piece.system.quantite,
cout: piece.system.cout,
conteneurId: this.actor.findConteneur(piece)?.id || ""
}))
const conteneurOptions = [
{ id: "", name: "— Aucun —" },
...conteneurs.map(c => ({ id: c.id, name: c.name }))
]
return { monnaies: monnaieData, conteneurs: conteneurOptions, fortune }
}
async _onRender(context, options) {
this.element.querySelectorAll(".denier-moins").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const piece = this.actor.items.get(id)
if (!piece) return
const q = Number(piece.system.quantite)
if (q <= 0) return
await piece.update({ "system.quantite": Math.max(0, q - 1) })
this.render(true)
})
})
this.element.querySelectorAll(".denier-plus").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const piece = this.actor.items.get(id)
if (!piece) return
await piece.update({ "system.quantite": Number(piece.system.quantite) + 1 })
this.render(true)
})
})
this.element.querySelectorAll(".denier-edit").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const piece = this.actor.items.get(id)
if (piece) piece.sheet.render(true)
})
})
this.element.querySelectorAll(".denier-conteneur").forEach(el => {
el.addEventListener("change", async event => {
const id = RdDSheetUtility.getItemId(event)
const newConteneurId = String(event.currentTarget.value)
const piece = this.actor.items.get(id)
if (!piece) return
const currentConteneur = this.actor.findConteneur(piece)
const newConteneur = newConteneurId ? this.actor.items.get(newConteneurId) : null
if (currentConteneur) {
await this.actor.enleverDeConteneur(piece, currentConteneur, () => {})
}
if (newConteneur) {
await this.actor.ajouterDansConteneur(piece, newConteneur, () => {})
}
})
})
}
}
+122
View File
@@ -0,0 +1,122 @@
import { ITEM_TYPES } from "./constants.js"
import { RdDSheetUtility } from "./rdd-sheet-utility.js"
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
export class DialogGererHerbes extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
tag: "form",
classes: ["dialog-gerer-herbes"],
form: {
submitOnChange: false,
closeOnSubmit: false
},
position: {
width: 650,
height: "auto"
},
window: {
title: "Gérer ses herbes",
icon: "fa-solid fa-leaf"
}
}
static PARTS = {
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-herbes.hbs" }
}
static async create(actor) {
const dialog = new DialogGererHerbes({ actor })
dialog.render(true)
}
constructor(options) {
super(options)
this.actor = options.actor
}
async _prepareContext() {
const herbes = this.actor.itemTypes[ITEM_TYPES.herbe] || []
const conteneurs = this.actor.itemTypes[ITEM_TYPES.conteneur] || []
const herbeData = herbes.map(herbe => ({
id: herbe.id,
name: herbe.name,
img: herbe.img,
quantite: herbe.system.quantite,
categorie: herbe.system.categorie,
niveau: herbe.system.niveau,
isHerbeAPotion: herbe.isHerbeAPotion(),
conteneurId: this.actor.findConteneur(herbe)?.id || ""
}))
const conteneurOptions = [
{ id: "", name: "— Aucun —" },
...conteneurs.map(c => ({ id: c.id, name: c.name }))
]
return { herbes: herbeData, conteneurs: conteneurOptions }
}
async _onRender(context, options) {
this.element.querySelectorAll(".herbe-moins").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const herbe = this.actor.items.get(id)
if (!herbe) return
const q = Number(herbe.system.quantite)
if (q <= 0) return
await herbe.update({ "system.quantite": Math.max(0, q - 1) })
this.render(true)
})
})
this.element.querySelectorAll(".herbe-plus").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const herbe = this.actor.items.get(id)
if (!herbe) return
await herbe.update({ "system.quantite": Number(herbe.system.quantite) + 1 })
this.render(true)
})
})
this.element.querySelectorAll(".herbe-edit").forEach(el => {
el.addEventListener("click", async event => {
const herbeId = RdDSheetUtility.getItemId(event)
const herbe = this.actor.items.get(herbeId)
if (herbe) herbe.sheet.render(true)
})
})
this.element.querySelectorAll(".herbe-decoction").forEach(el => {
el.addEventListener("click", async event => {
const herbeId = RdDSheetUtility.getItemId(event)
const herbe = this.actor.items.get(herbeId)
if (herbe) this.actor.fabriquerDecoctionHerbe(herbe)
})
})
this.element.querySelectorAll(".herbe-conteneur").forEach(el => {
el.addEventListener("change", async event => {
const herbeId = RdDSheetUtility.getItemId(event)
const newConteneurId = String(event.currentTarget.value)
const herbe = this.actor.items.get(herbeId)
if (!herbe) return
const currentConteneur = this.actor.findConteneur(herbe)
const newConteneur = newConteneurId ? this.actor.items.get(newConteneurId) : null
if (currentConteneur) {
await this.actor.enleverDeConteneur(herbe, currentConteneur, () => {})
}
if (newConteneur) {
await this.actor.ajouterDansConteneur(herbe, newConteneur, () => {})
}
})
})
}
}
+110
View File
@@ -0,0 +1,110 @@
import { ITEM_TYPES } from "./constants.js"
import { RdDSheetUtility } from "./rdd-sheet-utility.js"
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
export class DialogGererIngredients extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
tag: "form",
classes: ["dialog-gerer-ingredients"],
form: {
submitOnChange: false,
closeOnSubmit: false
},
position: {
width: 650,
height: "auto"
},
window: {
title: "Gérer ses ingrédients",
icon: "fa-solid fa-flask"
}
}
static PARTS = {
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-ingredients.hbs" }
}
static async create(actor) {
const dialog = new DialogGererIngredients({ actor })
dialog.render(true)
}
constructor(options) {
super(options)
this.actor = options.actor
}
async _prepareContext() {
const ingredients = this.actor.itemTypes[ITEM_TYPES.ingredient] || []
const conteneurs = this.actor.itemTypes[ITEM_TYPES.conteneur] || []
const ingredientData = ingredients.map(ing => ({
id: ing.id,
name: ing.name,
img: ing.img,
quantite: ing.system.quantite,
categorie: ing.system.categorie,
conteneurId: this.actor.findConteneur(ing)?.id || ""
}))
const conteneurOptions = [
{ id: "", name: "— Aucun —" },
...conteneurs.map(c => ({ id: c.id, name: c.name }))
]
return { ingredients: ingredientData, conteneurs: conteneurOptions }
}
async _onRender(context, options) {
this.element.querySelectorAll(".ingredient-moins").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const ing = this.actor.items.get(id)
if (!ing) return
const q = Number(ing.system.quantite)
if (q <= 0) return
await ing.update({ "system.quantite": Math.max(0, q - 1) })
this.render(true)
})
})
this.element.querySelectorAll(".ingredient-plus").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const ing = this.actor.items.get(id)
if (!ing) return
await ing.update({ "system.quantite": Number(ing.system.quantite) + 1 })
this.render(true)
})
})
this.element.querySelectorAll(".ingredient-edit").forEach(el => {
el.addEventListener("click", async event => {
const ingId = RdDSheetUtility.getItemId(event)
const ing = this.actor.items.get(ingId)
if (ing) ing.sheet.render(true)
})
})
this.element.querySelectorAll(".ingredient-conteneur").forEach(el => {
el.addEventListener("change", async event => {
const ingId = RdDSheetUtility.getItemId(event)
const newConteneurId = String(event.currentTarget.value)
const ing = this.actor.items.get(ingId)
if (!ing) return
const currentConteneur = this.actor.findConteneur(ing)
const newConteneur = newConteneurId ? this.actor.items.get(newConteneurId) : null
if (currentConteneur) {
await this.actor.enleverDeConteneur(ing, currentConteneur, () => {})
}
if (newConteneur) {
await this.actor.ajouterDansConteneur(ing, newConteneur, () => {})
}
})
})
}
}
+121
View File
@@ -0,0 +1,121 @@
import { ITEM_TYPES } from "./constants.js"
import { RdDSheetUtility } from "./rdd-sheet-utility.js"
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
export class DialogGererPotions extends HandlebarsApplicationMixin(ApplicationV2) {
static DEFAULT_OPTIONS = {
tag: "form",
classes: ["dialog-gerer-potions"],
form: {
submitOnChange: false,
closeOnSubmit: false
},
position: {
width: 700,
height: "auto"
},
window: {
title: "Gérer ses potions",
icon: "fa-solid fa-flask"
}
}
static PARTS = {
form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-potions.hbs" }
}
static async create(actor) {
const dialog = new DialogGererPotions({ actor })
dialog.render(true)
}
constructor(options) {
super(options)
this.actor = options.actor
}
async _prepareContext() {
const potions = this.actor.itemTypes[ITEM_TYPES.potion] || []
const conteneurs = this.actor.itemTypes[ITEM_TYPES.conteneur] || []
const potionData = potions.map(p => ({
id: p.id,
name: p.name,
img: p.img,
quantite: p.system.quantite,
categorie: p.system.categorie,
conteneurId: this.actor.findConteneur(p)?.id || ""
}))
const conteneurOptions = [
{ id: "", name: "— Aucun —" },
...conteneurs.map(c => ({ id: c.id, name: c.name }))
]
return { potions: potionData, conteneurs: conteneurOptions }
}
async _onRender(context, options) {
this.element.querySelectorAll(".potion-moins").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const potion = this.actor.items.get(id)
if (!potion) return
const q = Number(potion.system.quantite)
if (q <= 0) return
await potion.update({ "system.quantite": Math.max(0, q - 1) })
this.render(true)
})
})
this.element.querySelectorAll(".potion-plus").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const potion = this.actor.items.get(id)
if (!potion) return
await potion.update({ "system.quantite": Number(potion.system.quantite) + 1 })
this.render(true)
})
})
this.element.querySelectorAll(".potion-consommer").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const potion = this.actor.items.get(id)
if (potion) {
await this.actor.consommerPotion(potion)
this.render(true)
}
})
})
this.element.querySelectorAll(".potion-edit").forEach(el => {
el.addEventListener("click", async event => {
const id = RdDSheetUtility.getItemId(event)
const potion = this.actor.items.get(id)
if (potion) potion.sheet.render(true)
})
})
this.element.querySelectorAll(".potion-conteneur").forEach(el => {
el.addEventListener("change", async event => {
const id = RdDSheetUtility.getItemId(event)
const newConteneurId = String(event.currentTarget.value)
const potion = this.actor.items.get(id)
if (!potion) return
const currentConteneur = this.actor.findConteneur(potion)
const newConteneur = newConteneurId ? this.actor.items.get(newConteneurId) : null
if (currentConteneur) {
await this.actor.enleverDeConteneur(potion, currentConteneur, () => {})
}
if (newConteneur) {
await this.actor.ajouterDansConteneur(potion, newConteneur, () => {})
}
})
})
}
}
+7 -15
View File
@@ -2,7 +2,7 @@ import { ChatUtility } from "./chat-utility.js";
import { Grammar } from "./grammar.js";
import { RdDInitiative } from "./initiative.mjs";
import { RdDItem } from "./item.js";
import { CATEGORIES_COMPETENCES, SANS_COMPETENCE } from "./item/base-items.js";
import { BASE_SANS_COMPETENCE, CATEGORIES_COMPETENCES } from "./item/base-items.js";
import { Misc } from "./misc.js";
const competenceTroncs = [["Esquive", "Dague", "Corps à corps"],
@@ -47,6 +47,10 @@ export class RdDItemCompetence extends RdDItem {
static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/competence_defaut.webp" }
static async onReady(){
RdDItemCompetence.SANS_COMPETENCE = await Item.create(BASE_SANS_COMPETENCE, { temporary: true })
}
isNiveauBase() {
return this.system.niveau == this.system.base && this.system.xp == 0
}
@@ -128,18 +132,6 @@ export class RdDItemCompetence extends RdDItem {
});
}
/* -------------------------------------------- */
static isCompetenceArme(competence) {
if (competence.isCompetence() && !competence.isCorpsACorps() && !competence.isEsquive()) {
switch (competence.system.categorie) {
case 'melee':
case 'tir':
case 'lancer':
return true;
}
}
return false;
}
/* -------------------------------------------- */
static isArmeUneMain(competence) {
@@ -238,6 +230,7 @@ export class RdDItemCompetence extends RdDItem {
if (xpManquant > 0 && stressTransforme > 0 && item.system.niveau < item.system.niveau_archetype) {
item.system.stressXpMax = Math.min(xpManquant, stressTransforme);
}
item.system.isArchetypeAtteint = item.system.niveau >= item.system.niveau_archetype;
}
/* -------------------------------------------- */
@@ -248,7 +241,7 @@ export class RdDItemCompetence extends RdDItem {
/* -------------------------------------------- */
static findCompetence(list, idOrName, options = {}) {
if (idOrName == undefined || idOrName == "") {
return RdDItemCompetence.sansCompetence();
return RdDItemCompetence.SANS_COMPETENCE
}
options = foundry.utils.mergeObject(options, { preFilter: it => it.isCompetence(), description: 'compétence' }, { overwrite: false, inplace: false });
return RdDItemCompetence.findFirstItem(list, idOrName, options);
@@ -260,7 +253,6 @@ export class RdDItemCompetence extends RdDItem {
return Misc.findAllLike(name, list, options);
}
static sansCompetence() { return SANS_COMPETENCE }
static findFirstItem(list, idOrName, options) {
return list.find(it => it.id == idOrName && options.preFilter(it))
+1 -2
View File
@@ -14,7 +14,6 @@ import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js";
import { RdDTextEditor } from "./apps/rdd-text-roll-editor.js";
import { ItemAction } from "./item/item-actions.js";
import { SANS_COMPETENCE } from "./item/base-items.js";
import { Apprecier } from "./moral/apprecier.mjs";
import { CARACS } from "./rdd-carac.js";
@@ -105,7 +104,7 @@ export class RdDItemSheetV1 extends foundry.appv1.sheets.ItemSheet {
descriptionmj: await RdDTextEditor.enrichHTML(this.item.system.descriptionmj, this.item),
isComestible: this.item.getUtilisationCuisine(),
options: RdDSheetUtility.mergeDocumentRights({}, this.item, this.isEditable),
competences: [SANS_COMPETENCE, ...competences],
competences: [await RdDItemCompetence.SANS_COMPETENCE, ...competences],
categories: RdDItem.getCategories(this.item.type),
isAppreciable: Apprecier.isAppreciable(this.item)
}
+3 -1
View File
@@ -240,7 +240,9 @@ export class RdDItem extends Item {
}
isCompetenceArme() {
return this.isCompetence() && ['melee', 'tir', 'lancer'].includes(this.system.categorie)
return this.isCompetence()
&& ['melee', 'tir', 'lancer'].includes(this.system.categorie)
&& !this.isEsquive()
}
isCompetencePossession() { return ITEM_TYPES.competencecreature == this.type && this.system.categorie == CATEGORIES_COMPETENCES_CREATURES.possession.key }
+1 -1
View File
@@ -34,7 +34,7 @@ export const PAS_DE_DRACONIC = { name: 'Pas de draconic', type: 'competence', sy
export const BASE_CORPS_A_CORPS = { name: 'Corps à Corps', type: 'competence', system: { niveau: -6, defaut_carac: "melee" }, img: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp' }
export const BASE_ESQUIVE = { name: 'Esquive', type: 'competence', system: { niveau: -6, defaut_carac: "derobee" }, img: 'systems/foundryvtt-reve-de-dragon/icons/competence_esquive.webp' }
export const SANS_COMPETENCE = {
export const BASE_SANS_COMPETENCE = {
name: "Sans compétence",
type: ITEM_TYPES.competence,
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp",
+15 -2
View File
@@ -92,6 +92,12 @@ const _LIRE = {
actorFilter: actor => actor.isPersonnage(),
action: (item, actor) => actor.actionLire(item)
}
const _MARQUER_LU = {
code: 'item-marquer-lu', label: 'Marquer comme lu',
icon: it => it.system.lu ? 'fa-solid fa-book-open' : 'fa-regular fa-book',
actorFilter: actor => actor.isPersonnage(),
action: async (item, actor) => { await actor.creerCopieLue(item) }
}
const _REFOULER = {
code: 'item-refouler', label: 'Refouler', icon: 'fa-solid fa-burst',
@@ -107,15 +113,22 @@ const _SORT_RESERVE = {
action: (item, actor) => actor.addSortReserve(item)
}
const _VIDER = {
code: 'item-vider', label: 'Vider le conteneur', icon: 'fa-solid fa-box-open',
filter: it => it.isConteneur() && (it.system.contenu?.length > 0),
actorFilter: actor => actor.isOwner,
action: (item, actor) => item.update({ 'system.contenu': [] })
}
export const COMMON_ACTIONS = [_EQUIPER]
export const DEFAULT_ACTIONS = [_ACHETER, _SPLIT, _SPACEHOLDER, _VENDRE, _MONTRER, _EDIT, _DELETE]
export const ITEM_ACTIONS = {
faune: [_CUISINER, _MANGER_CRU],
ingredient: [_CUISINER, _MANGER_CRU],
conteneur: [_OUVRIR],
conteneur: [_VIDER, _OUVRIR],
herbe: [_DECOCTION, _CUISINER, _MANGER_CRU],
livre: [_LIRE],
livre: [_LIRE, _MARQUER_LU],
nourritureboisson: [_MANGER, _BOIRE],
ombre: [_REFOULER],
plante: [_CUISINER, _MANGER_CRU],
+6 -4
View File
@@ -3,6 +3,8 @@ import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { LIST_CARAC_PERSONNAGE, RdDCarac } from "../rdd-carac.js";
const _warn = msg => ui.notifications?.warn(msg)
export class RdDItemRace extends RdDItem {
static get ITEM_TYPE() { return ITEM_TYPES.race }
@@ -15,14 +17,14 @@ export class RdDItemRace extends RdDItem {
const race = RdDItemRace.getRace(actor)
if (code == LIST_CARAC_PERSONNAGE.force.code) {
if (!race.isForceValid(actor, value)) {
ui.notifications.warn(race.system.carac.force.limitmessage)
_warn(race.system.carac.force.limitmessage)
return false
}
}
if (code == LIST_CARAC_PERSONNAGE.taille.code) {
const carac = RdDCarac.carac(code)
if (race.isMax(actor, code, value - 1)) {
ui.notifications.warn(`${value} est supérieure au maximum de ${carac.label}`)
_warn(`${value} est supérieure au maximum de ${carac.label}`)
return false
}
}
@@ -73,13 +75,13 @@ export class RdDItemRace extends RdDItem {
getValidTaille(taille) {
const min = Math.max(this.system.carac.taille.min, 0)
if (min > taille) {
ui.notifications.warn("La Taille est inférieur au minimum racial")
_warn("La Taille est inférieur au minimum racial")
return min
}
const raceMax = this.system.carac.taille.max == undefined ? 15 + this.system.carac.taille.value : this.system.carac.taille.max
const max = raceMax < 0 ? taille + 1 : raceMax
if (max < taille) {
ui.notifications.warn("La Taille est supérieure au maximum racial")
_warn("La Taille est supérieure au maximum racial")
return max
}
return taille
+2 -2
View File
@@ -1,6 +1,6 @@
import { ChatUtility } from "../chat-utility.js"
import { ITEM_TYPES } from "../constants.js"
import { SANS_COMPETENCE } from "../item/base-items.js"
import { RdDItemCompetence } from "../item-competence.js"
import { Misc } from "../misc.js"
import { CARACS } from "../rdd-carac.js"
import { RdDUtility } from "../rdd-utility.js"
@@ -126,7 +126,7 @@ export class Apprecier {
}
getCompetenceAppreciation() {
return [SANS_COMPETENCE.name, ""].includes(this.appreciation.competence) ? SANS_COMPETENCE : this.actor.getCompetence(this.appreciation.competence)
return [RdDItemCompetence.SANS_COMPETENCE.name, ""].includes(this.appreciation.competence) ? RdDItemCompetence.SANS_COMPETENCE : this.actor.getCompetence(this.appreciation.competence)
}
rollAppreciation(rollCallbacks = []) {
+1
View File
@@ -405,6 +405,7 @@ export class SystemReveDeDragon {
RdDDice.onReady()
RollDialog.onReady()
ChatRollResult.onReady()
RdDItemCompetence.onReady()
/* -------------------------------------------- */
/* Affiche/Init le calendrier */
+46 -8
View File
@@ -145,6 +145,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/possessions.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/resonances.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/taches.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/livres.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/jeux.hbs',
@@ -174,6 +175,10 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire-item.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/tooltip-bonmoments.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-herbes.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-ingredients.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-deniers.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/dialog-gerer-potions.hbs',
//Items
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs',
'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete.hbs',
@@ -414,6 +419,7 @@ export class RdDUtility {
let objet = inventaires.find(objet => (id == objet._id));
if (objet) {
objet.estContenu = true;
objet.parentConteneurName = conteneur.name;
objetVersConteneur[id] = conteneur._id;
conteneur.subItems.push(objet);
}
@@ -474,6 +480,8 @@ export class RdDUtility {
item: item,
vide: isConteneur && item.system.contenu.length == 0,
ouvert: isConteneur && RdDUtility.getAfficheContenu(item._id),
sature: isConteneur && item.system.encTotal > item.system.capacite,
fillPercent: isConteneur ? Math.min(100, (item.system.encTotal / (item.system.capacite || 1)) * 100) : 0,
options: options
};
optionsArbre.ouvert = inventaire.ouvert
@@ -484,7 +492,7 @@ export class RdDUtility {
return ligneObjet;
}
static filterItemsPerTypeForSheet(formData, itemTypes) {
static filterItemsPerTypeForSheet(formData, itemTypes, triEquipement = 'name') {
Object.values(ITEM_TYPES).forEach(t => {
formData[RdDItem.itemTypePluriel(t)] = Misc.arrayOrEmpty(itemTypes[t])
itemTypes[t].forEach(item => item.actions = item.itemActions())
@@ -494,7 +502,7 @@ export class RdDUtility {
formData.competences = formData.competences.concat(formData.competencecreatures)
formData.monnaies = formData.monnaies.sort(Monnaie.triValeurEntiere())
formData.inventaires = RdDUtility.prepareInventaire(itemTypes)
formData.inventaires = RdDUtility.prepareInventaire(itemTypes, triEquipement)
}
static buildInventaireConteneur(actorId, itemId, options) {
@@ -502,7 +510,7 @@ export class RdDUtility {
const item = actor?.items.get(itemId)
if (item?.type == ITEM_TYPES.conteneur) {
const formData = {}
RdDUtility.filterItemsPerTypeForSheet(formData, actor.itemTypes);
RdDUtility.filterItemsPerTypeForSheet(formData, actor.itemTypes, options?.triEquipement);
RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.inventaires);
item.subItems = formData.conteneurs.find(it => it._id == itemId)?.subItems;
@@ -511,11 +519,25 @@ export class RdDUtility {
return ''
}
static prepareInventaire(itemTypes) {
return RdDItem.getItemTypesInventaire('all')
static prepareInventaire(itemTypes, triEquipement = 'name') {
let items = RdDItem.getItemTypesInventaire('all')
.map(t => Misc.arrayOrEmpty(itemTypes[t]))
.reduce((a, b) => a.concat(b), [])
.sort(Misc.ascending(it => it.name))
.reduce((a, b) => a.concat(b), []);
items = items.filter(it => !(it.type === ITEM_TYPES.livre && it.system.estCopieLue));
switch (triEquipement) {
case 'type':
items.sort(Misc.ascending(it => Misc.typeName('Item', it.type)));
break;
case 'enc':
items.sort(Misc.ascending(it => it.system.encombrement ?? 0));
break;
case 'quantite':
items.sort(Misc.descending(it => it.system.quantite ?? 0));
break;
default:
items.sort(Misc.ascending(it => it.name));
}
return items;
}
/* -------------------------------------------- */
@@ -525,7 +547,23 @@ export class RdDUtility {
const profondeur = optionsArbre.profondeur;
optionsArbre.profondeur++;
const lignesContenu = conteneur.subItems.sort(Misc.ascending(it => it.name))
const sortBy = options.triEquipement || 'name';
let sorted = [...conteneur.subItems];
sorted = sorted.filter(it => !(it.type === ITEM_TYPES.livre && it.system.estCopieLue));
switch (sortBy) {
case 'type':
sorted.sort(Misc.ascending(it => Misc.typeName('Item', it.type)));
break;
case 'enc':
sorted.sort(Misc.ascending(it => it.system.encombrement ?? 0));
break;
case 'quantite':
sorted.sort(Misc.descending(it => it.system.quantite ?? 0));
break;
default:
sorted.sort(Misc.ascending(it => it.name));
}
const lignesContenu = sorted
.map(contenu => this.buildLigneInventaire(contenu, options, optionsArbre));
return `<ul class='item-list alterne-list ${display} list-item-margin${Math.min(profondeur, 6)}'>`
+3 -3
View File
@@ -7,7 +7,7 @@ import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
export const PART_COMP = "comp"
const SANS_COMPETENCE = { key: '', label: "Sans compétence", value: 0 }
const SELECT_SANS_COMPETENCE = { key: '', label: "Sans compétence", value: 0 }
export class RollPartComp extends RollPartSelect {
@@ -34,7 +34,7 @@ export class RollPartComp extends RollPartSelect {
refs.all = all.filter(comp => Grammar.includesLowerCaseNoAccent(comp.label, selectedComp))
}
else {
refs.all = all.filter(comp => comp == SANS_COMPETENCE)
refs.all = all.filter(comp => comp == SELECT_SANS_COMPETENCE)
}
}
}
@@ -63,7 +63,7 @@ export class RollPartComp extends RollPartSelect {
.sort(Misc.ascending(it => Grammar.toLowerCaseNoAccentNoSpace(it.label)))
/* TODO: filter competences */
const listCompetences = [
SANS_COMPETENCE,
SELECT_SANS_COMPETENCE,
...competences
]
return listCompetences
@@ -17,7 +17,7 @@ system:
tout personnage ayant au moins z&eacute;ro en Botanique.</p>
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir un prisme de cristal alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -13,7 +13,7 @@ system:
tonifiante.</p>
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -11,7 +11,7 @@ system:
<p>Une livre de coquilles donne 4 &eacute;cailles de poudre de nacre.</p>
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -7,7 +7,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: null
descriptionmj: ''
but: Amplifier le pouvoir de guérison des herbes de soin.
utilisation: null
utilisation: ''
enchantement: >-
<p>Une surpotion de soin peut &ecirc;tre enchant&eacute;e comme une simple
potion, les points de r&ecirc;ve conf&eacute;r&eacute;s &eacute;tant
@@ -11,7 +11,7 @@ system:
(INTELLECT/Botanique &agrave; -2).</p>
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -8,7 +8,7 @@ system:
description: ''
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
@@ -13,7 +13,7 @@ system:
succ&eacute;dan&eacute;.</p>
descriptionmj: ''
but: Obtenir trois doses du remède alchimique.
utilisation: null
utilisation: ''
enchantement: ''
sureffet: ''
manipulation: >-
+2
View File
@@ -804,6 +804,8 @@
},
"livre": {
"templates": ["description", "inventaire"],
"lu": false,
"estCopieLue": false,
"competence": "",
"auteur": "",
"difficulte": 0,
+4
View File
@@ -123,7 +123,11 @@
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/chirurgie.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvres.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/jeux.hbs"}}
<div class="flexrow" style="gap:0.5rem;flex-wrap:wrap">
<a class="chat-card-button creer-une-oeuvre">Créer une oeuvre</a>
</div>
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/alchimie.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/livres.hbs"}}
</div>
{{/if}}
{{#if options.isObserver}}{{!-- hautreve Tab --}}
+2 -1
View File
@@ -1,5 +1,5 @@
{{#unless system.isHidden}}
<li class="item flexrow list-item {{#if system.isLevelUp}}xp-level-up{{/if}}" data-item-id="{{_id}}">
<li class="item flexrow list-item {{#if system.isLevelUp}}xp-level-up{{/if}} {{#if system.isArchetypeAtteint}}at-archetype{{/if}}" data-item-id="{{_id}}">
<a class="list-item-label roll-competence flexrow" name="{{name}}" data-tooltip="Niveau {{plusMoins system.niveau}} en {{name}}">
<img class="sheet-competence-img" src="{{img}}"/>
<span>{{name}}</span>
@@ -44,6 +44,7 @@
compname="{{name}}" value="{{plusMoins system.niveau_archetype}}"
data-tooltip="Niveau d'archétype {{plusMoins system.niveau_archetype}}"
{{#if (not @root.options.vueArchetype)}}disabled{{/if}} />
{{#if system.isArchetypeAtteint}}<i class="fa-solid fa-check archetype-check"></i>{{/if}}
</div>
{{>'systems/foundryvtt-reve-de-dragon/templates/actor/item-action-controls.hbs' item=this options=@root.options}}
{{/if}}
+38
View File
@@ -0,0 +1,38 @@
<div>
<span class="item-name"><h4>Fortune totale: {{fortune.sols}} sols {{fortune.deniers}} deniers</h4></span>
<ul class="item-list alterne-list">
<li class="competence-header flexrow" style="padding:0 3px">
<span class="equipement-nom" style="padding-left:calc(24px + 0.2rem)">Nom</span>
<span class="equipement-detail">Valeur</span>
<span class="equipement-detail">Quantité</span>
<span class="equipement-detail" style="flex:2">Conteneur</span>
<span class="item-actions-controls item-controls">Actions</span>
</li>
{{#each monnaies as |piece|}}
<li class="item flexrow list-item" data-item-id="{{piece.id}}">
<span class="equipement-nom">
<img class="sheet-competence-img" src="{{piece.img}}" data-tooltip="{{piece.name}}"/>
<span>{{piece.name}}</span>
</span>
<span class="equipement-detail">{{piece.cout}} Sols</span>
<span class="equipement-detail" style="gap:0.125rem">
<a class="denier-moins"><i class="fa-solid fa-square-minus"></i></a>
<span>{{piece.quantite}}</span>
<a class="denier-plus"><i class="fa-solid fa-square-plus"></i></a>
</span>
<span class="equipement-detail" style="flex:2">
<select class="denier-conteneur">
{{#select piece.conteneurId}}
{{#each ../conteneurs as |c|}}
<option value="{{c.id}}">{{c.name}}</option>
{{/each}}
{{/select}}
</select>
</span>
<span class="item-controls">
<a class="denier-edit" data-tooltip="Éditer"><i class="fa-solid fa-edit"></i></a>
</span>
</li>
{{/each}}
</ul>
</div>
+42
View File
@@ -0,0 +1,42 @@
<div>
<ul class="item-list alterne-list">
<li class="competence-header flexrow" style="padding:0 3px">
<span class="equipement-nom" style="padding-left:calc(24px + 0.2rem)">Nom</span>
<span class="equipement-detail">Catégorie</span>
<span class="equipement-detail">Niveau</span>
<span class="equipement-detail">Quantité</span>
<span class="equipement-detail" style="flex:2">Conteneur</span>
<span class="item-actions-controls item-controls">Actions</span>
</li>
{{#each herbes as |herbe|}}
<li class="item flexrow list-item" data-item-id="{{herbe.id}}">
<span class="equipement-nom">
<img class="sheet-competence-img" src="{{herbe.img}}" data-tooltip="{{herbe.name}}"/>
<span>{{herbe.name}}</span>
</span>
<span class="equipement-detail">{{herbe.categorie}}</span>
<span class="equipement-detail">{{herbe.niveau}}</span>
<span class="equipement-detail" style="gap:0.125rem">
<a class="herbe-moins"><i class="fa-solid fa-square-minus"></i></a>
<span>{{herbe.quantite}}</span>
<a class="herbe-plus"><i class="fa-solid fa-square-plus"></i></a>
</span>
<span class="equipement-detail" style="flex:2">
<select class="herbe-conteneur">
{{#select herbe.conteneurId}}
{{#each ../conteneurs as |c|}}
<option value="{{c.id}}">{{c.name}}</option>
{{/each}}
{{/select}}
</select>
</span>
<span class="item-controls">
{{#if herbe.isHerbeAPotion}}
<a class="herbe-decoction" data-tooltip="Décoction"><i class="fa-solid fa-flask-vial"></i></a>
{{/if}}
<a class="herbe-edit" data-tooltip="Éditer"><i class="fa-solid fa-edit"></i></a>
</span>
</li>
{{/each}}
</ul>
</div>
@@ -0,0 +1,37 @@
<div>
<ul class="item-list alterne-list">
<li class="competence-header flexrow" style="padding:0 3px">
<span class="equipement-nom" style="padding-left:calc(24px + 0.2rem)">Nom</span>
<span class="equipement-detail">Catégorie</span>
<span class="equipement-detail">Quantité</span>
<span class="equipement-detail" style="flex:2">Conteneur</span>
<span class="item-actions-controls item-controls">Actions</span>
</li>
{{#each ingredients as |ing|}}
<li class="item flexrow list-item" data-item-id="{{ing.id}}">
<span class="equipement-nom">
<img class="sheet-competence-img" src="{{ing.img}}" data-tooltip="{{ing.name}}"/>
<span>{{ing.name}}</span>
</span>
<span class="equipement-detail">{{ing.categorie}}</span>
<span class="equipement-detail" style="gap:0.125rem">
<a class="ingredient-moins"><i class="fa-solid fa-square-minus"></i></a>
<span>{{ing.quantite}}</span>
<a class="ingredient-plus"><i class="fa-solid fa-square-plus"></i></a>
</span>
<span class="equipement-detail" style="flex:2">
<select class="ingredient-conteneur">
{{#select ing.conteneurId}}
{{#each ../conteneurs as |c|}}
<option value="{{c.id}}">{{c.name}}</option>
{{/each}}
{{/select}}
</select>
</span>
<span class="item-controls">
<a class="ingredient-edit" data-tooltip="Éditer"><i class="fa-solid fa-edit"></i></a>
</span>
</li>
{{/each}}
</ul>
</div>
+38
View File
@@ -0,0 +1,38 @@
<div>
<ul class="item-list alterne-list">
<li class="competence-header flexrow" style="padding:0 3px">
<span class="equipement-nom" style="padding-left:calc(24px + 0.2rem)">Nom</span>
<span class="equipement-detail">Catégorie</span>
<span class="equipement-detail">Quantité</span>
<span class="equipement-detail" style="flex:2">Conteneur</span>
<span class="item-actions-controls item-controls">Actions</span>
</li>
{{#each potions as |p|}}
<li class="item flexrow list-item" data-item-id="{{p.id}}">
<span class="equipement-nom">
<img class="sheet-competence-img" src="{{p.img}}" data-tooltip="{{p.name}}"/>
<span>{{p.name}}</span>
</span>
<span class="equipement-detail">{{p.categorie}}</span>
<span class="equipement-detail" style="gap:0.125rem">
<a class="potion-moins"><i class="fa-solid fa-square-minus"></i></a>
<span>{{p.quantite}}</span>
<a class="potion-plus"><i class="fa-solid fa-square-plus"></i></a>
</span>
<span class="equipement-detail" style="flex:2">
<select class="potion-conteneur">
{{#select p.conteneurId}}
{{#each ../conteneurs as |c|}}
<option value="{{c.id}}">{{c.name}}</option>
{{/each}}
{{/select}}
</select>
</span>
<span class="item-controls">
<a class="potion-consommer" data-tooltip="Consommer"><i class="fa-solid fa-vial"></i></a>
<a class="potion-edit" data-tooltip="Éditer"><i class="fa-solid fa-edit"></i></a>
</span>
</li>
{{/each}}
</ul>
</div>
+18 -4
View File
@@ -13,23 +13,37 @@
</a>
{{else}}
<img class="sheet-competence-img" src="{{item.img}}"/>
<span>{{item.name}}{{#if item.system.magique}} <i class="fa-solid fa-sparkles" data-tooltip="Enchantement"></i>{{/if}}</span>
<a class="item-edit">{{item.name}}{{#if item.system.magique}} <i class="fa-solid fa-sparkles" data-tooltip="Enchantement"></i>{{/if}}</a>
{{#if (eq item.type 'arme')}}
{{>"systems/foundryvtt-reve-de-dragon/templates/item/icon-arme-broken.hbs" item}}
{{/if}}
</span>
{{/if}}
{{#if item.parentConteneurName}}
<span class="conteneur-parent-badge">{{item.parentConteneurName}}</span>
{{/if}}
</span>
<span class="equipement-detail">
{{#if (gt item.system.quantite 1)}}
<a class="item-quantite-moins" data-tooltip="Diminuer la quantité de {{item.name}}"><i class="fa-solid fa-square-minus"></i></a>
{{/if}}
{{item.system.quantite}}
<input class="item-quantite number-x2" type="number" data-dtype="Number"
value="{{item.system.quantite}}" data-item-id="{{item._id}}" />
{{#if @root.options.isGM}}
<a class="item-quantite-plus" data-tooltip="Augmenter la quantité de {{item.name}}"><i class="fa-solid fa-square-plus"></i></a>
{{/if}}
</span>
<span class="equipement-detail">{{numberFormat item.system.encTotal decimals=2}}</span>
<span class="equipement-detail {{#if sature}}conteneur-sature{{/if}}">
{{#if (eq item.type 'conteneur')}}
{{numberFormat item.system.encTotal decimals=2}} / {{item.system.capacite}}
{{else}}
{{numberFormat item.system.encTotal decimals=2}}
{{/if}}
{{#if (eq item.type 'conteneur')}}
<div class="conteneur-capacity-bar">
<div class="conteneur-capacity-fill" style="width: {{fillPercent}}%"></div>
</div>
{{/if}}
</span>
{{>'systems/foundryvtt-reve-de-dragon/templates/actor/item-action-controls.hbs' item=item options=options}}
</li>
{{/if}}
+9 -29
View File
@@ -1,29 +1,9 @@
{{#if @root.options.isObserver}}
<span class="item-name"><h4>Argent et Monnaies (fortune: {{calc.fortune.sols}} sols {{calc.fortune.deniers}} deniers)</h4></span>
<ul class="item-list alterne-list">
{{#each monnaies as |piece id|}}
<li class="item flexrow list-item" data-item-id="{{piece._id}}">
<img class="sheet-competence-img" src="{{piece.img}}" data-tooltip="{{piece.name}}"/>
<span class="equipement-nom {{#unless (gt piece.system.cout 0)}}field-error{{/unless}}">
{{piece.name}} ({{piece.system.cout}} Sols)
</span>
<span class="equipement-detail">
{{#if @root.options.isOwner}}
<span class="item-controls">
<a class="item-quantite-moins"><i class="fa-solid fa-square-minus"></i></a>
</span>
<span class="equipement-detail">{{piece.system.quantite}}</span>
<span class="item-controls">
<a class="item-quantite-plus"><i class="fa-solid fa-square-plus"></i></a>
</span>
{{else}}
{{piece.system.quantite}}
{{/if}}
</span>
{{#if @root.options.isOwner}}
{{>'systems/foundryvtt-reve-de-dragon/templates/actor/item-action-controls.hbs' item=signe options=@root.options}}
{{/if}}
</li>
{{/each}}
</ul>
{{/if}}
<span class="item-name">
<h4>Argent et Monnaies (fortune: {{calc.fortune.sols}} sols {{calc.fortune.deniers}} deniers)</h4>
{{#if @root.options.isOwner}}
<a class="gerer-deniers chat-card-button" data-tooltip="Gérer ses deniers">Gérer ses deniers</a>
{{#if herbes.length}}<a class="gerer-herbes chat-card-button">Gérer ses herbes</a>{{/if}}
{{#if ingredients.length}}<a class="gerer-ingredients chat-card-button">Gérer ses ingrédients</a>{{/if}}
{{#if potions.length}}<a class="gerer-potions chat-card-button">Gérer ses potions</a>{{/if}}
{{/if}}
</span>
+8 -1
View File
@@ -1,4 +1,3 @@
<h4>Equipement</h4>
<span class="item-name">
{{#if options.isOwner}}
<a class="chat-card-button creer-un-objet" data-tooltip="Créer un nouvel objet">Nouvel objet</a>
@@ -8,6 +7,14 @@
{{/if}}
<span class="embed-inline">
<input class="recherche flex-grow" type="text" value="{{options.recherche.text}}" name="recherche" size="8" data-dtype="String" placeholder="" data-tooltip="Rechercher dans l'équipement"/>
<select class="tri-equipement" data-tooltip="Trier par">
{{#select options.triEquipement}}
<option value="name">Nom</option>
<option value="type">Type</option>
<option value="enc">Enc.</option>
<option value="quantite">Quantité</option>
{{/select}}
</select>
</span>
{{#if calc.surEncombrementMessage}}<b>{{calc.surEncombrementMessage}}</b> &hyphen;{{/if}}
Encombrement: {{numberFormat calc.encTotal decimals=2}} (max: {{system.attributs.encombrement.value}})
+10
View File
@@ -0,0 +1,10 @@
<h3>Livres lus</h3>
<ul class="item-list alterne-list">
{{#each livresLus as |livre id|}}
<li class="item flexrow list-item" data-item-id="{{livre._id}}">
<img class="sheet-competence-img" src="{{livre.img}}" />
<span class="list-item-label">{{livre.name}}</span>
{{>'systems/foundryvtt-reve-de-dragon/templates/actor/item-action-controls.hbs' item=livre options=@root.options}}
</li>
{{/each}}
</ul>
+1 -1
View File
@@ -1,4 +1,4 @@
<h3>Oeuvres diverses</h3><a class="chat-card-button creer-une-oeuvre">Créer une oeuvre</a>
<h3>Oeuvres diverses</h3>
<ul class="item-list alterne-list">
{{#each (trier chants) as |chant id|}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/oeuvre.hbs" oeuvre=chant typeOeuvre="Chant" classOeuvre="action-chant list-item-label"}}
@@ -8,35 +8,35 @@
{{/if}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/sexe-aleatoire.hbs"
label="Sexe" fieldname="sexe" type="text"
value=current.system.sexe checked=checked.sexe
value=current.sexe checked=checked.sexe
}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs"
label="Age" fieldname="age" type="entier" min=10 max=100
value=current.system.age checked=checked.age
value=current.age checked=checked.age
}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs"
label="Taille" fieldname="taille" type="text"
value=current.system.taille checked=checked.taille
value=current.taille checked=checked.taille
}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs"
label="Poids" fieldname="poids" type="text"
value=current.system.poids checked=checked.poids
value=current.poids checked=checked.poids
}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs"
label="Main directrice" fieldname="main" type="text"
value=current.system.main checked=checked.main
value=current.main checked=checked.main
}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs"
label="Heure de naissance" fieldname="heure" type="heure"
value=current.system.heure checked=checked.heure
value=current.heure checked=checked.heure
}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs"
label="Cheveux" fieldname="cheveux" type="text"
value=current.system.cheveux checked=checked.cheveux
value=current.cheveux checked=checked.cheveux
}}
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/random/champ-aleatoire.hbs"
label="Yeux" fieldname="yeux" type="text"
value=current.system.yeux checked=checked.yeux
value=current.yeux checked=checked.yeux
}}
</div>
<hr>
+1 -1
View File
@@ -4,7 +4,7 @@
<section class="sheet-body">
<div class="form-group">
<label class="item-label" for="system.but">But</label>
<inputname="system.but" value="{{system.but}}" class="attribute-value" type="text" data-dtype="String"/>
<input name="system.but" value="{{system.but}}" class="attribute-value" type="text" data-dtype="String"/>
</div>
<div class="flexcol">
<span><label class="item-label">Manipulation : </label></span>