Compare commits

..

7 Commits

Author SHA1 Message Date
8fdf49bfce Message d'information affiché une seule fois toutes les 24 heures
All checks were successful
Release Creation / build (release) Successful in 10m20s
2025-12-30 10:30:14 +01:00
f897a94e60 Merge pull request '## 13.0.27 - Les lunettes d'Illysis' (#791) from feature/v13-corrections into v13
Reviewed-on: https, #791
2025-12-30 10:19:45 +01:00
e6aed2d554 Amélioration des choix d'attaque 2025-12-30 01:30:19 +01:00
0dd671d8a5 Mise à jour d'images
- ajout d'un coffre
- ajout de tissu soie
- correction d'image pour utiliser le style classique
2025-12-30 01:30:15 +01:00
a5c4303012 Les conteneurs ouverts sont mis à jour 2025-12-30 01:29:31 +01:00
ad84e36d43 Suppression timestamp doublon 2025-12-30 01:25:29 +01:00
af9ecda30f fix changelog 2025-12-30 01:25:29 +01:00
47 changed files with 128 additions and 83 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><defs><radialGradient id="skoll-open-chest-gradient-2"><stop offset="0%" stop-color="#b8e986" stop-opacity="1"></stop><stop offset="100%" stop-color="#ffe0af" stop-opacity="1"></stop></radialGradient><linearGradient x1="0" x2="0" y1="0" y2="1" id="skoll-open-chest-gradient-5"><stop offset="0%" stop-color="#417505" stop-opacity="1"></stop><stop offset="100%" stop-color="#8b572a" stop-opacity="1"></stop></linearGradient></defs><g class="" transform="translate(1,0)" style=""><path d="M457.03 213.037 416.514 100.24C425 77.232 433.27 68.075 437.527 64.633c3.162-2.563 5.922-3.534 8.185-2.904 4.134 1.168 8.775 7.7 12.278 17.456 11.266 31.347 10.377 87.094-.96 133.85zm-324.287-17.9 312.804 34.84-43.82-122.1L145.558 79.34c2.593 36.102-1.913 79.913-12.817 115.796zM128.98 77.5l-45.06-5.02 37.03 103.123c7.773-32.06 10.625-68.357 8.03-98.102zm-27.52-50.31c-3.793 3.1-10.77 10.666-18.25 28.566L402.23 91.3c5.333-13.695 11.37-24.702 17.88-32.495L108.796 24.13c-2.573-.29-5.415 1.51-7.338 3.06zm280.63 283.338.61 169.352 66.352-53.63-.61-169.35zM366.163 487.9 46.62 452.306 46 278.396l319.553 35.594zM216.726 337.648a9.777 9.777 0 0 0 5.168-9.405c-.248-6.367-5.364-12.092-11.43-12.857h-.125c-6.14-.693-10.904 3.927-10.646 10.335a13.973 13.973 0 0 0 5.767 10.543l-2.17 25.073 17.57 2.005zm219.28-91.983-307.46-34.25v59.54l242.712 27.037zM58.31 263.13l54.34 6.058v-49.98z" fill="#fff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,6 +1,16 @@
# 13.0 # 13.0
## 13.0.25 - La ménagerie d'Illysis ## 13.0.27 - Les lunettes d'Illysis
- Les heures ne sont plus affichées en doublon sur les messages
- Les conteneurs ouverts sont mis à jour si leur contenu change
- Le style des icones d'objets est homogénéisé
- Ajout d'icônes: coffre, tissu de soie, parchemin
- Amélioration des choix d'actions de combat
- le choix d'initiative limite les attaques disponibles
- les compétences sont triées dans l'ordre décroissant
## 13.0.26 - La ménagerie d'Illysis
- Correction: on peut de nouveau modifier les caractéristiques des créatures - Correction: on peut de nouveau modifier les caractéristiques des créatures

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
icons/objets/coffre.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
icons/objets/parchemin.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -255,9 +255,9 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
await super.onCreateItem(item, options, id) await super.onCreateItem(item, options, id)
} }
async onUpdateItem(item, options, id) { async onUpdateItem(item, updates, options, id) {
await this.changeItemEffects(item); await this.changeItemEffects(item);
await super.onUpdateItem(item, options, id) await super.onUpdateItem(item, updates, options, id)
} }
async onDeleteItem(item, options, id) { async onDeleteItem(item, options, id) {

View File

@@ -50,7 +50,7 @@ export class RdDBaseActor extends Actor {
Handlebars.registerHelper('actor-isFeminin', actor => actor.isFeminin()) Handlebars.registerHelper('actor-isFeminin', actor => actor.isFeminin())
Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id)) Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id)) Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
Hooks.on("updateItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onUpdateItem(item, options, id)) Hooks.on("updateItem", (item, updates, options, id) => Misc.documentIfResponsible(item.parent)?.onUpdateItem(item, updates, options, id))
Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id)) Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id))
Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId)) Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId))
} }
@@ -330,7 +330,9 @@ export class RdDBaseActor extends Actor {
async onCreateItem(item, options, id) { async onCreateItem(item, options, id) {
} }
async onUpdateItem(item, options, id) { async onUpdateItem(item, updates, options, id) {
const conteneur = item.findConteneur()
conteneur?.render(options.render)
} }
async onDeleteItem(item, options, id) { async onDeleteItem(item, options, id) {
@@ -339,9 +341,8 @@ export class RdDBaseActor extends Actor {
} }
} }
async _removeItemFromConteneur(item) { async _removeItemFromConteneur(item) {
const updates = this.items.filter(it => it.isConteneur() && it.system.contenu.includes(item.id)) const updates = this.findConteneur(item)
.map(conteneur => { .map(conteneur => {
const nouveauContenu = conteneur.system.contenu.filter(id => id != item.id) const nouveauContenu = conteneur.system.contenu.filter(id => id != item.id)
return { _id: conteneur.id, 'system.contenu': nouveauContenu } return { _id: conteneur.id, 'system.contenu': nouveauContenu }
@@ -371,7 +372,7 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async cleanupConteneurs() { async cleanupConteneurs() {
if (Misc.isOwnerPlayer(this)) { if (Misc.isOwnerPlayer(this)) {
let updates = this.itemTypes['conteneur'] let updates = this.itemTypes[ITEM_TYPES.conteneur]
.filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0) .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) { if (updates.length > 0) {
@@ -382,7 +383,7 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
getFortune() { getFortune() {
return Monnaie.getFortune(this.itemTypes['monnaie']); return Monnaie.getFortune(this.itemTypes[ITEM_TYPES.monnaie]);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -663,7 +664,7 @@ export class RdDBaseActor extends Actor {
if (srcId != destId && itemId != destId) { // déplacement de l'objet if (srcId != destId && itemId != destId) { // déplacement de l'objet
const src = this.getItem(srcId); const src = this.getItem(srcId);
const dest = this.getItem(destId); const dest = this.getItem(destId);
const cible = this.getContenantOrParent(dest); const cible = this.findConteneurOrParent(dest);
const [empilable, message] = item.isInventaireEmpilable(dest); const [empilable, message] = item.isInventaireEmpilable(dest);
if (empilable) { if (empilable) {
await dest.empiler(item) await dest.empiler(item)
@@ -685,15 +686,15 @@ export class RdDBaseActor extends Actor {
return result; return result;
} }
getContenantOrParent(dest) { findConteneurOrParent(dest) {
if (!dest || dest.isConteneur()) { if (!dest || dest.isConteneur()) {
return dest; return dest;
} }
return this.getContenant(dest); return this.findConteneur(dest)
} }
getContenant(item) { findConteneur(item) {
return this.itemTypes['conteneur'].find(it => it.system.contenu.includes(item.id)); return this.itemTypes[ITEM_TYPES.conteneur].find(it => it.system.contenu.includes(item.id));
} }

View File

@@ -215,11 +215,13 @@ export class ChatUtility {
static async onRenderChatMessage(chatMessage, html, data) { static async onRenderChatMessage(chatMessage, html, data) {
const rddTimestamp = chatMessage.getFlag(SYSTEM_RDD, 'rdd-timestamp') const rddTimestamp = chatMessage.getFlag(SYSTEM_RDD, 'rdd-timestamp')
if (rddTimestamp) { const heureRdD = $(html).find('header.message-header .heure-rdd')
if (rddTimestamp && heureRdD.length==0) {
const messageTimestamp = $(html).find('header.message-header .message-timestamp');
const timestamp = new RdDTimestamp(rddTimestamp); const timestamp = new RdDTimestamp(rddTimestamp);
const timestampData = timestamp.toCalendrier(); const timestampData = timestamp.toCalendrier();
const dateHeure = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData); const dateHeure = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData);
$(html).find('header.message-header .message-timestamp').after(dateHeure) messageTimestamp.after(dateHeure)
} }
} }
@@ -238,25 +240,4 @@ export class ChatUtility {
const date = new Date(chatMessage.timestamp); const date = new Date(chatMessage.timestamp);
return date?.toISOString().substring(0, 10) return date?.toISOString().substring(0, 10)
} }
// async flush() {
// const question = game.i18n.localize("AreYouSure");
// const warning = game.i18n.localize("CHAT.FlushWarning");
// return foundry.applications.api.DialogV2.confirm({
// window: {title: "CHAT.FlushTitle"},
// content: `<p><strong>${question}</strong> ${warning}</p>`,
// position: {
// top: window.innerHeight - 150,
// left: window.innerWidth - 720
// },
// yes: {
// callback: async () => {
// await this.documentClass.deleteDocuments([], {deleteAll: true});
// const jumpToBottomElement = document.querySelector(".jump-to-bottom");
// jumpToBottomElement.hidden = true;
// }
// }
// });
// }
} }

View File

@@ -7,8 +7,6 @@ const fields = foundry.applications.fields
export class DialogFlushByDate { export class DialogFlushByDate {
static async init() { static async init() {
Hooks.on("renderChatMessageHTML", async (app, html, msg) => await ChatUtility.onRenderChatMessage(app, html, msg))
Hooks.on("createChatMessage", async (chatMessage, options, id) => await ChatUtility.onCreateChatMessage(chatMessage, options, id))
Hooks.once("renderChatLog", async () => await DialogFlushByDate.onFirstRenderChatLog()) Hooks.once("renderChatLog", async () => await DialogFlushByDate.onFirstRenderChatLog())
} }

View File

@@ -13,7 +13,7 @@ export class Distance {
} }
return undefined return undefined
} }
switch (attaque.comp.type) { switch (attaque.comp?.type) {
case ITEM_TYPES.competence: return mapTypeAttaque(attaque.main) case ITEM_TYPES.competence: return mapTypeAttaque(attaque.main)
case ITEM_TYPES.competencecreature: return mapTypeAttaque(attaque.arme.system.categorie) case ITEM_TYPES.competencecreature: return mapTypeAttaque(attaque.arme.system.categorie)
} }

View File

@@ -81,13 +81,13 @@ export const defaultItemImg = {
souffle: "systems/foundryvtt-reve-de-dragon/icons/souffle_dragon.webp", souffle: "systems/foundryvtt-reve-de-dragon/icons/souffle_dragon.webp",
tarot: "systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp", tarot: "systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp",
tete: "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp", tete: "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp",
monnaie:"systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp", monnaie: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp",
munition: "systems/foundryvtt-reve-de-dragon/icons/objets/fleche.webp" munition: "systems/foundryvtt-reve-de-dragon/icons/objets/fleche.webp"
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RdDItem extends Item { export class RdDItem extends Item {
static get defaultIcon() { static get defaultIcon() {
return undefined; return undefined;
} }
@@ -185,6 +185,13 @@ export class RdDItem extends Item {
isCompetencePersonnage() { return this.type == ITEM_TYPES.competence } isCompetencePersonnage() { return this.type == ITEM_TYPES.competence }
isCompetenceCreature() { return this.type == ITEM_TYPES.competencecreature } isCompetenceCreature() { return this.type == ITEM_TYPES.competencecreature }
isConteneur() { return this.type == ITEM_TYPES.conteneur } isConteneur() { return this.type == ITEM_TYPES.conteneur }
findConteneur() {
if (this.isInventaire('all') && this.parent) {
return this.parent.findConteneur(this)
}
return undefined
}
isMonnaie() { return this.type == ITEM_TYPES.monnaie } isMonnaie() { return this.type == ITEM_TYPES.monnaie }
isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; } isNourritureBoisson() { return this.type == ITEM_TYPES.nourritureboisson; }
isService() { return this.type == ITEM_TYPES.service } isService() { return this.type == ITEM_TYPES.service }

View File

@@ -1,4 +1,4 @@
import { ACTOR_TYPES } from "../constants.js" import { ACTOR_TYPES, ITEM_TYPES } from "../constants.js"
import { Misc } from "../misc.js" import { Misc } from "../misc.js"
import { RdDSheetUtility } from "../rdd-sheet-utility.js" import { RdDSheetUtility } from "../rdd-sheet-utility.js"
import { RdDUtility } from "../rdd-utility.js" import { RdDUtility } from "../rdd-utility.js"
@@ -11,33 +11,35 @@ import { RdDUtility } from "../rdd-utility.js"
const _SPACEHOLDER = { placeholder: true } const _SPACEHOLDER = { placeholder: true }
const _VENDRE = { const _VENDRE = {
code: 'item-vendre', label: 'Vendre ou donner', icon: it => 'fa-solid fa-comments-dollar', code: 'item-vendre', label: 'Vendre ou donner', icon: 'fa-solid fa-comments-dollar',
filter: it => Misc.toInt(it.system.quantite) > 0 || it.parent?.type == ACTOR_TYPES.commerce, filter: it => Misc.toInt(it.system.quantite) > 0 || it.parent?.type == ACTOR_TYPES.commerce,
action: (item, actor) => item.proposerVente() action: (item, actor) => item.proposerVente()
} }
const _ACHETER = { const _ACHETER = {
code: 'item-acheter', label: 'Acheter', icon: it => 'fa-regular fa-coins', code: 'item-acheter', label: 'Acheter', icon: 'fa-regular fa-coins',
filter: it => it.parent?.type == ACTOR_TYPES.commerce, filter: it => it.parent?.type == ACTOR_TYPES.commerce,
allowLimited: true, allowLimited: true,
action: (item, actor) => actor.vente(item) action: (item, actor) => actor.vente(item)
} }
const _MONTRER = { const _MONTRER = {
code: 'item-montrer', label: 'Montrer', icon: it => 'fa-solid fa-comment', code: 'item-montrer', label: 'Montrer', icon: 'fa-solid fa-comment',
allowLimited: true, allowLimited: true,
action: (item, actor) => item.postItemToChat() action: (item, actor) => item.postItemToChat()
} }
const _SPLIT = { const _SPLIT = {
code: 'item-split', label: 'Séparer le goupe', icon: it => 'fa-solid fa-unlink', code: 'item-split', label: 'Séparer le goupe', icon: 'fa-solid fa-unlink',
filter: it => Misc.toInt(it.system.quantite) > 1 && it.parent?.type != ACTOR_TYPES.commerce, filter: it => Misc.toInt(it.system.quantite) > 1 && it.parent?.type != ACTOR_TYPES.commerce,
action: (item, actor) => RdDSheetUtility.splitItem(item, actor) action: (item, actor) => RdDSheetUtility.splitItem(item, actor)
} }
const _EDIT = { const _EDIT = {
code: 'item-edit', label: 'Editer', icon: it => 'fa-solid fa-edit', code: 'item-edit', label: it => it.type == ITEM_TYPES.conteneur ? 'Ouvrir' : 'Editer',
icon: 'fa-solid fa-edit',
action: (item, actor) => item.sheet.render(true) action: (item, actor) => item.sheet.render(true)
} }
const _DELETE = { const _DELETE = {
code: 'item-delete', label: 'Supprimer', icon: it => 'fa-solid fa-trash', code: 'item-delete', label: 'Supprimer', icon: 'fa-solid fa-trash',
optionsFilter: options => options.isOwner, optionsFilter: options => options.isOwner,
action: (item, actor) => RdDUtility.confirmActorItemDelete(item, actor) action: (item, actor) => RdDUtility.confirmActorItemDelete(item, actor)
} }
@@ -49,48 +51,48 @@ const _EQUIPER = {
const _CUISINER = { const _CUISINER = {
code: 'item-cuisiner', label: 'Cuisiner', code: 'item-cuisiner', label: 'Cuisiner',
img: it => 'systems/foundryvtt-reve-de-dragon/assets/actions/cuisine.svg', img: 'systems/foundryvtt-reve-de-dragon/assets/actions/cuisine.svg',
// icon: it => 'fa-solid fa-spoon',
filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0, filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0,
action: (item, actor) => actor.preparerNourriture(item) action: (item, actor) => actor.preparerNourriture(item)
} }
const _MANGER_CRU = { const _MANGER_CRU = {
code: 'item-manger-cru', label: 'Manger cru', icon: it => 'fa-solid fa-drumstick-bite', code: 'item-manger-cru', label: 'Manger cru', icon: 'fa-solid fa-drumstick-bite',
filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0, filter: it => it.getUtilisation() == 'cuisine' && it.system.sust > 0,
action: (item, actor) => actor.mangerNourriture(item) action: (item, actor) => actor.mangerNourriture(item)
} }
const _MANGER = { const _MANGER = {
code: 'item-manger', label: 'Manger', icon: it => 'fa-solid fa-utensils', code: 'item-manger', label: 'Manger', icon: 'fa-solid fa-utensils',
filter: it => !(it.system.boisson), filter: it => !(it.system.boisson),
action: (item, actor) => actor.mangerNourriture(item) action: (item, actor) => actor.mangerNourriture(item)
} }
const _BOIRE = { const _BOIRE = {
code: 'item-boire', label: 'Boire', icon: it => 'fa-solid fa-glass-water', code: 'item-boire', label: 'Boire', icon: 'fa-solid fa-glass-water',
filter: it => it.system.boisson, filter: it => it.system.boisson,
action: (item, actor) => actor.mangerNourriture(item) action: (item, actor) => actor.mangerNourriture(item)
} }
const _DECOCTION = { const _DECOCTION = {
code: 'item-decoction', label: 'Décoction', icon: it => 'fa-solid fa-flask-vial', code: 'item-decoction', label: 'Décoction', icon: 'fa-solid fa-flask-vial',
action: (item, actor) => actor.fabriquerDecoctionHerbe(item) action: (item, actor) => actor.fabriquerDecoctionHerbe(item)
} }
const _OUVRIR = { const _OUVRIR = {
code: 'item-edit', label: 'Ouvrir', icon: it => 'fa-solid fa-eye', code: 'item-edit', label: 'Ouvrir',
img: 'systems/foundryvtt-reve-de-dragon/assets/actions/ouvrir.svg',
action: (item, actor) => item.sheet.render(true) action: (item, actor) => item.sheet.render(true)
} }
const _LIRE = { const _LIRE = {
code: 'item-lire', label: 'Lire', icon: it => 'fa-solid fa-book-open', code: 'item-lire', label: 'Lire', icon: 'fa-solid fa-book-open',
action: (item, actor) => actor.actionLire(item) action: (item, actor) => actor.actionLire(item)
} }
const _REFOULER = { const _REFOULER = {
code: 'item-refouler', label: 'Refouler', icon: it => 'fa-solid fa-burst', code: 'item-refouler', label: 'Refouler', icon: 'fa-solid fa-burst',
filter: it => it.system.refoulement > 0, filter: it => it.system.refoulement > 0,
action: (item, actor) => actor.actionRefoulement(item) action: (item, actor) => actor.actionRefoulement(item)
} }
const _SORT_RESERVE = { const _SORT_RESERVE = {
code: 'item-sortreserve-add', label: 'Ajouter en réserve', icon: it => 'fa-solid fa-sparkles', code: 'item-sortreserve-add', label: 'Ajouter en réserve', icon: 'fa-solid fa-sparkles',
filter: it => game.user.isGM && !it.system.isrituel, filter: it => game.user.isGM && !it.system.isrituel,
action: (item, actor) => actor.addSortReserve(item) action: (item, actor) => actor.addSortReserve(item)
} }
@@ -121,20 +123,33 @@ export class ItemAction {
&& (!action.optionsFilter || action.optionsFilter(options)) && (!action.optionsFilter || action.optionsFilter(options))
} }
static label(action, item) {
static img(action, item) { if (action.placeholder) {
if (action.placeholder){
return "" return ""
} }
if (action?.img) { return ItemAction.getParam(action.label, item)
return `<img src="${action.img(item)}" />` }
static img(action, item) {
if (action.placeholder) {
return ""
} }
if (action?.icon) {
return `<i class="${action.icon(item)}"></i>` const img = ItemAction.getParam(action.img, item)
if (img) {
return `<img src="${img}" />`
}
const icon = ItemAction.getParam(action.icon, item)
if (icon) {
return `<i class="${icon}"></i>`
} }
return action.label return action.label
} }
static getParam(p, item) {
return typeof (p) == 'function' ? p(item) : p
}
static async onActionItem(event, actor, options) { static async onActionItem(event, actor, options) {
const code = $(event.currentTarget).data('code') const code = $(event.currentTarget).data('code')
const item = RdDSheetUtility.getItem(event, actor) const item = RdDSheetUtility.getItem(event, actor)

View File

@@ -70,7 +70,7 @@ export class RdDBonus {
dmgParticuliere: RdDBonus._dmgParticuliere(rollData), dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0, dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0,
mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme?.system.mortalite), mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme?.system.mortalite),
dmgActor: RdDBonus.bonusDmg(actor, attaque.carac.key, dmgArme, attaque.forceRequise), dmgActor: RdDBonus.bonusDmg(actor, attaque.carac?.key, dmgArme, attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - (attaque.forceRequise ?? 0)), dmgForceInsuffisante: Math.min(0, actor.getForce() - (attaque.forceRequise ?? 0)),
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0 dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0
} }

View File

@@ -63,6 +63,10 @@ export class RdDCombatManager extends Combat {
it.token.id == tokenId it.token.id == tokenId
) )
} }
static getRangInitiativeCombatant(actorId, tokenId) {
const combatant = RdDCombatManager.getCombatant(actorId, tokenId)
return combatant?.system.init?.rang
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async nextRound() { async nextRound() {
await this.finDeRound(); await this.finDeRound();

View File

@@ -358,6 +358,15 @@ export class SystemReveDeDragon {
}, },
default: "aucun" default: "aucun"
}) })
/* -------------------------------------------- */
game.settings.register(SYSTEM_RDD, "info-msg-timeout", {
name: "Gestion du timeout d'affichage du message d'information",
scope: "world",
config: false,
type: Number,
default: 0
})
} }
async onReady() { async onReady() {
@@ -416,7 +425,16 @@ export class SystemReveDeDragon {
<br>Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs} <br>Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}
<br>La commande <code>/aide</code> dans le chat permet de voir les commandes spécifiques à Rêve de Dragon.</div> <br>La commande <code>/aide</code> dans le chat permet de voir les commandes spécifiques à Rêve de Dragon.</div>
` }) ` })
// Try to fetch the welcome message from the github repo "welcome-message-ecryme.html" // Get last message display time
const lastDisplay = game.settings.get(SYSTEM_RDD, "info-msg-timeout") || 0
const now = Date.now()
const oneDayMs = 24 * 60 * 60 * 1000
if (now - lastDisplay < oneDayMs) {
return // Already displayed in the last 24 hours
}
// Update last display time
game.settings.set(SYSTEM_RDD, "info-msg-timeout", now)
// Try to fetch the welcome message from the github repo RDD_INFO_MESSAGE_URL
fetch(RDD_INFO_MESSAGE_URL) fetch(RDD_INFO_MESSAGE_URL)
.then(response => response.text()) .then(response => response.text())
.then(html => { .then(html => {

View File

@@ -93,7 +93,7 @@ export class RdDSheetUtility {
static async renderItemBranch(actor, item) { static async renderItemBranch(actor, item) {
while (item) { while (item) {
await item.sheet?.render() await item.sheet?.render()
item = actor.getContenant(item) item = actor.findConteneur(item)
} }
} }
} }

View File

@@ -348,6 +348,7 @@ export class RdDUtility {
// Items // Items
Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field)); Handlebars.registerHelper('rarete-getChamp', (rarete, field) => RdDRaretes.getChamp(rarete, field));
Handlebars.registerHelper('item-action-applies', (action, item, options) => ItemAction.applies(action, item, options)) Handlebars.registerHelper('item-action-applies', (action, item, options) => ItemAction.applies(action, item, options))
Handlebars.registerHelper('item-action-label', (action, item) => new Handlebars.SafeString(ItemAction.label(action, item)))
Handlebars.registerHelper('item-action-img', (action, item) => new Handlebars.SafeString(ItemAction.img(action, item))) Handlebars.registerHelper('item-action-img', (action, item) => new Handlebars.SafeString(ItemAction.img(action, item)))
Handlebars.registerHelper('item-name', (item) => item.nameDisplay) Handlebars.registerHelper('item-name', (item) => item.nameDisplay)

View File

@@ -1,8 +1,11 @@
import { Distance } from "../combat/distance.mjs" import { Distance } from "../combat/distance.mjs"
import { RDD_CONFIG } from "../constants.js" import { RDD_CONFIG } from "../constants.js"
import { MAP_PHASE, RdDInitiative } from "../initiative.mjs"
import { ATTAQUE_TYPE_MELEE } from "../item/arme.js" import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { Misc } from "../misc.js"
import { RdDBonus } from "../rdd-bonus.js" import { RdDBonus } from "../rdd-bonus.js"
import { CARACS } from "../rdd-carac.js" import { CARACS } from "../rdd-carac.js"
import { RdDCombatManager } from "../rdd-combat.js"
import { RdDEmpoignade } from "../rdd-empoignade.js" import { RdDEmpoignade } from "../rdd-empoignade.js"
import { DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP } from "./roll-constants.mjs" import { DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP } from "./roll-constants.mjs"
import RollDialog from "./roll-dialog.mjs" import RollDialog from "./roll-dialog.mjs"
@@ -17,9 +20,9 @@ export const PART_ATTAQUE = 'attaque'
const TACTIQUES = RdDBonus.tactiques.filter(it => it.isTactique) const TACTIQUES = RdDBonus.tactiques.filter(it => it.isTactique)
const FILTER_ATTAQUE_EMPOIGNADE = attaque => attaque.arme.isEmpoignade() const FILTER_ATTAQUE_EMPOIGNADE = attaque => attaque.arme?.isEmpoignade()
const FILTER_ATTAQUE_NON_EMPOIGNADE = attaque => !attaque.arme.isEmpoignade() const FILTER_ATTAQUE_RANG = rang => attaque => !attaque.arme?.isEmpoignade() && (attaque.rang == rang || rang == undefined)
const FILTER_ATTAQUE_EMPOIGNE = attaque => attaque.arme.isUtilisableEmpoigne() && ATTAQUE_TYPE_MELEE.includes(attaque.main) const FILTER_ATTAQUE_EMPOIGNE = attaque => attaque.arme?.isUtilisableEmpoigne() && ATTAQUE_TYPE_MELEE.includes(attaque.main)
export class RollPartAttaque extends RollPartSelect { export class RollPartAttaque extends RollPartSelect {
@@ -33,8 +36,9 @@ export class RollPartAttaque extends RollPartSelect {
loadRefs(rollData) { loadRefs(rollData) {
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
const attaques = rollData.active.actor.listAttaques() const attaques = rollData.active.actor.listAttaques()
.sort(Misc.descending(it => it.comp?.system.niveau ?? -8))
refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData)) refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData))
this.filterAttaquesEmpoignade(rollData) this.filterAttaquesInitiative(rollData)
refs.tactiques = TACTIQUES refs.tactiques = TACTIQUES
if (refs.attaques.length > 0) { if (refs.attaques.length > 0) {
const attaque = this.findAttaque(refs.attaques, this.getSaved(rollData)) const attaque = this.findAttaque(refs.attaques, this.getSaved(rollData))
@@ -78,24 +82,27 @@ export class RollPartAttaque extends RollPartSelect {
attaque.tactique = TACTIQUES[0] attaque.tactique = TACTIQUES[0]
attaque.initialDiff = attaque.comp?.system.default_diffLibre ?? 0 attaque.initialDiff = attaque.comp?.system.default_diffLibre ?? 0
attaque.distance = Distance.ajustements(rollData.active?.token, rollData.opponent?.token, attaque) attaque.distance = Distance.ajustements(rollData.active?.token, rollData.opponent?.token, attaque)
attaque.rang = RdDInitiative.phaseArme(attaque.comp?.system.categorie, attaque.arme)?.rang
return attaque return attaque
} }
prepareContext(rollData) { prepareContext(rollData) {
this.filterAttaquesEmpoignade(rollData) this.filterAttaquesInitiative(rollData)
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
current.dmg = RdDBonus.dmgRollV2(rollData, current) current.dmg = RdDBonus.dmgRollV2(rollData, current)
} }
filterAttaquesEmpoignade(rollData) { filterAttaquesInitiative(rollData) {
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
const isEmpoignade = RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId) const rang = RdDCombatManager.getRangInitiativeCombatant(rollData.ids.actorId, rollData.ids.actorTokenId)
const isEmpoignade = MAP_PHASE.empoignade.rang == rang
//const isEmpoignade = RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
refs.isEmpoignadeEnCours = RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor) refs.isEmpoignadeEnCours = RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor)
const filterAttaques = isEmpoignade ? const filterAttaques = isEmpoignade ?
FILTER_ATTAQUE_EMPOIGNADE FILTER_ATTAQUE_EMPOIGNADE
: refs.isEmpoignadeEnCours : refs.isEmpoignadeEnCours
? FILTER_ATTAQUE_EMPOIGNE ? FILTER_ATTAQUE_EMPOIGNE
: FILTER_ATTAQUE_NON_EMPOIGNADE : FILTER_ATTAQUE_RANG(rang)
refs.attaques = refs.all.filter(filterAttaques) refs.attaques = refs.all.filter(filterAttaques)
} }
@@ -168,8 +175,8 @@ export class RollPartAttaque extends RollPartSelect {
if (this.visible(rollData)) { if (this.visible(rollData)) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
switch (part.code) { switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [current.carac.key]) case PART_CARAC: return part.filterCaracs(rollData, [current.carac?.key])
case PART_COMP: return part.filterComps(rollData, [current.comp.name]) case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
case PART_DIFF: { case PART_DIFF: {
if (Distance.typeAttaqueDistance(current)) { if (Distance.typeAttaqueDistance(current)) {
part.setDiff(rollData, { type: DIFF.DEFAUT }) part.setDiff(rollData, { type: DIFF.DEFAUT })

View File

@@ -2,7 +2,7 @@
{{#each item.actions as |action|}} {{#each item.actions as |action|}}
{{#if action.placeholder}}&nbsp; {{#if action.placeholder}}&nbsp;
{{else if (item-action-applies action ../item ../options)}} {{else if (item-action-applies action ../item ../options)}}
<a class="actionItem" data-tooltip="{{action.label}}" data-code="{{action.code}}"> <a class="actionItem" data-tooltip="{{item-action-label action ../item}}" data-code="{{action.code}}">
{{item-action-img action ../item}} {{item-action-img action ../item}}
</a> </a>
{{/if}} {{/if}}

View File

@@ -1,3 +1,3 @@
<span class="message-metadata heure-rdd"> <span class="heure-rdd">
{{this.jourDuMois}} {{this.mois.label}} {{timestamp-imgSigne this.heure}} {{this.jourDuMois}} {{this.mois.label}} {{timestamp-imgSigne this.heure}}
</span> </span>

View File

@@ -1,3 +1,4 @@
{{#if current.key}}
<subline> <subline>
<select name="select-attaque" {{#if rollData.type.retry}}disabled{{/if}}> <select name="select-attaque" {{#if rollData.type.retry}}disabled{{/if}}>
{{selectOptions refs.attaques selected=current.key valueAttr="key" labelAttr="label"}} {{selectOptions refs.attaques selected=current.key valueAttr="key" labelAttr="label"}}
@@ -85,4 +86,5 @@
</ul> </ul>
</subline> </subline>
{{/if}} {{/if}}
</roll-part-detail> </roll-part-detail>
{{/if}}